Initial commit
This commit is contained in:
		
							
								
								
									
										163
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,163 @@
 | 
				
			|||||||
 | 
					# Byte-compiled / optimized / DLL files
 | 
				
			||||||
 | 
					__pycache__/
 | 
				
			||||||
 | 
					*.py[cod]
 | 
				
			||||||
 | 
					*$py.class
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# C extensions
 | 
				
			||||||
 | 
					*.so
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Distribution / packaging
 | 
				
			||||||
 | 
					.Python
 | 
				
			||||||
 | 
					build/
 | 
				
			||||||
 | 
					develop-eggs/
 | 
				
			||||||
 | 
					dist/
 | 
				
			||||||
 | 
					downloads/
 | 
				
			||||||
 | 
					eggs/
 | 
				
			||||||
 | 
					.eggs/
 | 
				
			||||||
 | 
					lib/
 | 
				
			||||||
 | 
					lib64/
 | 
				
			||||||
 | 
					parts/
 | 
				
			||||||
 | 
					sdist/
 | 
				
			||||||
 | 
					var/
 | 
				
			||||||
 | 
					wheels/
 | 
				
			||||||
 | 
					share/python-wheels/
 | 
				
			||||||
 | 
					*.egg-info/
 | 
				
			||||||
 | 
					.installed.cfg
 | 
				
			||||||
 | 
					*.egg
 | 
				
			||||||
 | 
					MANIFEST
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PyInstaller
 | 
				
			||||||
 | 
					#  Usually these files are written by a python script from a template
 | 
				
			||||||
 | 
					#  before PyInstaller builds the exe, so as to inject date/other infos into it.
 | 
				
			||||||
 | 
					*.manifest
 | 
				
			||||||
 | 
					*.spec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Installer logs
 | 
				
			||||||
 | 
					pip-log.txt
 | 
				
			||||||
 | 
					pip-delete-this-directory.txt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Unit test / coverage reports
 | 
				
			||||||
 | 
					htmlcov/
 | 
				
			||||||
 | 
					.tox/
 | 
				
			||||||
 | 
					.nox/
 | 
				
			||||||
 | 
					.coverage
 | 
				
			||||||
 | 
					.coverage.*
 | 
				
			||||||
 | 
					.cache
 | 
				
			||||||
 | 
					nosetests.xml
 | 
				
			||||||
 | 
					coverage.xml
 | 
				
			||||||
 | 
					*.cover
 | 
				
			||||||
 | 
					*.py,cover
 | 
				
			||||||
 | 
					.hypothesis/
 | 
				
			||||||
 | 
					.pytest_cache/
 | 
				
			||||||
 | 
					cover/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Translations
 | 
				
			||||||
 | 
					*.mo
 | 
				
			||||||
 | 
					*.pot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Django stuff:
 | 
				
			||||||
 | 
					*.log
 | 
				
			||||||
 | 
					local_settings.py
 | 
				
			||||||
 | 
					db.sqlite3
 | 
				
			||||||
 | 
					db.sqlite3-journal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Flask stuff:
 | 
				
			||||||
 | 
					instance/
 | 
				
			||||||
 | 
					.webassets-cache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Scrapy stuff:
 | 
				
			||||||
 | 
					.scrapy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Sphinx documentation
 | 
				
			||||||
 | 
					docs/_build/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PyBuilder
 | 
				
			||||||
 | 
					.pybuilder/
 | 
				
			||||||
 | 
					target/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Jupyter Notebook
 | 
				
			||||||
 | 
					.ipynb_checkpoints
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# IPython
 | 
				
			||||||
 | 
					profile_default/
 | 
				
			||||||
 | 
					ipython_config.py
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# pyenv
 | 
				
			||||||
 | 
					#   For a library or package, you might want to ignore these files since the code is
 | 
				
			||||||
 | 
					#   intended to run in multiple environments; otherwise, check them in:
 | 
				
			||||||
 | 
					# .python-version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# pipenv
 | 
				
			||||||
 | 
					#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
 | 
				
			||||||
 | 
					#   However, in case of collaboration, if having platform-specific dependencies or dependencies
 | 
				
			||||||
 | 
					#   having no cross-platform support, pipenv may install dependencies that don't work, or not
 | 
				
			||||||
 | 
					#   install all needed dependencies.
 | 
				
			||||||
 | 
					#Pipfile.lock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# poetry
 | 
				
			||||||
 | 
					#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
 | 
				
			||||||
 | 
					#   This is especially recommended for binary packages to ensure reproducibility, and is more
 | 
				
			||||||
 | 
					#   commonly ignored for libraries.
 | 
				
			||||||
 | 
					#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
 | 
				
			||||||
 | 
					#poetry.lock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# pdm
 | 
				
			||||||
 | 
					#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
 | 
				
			||||||
 | 
					#pdm.lock
 | 
				
			||||||
 | 
					#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
 | 
				
			||||||
 | 
					#   in version control.
 | 
				
			||||||
 | 
					#   https://pdm.fming.dev/#use-with-ide
 | 
				
			||||||
 | 
					.pdm.toml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
 | 
				
			||||||
 | 
					__pypackages__/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Celery stuff
 | 
				
			||||||
 | 
					celerybeat-schedule
 | 
				
			||||||
 | 
					celerybeat.pid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# SageMath parsed files
 | 
				
			||||||
 | 
					*.sage.py
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Environments
 | 
				
			||||||
 | 
					.env
 | 
				
			||||||
 | 
					.venv
 | 
				
			||||||
 | 
					env/
 | 
				
			||||||
 | 
					venv/
 | 
				
			||||||
 | 
					ENV/
 | 
				
			||||||
 | 
					env.bak/
 | 
				
			||||||
 | 
					venv.bak/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Spyder project settings
 | 
				
			||||||
 | 
					.spyderproject
 | 
				
			||||||
 | 
					.spyproject
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Rope project settings
 | 
				
			||||||
 | 
					.ropeproject
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# mkdocs documentation
 | 
				
			||||||
 | 
					/site
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# mypy
 | 
				
			||||||
 | 
					.mypy_cache/
 | 
				
			||||||
 | 
					.dmypy.json
 | 
				
			||||||
 | 
					dmypy.json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Pyre type checker
 | 
				
			||||||
 | 
					.pyre/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# pytype static type analyzer
 | 
				
			||||||
 | 
					.pytype/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Cython debug symbols
 | 
				
			||||||
 | 
					cython_debug/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PyCharm
 | 
				
			||||||
 | 
					#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can
 | 
				
			||||||
 | 
					#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
 | 
				
			||||||
 | 
					#  and can be added to the global gitignore or merged into this file.  For a more nuclear
 | 
				
			||||||
 | 
					#  option (not recommended) you can uncomment the following to ignore the entire idea folder.
 | 
				
			||||||
 | 
					#.idea/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Application
 | 
				
			||||||
 | 
					config.ini
 | 
				
			||||||
							
								
								
									
										21
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					MIT License
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Copyright (c) 2022 niku
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
				
			||||||
 | 
					of this software and associated documentation files (the "Software"), to deal
 | 
				
			||||||
 | 
					in the Software without restriction, including without limitation the rights
 | 
				
			||||||
 | 
					to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
				
			||||||
 | 
					copies of the Software, and to permit persons to whom the Software is
 | 
				
			||||||
 | 
					furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The above copyright notice and this permission notice shall be included in all
 | 
				
			||||||
 | 
					copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
				
			||||||
 | 
					AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
				
			||||||
 | 
					OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
				
			||||||
 | 
					SOFTWARE.
 | 
				
			||||||
							
								
								
									
										16
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					# contrib-scribe
 | 
				
			||||||
 | 
					Your contribution heatmap is a canvas for creativity.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Prerequisites
 | 
				
			||||||
 | 
					- systemd
 | 
				
			||||||
 | 
					- python (>=3.10)
 | 
				
			||||||
 | 
					- git
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Usage
 | 
				
			||||||
 | 
					```sh
 | 
				
			||||||
 | 
					# Start
 | 
				
			||||||
 | 
					./scripts/install.sh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Stop
 | 
				
			||||||
 | 
					./scripts/uninstall.sh
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
							
								
								
									
										4
									
								
								config.ini.sample
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								config.ini.sample
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					[contrib_scribe]
 | 
				
			||||||
 | 
					repo_path = ~/path/to/repo
 | 
				
			||||||
 | 
					message = hi there
 | 
				
			||||||
 | 
					commit_count = 3
 | 
				
			||||||
							
								
								
									
										342
									
								
								contrib_scribe.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										342
									
								
								contrib_scribe.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,342 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env python3
 | 
				
			||||||
 | 
					import base64
 | 
				
			||||||
 | 
					import configparser
 | 
				
			||||||
 | 
					import typing
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					import pathlib
 | 
				
			||||||
 | 
					from datetime import date, timedelta
 | 
				
			||||||
 | 
					import subprocess as sub
 | 
				
			||||||
 | 
					import appdirs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CharRepr = typing.List[typing.List[typing.Literal[0] | typing.Literal[1]]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CHAR_SPACING = 1
 | 
				
			||||||
 | 
					FONT_MAP: typing.Mapping[str, CharRepr] = {
 | 
				
			||||||
 | 
					    "A": [
 | 
				
			||||||
 | 
					        [0, 1, 1, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 1, 1, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "B": [
 | 
				
			||||||
 | 
					        [1, 1, 1, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 1, 1, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 1, 1, 0],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "C": [
 | 
				
			||||||
 | 
					        [0, 1, 1, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [0, 1, 1, 0],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "D": [
 | 
				
			||||||
 | 
					        [1, 1, 1, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 1, 1, 0],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "E": [
 | 
				
			||||||
 | 
					        [1, 1, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0],
 | 
				
			||||||
 | 
					        [1, 1, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0],
 | 
				
			||||||
 | 
					        [1, 1, 1],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "F": [
 | 
				
			||||||
 | 
					        [1, 1, 1, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0, 0],
 | 
				
			||||||
 | 
					        [1, 1, 1, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0, 0],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "G": [
 | 
				
			||||||
 | 
					        [0, 1, 1, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 0],
 | 
				
			||||||
 | 
					        [1, 0, 1, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [0, 1, 1, 1],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "H": [
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 1, 1, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "I": [
 | 
				
			||||||
 | 
					        [1],
 | 
				
			||||||
 | 
					        [1],
 | 
				
			||||||
 | 
					        [1],
 | 
				
			||||||
 | 
					        [1],
 | 
				
			||||||
 | 
					        [1],
 | 
				
			||||||
 | 
					        [1],
 | 
				
			||||||
 | 
					        [1],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "J": [
 | 
				
			||||||
 | 
					        [0, 0, 1],
 | 
				
			||||||
 | 
					        [0, 0, 1],
 | 
				
			||||||
 | 
					        [0, 0, 1],
 | 
				
			||||||
 | 
					        [0, 0, 1],
 | 
				
			||||||
 | 
					        [0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 1, 1],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "K": [
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 1, 0],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "L": [
 | 
				
			||||||
 | 
					        [1, 0, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0],
 | 
				
			||||||
 | 
					        [1, 1, 1],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "M": [
 | 
				
			||||||
 | 
					        [0, 1, 0, 1, 0],
 | 
				
			||||||
 | 
					        [1, 0, 1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1, 0, 1],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "N": [
 | 
				
			||||||
 | 
					        [1, 1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1, 1],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "O": [
 | 
				
			||||||
 | 
					        [0, 1, 1, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [0, 1, 1, 0],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "P": [
 | 
				
			||||||
 | 
					        [1, 1, 1, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 1, 1, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0, 0],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "Q": [
 | 
				
			||||||
 | 
					        [0, 1, 1, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1, 1],
 | 
				
			||||||
 | 
					        [0, 1, 1, 1],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "R": [
 | 
				
			||||||
 | 
					        [0, 1, 1, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 1, 1, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "S": [
 | 
				
			||||||
 | 
					        [0, 1, 1, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 0],
 | 
				
			||||||
 | 
					        [0, 1, 1, 0],
 | 
				
			||||||
 | 
					        [0, 0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 0, 1],
 | 
				
			||||||
 | 
					        [0, 1, 1, 0],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "T": [
 | 
				
			||||||
 | 
					        [1, 1, 1],
 | 
				
			||||||
 | 
					        [0, 1, 0],
 | 
				
			||||||
 | 
					        [0, 1, 0],
 | 
				
			||||||
 | 
					        [0, 1, 0],
 | 
				
			||||||
 | 
					        [0, 1, 0],
 | 
				
			||||||
 | 
					        [0, 1, 0],
 | 
				
			||||||
 | 
					        [0, 1, 0],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "U": [
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 1, 1],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "V": [
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [0, 1, 0],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "W": [
 | 
				
			||||||
 | 
					        [1, 0, 1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1, 0, 1],
 | 
				
			||||||
 | 
					        [0, 1, 0, 1, 0],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "X": [
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [0, 1, 0],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "Y": [
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 0, 1],
 | 
				
			||||||
 | 
					        [1, 1, 1],
 | 
				
			||||||
 | 
					        [0, 0, 1],
 | 
				
			||||||
 | 
					        [0, 0, 1],
 | 
				
			||||||
 | 
					        [1, 1, 1],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "Z": [
 | 
				
			||||||
 | 
					        [1, 1, 1, 1],
 | 
				
			||||||
 | 
					        [0, 0, 0, 1],
 | 
				
			||||||
 | 
					        [0, 0, 1, 0],
 | 
				
			||||||
 | 
					        [0, 1, 0, 0],
 | 
				
			||||||
 | 
					        [0, 1, 0, 0],
 | 
				
			||||||
 | 
					        [1, 0, 0, 0],
 | 
				
			||||||
 | 
					        [1, 1, 1, 1],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    " ": [
 | 
				
			||||||
 | 
					        [0, 0],
 | 
				
			||||||
 | 
					        [0, 0],
 | 
				
			||||||
 | 
					        [0, 0],
 | 
				
			||||||
 | 
					        [0, 0],
 | 
				
			||||||
 | 
					        [0, 0],
 | 
				
			||||||
 | 
					        [0, 0],
 | 
				
			||||||
 | 
					        [0, 0],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def message_to_matrix(message: str) -> CharRepr:
 | 
				
			||||||
 | 
					    matrix: CharRepr = [[] for _ in range(7)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for char in message:
 | 
				
			||||||
 | 
					        crepr = FONT_MAP.get(char)
 | 
				
			||||||
 | 
					        if not crepr:
 | 
				
			||||||
 | 
					            raise ValueError(f'No repr for "{char}" found in font mapping.')
 | 
				
			||||||
 | 
					        for i in range(7):
 | 
				
			||||||
 | 
					            matrix[i].extend(crepr[i])
 | 
				
			||||||
 | 
					            matrix[i].extend([0] * CHAR_SPACING)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return matrix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def print_char_matrix(matrix: CharRepr):
 | 
				
			||||||
 | 
					    fill = {0: " ", 1: "█"}
 | 
				
			||||||
 | 
					    for row in matrix:
 | 
				
			||||||
 | 
					        print("".join(fill[char] for char in row))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    print(f"\nWidth: {len(matrix[0])}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def parse_config(path: str) -> typing.Mapping[str, str]:
 | 
				
			||||||
 | 
					    parser = configparser.ConfigParser()
 | 
				
			||||||
 | 
					    parser.read(path)
 | 
				
			||||||
 | 
					    return parser["contrib_scribe"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def parse_state(path: pathlib.Path) -> date:
 | 
				
			||||||
 | 
					    if not path.exists():
 | 
				
			||||||
 | 
					        state = date.today()
 | 
				
			||||||
 | 
					        if (weekday := state.weekday()) != 6:
 | 
				
			||||||
 | 
					            state += timedelta(days=6 - weekday)
 | 
				
			||||||
 | 
					        with path.open("w") as fp:
 | 
				
			||||||
 | 
					            fp.write(str(state))
 | 
				
			||||||
 | 
					        return state
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    with path.open("r") as fp:
 | 
				
			||||||
 | 
					        return date.fromisoformat(fp.read())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def main(args: typing.Sequence[str]) -> int:
 | 
				
			||||||
 | 
					    if len(args) < 1:
 | 
				
			||||||
 | 
					        print("Usage: contrib_scribe.py <config.ini>", file=sys.stderr)
 | 
				
			||||||
 | 
					        return 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cfg = parse_config(args[0])
 | 
				
			||||||
 | 
					    cfg_id = base64.b32encode(bytearray(args[0], "ascii")).decode("utf-8")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    data_dir = appdirs.user_data_dir("contrib-scribe")
 | 
				
			||||||
 | 
					    data_path = pathlib.Path(data_dir)
 | 
				
			||||||
 | 
					    data_path.mkdir(parents=True, exist_ok=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    state_path = data_path / cfg_id
 | 
				
			||||||
 | 
					    start_date = parse_state(state_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    message = cfg["message"].upper()
 | 
				
			||||||
 | 
					    matrix = message_to_matrix(message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    print_char_matrix(matrix)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    day_diff = (date.today() - start_date).days
 | 
				
			||||||
 | 
					    row, column = day_diff % 7, day_diff // 7
 | 
				
			||||||
 | 
					    if matrix[row][column] == 0:
 | 
				
			||||||
 | 
					        print("No commit's to do today!")
 | 
				
			||||||
 | 
					        return 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    file_path = pathlib.Path(cfg["repo_path"]) / "test"
 | 
				
			||||||
 | 
					    commit_count = int(cfg["commit_count"])
 | 
				
			||||||
 | 
					    for i in range(commit_count):
 | 
				
			||||||
 | 
					        file_path.write_text(str(i))
 | 
				
			||||||
 | 
					        commands = [["git", "add", "."], ["git", "commit", "-m", str(i)]]
 | 
				
			||||||
 | 
					        for command in commands:
 | 
				
			||||||
 | 
					            sub.call(command, cwd=file_path.parent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sub.call(["git", "push"], cwd=file_path.parent)
 | 
				
			||||||
 | 
					    print("Did daily commit")
 | 
				
			||||||
 | 
					    return 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    raise SystemExit(main(sys.argv[1:]))
 | 
				
			||||||
							
								
								
									
										6
									
								
								contrib_scribe_test.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								contrib_scribe_test.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					import contrib_scribe
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_font_size():
 | 
				
			||||||
 | 
					    for char, crepr in contrib_scribe.FONT.items():
 | 
				
			||||||
 | 
					        assert len(crepr) == 7, f'"{char}" is taller than 7 pixels'
 | 
				
			||||||
							
								
								
									
										1
									
								
								requirements-dev.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								requirements-dev.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					pytest
 | 
				
			||||||
							
								
								
									
										1
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					appdirs
 | 
				
			||||||
							
								
								
									
										26
									
								
								scripts/install.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								scripts/install.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					# Sets up the necessary systemd timer & service. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					service_name=contrib-writer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sudo tee /usr/lib/systemd/user/$service_name.service &>/dev/null <<EOF
 | 
				
			||||||
 | 
					[Unit]
 | 
				
			||||||
 | 
					Description=Your contribution heatmap is a canvas for creativity.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Service]
 | 
				
			||||||
 | 
					ExecStart=$(pwd)/contrib_scribe.py $(pwd)/config.ini 
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sudo tee /usr/lib/systemd/user/$service_name.timer &>/dev/null <<EOF
 | 
				
			||||||
 | 
					[Unit]
 | 
				
			||||||
 | 
					Description=Your contribution heatmap is a canvas for creativity.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Timer]
 | 
				
			||||||
 | 
					OnCalendar=daily
 | 
				
			||||||
 | 
					Persistent=true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Install]
 | 
				
			||||||
 | 
					WantedBy=timers.target
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					systemctl enable --user contrib-writer.timer --now
 | 
				
			||||||
							
								
								
									
										10
									
								
								scripts/uninstall.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										10
									
								
								scripts/uninstall.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					# Deletes related created systemd timer & service. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					service_name=contrib-writer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					systemctl disable --user $service_name.timer
 | 
				
			||||||
 | 
					systemctl stop --user $service_name.timer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sudo rm /usr/lib/systemd/user/$service_name.service
 | 
				
			||||||
 | 
					sudo rm /usr/lib/systemd/user/$service_name.timer
 | 
				
			||||||
		Reference in New Issue
	
	Block a user