[
  {
    "path": ".gitignore",
    "content": "*.pyc\ngitfiti.sh\ngitfiti.ps1\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: python\npython:\n  - \"2.7\"\n  - \"3.4\"\n  - \"3.5\"\n  - \"3.5-dev\"\n  - \"nightly\"\ninstall:\n  - \"pip install pytest\"\nscript:\n  - \"py.test tests\"\nsudo: false\nnotifications:\n  email: false\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2013 Eric Romano (@gelstudios).\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE"
  },
  {
    "path": "README.md",
    "content": "[![Build Status](https://travis-ci.org/gelstudios/gitfiti.svg?branch=master)](https://travis-ci.org/gelstudios/gitfiti)\n\n**gitfiti** _noun_ : Carefully crafted graffiti in a github commit history calendar.  \n\nAn example of gitfiti in the wild:  \n![screenshot of gitfiti](https://raw.github.com/gelstudios/gitfiti/master/gitfiti-screenshot.png \"screenshot\")\n\n`gitfiti.py` is a tool to decorate your github account's commit history calendar by (blatantly) abusing git's ability to accept commits _in the past_.\n\nHow? `gitfiti.py` generates a script (powershell or bash) that makes commits with the GIT_AUTHOR_DATE and GIT_COMMITTER_DATE environment variables set for each targeted pixel.\n\nSince this is likely to clobber repo's history, it is highly recommend that you create a _new_ github repo when using gitfiti. Also, the generated script assumes you are using public-key authentication with git.\n\n### Pixel Art\n\n![pixel art examples](https://raw.github.com/gelstudios/gitfiti/master/pixels-large.png \"pixel art\")  \nIncluded \"art\" from left to right: kitty, oneup, oneup2, hackerschool, octocat, octocat2\n\n### Usage\n\n1. Create a new github repo to store your handiwork.\n2. Run `gitfiti.py` and follow the prompts for username, art selection, offset, and repo name.\n\nFor Python 3, use `python3`.\n\n```console\n    $ python3 ./gitfiti.py\n\n              _ __  _____ __  _\n       ____ _(_) /_/ __(_) /_(_)\n      / __ `/ / __/ /_/ / __/ /\n     / /_/ / / /_/ __/ / /_/ /\n     \\__, /_/\\__/_/ /_/\\__/_/\n    /____/\n\n    Enter GitHub URL (leave blank to use https://github.com/):\n```\n\nFor Python 2, use `python2`.\n\n```console\n    $ python2 ./gitfiti.py\n\n              _ __  _____ __  _\n       ____ _(_) /_/ __(_) /_(_)\n      / __ `/ / __/ /_/ / __/ /\n     / /_/ / / /_/ __/ / /_/ /\n     \\__, /_/\\__/_/ /_/\\__/_/\n    /____/\n\n    Enter GitHub URL (leave blank to use https://github.com/):\n```\n\n3. Run the generated `gitfiti.sh` or `gitfiti.ps1` from your home directory (or any non-git tracked dir) and watch it go to work.\n4. Wait... Seriously, you'll probably need to wait a day or two for the gitfiti to show in your commit graph.\n\n### User Templates\n\nThe file format for personal templates is the following:\n\n1. Each template starts off with a \":\" and then a name (eg. \":foo\")\n2. Each line after that is part of a json-recognizable array.\n3. The array contain values 0-4, 0 being blank and 4 being dark green.\n4. To add multiple templates, just add another name tag as described in 1.\n\nFor example:\n\n```\n:center-blank\n[[1,1,1,1,1,1,1],\n[1,1,1,1,1,1,1],\n[1,1,1,1,1,1,1],\n[1,1,1,0,1,1,1],\n[1,1,1,1,1,1,1],\n[1,1,1,1,1,1,1],\n[1,1,1,1,1,1,1]]\n```\n\nThis would output a 7 x 7 light green square with a single blank center square.\n\nOnce you have a file with templates, enter its name when prompted and the templates will be added to the list of options.\n\n### Removal\n\nFortunately if you regret your gitfiti in the morning, removing it is fairly easy: delete the repo you created for your gitfiti (and wait).\n\n### License\n\ngitfiti is released under [The MIT license (MIT)](http://opensource.org/licenses/MIT)\n\n---\n\n#### Todo\n\n- ~~Remove 'requests' dependency~~ [_thanks empathetic-alligator_](https://github.com/empathetic-alligator)\n- ~~Web interface~~ See several web-based things below\n- ~~Load \"art\" from a file~~ [_thanks empathetic-alligator_](https://github.com/empathetic-alligator)\n- Load commit content from a file\n- Text/alphabet option\n- ~~powershell support!~~ [_thanks axzn_](https://github.com/axzn)\n- ...\n- Profit?\n\n#### Notable derivatives or mentions\n\n- [Vincent Van Git](https://github.com/jh3y/vincent-van-git) Vincent, which offers a [very slick web ui](https://vincent-van-git.netlify.app/) to generate a gitfiti script\n- [github-calendar-customerizer](https://github.com/ZachSaucier/github-calendar-customizer) from ZachSaucier, another very [nice web GUI](https://codepen.io/ZachSaucier/full/PzVRBy) for generating gitfiti templates\n- [git-art](https://github.com/jamesjarvis/git-art) from jamesjarvis, a work-alike web based [editor GUI](https://jamesjarvis.github.io/git-art/) that generates the script too\n- [Pikesley's](https://github.com/pikesley) Pokrovsky, which offers Github History Vandalism [as a Service!](http://pokrovsky.herokuapp.com/)\n- [PSVandalism](https://github.com/DenisBalan/PSVandalism) Wrapper around Pokrovsky, which makes possible vandalising Github History from Powershell\n- [github-board](https://github.com/bayandin/github-board) commits gitfiti from easy templates\n- [ghdecoy](https://github.com/tickelton/ghdecoy) fills the contribution graph with random data (sneaky!)\n- [Gitfiti Painter](http://codepen.io/cbas/pen/vOXeKV) visual drawing tool for artists to easily create templates\n- [git-draw](https://github.com/ben174/git-draw) a Chrome extension which will allow you to freely draw on your commit map(!)\n- [github-jack](https://github.com/tardypad/github-jack) a pure bash version with space invaders and shining creepypasta\n- [github-graffiti](https://github.com/mavrk/github-graffiti) a GUI editor with a bash script to allow custom designs on your commit map\n- [Paint GitHub](https://paintgithub.com/) is the most convenient way to paint your GitHub contribution graph!\n- [contribution-pixel-messages](https://github.com/abulvenz/contribution-pixel-messages) generates a date plan from an editable GUI\n- Seen something else? Submit a pull request or open an issue!\n"
  },
  {
    "path": "gitfiti.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (c) 2013 Eric Romano (@gelstudios)\n# released under The MIT license (MIT) http://opensource.org/licenses/MIT\n#\n\"\"\"\ngitfiti\n\nnoun : Carefully crafted graffiti in a GitHub commit history calendar\n\"\"\"\n\nfrom datetime import datetime, timedelta\nimport itertools\nimport json\nimport math\nimport os\ntry:\n    # Python 3+\n    from urllib.error import HTTPError, URLError\n    from urllib.request import urlopen\nexcept ImportError:\n    # Python 2\n    from urllib2 import HTTPError, URLError, urlopen\n\ntry:\n    # Python 2\n    raw_input\nexcept NameError:\n    # Python 3 (Python 2's `raw_input` was renamed to `input`)\n    raw_input = input\n\n\nGITHUB_BASE_URL = 'https://github.com/'\nFALLBACK_IMAGE = 'kitty'\n\n\nTITLE = '''\n          _ __  _____ __  _\n   ____ _(_) /_/ __(_) /_(_)\n  / __ `/ / __/ /_/ / __/ /\n / /_/ / / /_/ __/ / /_/ /\n \\__, /_/\\__/_/ /_/\\__/_/\n/____/\n'''\n\n\nKITTY = [\n  [0,0,0,4,0,0,0,0,4,0,0,0],\n  [0,0,4,2,4,4,4,4,2,4,0,0],\n  [0,0,4,2,2,2,2,2,2,4,0,0],\n  [2,2,4,2,4,2,2,4,2,4,2,2],\n  [0,0,4,2,2,3,3,2,2,4,0,0],\n  [2,2,4,2,2,2,2,2,2,4,2,2],\n  [0,0,0,3,4,4,4,4,3,0,0,0],\n]\n\nONEUP = [\n  [0,4,4,4,4,4,4,4,0],\n  [4,3,2,2,1,2,2,3,4],\n  [4,2,2,1,1,1,2,2,4],\n  [4,3,4,4,4,4,4,3,4],\n  [4,4,1,4,1,4,1,4,4],\n  [0,4,1,1,1,1,1,4,0],\n  [0,0,4,4,4,4,4,0,0],\n]\n\nONEUP2 = [\n  [0,0,4,4,4,4,4,4,4,0,0],\n  [0,4,2,2,1,1,1,2,2,4,0],\n  [4,3,2,2,1,1,1,2,2,3,4],\n  [4,3,3,4,4,4,4,4,3,3,4],\n  [0,4,4,1,4,1,4,1,4,4,0],\n  [0,0,4,1,1,1,1,1,4,0,0],\n  [0,0,0,4,4,4,4,4,0,0,0],\n]\n\nHACKERSCHOOL = [\n  [4,4,4,4,4,4],\n  [4,3,3,3,3,4],\n  [4,1,3,3,1,4],\n  [4,3,3,3,3,4],\n  [4,4,4,4,4,4],\n  [0,0,4,4,0,0],\n  [4,4,4,4,4,4],\n]\n\nOCTOCAT = [\n  [0,0,0,4,0,0,0,4,0],\n  [0,0,4,4,4,4,4,4,4],\n  [0,0,4,1,3,3,3,1,4],\n  [4,0,3,4,3,3,3,4,3],\n  [0,4,0,0,4,4,4,0,0],\n  [0,0,4,4,4,4,4,4,4],\n  [0,0,4,0,4,0,4,0,4],\n]\n\nOCTOCAT2 = [\n  [0,0,4,0,0,4,0],\n  [0,4,4,4,4,4,4],\n  [0,4,1,3,3,1,4],\n  [0,4,4,4,4,4,4],\n  [4,0,0,4,4,0,0],\n  [0,4,4,4,4,4,0],\n  [0,0,0,4,4,4,0],\n]\n\nHELLO = [\n  [0,1,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,4],\n  [0,2,0,0,0,0,0,0,0,2,0,2,0,0,0,0,0,4],\n  [0,3,3,3,0,2,3,3,0,3,0,3,0,1,3,1,0,3],\n  [0,4,0,4,0,4,0,4,0,4,0,4,0,4,0,4,0,3],\n  [0,3,0,3,0,3,3,3,0,3,0,3,0,3,0,3,0,2],\n  [0,2,0,2,0,2,0,0,0,2,0,2,0,2,0,2,0,0],\n  [0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,4],\n]\n\nHEART1 = [\n  [0,1,1,0,1,1,0],\n  [1,3,3,1,3,3,1],\n  [1,3,4,3,4,3,1],\n  [1,3,4,4,4,3,1],\n  [0,1,3,4,3,1,0],\n  [0,0,1,3,1,0,0],\n  [0,0,0,1,0,0,0],        \n]\n\nHEART2 = [\n  [0,5,5,0,5,5,0],\n  [5,3,3,5,3,3,5],\n  [5,3,1,3,1,3,5],\n  [5,3,1,1,1,3,5],\n  [0,5,3,1,3,5,0],\n  [0,0,5,3,5,0,0],\n  [0,0,0,5,0,0,0],        \n]\n\nHIREME = [\n  [1,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\n  [2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\n  [3,3,3,0,2,0,3,3,3,0,2,3,3,0,0,3,3,0,3,0,0,2,3,3],\n  [4,0,4,0,4,0,4,0,0,0,4,0,4,0,0,4,0,4,0,4,0,4,0,4],\n  [3,0,3,0,3,0,3,0,0,0,3,3,3,0,0,3,0,3,0,3,0,3,3,3],\n  [2,0,2,0,2,0,2,0,0,0,2,0,0,0,0,2,0,2,0,2,0,2,0,0],\n  [1,0,1,0,1,0,1,0,0,0,1,1,1,0,0,1,0,1,0,1,0,1,1,1],\n]\n\nBEER = [\n  [0,0,0,0,0,0,0,3,3,3,0,0,3,3,3,0,3,3,3,0,3,3,3,0,0],\n  [0,0,1,1,1,1,0,3,0,0,3,0,3,0,0,0,3,0,0,0,3,0,0,3,0],\n  [0,2,2,2,2,2,0,3,0,0,3,0,3,0,0,0,3,0,0,0,3,0,0,3,0],\n  [2,0,2,2,2,2,0,3,3,3,0,0,3,3,3,0,3,3,3,0,3,3,3,0,0],\n  [2,0,2,2,2,2,0,3,0,0,3,0,3,0,0,0,3,0,0,0,3,0,3,0,0],\n  [0,2,2,2,2,2,0,3,0,0,3,0,3,0,0,0,3,0,0,0,3,0,0,3,0],\n  [0,0,2,2,2,2,0,3,3,3,0,0,3,3,3,0,3,3,3,0,3,0,0,3,0],\n]\n\nGLIDERS = [\n  [0,0,0,4,0,4,0,0,0,0,4,0,0,0],\n  [0,4,0,4,0,0,4,4,0,0,0,4,0,0],\n  [0,0,4,4,0,4,4,0,0,4,4,4,0,0],\n  [0,0,0,0,0,0,0,0,0,0,0,0,0,0],\n  [0,4,0,4,0,0,0,4,0,0,0,0,0,0],\n  [0,0,4,4,0,4,0,4,0,0,0,0,0,0],\n  [0,0,4,0,0,0,4,4,0,0,0,0,0,0],\n]\n\nHEART = [\n  [0,4,4,0,4,4,0],\n  [4,2,2,4,2,2,4],\n  [4,2,2,2,2,2,4],\n  [4,2,2,2,2,2,4],\n  [0,4,2,2,2,4,0],\n  [0,0,4,2,4,0,0],\n  [0,0,0,4,0,0,0],\n]\n\nHEART_SHINY = [\n  [0,4,4,0,4,4,0],\n  [4,2,0,4,2,2,4],\n  [4,0,2,2,2,2,4],\n  [4,2,2,2,2,2,4],\n  [0,4,2,2,2,4,0],\n  [0,0,4,2,4,0,0],\n  [0,0,0,4,0,0,0],\n]\n\nASCII_TO_NUMBER = {\n  '_': 0,\n  '_': 1,\n  '~': 2,\n  '=': 3,\n  '*': 4,\n}\n\n\ndef str_to_sprite(content):\n    # Break out lines and filter any excess\n    lines = content.split('\\n')\n    def is_empty_line(line):\n        return len(line) != 0\n    lines = filter(is_empty_line, lines)\n\n    # Break up lines into each character\n    split_lines = [list(line) for line in lines]\n\n    # Replace each character with its numeric equivalent\n    for line in split_lines:\n        for index, char in enumerate(line):\n            line[index] = ASCII_TO_NUMBER.get(char, 0)\n\n    # Return the formatted str\n    return split_lines\n\n\nONEUP_STR = str_to_sprite('''\n *******\n*=~~-~~=*\n*~~---~~*\n*=*****=*\n**-*-*-**\n *-----*\n  *****\n''')\n\n\nIMAGES = {\n  'kitty': KITTY,\n  'oneup': ONEUP,\n  'oneup2': ONEUP2,\n  'hackerschool': HACKERSCHOOL,\n  'octocat': OCTOCAT,\n  'octocat2': OCTOCAT2,\n  'hello': HELLO,\n  'heart1': HEART1,\n  'heart2': HEART2,\n  'hireme': HIREME,\n  'oneup_str': ONEUP_STR,\n  'beer': BEER,\n  'gliders': GLIDERS,\n  'heart' : HEART, \n  'heart_shiny' : HEART_SHINY,\n}\n\nSHELLS = {\n  'bash': 'sh',\n  'powershell': 'ps1',\n}\n\ndef load_images(img_names):\n    \"\"\"loads user images from given file(s)\"\"\"\n    if img_names[0] == '':\n        return {}\n\n    for image_name in img_names:\n        with open(image_name) as img:\n            loaded_imgs = {}\n            img_list = ''\n            img_line = ' '\n            name = img.readline().replace('\\n', '')\n            name = name[1:]\n\n            while True:\n                img_line = img.readline()\n                if img_line == '':\n                    break\n\n                img_line.replace('\\n', '')\n                if img_line[0] == ':':\n                    loaded_imgs[name] = json.loads(img_list)\n                    name = img_line[1:]\n                    img_list = ''\n                else:\n                    img_list += img_line\n\n            loaded_imgs[name] = json.loads(img_list)\n\n            return loaded_imgs\n\n\ndef retrieve_contributions_calendar(username, base_url):\n    \"\"\"retrieves the GitHub commit calendar data for a username\"\"\"\n    base_url = base_url + 'users/' + username\n\n    try:\n        url = base_url + '/contributions'\n        page = urlopen(url)\n    except (HTTPError, URLError) as e:\n        print('There was a problem fetching data from {0}'.format(url))\n        print(e)\n        raise SystemExit\n\n    return page.read().decode('utf-8')\n\n\ndef parse_contributions_calendar(contributions_calendar):\n    \"\"\"Yield daily counts extracted from the embedded contributions SVG.\"\"\"\n    for line in contributions_calendar.splitlines():\n        # a valid line looks like this:\n        # <rect width=\"11\" height=\"11\" x=\"-31\" y=\"0\" class=\"ContributionCalendar-day\" data-date=\"2023-02-26\" data-level=\"3\" rx=\"2\" ry=\"2\">23 contributions on Sunday, February 26, 2023</rect>\n        if 'data-date=' in line:\n            commit = line.split('>')[1].split()[0] # yuck\n\n            if commit.isnumeric():\n                yield int(commit)\n\n\ndef find_max_daily_commits(contributions_calendar):\n    \"\"\"finds the highest number of commits in one day\"\"\"\n    daily_counts = parse_contributions_calendar(contributions_calendar)\n    return max(daily_counts, default=0)\n\n\ndef calculate_multiplier(max_commits):\n    \"\"\"calculates a multiplier to scale GitHub colors to commit history\"\"\"\n    m = max_commits / 4.0\n\n    if m == 0:\n        return 1\n\n    m = math.ceil(m)\n    m = int(m)\n    return m\n\n\ndef get_start_date():\n    \"\"\"returns a datetime object for the first sunday after one year ago today\n    at 12:00 noon\"\"\"\n    today = datetime.today()\n    date = datetime(today.year - 1, today.month, today.day, 12)\n    weekday = datetime.weekday(date)\n\n    while weekday < 6:\n        date = date + timedelta(1)\n        weekday = datetime.weekday(date)\n\n    return date\n\n\ndef generate_next_dates(start_date, offset=0):\n    \"\"\"generator that returns the next date, requires a datetime object as\n    input. The offset is in weeks\"\"\"\n    start = offset * 7\n    for i in itertools.count(start):\n        yield start_date + timedelta(i)\n\n\ndef generate_values_in_date_order(image, multiplier=1):\n    height = 7\n    width = len(image[0])\n\n    for w in range(width):\n        for h in range(height):\n            yield image[h][w] * multiplier\n\n\ndef commit(commitdate, shell):\n    template_bash = (\n        '''GIT_AUTHOR_DATE={0} GIT_COMMITTER_DATE={1} '''\n        '''git commit --allow-empty -m \"gitfiti\" > /dev/null\\n'''\n    )\n    \n    template_powershell = (\n        '''$Env:GIT_AUTHOR_DATE=\"{0}\"\\n$Env:GIT_COMMITTER_DATE=\"{1}\"\\n'''\n        '''git commit --allow-empty -m \"gitfiti\" | Out-Null\\n'''\n    )\n\n    template = template_bash if shell == 'bash' else template_powershell\n\n    return template.format(commitdate.isoformat(), commitdate.isoformat())\n\n\ndef fake_it(image, start_date, username, repo, git_url, shell, offset=0, multiplier=1):\n    template_bash = (\n        '#!/usr/bin/env bash\\n'\n        'REPO={0}\\n'\n        'git init $REPO\\n'\n        'cd $REPO\\n'\n        'touch README.md\\n'\n        'git add README.md\\n'\n        'touch gitfiti\\n'\n        'git add gitfiti\\n'\n        '{1}\\n'\n        'git branch -M main\\n'\n        'git remote add origin {2}:{3}/$REPO.git\\n'\n        'git pull origin main\\n'\n        'git push -u origin main\\n'\n    )\n\n    template_powershell = (\n        'cd $PSScriptRoot\\n'\n        '$REPO=\"{0}\"\\n'\n        'git init $REPO\\n'\n        'cd $REPO\\n'\n        'New-Item README.md -ItemType file | Out-Null\\n'\n        'git add README.md\\n'\n        'New-Item gitfiti -ItemType file | Out-Null\\n'\n        'git add gitfiti\\n'\n        '{1}\\n'\n        'git branch -M main\\n'\n        'git remote add origin {2}:{3}/$REPO.git\\n'\n        'git pull origin main\\n'\n        'git push -u origin main\\n'\n    )\n\n    template = template_bash if shell == 'bash' else template_powershell\n\n    strings = []\n    for value, date in zip(generate_values_in_date_order(image, multiplier),\n            generate_next_dates(start_date, offset)):\n        for _ in range(value):\n            strings.append(commit(date, shell))\n\n    return template.format(repo, ''.join(strings), git_url, username)\n\n\ndef save(output, filename):\n    \"\"\"Saves the list to a given filename\"\"\"\n    with open(filename, 'w') as f:\n        f.write(output)\n    os.chmod(filename, 0o755) # add execute permissions\n\n\ndef request_user_input(prompt='> '):\n    \"\"\"Request input from the user and return what has been entered.\"\"\"\n    return raw_input(prompt)\n\n\ndef main():\n    print(TITLE)\n\n    ghe = request_user_input(\n        'Enter GitHub URL (leave blank to use {}): '.format(GITHUB_BASE_URL))\n\n    username = request_user_input('Enter your GitHub username: ')\n\n    git_base = ghe if ghe else GITHUB_BASE_URL\n\n    contributions_calendar = retrieve_contributions_calendar(username, git_base)\n\n    max_daily_commits = find_max_daily_commits(contributions_calendar)\n\n    m = calculate_multiplier(max_daily_commits)\n\n    repo = request_user_input(\n        'Enter the name of the repository to use by gitfiti: ')\n\n    offset = request_user_input(\n        'Enter the number of weeks to offset the image (from the left): ')\n\n    offset = int(offset) if offset.strip() else 0\n\n    print((\n        'By default gitfiti.py matches the darkest pixel to the highest\\n'\n        'number of commits found in your GitHub commit/activity calendar,\\n'\n        '\\n'\n        'Currently this is: {0} commits\\n'\n        '\\n'\n        'Enter the word \"gitfiti\" to exceed your max\\n'\n        '(this option generates WAY more commits)\\n'\n        'Any other input will cause the default matching behavior'\n    ).format(max_daily_commits))\n    match = request_user_input()\n\n    match = m if (match == 'gitfiti') else 1\n\n    print('Enter file(s) to load images from (blank if not applicable)')\n    img_names = request_user_input().split(' ')\n\n    loaded_images = load_images(img_names)\n    images = dict(IMAGES, **loaded_images)\n\n    print('Enter the image name to gitfiti')\n    print('Images: ' + ', '.join(images.keys()))\n    image = request_user_input()\n\n    image_name_fallback = FALLBACK_IMAGE\n\n    if not image:\n        image = IMAGES[image_name_fallback]\n    else:\n        try:\n            image = images[image]\n        except:\n            image = IMAGES[image_name_fallback]\n\n    start_date = get_start_date()\n    fake_it_multiplier = m * match\n\n    if not ghe:\n        git_url = 'git@github.com'\n    else:\n        git_url = request_user_input('Enter Git URL like git@site.github.com: ')\n        \n    shell = ''\n    while shell not in SHELLS.keys(): \n        shell = request_user_input(\n            'Enter the target shell ({}): '.format(' or '.join(SHELLS.keys())))\n\n    output = fake_it(image, start_date, username, repo, git_url, shell, offset,\n                     fake_it_multiplier)\n\n    output_filename = 'gitfiti.{}'.format(SHELLS[shell])\n    save(output, output_filename)\n    print('{} saved.'.format(output_filename))\n    print('Create a new(!) repo named {0} at {1} and run the script'.format(repo, git_base))\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_find_max_daily_commits.py",
    "content": "from gitfiti import find_max_daily_commits, parse_contributions_calendar\n\n\nCONTRIBUTIONS_CALENDAR_SVG = '''\\\n<svg width=\"721\" height=\"110\" class=\"js-calendar-graph-svg\">\n  <g transform=\"translate(20, 20)\">\n      <g transform=\"translate(624, 0)\">\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"0\" fill=\"#eeeeee\" data-count=\"0\" data-date=\"2016-05-08\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"13\" fill=\"#eeeeee\" data-count=\"0\" data-date=\"2016-05-09\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"26\" fill=\"#eeeeee\" data-count=\"0\" data-date=\"2016-05-10\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"39\" fill=\"#eeeeee\" data-count=\"0\" data-date=\"2016-05-11\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"52\" fill=\"#d6e685\" data-count=\"6\" data-date=\"2016-05-12\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"65\" fill=\"#eeeeee\" data-count=\"0\" data-date=\"2016-05-13\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"78\" fill=\"#eeeeee\" data-count=\"0\" data-date=\"2016-05-14\"/>\n      </g>\n      <g transform=\"translate(637, 0)\">\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"0\" fill=\"#eeeeee\" data-count=\"0\" data-date=\"2016-05-15\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"13\" fill=\"#eeeeee\" data-count=\"0\" data-date=\"2016-05-16\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"26\" fill=\"#eeeeee\" data-count=\"0\" data-date=\"2016-05-17\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"39\" fill=\"#eeeeee\" data-count=\"0\" data-date=\"2016-05-18\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"52\" fill=\"#eeeeee\" data-count=\"0\" data-date=\"2016-05-19\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"65\" fill=\"#eeeeee\" data-count=\"0\" data-date=\"2016-05-20\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"78\" fill=\"#d6e685\" data-count=\"6\" data-date=\"2016-05-21\"/>\n      </g>\n      <g transform=\"translate(650, 0)\">\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"0\" fill=\"#1e6823\" data-count=\"84\" data-date=\"2016-05-22\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"13\" fill=\"#d6e685\" data-count=\"16\" data-date=\"2016-05-23\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"26\" fill=\"#d6e685\" data-count=\"4\" data-date=\"2016-05-24\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"39\" fill=\"#d6e685\" data-count=\"8\" data-date=\"2016-05-25\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"52\" fill=\"#eeeeee\" data-count=\"0\" data-date=\"2016-05-26\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"65\" fill=\"#eeeeee\" data-count=\"0\" data-date=\"2016-05-27\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"78\" fill=\"#eeeeee\" data-count=\"0\" data-date=\"2016-05-28\"/>\n      </g>\n      <g transform=\"translate(663, 0)\">\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"0\" fill=\"#eeeeee\" data-count=\"0\" data-date=\"2016-05-29\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"13\" fill=\"#8cc665\" data-count=\"25\" data-date=\"2016-05-30\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"26\" fill=\"#1e6823\" data-count=\"66\" data-date=\"2016-05-31\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"39\" fill=\"#d6e685\" data-count=\"20\" data-date=\"2016-06-01\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"52\" fill=\"#d6e685\" data-count=\"10\" data-date=\"2016-06-02\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"65\" fill=\"#eeeeee\" data-count=\"0\" data-date=\"2016-06-03\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"78\" fill=\"#eeeeee\" data-count=\"0\" data-date=\"2016-06-04\"/>\n      </g>\n      <g transform=\"translate(676, 0)\">\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"0\" fill=\"#8cc665\" data-count=\"33\" data-date=\"2016-06-05\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"13\" fill=\"#d6e685\" data-count=\"9\" data-date=\"2016-06-06\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"26\" fill=\"#eeeeee\" data-count=\"0\" data-date=\"2016-06-07\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"39\" fill=\"#eeeeee\" data-count=\"0\" data-date=\"2016-06-08\"/>\n          <rect class=\"day\" width=\"11\" height=\"11\" y=\"52\" fill=\"#d6e685\" data-count=\"7\" data-date=\"2016-06-09\"/>\n      </g>\n      <text x=\"0\" y=\"-5\" class=\"month\">Jun</text>\n      <text x=\"52\" y=\"-5\" class=\"month\">Jul</text>\n      <text x=\"104\" y=\"-5\" class=\"month\">Aug</text>\n      <text x=\"169\" y=\"-5\" class=\"month\">Sep</text>\n      <text x=\"221\" y=\"-5\" class=\"month\">Oct</text>\n      <text x=\"273\" y=\"-5\" class=\"month\">Nov</text>\n      <text x=\"338\" y=\"-5\" class=\"month\">Dec</text>\n      <text x=\"390\" y=\"-5\" class=\"month\">Jan</text>\n      <text x=\"455\" y=\"-5\" class=\"month\">Feb</text>\n      <text x=\"507\" y=\"-5\" class=\"month\">Mar</text>\n      <text x=\"559\" y=\"-5\" class=\"month\">Apr</text>\n      <text x=\"611\" y=\"-5\" class=\"month\">May</text>\n    <text text-anchor=\"middle\" class=\"wday\" dx=\"-10\" dy=\"9\" style=\"display: none;\">S</text>\n    <text text-anchor=\"middle\" class=\"wday\" dx=\"-10\" dy=\"22\">M</text>\n    <text text-anchor=\"middle\" class=\"wday\" dx=\"-10\" dy=\"35\" style=\"display: none;\">T</text>\n    <text text-anchor=\"middle\" class=\"wday\" dx=\"-10\" dy=\"48\">W</text>\n    <text text-anchor=\"middle\" class=\"wday\" dx=\"-10\" dy=\"61\" style=\"display: none;\">T</text>\n    <text text-anchor=\"middle\" class=\"wday\" dx=\"-10\" dy=\"74\">F</text>\n    <text text-anchor=\"middle\" class=\"wday\" dx=\"-10\" dy=\"87\" style=\"display: none;\">S</text>\n  </g>\n</svg>\n'''\n\n\ndef test_parse_contributions_calendar():\n    expected = [\n        0, 0, 0, 0, 6, 0, 0,\n        0, 0, 0, 0, 0, 0, 6,\n        84, 16, 4, 8, 0, 0, 0,\n        0, 25, 66, 20, 10, 0, 0,\n        33, 9, 0, 0, 7,\n    ]\n\n    actual = parse_contributions_calendar(CONTRIBUTIONS_CALENDAR_SVG)\n\n    assert list(actual) == expected\n\n\ndef test_find_max_daily_commits():\n    assert find_max_daily_commits(CONTRIBUTIONS_CALENDAR_SVG) == 84\n"
  },
  {
    "path": "tests/test_str_to_sprite.py",
    "content": "from gitfiti import str_to_sprite, ONEUP_STR\n\n\nSYMBOLS = '''\n ******* \n*=~~-~~=*\n*~~---~~*\n*=*****=*\n**-*-*-**\n *-----* \n  *****  \n'''\n\nNUMBERS = [\n  [0,4,4,4,4,4,4,4,0],\n  [4,3,2,2,0,2,2,3,4],\n  [4,2,2,0,0,0,2,2,4],\n  [4,3,4,4,4,4,4,3,4],\n  [4,4,0,4,0,4,0,4,4],\n  [0,4,0,0,0,0,0,4,0],\n  [0,0,4,4,4,4,4,0,0],\n]\n\n\ndef test_symbols_to_numbers():\n    actual = str_to_sprite(SYMBOLS)\n    assert actual == NUMBERS\n"
  }
]