diff --git a/.env.example b/.env.example index 23d0d27..c273a36 100644 --- a/.env.example +++ b/.env.example @@ -1,22 +1,22 @@ -INPUT_WAKATIME_API_KEY="" -INPUT_PUSH_BRANCH_NAME="main" -INPUT_SECTION_NAME="waka" -INPUT_SHOW_TIMEZONE="True" -INPUT_SHOW_PROJECTS="False" -INPUT_SHOW_EDITORS="False" -INPUT_SHOW_OS="False" -INPUT_SHOW_LANGUAGE="False" -INPUT_GH_TOKEN="" -INPUT_SYMBOL_VERSION="1" -INPUT_SHOW_LINES_OF_CODE="False" -INPUT_SHOW_LOC_CHART="False" -INPUT_SHOW_PROFILE_VIEWS="False" -INPUT_SHOW_TOTAL_CODE_TIME="True" -INPUT_SHOW_SHORT_INFO="False" -INPUT_SHOW_COMMIT="False" -INPUT_SHOW_DAYS_OF_WEEK="True" -INPUT_SHOW_LANGUAGE_PER_REPO="True" -INPUT_SHOW_UPDATED_DATE="True" -INPUT_UPDATED_DATE_FORMAT="%d/%m/%Y %H:%M:%S" -INPUT_COMMIT_BY_ME="False" -INPUT_COMMIT_MESSAGE="Updated with Dev Metrics" \ No newline at end of file +INPUT_WAKATIME_API_KEY=YOUR_WAKATIME_API_KEY +INPUT_PUSH_BRANCH_NAME=main +INPUT_SECTION_NAME=waka +INPUT_SHOW_TIMEZONE=True +INPUT_SHOW_PROJECTS=True +INPUT_SHOW_EDITORS=True +INPUT_SHOW_OS=True +INPUT_SHOW_LANGUAGE=True +INPUT_GH_TOKEN=YOUR_GITHUB_TOKEN_KEY +INPUT_SYMBOL_VERSION=1 +INPUT_SHOW_LINES_OF_CODE=True +INPUT_SHOW_LOC_CHART=True +INPUT_SHOW_PROFILE_VIEWS=True +INPUT_SHOW_TOTAL_CODE_TIME=True +INPUT_SHOW_SHORT_INFO=True +INPUT_SHOW_COMMIT=True +INPUT_SHOW_DAYS_OF_WEEK=True +INPUT_SHOW_LANGUAGE_PER_REPO=True +INPUT_SHOW_UPDATED_DATE=True +INPUT_UPDATED_DATE_FORMAT=%d/%m/%Y %H:%M:%S +INPUT_COMMIT_BY_ME=False +INPUT_COMMIT_MESSAGE=Updated with Dev Metrics diff --git a/.github/workflows/build_image.yml b/.github/workflows/build_image.yml new file mode 100644 index 0000000..4ae01df --- /dev/null +++ b/.github/workflows/build_image.yml @@ -0,0 +1,37 @@ +name: PUBLISH_IMAGE + +on: + push: + +jobs: + publish-server-image: + name: Publish 'waka-readme-stats' image + runs-on: ubuntu-latest + + steps: + - name: Checkout 🛎️ + uses: actions/checkout@v3 + + - name: Log in to the container registry 🚪 + uses: docker/login-action@v2 + with: + username: wakareadmestats + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker 🏋️ + id: meta + uses: docker/metadata-action@v4 + with: + images: wakareadmestats/waka-readme-stats + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + + - name: Build and push Docker image 🏗️ + uses: docker/build-push-action@v3 + with: + push: ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/releases') }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.gitignore b/.gitignore index fd76478..082302f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,20 @@ +# Environment files: *.env + +# Generated graph images: *.png + +# Library roots: node_modules/ +venv/ + +# Python caches: __pycache__/ +# Package manager configuration files: +package.json +package-lock.json + +# IDE configuration files: .vscode +.idea diff --git a/Dockerfile b/Dockerfile index df5fbc1..5fa5d87 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,12 @@ FROM python:3.9-alpine -ADD requirements.txt /requirements.txt +ENV PYTHONUNBUFFERED 1 +ENV PYTHONDONTWRITEBYTECODE 1 + +WORKDIR /waka-readme-stats + +ADD requirements.txt ./requirements.txt RUN apk add --no-cache g++ jpeg-dev zlib-dev libjpeg make && pip3 install -r requirements.txt -ADD main.py /main.py -ADD loc.py /loc.py -ADD make_bar_graph.py /make_bar_graph.py -ADD colors.json /colors.json -ADD translation.json /translation.json - -ENTRYPOINT ["python", "/main.py"] +ADD sources/* ./ +ENTRYPOINT python3 /waka-readme-stats/main.py diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fcec175 --- /dev/null +++ b/Makefile @@ -0,0 +1,49 @@ +.ONESHELL: +.DEFAULT_GOAL = help +SHELL = /bin/bash + +PATH := venv/bin:node_modules/.bin:$(PATH) + + +help: + @ # Print help commands + echo "Welcome to 'waka-readme-stats' GitHub Actions!" + echo "The action can be tested locally with: 'make run'." + echo "NB! For local testing Python version 3.6+ and NodeJS version between 14 and 16 are required." + echo "The action image can be built locally with: 'make container'." + echo "NB! For local container building Docker version 20+ is required." + echo "The action directory and image can be cleaned with: 'make clean'." +.PHONY: help + +venv: + @ # Install Python virtual environment and dependencies + python3 -m venv venv + pip install --upgrade pip + pip install -r requirements.txt + +node_modules: + @ # Install NodeJS dependencies + npm i npm@next-8 + npm i vega vega-lite vega-cli canvas + + +run-locally: venv node_modules + @ # Run action locally + source <(cat .env.example | sed 's/=/=/' | sed 's/^/export /') && python3 ./sources/main.py +.PHONY: run-locally + +run-container: + @ # Run action in container + docker build -t waka-readme-stats -f Dockerfile . + docker run --env-file .env.example waka-readme-stats +.PHONY: run-container + + +clean: + @ # Clean all build files, including: libraries, package manager configs, docker images and containers + rm -rf venv + rm -rf node_modules + rm -f package*.json + docker rm -f waka-readme-stats 2>/dev/null || true + docker rmi $(docker images | grep "waka-readme-stats") 2> /dev/null || true +.PHONY: clean diff --git a/action.yml b/action.yml index 3643eb2..2e42fc5 100644 --- a/action.yml +++ b/action.yml @@ -11,7 +11,7 @@ inputs: WAKATIME_API_KEY: description: 'Your Wakatime API Key' required: true - + SECTION_NAME: description: 'Name used in readme to scope the updated section' required: false @@ -134,7 +134,7 @@ inputs: runs: using: 'docker' - image: 'Dockerfile' + image: 'docker://wakareadmestats/waka-readme-stats:master' branding: icon: 'activity' diff --git a/colors.json b/colors.json deleted file mode 100644 index e050e30..0000000 --- a/colors.json +++ /dev/null @@ -1,2358 +0,0 @@ -{ - "1C Enterprise": { - "color": "#814CCC", - "url": "https://github.com/trending?l=1C-Enterprise" - }, - "4D": { - "color": null, - "url": "https://github.com/trending?l=4D" - }, - "ABAP CDS": { - "color": "#555e25", - "url": "https://github.com/trending?l=ABAP-CDS" - }, - "ABAP": { - "color": "#E8274B", - "url": "https://github.com/trending?l=ABAP" - }, - "ABNF": { - "color": null, - "url": "https://github.com/trending?l=ABNF" - }, - "AGS Script": { - "color": "#B9D9FF", - "url": "https://github.com/trending?l=AGS-Script" - }, - "AL": { - "color": "#3AA2B5", - "url": "https://github.com/trending?l=AL" - }, - "AMPL": { - "color": "#E6EFBB", - "url": "https://github.com/trending?l=AMPL" - }, - "ANTLR": { - "color": "#9DC3FF", - "url": "https://github.com/trending?l=ANTLR" - }, - "API Blueprint": { - "color": "#2ACCA8", - "url": "https://github.com/trending?l=API-Blueprint" - }, - "APL": { - "color": "#5A8164", - "url": "https://github.com/trending?l=APL" - }, - "ASL": { - "color": null, - "url": "https://github.com/trending?l=ASL" - }, - "ASN.1": { - "color": null, - "url": "https://github.com/trending?l=ASN.1" - }, - "ASP": { - "color": "#6a40fd", - "url": "https://github.com/trending?l=ASP" - }, - "ASP.NET": { - "color": "#9400ff", - "url": "https://github.com/trending?l=ASP.NET" - }, - "ATS": { - "color": "#1ac620", - "url": "https://github.com/trending?l=ATS" - }, - "ActionScript": { - "color": "#882B0F", - "url": "https://github.com/trending?l=ActionScript" - }, - "Ada": { - "color": "#02f88c", - "url": "https://github.com/trending?l=Ada" - }, - "Adobe Font Metrics": { - "color": null, - "url": "https://github.com/trending?l=Adobe-Font Metrics" - }, - "Agda": { - "color": "#315665", - "url": "https://github.com/trending?l=Agda" - }, - "Alloy": { - "color": "#64C800", - "url": "https://github.com/trending?l=Alloy" - }, - "Alpine Abuild": { - "color": null, - "url": "https://github.com/trending?l=Alpine-Abuild" - }, - "Altium Designer": { - "color": null, - "url": "https://github.com/trending?l=Altium-Designer" - }, - "AngelScript": { - "color": "#C7D7DC", - "url": "https://github.com/trending?l=AngelScript" - }, - "Ant Build System": { - "color": null, - "url": "https://github.com/trending?l=Ant-Build System" - }, - "ApacheConf": { - "color": null, - "url": "https://github.com/trending?l=ApacheConf" - }, - "Apex": { - "color": "#1797c0", - "url": "https://github.com/trending?l=Apex" - }, - "Apollo Guidance Computer": { - "color": "#0B3D91", - "url": "https://github.com/trending?l=Apollo-Guidance-Computer" - }, - "AppleScript": { - "color": "#101F1F", - "url": "https://github.com/trending?l=AppleScript" - }, - "Arc": { - "color": "#aa2afe", - "url": "https://github.com/trending?l=Arc" - }, - "Arduino": { - "color": null, - "url": "https://github.com/trending?l=Arduino" - }, - "AsciiDoc": { - "color": null, - "url": "https://github.com/trending?l=AsciiDoc" - }, - "AspectJ": { - "color": "#a957b0", - "url": "https://github.com/trending?l=AspectJ" - }, - "Assembly": { - "color": "#6E4C13", - "url": "https://github.com/trending?l=Assembly" - }, - "Asymptote": { - "color": "#4a0c0c", - "url": "https://github.com/trending?l=Asymptote" - }, - "Augeas": { - "color": null, - "url": "https://github.com/trending?l=Augeas" - }, - "AutoHotkey": { - "color": "#6594b9", - "url": "https://github.com/trending?l=AutoHotkey" - }, - "AutoIt": { - "color": "#1C3552", - "url": "https://github.com/trending?l=AutoIt" - }, - "Avro IDL": { - "color": null, - "url": "https://github.com/trending?l=Avro-IDL" - }, - "Awk": { - "color": null, - "url": "https://github.com/trending?l=Awk" - }, - "Ballerina": { - "color": "#FF5000", - "url": "https://github.com/trending?l=Ballerina" - }, - "Batchfile": { - "color": "#C1F12E", - "url": "https://github.com/trending?l=Batchfile" - }, - "Beef": { - "color": "#a52f4e", - "url": "https://github.com/trending?l=Beef" - }, - "Befunge": { - "color": null, - "url": "https://github.com/trending?l=Befunge" - }, - "BibTeX": { - "color": null, - "url": "https://github.com/trending?l=BibTeX" - }, - "Bison": { - "color": "#6A463F", - "url": "https://github.com/trending?l=Bison" - }, - "BitBake": { - "color": null, - "url": "https://github.com/trending?l=BitBake" - }, - "Blade": { - "color": "#f7523f", - "url": "https://github.com/trending?l=Blade" - }, - "BlitzBasic": { - "color": null, - "url": "https://github.com/trending?l=BlitzBasic" - }, - "BlitzMax": { - "color": "#cd6400", - "url": "https://github.com/trending?l=BlitzMax" - }, - "Bluespec": { - "color": null, - "url": "https://github.com/trending?l=Bluespec" - }, - "Boo": { - "color": "#d4bec1", - "url": "https://github.com/trending?l=Boo" - }, - "Boogie": { - "color": "#c80fa0", - "url": "https://github.com/trending?l=Boogie" - }, - "Brainfuck": { - "color": "#2F2530", - "url": "https://github.com/trending?l=Brainfuck" - }, - "Brightscript": { - "color": null, - "url": "https://github.com/trending?l=Brightscript" - }, - "Browserslist": { - "color": "#ffd539", - "url": "https://github.com/trending?l=Browserslist" - }, - "C#": { - "color": "#178600", - "url": "https://github.com/trending?l=Csharp" - }, - "C++": { - "color": "#f34b7d", - "url": "https://github.com/trending?l=C++" - }, - "C": { - "color": "#555555", - "url": "https://github.com/trending?l=C" - }, - "C-ObjDump": { - "color": null, - "url": "https://github.com/trending?l=C-ObjDump" - }, - "C2hs Haskell": { - "color": null, - "url": "https://github.com/trending?l=C2hs-Haskell" - }, - "CLIPS": { - "color": null, - "url": "https://github.com/trending?l=CLIPS" - }, - "CMake": { - "color": null, - "url": "https://github.com/trending?l=CMake" - }, - "COBOL": { - "color": null, - "url": "https://github.com/trending?l=COBOL" - }, - "CODEOWNERS": { - "color": null, - "url": "https://github.com/trending?l=CODEOWNERS" - }, - "COLLADA": { - "color": null, - "url": "https://github.com/trending?l=COLLADA" - }, - "CSON": { - "color": "#244776", - "url": "https://github.com/trending?l=CSON" - }, - "CSS": { - "color": "#563d7c", - "url": "https://github.com/trending?l=CSS" - }, - "CSV": { - "color": null, - "url": "https://github.com/trending?l=CSV" - }, - "CWeb": { - "color": null, - "url": "https://github.com/trending?l=CWeb" - }, - "Cabal Config": { - "color": null, - "url": "https://github.com/trending?l=Cabal-Config" - }, - "Cap'n Proto": { - "color": null, - "url": "https://github.com/trending?l=Cap'n-Proto" - }, - "CartoCSS": { - "color": null, - "url": "https://github.com/trending?l=CartoCSS" - }, - "Ceylon": { - "color": "#dfa535", - "url": "https://github.com/trending?l=Ceylon" - }, - "Chapel": { - "color": "#8dc63f", - "url": "https://github.com/trending?l=Chapel" - }, - "Charity": { - "color": null, - "url": "https://github.com/trending?l=Charity" - }, - "ChucK": { - "color": null, - "url": "https://github.com/trending?l=ChucK" - }, - "Cirru": { - "color": "#ccccff", - "url": "https://github.com/trending?l=Cirru" - }, - "Clarion": { - "color": "#db901e", - "url": "https://github.com/trending?l=Clarion" - }, - "Classic ASP": { - "color": "#6a40fd", - "url": "https://github.com/trending?l=Classic-ASP" - }, - "Clean": { - "color": "#3F85AF", - "url": "https://github.com/trending?l=Clean" - }, - "Click": { - "color": "#E4E6F3", - "url": "https://github.com/trending?l=Click" - }, - "Clojure": { - "color": "#db5855", - "url": "https://github.com/trending?l=Clojure" - }, - "Closure Templates": { - "color": "#0d948f", - "url": "https://github.com/trending?l=Closure-Templates" - }, - "Cloud Firestore Security Rules": { - "color": null, - "url": "https://github.com/trending?l=Cloud-Firestore Security Rules" - }, - "CoNLL-U": { - "color": null, - "url": "https://github.com/trending?l=CoNLL-U" - }, - "CodeQL": { - "color": null, - "url": "https://github.com/trending?l=CodeQL" - }, - "CoffeeScript": { - "color": "#244776", - "url": "https://github.com/trending?l=CoffeeScript" - }, - "ColdFusion CFC": { - "color": "#ed2cd6", - "url": "https://github.com/trending?l=ColdFusion-CFC" - }, - "ColdFusion": { - "color": "#ed2cd6", - "url": "https://github.com/trending?l=ColdFusion" - }, - "Common Lisp": { - "color": "#3fb68b", - "url": "https://github.com/trending?l=Common-Lisp" - }, - "Common Workflow Language": { - "color": "#B5314C", - "url": "https://github.com/trending?l=Common-Workflow-Language" - }, - "Component Pascal": { - "color": "#B0CE4E", - "url": "https://github.com/trending?l=Component-Pascal" - }, - "Cool": { - "color": null, - "url": "https://github.com/trending?l=Cool" - }, - "Coq": { - "color": null, - "url": "https://github.com/trending?l=Coq" - }, - "Cpp-ObjDump": { - "color": null, - "url": "https://github.com/trending?l=Cpp-ObjDump" - }, - "Creole": { - "color": null, - "url": "https://github.com/trending?l=Creole" - }, - "Crystal": { - "color": "#000100", - "url": "https://github.com/trending?l=Crystal" - }, - "Csound Document": { - "color": null, - "url": "https://github.com/trending?l=Csound-Document" - }, - "Csound Score": { - "color": null, - "url": "https://github.com/trending?l=Csound-Score" - }, - "Csound": { - "color": null, - "url": "https://github.com/trending?l=Csound" - }, - "Cuda": { - "color": "#3A4E3A", - "url": "https://github.com/trending?l=Cuda" - }, - "Cycript": { - "color": null, - "url": "https://github.com/trending?l=Cycript" - }, - "Cython": { - "color": null, - "url": "https://github.com/trending?l=Cython" - }, - "D": { - "color": "#ba595e", - "url": "https://github.com/trending?l=D" - }, - "D-ObjDump": { - "color": null, - "url": "https://github.com/trending?l=D-ObjDump" - }, - "DIGITAL Command Language": { - "color": null, - "url": "https://github.com/trending?l=DIGITAL-Command-Language" - }, - "DM": { - "color": "#447265", - "url": "https://github.com/trending?l=DM" - }, - "DNS Zone": { - "color": null, - "url": "https://github.com/trending?l=DNS-Zone" - }, - "DTrace": { - "color": null, - "url": "https://github.com/trending?l=DTrace" - }, - "Dafny": { - "color": "#FFEC25", - "url": "https://github.com/trending?l=Dafny" - }, - "Darcs Patch": { - "color": null, - "url": "https://github.com/trending?l=Darcs-Patch" - }, - "Dart": { - "color": "#00B4AB", - "url": "https://github.com/trending?l=Dart" - }, - "DataWeave": { - "color": "#003a52", - "url": "https://github.com/trending?l=DataWeave" - }, - "Dhall": { - "color": "#dfafff", - "url": "https://github.com/trending?l=Dhall" - }, - "Diff": { - "color": null, - "url": "https://github.com/trending?l=Diff" - }, - "DirectX 3D File": { - "color": null, - "url": "https://github.com/trending?l=DirectX-3D File" - }, - "Dockerfile": { - "color": "#384d54", - "url": "https://github.com/trending?l=Dockerfile" - }, - "Dogescript": { - "color": "#cca760", - "url": "https://github.com/trending?l=Dogescript" - }, - "Dylan": { - "color": "#6c616e", - "url": "https://github.com/trending?l=Dylan" - }, - "E": { - "color": "#ccce35", - "url": "https://github.com/trending?l=E" - }, - "EBNF": { - "color": null, - "url": "https://github.com/trending?l=EBNF" - }, - "ECL": { - "color": "#8a1267", - "url": "https://github.com/trending?l=ECL" - }, - "ECLiPSe": { - "color": null, - "url": "https://github.com/trending?l=ECLiPSe" - }, - "EJS": { - "color": "#a91e50", - "url": "https://github.com/trending?l=EJS" - }, - "EML": { - "color": null, - "url": "https://github.com/trending?l=EML" - }, - "EQ": { - "color": "#a78649", - "url": "https://github.com/trending?l=EQ" - }, - "Eagle": { - "color": null, - "url": "https://github.com/trending?l=Eagle" - }, - "Easybuild": { - "color": null, - "url": "https://github.com/trending?l=Easybuild" - }, - "Ecere Projects": { - "color": null, - "url": "https://github.com/trending?l=Ecere-Projects" - }, - "EditorConfig": { - "color": null, - "url": "https://github.com/trending?l=EditorConfig" - }, - "Edje Data Collection": { - "color": null, - "url": "https://github.com/trending?l=Edje-Data Collection" - }, - "Eiffel": { - "color": "#4d6977", - "url": "https://github.com/trending?l=Eiffel" - }, - "Elixir": { - "color": "#6e4a7e", - "url": "https://github.com/trending?l=Elixir" - }, - "Elm": { - "color": "#60B5CC", - "url": "https://github.com/trending?l=Elm" - }, - "Emacs Lisp": { - "color": "#c065db", - "url": "https://github.com/trending?l=Emacs-Lisp" - }, - "EmberScript": { - "color": "#FFF4F3", - "url": "https://github.com/trending?l=EmberScript" - }, - "Erlang": { - "color": "#B83998", - "url": "https://github.com/trending?l=Erlang" - }, - "F#": { - "color": "#b845fc", - "url": "https://github.com/trending?l=Fsharp" - }, - "F*": { - "color": "#572e30", - "url": "https://github.com/trending?l=F*" - }, - "FIGlet Font": { - "color": null, - "url": "https://github.com/trending?l=FIGlet-Font" - }, - "FLUX": { - "color": "#88ccff", - "url": "https://github.com/trending?l=FLUX" - }, - "Factor": { - "color": "#636746", - "url": "https://github.com/trending?l=Factor" - }, - "Fancy": { - "color": "#7b9db4", - "url": "https://github.com/trending?l=Fancy" - }, - "Fantom": { - "color": "#14253c", - "url": "https://github.com/trending?l=Fantom" - }, - "Faust": { - "color": "#c37240", - "url": "https://github.com/trending?l=Faust" - }, - "Filebench WML": { - "color": null, - "url": "https://github.com/trending?l=Filebench-WML" - }, - "Filterscript": { - "color": null, - "url": "https://github.com/trending?l=Filterscript" - }, - "Formatted": { - "color": null, - "url": "https://github.com/trending?l=Formatted" - }, - "Forth": { - "color": "#341708", - "url": "https://github.com/trending?l=Forth" - }, - "Fortran Free Form": { - "color": null, - "url": "https://github.com/trending?l=Fortran-Free-Form" - }, - "Fortran": { - "color": "#4d41b1", - "url": "https://github.com/trending?l=Fortran" - }, - "FreeMarker": { - "color": "#0050b2", - "url": "https://github.com/trending?l=FreeMarker" - }, - "Frege": { - "color": "#00cafe", - "url": "https://github.com/trending?l=Frege" - }, - "Futhark": { - "color": "#5f021f", - "url": "https://github.com/trending?l=Futhark" - }, - "G-code": { - "color": "#D08CF2", - "url": "https://github.com/trending?l=G-code" - }, - "GAML": { - "color": "#FFC766", - "url": "https://github.com/trending?l=GAML" - }, - "GAMS": { - "color": null, - "url": "https://github.com/trending?l=GAMS" - }, - "GAP": { - "color": null, - "url": "https://github.com/trending?l=GAP" - }, - "GCC Machine Description": { - "color": null, - "url": "https://github.com/trending?l=GCC-Machine-Description" - }, - "GDB": { - "color": null, - "url": "https://github.com/trending?l=GDB" - }, - "GDScript": { - "color": "#355570", - "url": "https://github.com/trending?l=GDScript" - }, - "GEDCOM": { - "color": null, - "url": "https://github.com/trending?l=GEDCOM" - }, - "GLSL": { - "color": null, - "url": "https://github.com/trending?l=GLSL" - }, - "GN": { - "color": null, - "url": "https://github.com/trending?l=GN" - }, - "Game Maker Language": { - "color": "#71b417", - "url": "https://github.com/trending?l=Game-Maker Language" - }, - "Gemfile.lock": { - "color": "#701516", - "url": "https://github.com/trending?l=Gemfile.lock" - }, - "Genie": { - "color": "#fb855d", - "url": "https://github.com/trending?l=Genie" - }, - "Genshi": { - "color": null, - "url": "https://github.com/trending?l=Genshi" - }, - "Gentoo Ebuild": { - "color": null, - "url": "https://github.com/trending?l=Gentoo-Ebuild" - }, - "Gentoo Eclass": { - "color": null, - "url": "https://github.com/trending?l=Gentoo-Eclass" - }, - "Gerber Image": { - "color": null, - "url": "https://github.com/trending?l=Gerber-Image" - }, - "Gettext Catalog": { - "color": null, - "url": "https://github.com/trending?l=Gettext-Catalog" - }, - "Gherkin": { - "color": "#5B2063", - "url": "https://github.com/trending?l=Gherkin" - }, - "Git Attributes": { - "color": null, - "url": "https://github.com/trending?l=Git-Attributes" - }, - "Git Config": { - "color": null, - "url": "https://github.com/trending?l=Git-Config" - }, - "Glyph Bitmap Distribution Format": { - "color": null, - "url": "https://github.com/trending?l=Glyph-Bitmap Distribution Format" - }, - "Glyph": { - "color": "#c1ac7f", - "url": "https://github.com/trending?l=Glyph" - }, - "Gnuplot": { - "color": "#f0a9f0", - "url": "https://github.com/trending?l=Gnuplot" - }, - "Go": { - "color": "#00ADD8", - "url": "https://github.com/trending?l=Go" - }, - "Golo": { - "color": "#88562A", - "url": "https://github.com/trending?l=Golo" - }, - "Gosu": { - "color": "#82937f", - "url": "https://github.com/trending?l=Gosu" - }, - "Grace": { - "color": null, - "url": "https://github.com/trending?l=Grace" - }, - "Gradle": { - "color": null, - "url": "https://github.com/trending?l=Gradle" - }, - "Grammatical Framework": { - "color": "#79aa7a", - "url": "https://github.com/trending?l=Grammatical-Framework" - }, - "Graph Modeling Language": { - "color": null, - "url": "https://github.com/trending?l=Graph-Modeling Language" - }, - "GraphQL": { - "color": "#e10098", - "url": "https://github.com/trending?l=GraphQL" - }, - "Graphviz (DOT)": { - "color": null, - "url": "https://github.com/trending?l=Graphviz-(DOT)" - }, - "Groovy Server Pages": { - "color": null, - "url": "https://github.com/trending?l=Groovy-Server-Pages" - }, - "Groovy": { - "color": "#e69f56", - "url": "https://github.com/trending?l=Groovy" - }, - "HAProxy": { - "color": null, - "url": "https://github.com/trending?l=HAProxy" - }, - "HCL": { - "color": null, - "url": "https://github.com/trending?l=HCL" - }, - "HLSL": { - "color": null, - "url": "https://github.com/trending?l=HLSL" - }, - "HTML+ECR": { - "color": null, - "url": "https://github.com/trending?l=HTML+ECR" - }, - "HTML+EEX": { - "color": null, - "url": "https://github.com/trending?l=HTML+EEX" - }, - "HTML+ERB": { - "color": null, - "url": "https://github.com/trending?l=HTML+ERB" - }, - "HTML+PHP": { - "color": null, - "url": "https://github.com/trending?l=HTML+PHP" - }, - "HTML+Razor": { - "color": null, - "url": "https://github.com/trending?l=HTML+Razor" - }, - "HTML": { - "color": "#e34c26", - "url": "https://github.com/trending?l=HTML" - }, - "HTTP": { - "color": null, - "url": "https://github.com/trending?l=HTTP" - }, - "HXML": { - "color": null, - "url": "https://github.com/trending?l=HXML" - }, - "Hack": { - "color": "#878787", - "url": "https://github.com/trending?l=Hack" - }, - "Haml": { - "color": "#ece2a9", - "url": "https://github.com/trending?l=Haml" - }, - "Handlebars": { - "color": "#f7931e", - "url": "https://github.com/trending?l=Handlebars" - }, - "Harbour": { - "color": "#0e60e3", - "url": "https://github.com/trending?l=Harbour" - }, - "Haskell": { - "color": "#5e5086", - "url": "https://github.com/trending?l=Haskell" - }, - "Haxe": { - "color": "#df7900", - "url": "https://github.com/trending?l=Haxe" - }, - "HiveQL": { - "color": "#dce200", - "url": "https://github.com/trending?l=HiveQL" - }, - "HolyC": { - "color": "#ffefaf", - "url": "https://github.com/trending?l=HolyC" - }, - "Hy": { - "color": "#7790B2", - "url": "https://github.com/trending?l=Hy" - }, - "HyPhy": { - "color": null, - "url": "https://github.com/trending?l=HyPhy" - }, - "IDL": { - "color": "#a3522f", - "url": "https://github.com/trending?l=IDL" - }, - "IGOR Pro": { - "color": "#0000cc", - "url": "https://github.com/trending?l=IGOR-Pro" - }, - "INI": { - "color": null, - "url": "https://github.com/trending?l=INI" - }, - "IRC log": { - "color": null, - "url": "https://github.com/trending?l=IRC-log" - }, - "Idris": { - "color": "#b30000", - "url": "https://github.com/trending?l=Idris" - }, - "Ignore List": { - "color": null, - "url": "https://github.com/trending?l=Ignore-List" - }, - "ImageJ Macro": { - "color": "#99AAFF", - "url": "https://github.com/trending?l=ImageJ-Macro" - }, - "Inform 7": { - "color": null, - "url": "https://github.com/trending?l=Inform-7" - }, - "Inno Setup": { - "color": null, - "url": "https://github.com/trending?l=Inno-Setup" - }, - "Io": { - "color": "#a9188d", - "url": "https://github.com/trending?l=Io" - }, - "Ioke": { - "color": "#078193", - "url": "https://github.com/trending?l=Ioke" - }, - "Isabelle ROOT": { - "color": null, - "url": "https://github.com/trending?l=Isabelle-ROOT" - }, - "Isabelle": { - "color": "#FEFE00", - "url": "https://github.com/trending?l=Isabelle" - }, - "J": { - "color": "#9EEDFF", - "url": "https://github.com/trending?l=J" - }, - "JFlex": { - "color": "#DBCA00", - "url": "https://github.com/trending?l=JFlex" - }, - "JSON with Comments": { - "color": null, - "url": "https://github.com/trending?l=JSON-with Comments" - }, - "JSON": { - "color": null, - "url": "https://github.com/trending?l=JSON" - }, - "JSON5": { - "color": null, - "url": "https://github.com/trending?l=JSON5" - }, - "JSONLD": { - "color": null, - "url": "https://github.com/trending?l=JSONLD" - }, - "JSONiq": { - "color": "#40d47e", - "url": "https://github.com/trending?l=JSONiq" - }, - "Jasmin": { - "color": null, - "url": "https://github.com/trending?l=Jasmin" - }, - "Java Properties": { - "color": null, - "url": "https://github.com/trending?l=Java-Properties" - }, - "Java Server Pages": { - "color": null, - "url": "https://github.com/trending?l=Java-Server-Pages" - }, - "Java": { - "color": "#b07219", - "url": "https://github.com/trending?l=Java" - }, - "JavaScript+ERB": { - "color": null, - "url": "https://github.com/trending?l=JavaScript+ERB" - }, - "JavaScript": { - "color": "#f1e05a", - "url": "https://github.com/trending?l=JavaScript" - }, - "Jinja": { - "color": "#a52a22", - "url": "https://github.com/trending?l=Jinja" - }, - "Jison Lex": { - "color": null, - "url": "https://github.com/trending?l=Jison-Lex" - }, - "Jison": { - "color": null, - "url": "https://github.com/trending?l=Jison" - }, - "Jolie": { - "color": "#843179", - "url": "https://github.com/trending?l=Jolie" - }, - "Jsonnet": { - "color": "#0064bd", - "url": "https://github.com/trending?l=Jsonnet" - }, - "Julia": { - "color": "#a270ba", - "url": "https://github.com/trending?l=Julia" - }, - "Jupyter Notebook": { - "color": "#DA5B0B", - "url": "https://github.com/trending?l=Jupyter-Notebook" - }, - "KRL": { - "color": "#28430A", - "url": "https://github.com/trending?l=KRL" - }, - "Kaitai Struct": { - "color": "#773b37", - "url": "https://github.com/trending?l=Kaitai-Struct" - }, - "KiCad Layout": { - "color": null, - "url": "https://github.com/trending?l=KiCad-Layout" - }, - "KiCad Legacy Layout": { - "color": null, - "url": "https://github.com/trending?l=KiCad-Legacy Layout" - }, - "KiCad Schematic": { - "color": null, - "url": "https://github.com/trending?l=KiCad-Schematic" - }, - "Kit": { - "color": null, - "url": "https://github.com/trending?l=Kit" - }, - "Kotlin": { - "color": "#A97BFF", - "url": "https://github.com/trending?l=Kotlin" - }, - "Kusto": { - "color": null, - "url": "https://github.com/trending?l=Kusto" - }, - "LFE": { - "color": "#4C3023", - "url": "https://github.com/trending?l=LFE" - }, - "LLVM": { - "color": "#185619", - "url": "https://github.com/trending?l=LLVM" - }, - "LOLCODE": { - "color": "#cc9900", - "url": "https://github.com/trending?l=LOLCODE" - }, - "LSL": { - "color": "#3d9970", - "url": "https://github.com/trending?l=LSL" - }, - "LTspice Symbol": { - "color": null, - "url": "https://github.com/trending?l=LTspice-Symbol" - }, - "LabVIEW": { - "color": null, - "url": "https://github.com/trending?l=LabVIEW" - }, - "Lark": { - "color": "#2980B9", - "url": "https://github.com/trending?l=Lark" - }, - "Lasso": { - "color": "#999999", - "url": "https://github.com/trending?l=Lasso" - }, - "Latte": { - "color": "#f2a542", - "url": "https://github.com/trending?l=Latte" - }, - "Lean": { - "color": null, - "url": "https://github.com/trending?l=Lean" - }, - "Less": { - "color": "#1d365d", - "url": "https://github.com/trending?l=Less" - }, - "Lex": { - "color": "#DBCA00", - "url": "https://github.com/trending?l=Lex" - }, - "LilyPond": { - "color": null, - "url": "https://github.com/trending?l=LilyPond" - }, - "Limbo": { - "color": null, - "url": "https://github.com/trending?l=Limbo" - }, - "Linker Script": { - "color": null, - "url": "https://github.com/trending?l=Linker-Script" - }, - "Linux Kernel Module": { - "color": null, - "url": "https://github.com/trending?l=Linux-Kernel Module" - }, - "Liquid": { - "color": "#67b8de", - "url": "https://github.com/trending?l=Liquid" - }, - "Literate Agda": { - "color": null, - "url": "https://github.com/trending?l=Literate-Agda" - }, - "Literate CoffeeScript": { - "color": null, - "url": "https://github.com/trending?l=Literate-CoffeeScript" - }, - "Literate Haskell": { - "color": null, - "url": "https://github.com/trending?l=Literate-Haskell" - }, - "LiveScript": { - "color": "#499886", - "url": "https://github.com/trending?l=LiveScript" - }, - "Logos": { - "color": null, - "url": "https://github.com/trending?l=Logos" - }, - "Logtalk": { - "color": null, - "url": "https://github.com/trending?l=Logtalk" - }, - "LookML": { - "color": "#652B81", - "url": "https://github.com/trending?l=LookML" - }, - "LoomScript": { - "color": null, - "url": "https://github.com/trending?l=LoomScript" - }, - "Lua": { - "color": "#000080", - "url": "https://github.com/trending?l=Lua" - }, - "M": { - "color": null, - "url": "https://github.com/trending?l=M" - }, - "M4": { - "color": null, - "url": "https://github.com/trending?l=M4" - }, - "M4Sugar": { - "color": null, - "url": "https://github.com/trending?l=M4Sugar" - }, - "MATLAB": { - "color": "#e16737", - "url": "https://github.com/trending?l=MATLAB" - }, - "MAXScript": { - "color": "#00a6a6", - "url": "https://github.com/trending?l=MAXScript" - }, - "MLIR": { - "color": "#5EC8DB", - "url": "https://github.com/trending?l=MLIR" - }, - "MQL4": { - "color": "#62A8D6", - "url": "https://github.com/trending?l=MQL4" - }, - "MQL5": { - "color": "#4A76B8", - "url": "https://github.com/trending?l=MQL5" - }, - "MTML": { - "color": "#b7e1f4", - "url": "https://github.com/trending?l=MTML" - }, - "MUF": { - "color": null, - "url": "https://github.com/trending?l=MUF" - }, - "Macaulay2": { - "color": "#d8ffff", - "url": "https://github.com/trending?l=Macaulay2" - }, - "Makefile": { - "color": "#427819", - "url": "https://github.com/trending?l=Makefile" - }, - "Mako": { - "color": null, - "url": "https://github.com/trending?l=Mako" - }, - "Markdown": { - "color": "#083fa1", - "url": "https://github.com/trending?l=Markdown" - }, - "Marko": { - "color": "#42bff2", - "url": "https://github.com/trending?l=Marko" - }, - "Mask": { - "color": "#f97732", - "url": "https://github.com/trending?l=Mask" - }, - "Mathematica": { - "color": null, - "url": "https://github.com/trending?l=Mathematica" - }, - "Maven POM": { - "color": null, - "url": "https://github.com/trending?l=Maven-POM" - }, - "Max": { - "color": "#c4a79c", - "url": "https://github.com/trending?l=Max" - }, - "MediaWiki": { - "color": null, - "url": "https://github.com/trending?l=MediaWiki" - }, - "Mercury": { - "color": "#ff2b2b", - "url": "https://github.com/trending?l=Mercury" - }, - "Meson": { - "color": "#007800", - "url": "https://github.com/trending?l=Meson" - }, - "Metal": { - "color": "#8f14e9", - "url": "https://github.com/trending?l=Metal" - }, - "Microsoft Developer Studio Project": { - "color": null, - "url": "https://github.com/trending?l=Microsoft-Developer Studio Project" - }, - "MiniD": { - "color": null, - "url": "https://github.com/trending?l=MiniD" - }, - "Mirah": { - "color": "#c7a938", - "url": "https://github.com/trending?l=Mirah" - }, - "Modelica": { - "color": null, - "url": "https://github.com/trending?l=Modelica" - }, - "Modula-2": { - "color": null, - "url": "https://github.com/trending?l=Modula-2" - }, - "Modula-3": { - "color": "#223388", - "url": "https://github.com/trending?l=Modula-3" - }, - "Module Management System": { - "color": null, - "url": "https://github.com/trending?l=Module-Management-System" - }, - "Monkey": { - "color": null, - "url": "https://github.com/trending?l=Monkey" - }, - "Moocode": { - "color": null, - "url": "https://github.com/trending?l=Moocode" - }, - "MoonScript": { - "color": null, - "url": "https://github.com/trending?l=MoonScript" - }, - "Motorola 68K Assembly": { - "color": null, - "url": "https://github.com/trending?l=Motorola-68K-Assembly" - }, - "Muse": { - "color": null, - "url": "https://github.com/trending?l=Muse" - }, - "Mustache": { - "color": "#724b3b", - "url": "https://github.com/trending?l=Mustache" - }, - "Myghty": { - "color": null, - "url": "https://github.com/trending?l=Myghty" - }, - "NASL": { - "color": null, - "url": "https://github.com/trending?l=NASL" - }, - "NCL": { - "color": "#28431f", - "url": "https://github.com/trending?l=NCL" - }, - "NEON": { - "color": null, - "url": "https://github.com/trending?l=NEON" - }, - "NL": { - "color": null, - "url": "https://github.com/trending?l=NL" - }, - "NPM Config": { - "color": null, - "url": "https://github.com/trending?l=NPM-Config" - }, - "NSIS": { - "color": null, - "url": "https://github.com/trending?l=NSIS" - }, - "NWScript": { - "color": "#111522", - "url": "https://github.com/trending?l=NWScript" - }, - "Nearley": { - "color": "#990000", - "url": "https://github.com/trending?l=Nearley" - }, - "Nemerle": { - "color": "#3d3c6e", - "url": "https://github.com/trending?l=Nemerle" - }, - "NetLinx+ERB": { - "color": "#747faa", - "url": "https://github.com/trending?l=NetLinx+ERB" - }, - "NetLinx": { - "color": "#0aa0ff", - "url": "https://github.com/trending?l=NetLinx" - }, - "NetLogo": { - "color": "#ff6375", - "url": "https://github.com/trending?l=NetLogo" - }, - "NewLisp": { - "color": "#87AED7", - "url": "https://github.com/trending?l=NewLisp" - }, - "Nextflow": { - "color": "#3ac486", - "url": "https://github.com/trending?l=Nextflow" - }, - "Nginx": { - "color": null, - "url": "https://github.com/trending?l=Nginx" - }, - "Nim": { - "color": "#ffc200", - "url": "https://github.com/trending?l=Nim" - }, - "Ninja": { - "color": null, - "url": "https://github.com/trending?l=Ninja" - }, - "Nit": { - "color": "#009917", - "url": "https://github.com/trending?l=Nit" - }, - "Nix": { - "color": "#7e7eff", - "url": "https://github.com/trending?l=Nix" - }, - "Nu": { - "color": "#c9df40", - "url": "https://github.com/trending?l=Nu" - }, - "NumPy": { - "color": "#9C8AF9", - "url": "https://github.com/trending?l=NumPy" - }, - "Nunjucks": { - "color": "#3d8137", - "url": "https://github.com/trending?l=Nunjucks" - }, - "OCaml": { - "color": "#3be133", - "url": "https://github.com/trending?l=OCaml" - }, - "ObjDump": { - "color": null, - "url": "https://github.com/trending?l=ObjDump" - }, - "Object Data Instance Notation": { - "color": null, - "url": "https://github.com/trending?l=Object-Data Instance Notation" - }, - "ObjectScript": { - "color": "#424893", - "url": "https://github.com/trending?l=ObjectScript" - }, - "Objective-C++": { - "color": "#6866fb", - "url": "https://github.com/trending?l=Objective-C++" - }, - "Objective-C": { - "color": "#438eff", - "url": "https://github.com/trending?l=Objective-C" - }, - "Objective-J": { - "color": "#ff0c5a", - "url": "https://github.com/trending?l=Objective-J" - }, - "Odin": { - "color": "#60AFFE", - "url": "https://github.com/trending?l=Odin" - }, - "Omgrofl": { - "color": "#cabbff", - "url": "https://github.com/trending?l=Omgrofl" - }, - "Opa": { - "color": null, - "url": "https://github.com/trending?l=Opa" - }, - "Opal": { - "color": "#f7ede0", - "url": "https://github.com/trending?l=Opal" - }, - "Open Policy Agent": { - "color": null, - "url": "https://github.com/trending?l=Open-Policy-Agent" - }, - "OpenCL": { - "color": null, - "url": "https://github.com/trending?l=OpenCL" - }, - "OpenEdge ABL": { - "color": null, - "url": "https://github.com/trending?l=OpenEdge-ABL" - }, - "OpenQASM": { - "color": "#AA70FF", - "url": "https://github.com/trending?l=OpenQASM" - }, - "OpenRC runscript": { - "color": null, - "url": "https://github.com/trending?l=OpenRC-runscript" - }, - "OpenSCAD": { - "color": null, - "url": "https://github.com/trending?l=OpenSCAD" - }, - "OpenStep Property List": { - "color": null, - "url": "https://github.com/trending?l=OpenStep-Property List" - }, - "OpenType Feature File": { - "color": null, - "url": "https://github.com/trending?l=OpenType-Feature File" - }, - "Org": { - "color": "#77aa99", - "url": "https://github.com/trending?l=Org" - }, - "Ox": { - "color": null, - "url": "https://github.com/trending?l=Ox" - }, - "Oxygene": { - "color": "#cdd0e3", - "url": "https://github.com/trending?l=Oxygene" - }, - "Oz": { - "color": "#fab738", - "url": "https://github.com/trending?l=Oz" - }, - "P4": { - "color": "#7055b5", - "url": "https://github.com/trending?l=P4" - }, - "PHP": { - "color": "#4F5D95", - "url": "https://github.com/trending?l=PHP" - }, - "PLSQL": { - "color": "#dad8d8", - "url": "https://github.com/trending?l=PLSQL" - }, - "PLpgSQL": { - "color": null, - "url": "https://github.com/trending?l=PLpgSQL" - }, - "POV-Ray SDL": { - "color": null, - "url": "https://github.com/trending?l=POV-Ray-SDL" - }, - "Pan": { - "color": "#cc0000", - "url": "https://github.com/trending?l=Pan" - }, - "Papyrus": { - "color": "#6600cc", - "url": "https://github.com/trending?l=Papyrus" - }, - "Parrot Assembly": { - "color": null, - "url": "https://github.com/trending?l=Parrot-Assembly" - }, - "Parrot Internal Representation": { - "color": null, - "url": "https://github.com/trending?l=Parrot-Internal-Representation" - }, - "Parrot": { - "color": "#f3ca0a", - "url": "https://github.com/trending?l=Parrot" - }, - "Pascal": { - "color": "#E3F171", - "url": "https://github.com/trending?l=Pascal" - }, - "Pawn": { - "color": "#dbb284", - "url": "https://github.com/trending?l=Pawn" - }, - "Pep8": { - "color": "#C76F5B", - "url": "https://github.com/trending?l=Pep8" - }, - "Perl": { - "color": "#0298c3", - "url": "https://github.com/trending?l=Perl" - }, - "Pic": { - "color": null, - "url": "https://github.com/trending?l=Pic" - }, - "Pickle": { - "color": null, - "url": "https://github.com/trending?l=Pickle" - }, - "PicoLisp": { - "color": null, - "url": "https://github.com/trending?l=PicoLisp" - }, - "PigLatin": { - "color": "#fcd7de", - "url": "https://github.com/trending?l=PigLatin" - }, - "Pike": { - "color": "#005390", - "url": "https://github.com/trending?l=Pike" - }, - "PlantUML": { - "color": null, - "url": "https://github.com/trending?l=PlantUML" - }, - "Pod 6": { - "color": null, - "url": "https://github.com/trending?l=Pod-6" - }, - "Pod": { - "color": null, - "url": "https://github.com/trending?l=Pod" - }, - "PogoScript": { - "color": "#d80074", - "url": "https://github.com/trending?l=PogoScript" - }, - "Pony": { - "color": null, - "url": "https://github.com/trending?l=Pony" - }, - "PostCSS": { - "color": null, - "url": "https://github.com/trending?l=PostCSS" - }, - "PostScript": { - "color": "#da291c", - "url": "https://github.com/trending?l=PostScript" - }, - "PowerBuilder": { - "color": "#8f0f8d", - "url": "https://github.com/trending?l=PowerBuilder" - }, - "PowerShell": { - "color": "#012456", - "url": "https://github.com/trending?l=PowerShell" - }, - "Prisma": { - "color": "#0c344b", - "url": "https://github.com/trending?l=Prisma" - }, - "Processing": { - "color": "#0096D8", - "url": "https://github.com/trending?l=Processing" - }, - "Proguard": { - "color": null, - "url": "https://github.com/trending?l=Proguard" - }, - "Prolog": { - "color": "#74283c", - "url": "https://github.com/trending?l=Prolog" - }, - "Propeller Spin": { - "color": "#7fa2a7", - "url": "https://github.com/trending?l=Propeller-Spin" - }, - "Protocol Buffer": { - "color": null, - "url": "https://github.com/trending?l=Protocol-Buffer" - }, - "Public Key": { - "color": null, - "url": "https://github.com/trending?l=Public-Key" - }, - "Pug": { - "color": "#a86454", - "url": "https://github.com/trending?l=Pug" - }, - "Puppet": { - "color": "#302B6D", - "url": "https://github.com/trending?l=Puppet" - }, - "Pure Data": { - "color": null, - "url": "https://github.com/trending?l=Pure-Data" - }, - "PureBasic": { - "color": "#5a6986", - "url": "https://github.com/trending?l=PureBasic" - }, - "PureScript": { - "color": "#1D222D", - "url": "https://github.com/trending?l=PureScript" - }, - "Python console": { - "color": null, - "url": "https://github.com/trending?l=Python-console" - }, - "Python traceback": { - "color": null, - "url": "https://github.com/trending?l=Python-traceback" - }, - "Python": { - "color": "#3572A5", - "url": "https://github.com/trending?l=Python" - }, - "Q#": { - "color": "#fed659", - "url": "https://github.com/trending?l=Qsharp" - }, - "QML": { - "color": "#44a51c", - "url": "https://github.com/trending?l=QML" - }, - "QMake": { - "color": null, - "url": "https://github.com/trending?l=QMake" - }, - "Qt Script": { - "color": "#00b841", - "url": "https://github.com/trending?l=Qt-Script" - }, - "Quake": { - "color": "#882233", - "url": "https://github.com/trending?l=Quake" - }, - "R": { - "color": "#198CE7", - "url": "https://github.com/trending?l=R" - }, - "RAML": { - "color": "#77d9fb", - "url": "https://github.com/trending?l=RAML" - }, - "RDoc": { - "color": null, - "url": "https://github.com/trending?l=RDoc" - }, - "REALbasic": { - "color": null, - "url": "https://github.com/trending?l=REALbasic" - }, - "REXX": { - "color": null, - "url": "https://github.com/trending?l=REXX" - }, - "RMarkdown": { - "color": null, - "url": "https://github.com/trending?l=RMarkdown" - }, - "RPC": { - "color": null, - "url": "https://github.com/trending?l=RPC" - }, - "RPM Spec": { - "color": null, - "url": "https://github.com/trending?l=RPM-Spec" - }, - "RUNOFF": { - "color": "#665a4e", - "url": "https://github.com/trending?l=RUNOFF" - }, - "Racket": { - "color": "#3c5caa", - "url": "https://github.com/trending?l=Racket" - }, - "Ragel": { - "color": "#9d5200", - "url": "https://github.com/trending?l=Ragel" - }, - "Raku": { - "color": "#0000fb", - "url": "https://github.com/trending?l=Raku" - }, - "Rascal": { - "color": "#fffaa0", - "url": "https://github.com/trending?l=Rascal" - }, - "Raw token data": { - "color": null, - "url": "https://github.com/trending?l=Raw-token data" - }, - "ReScript": { - "color": "#ed5051", - "url": "https://github.com/trending?l=ReScript" - }, - "Readline Config": { - "color": null, - "url": "https://github.com/trending?l=Readline-Config" - }, - "Reason": { - "color": "#ff5847", - "url": "https://github.com/trending?l=Reason" - }, - "Rebol": { - "color": "#358a5b", - "url": "https://github.com/trending?l=Rebol" - }, - "Record Jar": { - "color": "#0673ba", - "url": "https://github.com/trending?l=Record-Jar" - }, - "Red": { - "color": "#f50000", - "url": "https://github.com/trending?l=Red" - }, - "Redcode": { - "color": null, - "url": "https://github.com/trending?l=Redcode" - }, - "Redirect Rules": { - "color": null, - "url": "https://github.com/trending?l=Redirect-Rules" - }, - "Regular Expression": { - "color": null, - "url": "https://github.com/trending?l=Regular-Expression" - }, - "Ren'Py": { - "color": "#ff7f7f", - "url": "https://github.com/trending?l=Ren'Py" - }, - "RenderScript": { - "color": null, - "url": "https://github.com/trending?l=RenderScript" - }, - "Rich Text Format": { - "color": null, - "url": "https://github.com/trending?l=Rich-Text Format" - }, - "Ring": { - "color": "#2D54CB", - "url": "https://github.com/trending?l=Ring" - }, - "Riot": { - "color": "#A71E49", - "url": "https://github.com/trending?l=Riot" - }, - "RobotFramework": { - "color": null, - "url": "https://github.com/trending?l=RobotFramework" - }, - "Roff Manpage": { - "color": null, - "url": "https://github.com/trending?l=Roff-Manpage" - }, - "Roff": { - "color": "#ecdebe", - "url": "https://github.com/trending?l=Roff" - }, - "Rouge": { - "color": "#cc0088", - "url": "https://github.com/trending?l=Rouge" - }, - "Ruby": { - "color": "#701516", - "url": "https://github.com/trending?l=Ruby" - }, - "Rust": { - "color": "#dea584", - "url": "https://github.com/trending?l=Rust" - }, - "SAS": { - "color": "#B34936", - "url": "https://github.com/trending?l=SAS" - }, - "SCSS": { - "color": "#c6538c", - "url": "https://github.com/trending?l=SCSS" - }, - "SMT": { - "color": null, - "url": "https://github.com/trending?l=SMT" - }, - "SPARQL": { - "color": null, - "url": "https://github.com/trending?l=SPARQL" - }, - "SQF": { - "color": "#3F3F3F", - "url": "https://github.com/trending?l=SQF" - }, - "SQL": { - "color": null, - "url": "https://github.com/trending?l=SQL" - }, - "SQLPL": { - "color": null, - "url": "https://github.com/trending?l=SQLPL" - }, - "SRecode Template": { - "color": "#348a34", - "url": "https://github.com/trending?l=SRecode-Template" - }, - "SSH Config": { - "color": null, - "url": "https://github.com/trending?l=SSH-Config" - }, - "STON": { - "color": null, - "url": "https://github.com/trending?l=STON" - }, - "SVG": { - "color": "#ff9900", - "url": "https://github.com/trending?l=SVG" - }, - "SWIG": { - "color": null, - "url": "https://github.com/trending?l=SWIG" - }, - "Sage": { - "color": null, - "url": "https://github.com/trending?l=Sage" - }, - "SaltStack": { - "color": "#646464", - "url": "https://github.com/trending?l=SaltStack" - }, - "Sass": { - "color": "#a53b70", - "url": "https://github.com/trending?l=Sass" - }, - "Scala": { - "color": "#c22d40", - "url": "https://github.com/trending?l=Scala" - }, - "Scaml": { - "color": "#bd181a", - "url": "https://github.com/trending?l=Scaml" - }, - "Scheme": { - "color": "#1e4aec", - "url": "https://github.com/trending?l=Scheme" - }, - "Scilab": { - "color": null, - "url": "https://github.com/trending?l=Scilab" - }, - "Self": { - "color": "#0579aa", - "url": "https://github.com/trending?l=Self" - }, - "ShaderLab": { - "color": null, - "url": "https://github.com/trending?l=ShaderLab" - }, - "Shell": { - "color": "#89e051", - "url": "https://github.com/trending?l=Shell" - }, - "ShellSession": { - "color": null, - "url": "https://github.com/trending?l=ShellSession" - }, - "Shen": { - "color": "#120F14", - "url": "https://github.com/trending?l=Shen" - }, - "Sieve": { - "color": null, - "url": "https://github.com/trending?l=Sieve" - }, - "Singularity": { - "color": "#64E6AD", - "url": "https://github.com/trending?l=Singularity" - }, - "Slash": { - "color": "#007eff", - "url": "https://github.com/trending?l=Slash" - }, - "Slice": { - "color": "#003fa2", - "url": "https://github.com/trending?l=Slice" - }, - "Slim": { - "color": "#2b2b2b", - "url": "https://github.com/trending?l=Slim" - }, - "SmPL": { - "color": "#c94949", - "url": "https://github.com/trending?l=SmPL" - }, - "Smali": { - "color": null, - "url": "https://github.com/trending?l=Smali" - }, - "Smalltalk": { - "color": "#596706", - "url": "https://github.com/trending?l=Smalltalk" - }, - "Smarty": { - "color": null, - "url": "https://github.com/trending?l=Smarty" - }, - "Solidity": { - "color": "#AA6746", - "url": "https://github.com/trending?l=Solidity" - }, - "SourcePawn": { - "color": "#f69e1d", - "url": "https://github.com/trending?l=SourcePawn" - }, - "Spline Font Database": { - "color": null, - "url": "https://github.com/trending?l=Spline-Font Database" - }, - "Squirrel": { - "color": "#800000", - "url": "https://github.com/trending?l=Squirrel" - }, - "Stan": { - "color": "#b2011d", - "url": "https://github.com/trending?l=Stan" - }, - "Standard ML": { - "color": "#dc566d", - "url": "https://github.com/trending?l=Standard-ML" - }, - "Starlark": { - "color": "#76d275", - "url": "https://github.com/trending?l=Starlark" - }, - "Stata": { - "color": null, - "url": "https://github.com/trending?l=Stata" - }, - "StringTemplate": { - "color": "#3fb34f", - "url": "https://github.com/trending?l=StringTemplate" - }, - "Stylus": { - "color": "#ff6347", - "url": "https://github.com/trending?l=Stylus" - }, - "SubRip Text": { - "color": null, - "url": "https://github.com/trending?l=SubRip-Text" - }, - "SugarSS": { - "color": null, - "url": "https://github.com/trending?l=SugarSS" - }, - "SuperCollider": { - "color": "#46390b", - "url": "https://github.com/trending?l=SuperCollider" - }, - "Svelte": { - "color": "#ff3e00", - "url": "https://github.com/trending?l=Svelte" - }, - "Swift": { - "color": "#ffac45", - "url": "https://github.com/trending?l=Swift" - }, - "SystemVerilog": { - "color": "#DAE1C2", - "url": "https://github.com/trending?l=SystemVerilog" - }, - "TI Program": { - "color": "#A0AA87", - "url": "https://github.com/trending?l=TI-Program" - }, - "TLA": { - "color": null, - "url": "https://github.com/trending?l=TLA" - }, - "TOML": { - "color": null, - "url": "https://github.com/trending?l=TOML" - }, - "TSQL": { - "color": null, - "url": "https://github.com/trending?l=TSQL" - }, - "TSV": { - "color": null, - "url": "https://github.com/trending?l=TSV" - }, - "TSX": { - "color": null, - "url": "https://github.com/trending?l=TSX" - }, - "TXL": { - "color": null, - "url": "https://github.com/trending?l=TXL" - }, - "Tcl": { - "color": "#e4cc98", - "url": "https://github.com/trending?l=Tcl" - }, - "Tcsh": { - "color": null, - "url": "https://github.com/trending?l=Tcsh" - }, - "TeX": { - "color": "#3D6117", - "url": "https://github.com/trending?l=TeX" - }, - "Tea": { - "color": null, - "url": "https://github.com/trending?l=Tea" - }, - "Terra": { - "color": "#00004c", - "url": "https://github.com/trending?l=Terra" - }, - "Texinfo": { - "color": null, - "url": "https://github.com/trending?l=Texinfo" - }, - "Text": { - "color": null, - "url": "https://github.com/trending?l=Text" - }, - "Textile": { - "color": null, - "url": "https://github.com/trending?l=Textile" - }, - "Thrift": { - "color": null, - "url": "https://github.com/trending?l=Thrift" - }, - "Turing": { - "color": "#cf142b", - "url": "https://github.com/trending?l=Turing" - }, - "Turtle": { - "color": null, - "url": "https://github.com/trending?l=Turtle" - }, - "Twig": { - "color": "#c1d026", - "url": "https://github.com/trending?l=Twig" - }, - "Type Language": { - "color": null, - "url": "https://github.com/trending?l=Type-Language" - }, - "TypeScript": { - "color": "#2b7489", - "url": "https://github.com/trending?l=TypeScript" - }, - "Unified Parallel C": { - "color": "#4e3617", - "url": "https://github.com/trending?l=Unified-Parallel-C" - }, - "Unity3D Asset": { - "color": null, - "url": "https://github.com/trending?l=Unity3D-Asset" - }, - "Unix Assembly": { - "color": null, - "url": "https://github.com/trending?l=Unix-Assembly" - }, - "Uno": { - "color": "#9933cc", - "url": "https://github.com/trending?l=Uno" - }, - "UnrealScript": { - "color": "#a54c4d", - "url": "https://github.com/trending?l=UnrealScript" - }, - "UrWeb": { - "color": null, - "url": "https://github.com/trending?l=UrWeb" - }, - "V": { - "color": "#4f87c4", - "url": "https://github.com/trending?l=V" - }, - "VBA": { - "color": "#867db1", - "url": "https://github.com/trending?l=VBA" - }, - "VBScript": { - "color": "#15dcdc", - "url": "https://github.com/trending?l=VBScript" - }, - "VCL": { - "color": "#148AA8", - "url": "https://github.com/trending?l=VCL" - }, - "VHDL": { - "color": "#adb2cb", - "url": "https://github.com/trending?l=VHDL" - }, - "Vala": { - "color": "#fbe5cd", - "url": "https://github.com/trending?l=Vala" - }, - "Verilog": { - "color": "#b2b7f8", - "url": "https://github.com/trending?l=Verilog" - }, - "Vim Help File": { - "color": null, - "url": "https://github.com/trending?l=Vim-Help File" - }, - "Vim Snippet": { - "color": null, - "url": "https://github.com/trending?l=Vim-Snippet" - }, - "Vim script": { - "color": "#199f4b", - "url": "https://github.com/trending?l=Vim-script" - }, - "Visual Basic .NET": { - "color": "#945db7", - "url": "https://github.com/trending?l=Visual-Basic-.NET" - }, - "Volt": { - "color": "#1F1F1F", - "url": "https://github.com/trending?l=Volt" - }, - "Vue": { - "color": "#2c3e50", - "url": "https://github.com/trending?l=Vue" - }, - "Wavefront Material": { - "color": null, - "url": "https://github.com/trending?l=Wavefront-Material" - }, - "Wavefront Object": { - "color": null, - "url": "https://github.com/trending?l=Wavefront-Object" - }, - "Web Ontology Language": { - "color": null, - "url": "https://github.com/trending?l=Web-Ontology Language" - }, - "WebAssembly": { - "color": "#04133b", - "url": "https://github.com/trending?l=WebAssembly" - }, - "WebIDL": { - "color": null, - "url": "https://github.com/trending?l=WebIDL" - }, - "WebVTT": { - "color": null, - "url": "https://github.com/trending?l=WebVTT" - }, - "Wget Config": { - "color": null, - "url": "https://github.com/trending?l=Wget-Config" - }, - "Windows Registry Entries": { - "color": null, - "url": "https://github.com/trending?l=Windows-Registry Entries" - }, - "Wollok": { - "color": "#a23738", - "url": "https://github.com/trending?l=Wollok" - }, - "World of Warcraft Addon Data": { - "color": null, - "url": "https://github.com/trending?l=World-of Warcraft Addon Data" - }, - "X BitMap": { - "color": null, - "url": "https://github.com/trending?l=X-BitMap" - }, - "X Font Directory Index": { - "color": null, - "url": "https://github.com/trending?l=X-Font Directory Index" - }, - "X PixMap": { - "color": null, - "url": "https://github.com/trending?l=X-PixMap" - }, - "X10": { - "color": "#4B6BEF", - "url": "https://github.com/trending?l=X10" - }, - "XC": { - "color": "#99DA07", - "url": "https://github.com/trending?l=XC" - }, - "XCompose": { - "color": null, - "url": "https://github.com/trending?l=XCompose" - }, - "XML Property List": { - "color": null, - "url": "https://github.com/trending?l=XML-Property List" - }, - "XML": { - "color": null, - "url": "https://github.com/trending?l=XML" - }, - "XPages": { - "color": null, - "url": "https://github.com/trending?l=XPages" - }, - "XProc": { - "color": null, - "url": "https://github.com/trending?l=XProc" - }, - "XQuery": { - "color": "#5232e7", - "url": "https://github.com/trending?l=XQuery" - }, - "XS": { - "color": null, - "url": "https://github.com/trending?l=XS" - }, - "XSLT": { - "color": "#EB8CEB", - "url": "https://github.com/trending?l=XSLT" - }, - "Xojo": { - "color": null, - "url": "https://github.com/trending?l=Xojo" - }, - "Xonsh": { - "color": "#285EEF", - "url": "https://github.com/trending?l=Xonsh" - }, - "Xtend": { - "color": null, - "url": "https://github.com/trending?l=Xtend" - }, - "YAML": { - "color": "#cb171e", - "url": "https://github.com/trending?l=YAML" - }, - "YANG": { - "color": null, - "url": "https://github.com/trending?l=YANG" - }, - "YARA": { - "color": "#220000", - "url": "https://github.com/trending?l=YARA" - }, - "YASnippet": { - "color": "#32AB90", - "url": "https://github.com/trending?l=YASnippet" - }, - "Yacc": { - "color": "#4B6C4B", - "url": "https://github.com/trending?l=Yacc" - }, - "ZAP": { - "color": "#0d665e", - "url": "https://github.com/trending?l=ZAP" - }, - "ZIL": { - "color": "#dc75e5", - "url": "https://github.com/trending?l=ZIL" - }, - "Zeek": { - "color": null, - "url": "https://github.com/trending?l=Zeek" - }, - "ZenScript": { - "color": "#00BCD1", - "url": "https://github.com/trending?l=ZenScript" - }, - "Zephir": { - "color": "#118f9e", - "url": "https://github.com/trending?l=Zephir" - }, - "Zig": { - "color": "#ec915c", - "url": "https://github.com/trending?l=Zig" - }, - "Zimpl": { - "color": null, - "url": "https://github.com/trending?l=Zimpl" - }, - "cURL Config": { - "color": null, - "url": "https://github.com/trending?l=cURL-Config" - }, - "desktop": { - "color": null, - "url": "https://github.com/trending?l=desktop" - }, - "dircolors": { - "color": null, - "url": "https://github.com/trending?l=dircolors" - }, - "eC": { - "color": "#913960", - "url": "https://github.com/trending?l=eC" - }, - "edn": { - "color": null, - "url": "https://github.com/trending?l=edn" - }, - "fish": { - "color": null, - "url": "https://github.com/trending?l=fish" - }, - "jq": { - "color": "#c7254e", - "url": "https://github.com/trending?l=jq" - }, - "mIRC Script": { - "color": "#3d57c3", - "url": "https://github.com/trending?l=mIRC-Script" - }, - "mcfunction": { - "color": "#E22837", - "url": "https://github.com/trending?l=mcfunction" - }, - "mupad": { - "color": null, - "url": "https://github.com/trending?l=mupad" - }, - "nanorc": { - "color": null, - "url": "https://github.com/trending?l=nanorc" - }, - "nesC": { - "color": "#94B0C7", - "url": "https://github.com/trending?l=nesC" - }, - "ooc": { - "color": "#b0b77e", - "url": "https://github.com/trending?l=ooc" - }, - "q": { - "color": "#0040cd", - "url": "https://github.com/trending?l=q" - }, - "reStructuredText": { - "color": null, - "url": "https://github.com/trending?l=reStructuredText" - }, - "robots.txt": { - "color": null, - "url": "https://github.com/trending?l=robots.txt" - }, - "sed": { - "color": "#64b970", - "url": "https://github.com/trending?l=sed" - }, - "wdl": { - "color": "#42f1f4", - "url": "https://github.com/trending?l=wdl" - }, - "wisp": { - "color": "#7582D1", - "url": "https://github.com/trending?l=wisp" - }, - "xBase": { - "color": "#403a40", - "url": "https://github.com/trending?l=xBase" - } -} diff --git a/loc.py b/loc.py deleted file mode 100644 index 5e98026..0000000 --- a/loc.py +++ /dev/null @@ -1,107 +0,0 @@ -import re -import os -import base64 -import requests -from github import Github, InputGitAuthor -import datetime -from string import Template -import matplotlib.pyplot as plt -from io import StringIO, BytesIO -from dotenv import load_dotenv -import time - -from make_bar_graph import build_graph - - -class LinesOfCode: - - def __init__(self, id, username, ghtoken, repositoryData, ignored_repos): - self.id = id - self.username = username - - self.g = Github(ghtoken) - self.headers = {"Authorization": "Bearer " + ghtoken} - self.repositoryData = repositoryData - self.ignored_repos = ignored_repos - - def calculateLoc(self): - result = self.repositoryData - yearly_data = {} - for repo in result['data']['user']['repositories']['edges']: - if repo['node']['name'] not in self.ignored_repos: - self.getCommitStat(repo['node'], yearly_data) - time.sleep(0.7) - return yearly_data - - def plotLoc(self, yearly_data): - build_graph(yearly_data) - self.pushChart() - - def run_query_v3(self, endPoint): - # print(endPoint) - request = requests.get(endPoint, headers=self.headers) - if request.status_code == 401: - raise Exception("Invalid token {}.".format(request.status_code)) - elif request.status_code == 204: - return [] - else: - return request.json() - - def getQuarter(self, timeStamp): - month = datetime.datetime.fromisoformat(timeStamp).month - if month >= 1 and month <= 3: - return 1 - elif month >= 4 and month <= 6: - return 2 - elif month >= 7 and month <= 9: - return 3 - elif month >= 10 and month <= 12: - return 4 - - def getCommitStat(self, repoDetails, yearly_data): - commitsURL = 'https://api.github.com/repos/' + repoDetails['nameWithOwner'] + '/commits' - filteredCommitsEndPoint = commitsURL + '?author=' + self.username - filteredCommitsResult = self.run_query_v3(filteredCommitsEndPoint) - # This ignores the error message you get when you try to list commits for an empty repository - if not type(filteredCommitsResult) == list: - return - this_year = datetime.datetime.utcnow().year - - for i in range(len(filteredCommitsResult)): - iso_date = filteredCommitsResult[i]["commit"]["author"]["date"] - date = re.search(r'\d+-\d+-\d+', iso_date).group(0) - curr_year = datetime.datetime.fromisoformat(date).year - # if curr_year != this_year: - - individualCommitEndPoint = commitsURL + '/' + filteredCommitsResult[i]["sha"] - individualCommitResult = self.run_query_v3(individualCommitEndPoint) - - quarter = self.getQuarter(date) - if repoDetails['primaryLanguage'] is not None: - - if curr_year not in yearly_data: - yearly_data[curr_year] = {} - if quarter not in yearly_data[curr_year]: - yearly_data[curr_year][quarter] = {} - if repoDetails['primaryLanguage']['name'] not in yearly_data[curr_year][quarter]: - yearly_data[curr_year][quarter][repoDetails['primaryLanguage']['name']] = 0 - yearly_data[curr_year][quarter][repoDetails['primaryLanguage']['name']] += (individualCommitResult["stats"]["additions"] - individualCommitResult["stats"]['deletions']) - - # to find total - - # if 'total' not in yearly_data[curr_year]: - # yearly_data[curr_year]['total']={} - # if repoDetails['primaryLanguage']['name'] not in yearly_data[curr_year]['total']: - # yearly_data[curr_year]['total'][repoDetails['primaryLanguage']['name']]=0 - # yearly_data[curr_year]['total'][repoDetails['primaryLanguage']['name']]+=(result[i][1]+result[i][2]) - - def pushChart(self): - repo = self.g.get_repo(f"{self.username}/{self.username}") - committer = InputGitAuthor('readme-bot', '41898282+github-actions[bot]@users.noreply.github.com') - with open('bar_graph.png', 'rb') as input_file: - data = input_file.read() - try: - contents = repo.get_contents("charts/bar_graph.png") - repo.update_file(contents.path, "Charts Updated", data, contents.sha, committer=committer) - except Exception as e: - repo.create_file("charts/bar_graph.png", "Charts Added", data, committer=committer) diff --git a/main.py b/main.py deleted file mode 100644 index f8c1bc9..0000000 --- a/main.py +++ /dev/null @@ -1,593 +0,0 @@ -''' -Readme Development Metrics With waka time progress -''' -import re -import os -import base64 -from pytz import timezone -import pytz -import requests -from github import Github, GithubException, InputGitAuthor -import datetime -from string import Template -from loc import LinesOfCode -import time -import traceback -import humanize -from urllib.parse import quote -import json -import sys -from datetime import date -import math - -from dotenv import load_dotenv - -load_dotenv() - -START_COMMENT = f'' -END_COMMENT = f'' -listReg = f"{START_COMMENT}[\\s\\S]+{END_COMMENT}" - -waka_key = os.getenv('INPUT_WAKATIME_API_KEY') -ghtoken = os.getenv('INPUT_GH_TOKEN') -branchName = os.getenv('INPUT_PUSH_BRANCH_NAME') -showTimeZone = os.getenv('INPUT_SHOW_TIMEZONE') -showProjects = os.getenv('INPUT_SHOW_PROJECTS') -showEditors = os.getenv('INPUT_SHOW_EDITORS') -showOs = os.getenv('INPUT_SHOW_OS') -showCommit = os.getenv('INPUT_SHOW_COMMIT') -showLanguage = os.getenv('INPUT_SHOW_LANGUAGE') -show_loc = os.getenv('INPUT_SHOW_LINES_OF_CODE') -show_days_of_week = os.getenv('INPUT_SHOW_DAYS_OF_WEEK') -showLanguagePerRepo = os.getenv('INPUT_SHOW_LANGUAGE_PER_REPO') -showLocChart = os.getenv('INPUT_SHOW_LOC_CHART') -show_profile_view = os.getenv('INPUT_SHOW_PROFILE_VIEWS') -show_short_info = os.getenv('INPUT_SHOW_SHORT_INFO') -locale = os.getenv('INPUT_LOCALE') -commit_by_me = os.getenv('INPUT_COMMIT_BY_ME') -ignored_repos_name = str(os.getenv('INPUT_IGNORED_REPOS') or '').replace(' ', '').split(',') -show_updated_date = os.getenv('INPUT_SHOW_UPDATED_DATE') -updated_date_format = os.getenv('INPUT_UPDATED_DATE_FORMAT') -commit_message = os.getenv('INPUT_COMMIT_MESSAGE') -commit_username = os.getenv('INPUT_COMMIT_USERNAME') -commit_email = os.getenv('INPUT_COMMIT_EMAIL') -show_total_code_time = os.getenv('INPUT_SHOW_TOTAL_CODE_TIME') -symbol_version = os.getenv('INPUT_SYMBOL_VERSION').strip() -show_waka_stats = 'y' -# The GraphQL query to get commit data. -userInfoQuery = """ -{ - viewer { - login - email - id - } - } -""" -createContributedRepoQuery = Template("""query { - user(login: "$username") { - repositoriesContributedTo(last: 100, includeUserRepositories: true) { - nodes { - isFork - name - owner { - login - } - } - } - } - } -""") -createCommittedDateQuery = Template(""" -query { - repository(owner: "$owner", name: "$name") { - defaultBranchRef { - target { - ... on Commit { - history(first: 100, author: { id: "$id" }) { - edges { - node { - committedDate - } - } - } - } - } - } - } - } -""") - -get_loc_url = Template("""/repos/$owner/$repo/stats/code_frequency""") -get_profile_view = Template("""/repos/$owner/$repo/traffic/views?per=week""") -get_profile_traffic = Template("""/repos/$owner/$repo/traffic/popular/referrers""") -truthy = ['true', '1', 't', 'y', 'yes'] - - -def run_v3_api(query): - request = requests.get('https://api.github.com' + query, headers=headers) - if request.status_code == 200: - return request.json() - else: - raise Exception( - "Query failed to run by returning code of {}. {},... {}".format(request.status_code, query, - str(request.json()))) - - -repositoryListQuery = Template(""" -{ - user(login: "$username") { - repositories(orderBy: {field: CREATED_AT, direction: ASC}, last: 100, affiliations: [OWNER, COLLABORATOR, ORGANIZATION_MEMBER], isFork: false) { - totalCount - edges { - node { - object(expression:"master") { - ... on Commit { - history (author: { id: "$id" }){ - totalCount - } - } - } - primaryLanguage { - color - name - id - } - stargazers { - totalCount - } - collaborators { - totalCount - } - createdAt - name - owner { - id - login - } - nameWithOwner - } - } - } - location - createdAt - name - } -} -""") - - -def millify(n): - millnames = ['', ' Thousand', ' Million', ' Billion', ' Trillion'] - n = float(n) - millidx = max(0, min(len(millnames) - 1, - int(math.floor(0 - if n == 0 - else math.log10(abs(n)) / 3)))) - - return '{:.0f}{}'.format(n / 10 ** (3 * millidx), millnames[millidx]) - - -def run_query(query): - request = requests.post('https://api.github.com/graphql', json={'query': query}, headers=headers) - if request.status_code == 200: - return request.json() - else: - raise Exception("Query failed to run by returning code of {}. {}".format(request.status_code, query)) - - -def make_graph(percent: float): - '''Make progress graph from API graph''' - if (symbol_version == '1'): # version 1 - done_block = '█' - empty_block = '░' - elif (symbol_version == '2'): # version 2 - done_block = '⣿' - empty_block = '⣀' - elif (symbol_version == '3'): # version 3 - done_block = '⬛' - empty_block = '⬜' - else: - done_block = '█' # default is version 1 - empty_block = '░' - - pc_rnd = round(percent) - return f"{done_block * int(pc_rnd / 4)}{empty_block * int(25 - int(pc_rnd / 4))}" - - -def make_list(data: list): - '''Make List''' - data_list = [] - for l in data[:5]: - ln = len(l['name']) - ln_text = len(l['text']) - percent = "{:05.2f}".format(float(l['percent'])) - op = f"{l['name'][:25]}{' ' * (25 - ln)}{l['text']}{' ' * (20 - ln_text)}{make_graph(l['percent'])} {percent} % " - data_list.append(op) - return '\n'.join(data_list) - - -def make_commit_list(data: list): - '''Make List''' - data_list = [] - for l in data[:7]: - ln = len(l['name']) - ln_text = len(l['text']) - percent = "{:05.2f}".format(float(l['percent'])) - op = f"{l['name']}{' ' * ((15 - ln) + (11 - ln_text))}{l['text']}{' ' * (7)}{make_graph(l['percent'])} {percent} % " - data_list.append(op) - return '\n'.join(data_list) - - -def generate_commit_list(tz): - string = '' - result = run_query(userInfoQuery) # Execute the query - username = result["data"]["viewer"]["login"] - id = result["data"]["viewer"]["id"] - # print("user {}".format(username)) - - result = run_query(createContributedRepoQuery.substitute(username=username)) - nodes = result["data"]["user"]["repositoriesContributedTo"]["nodes"] - repos = [d for d in nodes if d['isFork'] is False] - - morning = 0 # 6 - 12 - daytime = 0 # 12 - 18 - evening = 0 # 18 - 24 - night = 0 # 0 - 6 - - Monday = 0 - Tuesday = 0 - Wednesday = 0 - Thursday = 0 - Friday = 0 - Saturday = 0 - Sunday = 0 - - for repository in repos: - result = run_query( - createCommittedDateQuery.substitute(owner=repository["owner"]["login"], name=repository["name"], id=id)) - try: - committed_dates = result["data"]["repository"]["defaultBranchRef"]["target"]["history"]["edges"] - for committedDate in committed_dates: - date = datetime.datetime.strptime(committedDate["node"]["committedDate"], - "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=pytz.utc).astimezone( - timezone(tz)) - hour = date.hour - weekday = date.strftime('%A') - if 6 <= hour < 12: - morning += 1 - if 12 <= hour < 18: - daytime += 1 - if 18 <= hour < 24: - evening += 1 - if 0 <= hour < 6: - night += 1 - - if weekday == "Monday": - Monday += 1 - if weekday == "Tuesday": - Tuesday += 1 - if weekday == "Wednesday": - Wednesday += 1 - if weekday == "Thursday": - Thursday += 1 - if weekday == "Friday": - Friday += 1 - if weekday == "Saturday": - Saturday += 1 - if weekday == "Sunday": - Sunday += 1 - except Exception as ex: - if str(ex) != "'NoneType' object is not subscriptable": - print("Exception occurred " + str(ex)) - - sumAll = morning + daytime + evening + night - sum_week = Sunday + Monday + Tuesday + Friday + Saturday + Wednesday + Thursday - title = translate['I am an Early'] if morning + daytime >= evening + night else translate['I am a Night'] - one_day = [ - {"name": "🌞 " + translate['Morning'], "text": str(morning) + " commits", - "percent": round((morning / sumAll) * 100, 2)}, - {"name": "🌆 " + translate['Daytime'], "text": str(daytime) + " commits", - "percent": round((daytime / sumAll) * 100, 2)}, - {"name": "🌃 " + translate['Evening'], "text": str(evening) + " commits", - "percent": round((evening / sumAll) * 100, 2)}, - {"name": "🌙 " + translate['Night'], "text": str(night) + " commits", - "percent": round((night / sumAll) * 100, 2)}, - ] - dayOfWeek = [ - {"name": translate['Monday'], "text": str(Monday) + " commits", "percent": round((Monday / sum_week) * 100, 2)}, - {"name": translate['Tuesday'], "text": str(Tuesday) + " commits", - "percent": round((Tuesday / sum_week) * 100, 2)}, - {"name": translate['Wednesday'], "text": str(Wednesday) + " commits", - "percent": round((Wednesday / sum_week) * 100, 2)}, - {"name": translate['Thursday'], "text": str(Thursday) + " commits", - "percent": round((Thursday / sum_week) * 100, 2)}, - {"name": translate['Friday'], "text": str(Friday) + " commits", "percent": round((Friday / sum_week) * 100, 2)}, - {"name": translate['Saturday'], "text": str(Saturday) + " commits", - "percent": round((Saturday / sum_week) * 100, 2)}, - {"name": translate['Sunday'], "text": str(Sunday) + " commits", "percent": round((Sunday / sum_week) * 100, 2)}, - ] - - string = string + '**' + title + '** \n\n' + '```text\n' + make_commit_list(one_day) + '\n\n```\n' - - if show_days_of_week.lower() in truthy: - max_element = { - 'percent': 0 - } - - for day in dayOfWeek: - if day['percent'] > max_element['percent']: - max_element = day - days_title = translate['I am Most Productive on'] % max_element['name'] - string = string + '📅 **' + days_title + '** \n\n' + '```text\n' + make_commit_list(dayOfWeek) + '\n\n```\n' - - return string - - -def get_waka_time_stats(): - stats = '' - request = requests.get( - f"https://wakatime.com/api/v1/users/current/stats/last_7_days?api_key={waka_key}") - no_activity = translate["No Activity Tracked This Week"] - - if request.status_code == 401 or request.status_code != 200: - print("Error With WAKA time API returned " + str(request.status_code) + " Response " + str(request.json())) - else: - data = request.json() - if showCommit.lower() in truthy: - stats = stats + generate_commit_list(tz=data['data']['timezone']) + '\n\n' - - if showTimeZone.lower() in truthy or showLanguage.lower() in truthy or showEditors.lower() in truthy or \ - showProjects.lower() in truthy or showOs.lower() in truthy: - stats += '📊 **' + translate['This Week I Spend My Time On'] + '** \n\n' - stats += '```text\n' - - if showTimeZone.lower() in truthy: - tzone = data['data']['timezone'] - stats = stats + '⌚︎ ' + translate['Timezone'] + ': ' + tzone + '\n\n' - - if showLanguage.lower() in truthy: - if len(data['data']['languages']) == 0: - lang_list = no_activity - else: - lang_list = make_list(data['data']['languages']) - stats = stats + '💬 ' + translate['Languages'] + ': \n' + lang_list + '\n\n' - - if showEditors.lower() in truthy: - if len(data['data']['editors']) == 0: - edit_list = no_activity - else: - edit_list = make_list(data['data']['editors']) - stats = stats + '🔥 ' + translate['Editors'] + ': \n' + edit_list + '\n\n' - - if showProjects.lower() in truthy: - if len(data['data']['projects']) == 0: - project_list = no_activity - else: - # Re-order the project list by percentage - data['data']['projects'] = sorted(data['data']['projects'], key=lambda x: x["percent"], - reverse=True) - project_list = make_list(data['data']['projects']) - stats = stats + '🐱‍💻 ' + translate['Projects'] + ': \n' + project_list + '\n\n' - - if showOs.lower() in truthy: - if len(data['data']['operating_systems']) == 0: - os_list = no_activity - else: - os_list = make_list(data['data']['operating_systems']) - stats = stats + '💻 ' + translate['operating system'] + ': \n' + os_list + '\n\n' - - stats += '```\n\n' - - return stats - - -def generate_language_per_repo(result): - language_count = {} - total = 0 - for repo in result['data']['user']['repositories']['edges']: - if repo['node']['primaryLanguage'] is None: - continue - language = repo['node']['primaryLanguage']['name'] - color_code = repo['node']['primaryLanguage']['color'] - total += 1 - if language not in language_count.keys(): - language_count[language] = {} - language_count[language]['count'] = 1 - else: - language_count[language]['count'] = language_count[language]['count'] + 1 - language_count[language]['color'] = color_code - data = [] - sorted_labels = list(language_count.keys()) - sorted_labels.sort(key=lambda x: language_count[x]['count'], reverse=True) - most_language_repo = sorted_labels[0] - for label in sorted_labels: - percent = round(language_count[label]['count'] / total * 100, 2) - extension = " repos" - if language_count[label]['count'] == 1: - extension = " repo" - data.append({ - "name": label, - "text": str(language_count[label]['count']) + extension, - "percent": percent - }) - - title = translate['I Mostly Code in'] % most_language_repo - return '**' + title + '** \n\n' + '```text\n' + make_list(data) + '\n\n```\n' - - -def get_yearly_data(): - repository_list = run_query(repositoryListQuery.substitute(username=username, id=id)) - loc = LinesOfCode(id, username, ghtoken, repository_list, ignored_repos_name) - yearly_data = loc.calculateLoc() - if showLocChart.lower() in truthy: - loc.plotLoc(yearly_data) - return yearly_data - - -def get_line_of_code(): - repositoryList = run_query(repositoryListQuery.substitute(username=username, id=id)) - loc = LinesOfCode(id, username, ghtoken, repositoryList, ignored_repos_name) - yearly_data = loc.calculateLoc() - total_loc = sum( - [yearly_data[year][quarter][lang] for year in yearly_data for quarter in yearly_data[year] for lang in - yearly_data[year][quarter]]) - return millify(int(total_loc)) - - -def get_short_info(github): - string = '**🐱 ' + translate['My GitHub Data'] + '** \n\n' - user_info = github.get_user() - if user_info.disk_usage is None: - disk_usage = humanize.naturalsize(0) - print("Please add new github personal access token with user permission") - else: - disk_usage = humanize.naturalsize(user_info.disk_usage) - request = requests.get('https://github-contributions.vercel.app/api/v1/' + user_info.login) - if request.status_code == 200 and len(request.json()['years']) > 0: - this_year_data = request.json()['years'][0] - total = this_year_data['total'] - year = this_year_data['year'] - string += '> 🏆 ' + translate['Contributions in the year'] % (humanize.intcomma(total), year) + '\n > \n' - - string += '> 📦 ' + translate["Used in GitHub's Storage"] % disk_usage + ' \n > \n' - is_hireable = user_info.hireable - public_repo = user_info.public_repos - private_repo = user_info.owned_private_repos - if private_repo is None: - private_repo = 0 - if is_hireable: - string += "> 💼 " + translate["Opted to Hire"] + "\n > \n" - else: - string += "> 🚫 " + translate["Not Opted to Hire"] + "\n > \n" - - string += '> 📜 ' - string += translate['public repositories'] % public_repo + " " + '\n > \n' if public_repo != 1 else translate[ - 'public repository'] % public_repo + " " + '\n > \n' - string += '> 🔑 ' - string += translate['private repositories'] % private_repo + " " + ' \n > \n' if private_repo != 1 else translate[ - 'private repository'] % private_repo + " " + '\n > \n' - - return string - - -def get_stats(github): - '''Gets API data and returns markdown progress''' - - stats = '' - repositoryList = run_query(repositoryListQuery.substitute(username=username, id=id)) - - if show_loc.lower() in truthy or showLocChart.lower() in truthy: - # This condition is written to calculate the lines of code because it is heavy process soo needs to be calculate once this will reduce the execution time - yearly_data = get_yearly_data() - - if show_total_code_time.lower() in truthy: - request = requests.get( - f"https://wakatime.com/api/v1/users/current/all_time_since_today?api_key={waka_key}") - if request.status_code == 401: - print("Error With WAKA time API returned " + str(request.status_code) + " Response " + str(request.json())) - elif "text" not in request.json()["data"]: - print("User stats are calculating. Try again later.") - else: - data = request.json() - stats += '![Code Time](http://img.shields.io/badge/' + quote( - str("Code Time")) + '-' + quote(str( - data['data']['text'])) + '-blue)\n\n' - - if show_profile_view.lower() in truthy: - data = run_v3_api(get_profile_view.substitute(owner=username, repo=username)) - stats += '![Profile Views](http://img.shields.io/badge/' + quote(str(translate['Profile Views'])) + '-' + str( - data['count']) + '-blue)\n\n' - - if show_loc.lower() in truthy: - stats += '![Lines of code](https://img.shields.io/badge/' + quote( - str(translate['From Hello World I have written'])) + '-' + quote( - str(get_line_of_code())) + '%20' + quote(str(translate['Lines of code'])) + '-blue)\n\n' - - if show_short_info.lower() in truthy: - stats += get_short_info(github) - - if show_waka_stats.lower() in truthy: - stats += get_waka_time_stats() - - if showLanguagePerRepo.lower() in truthy: - stats = stats + generate_language_per_repo(repositoryList) + '\n\n' - - if showLocChart.lower() in truthy: - stats += '**' + translate['Timeline'] + '**\n\n' - branch_name = github.get_repo(f'{username}/{username}').default_branch - stats = stats + '![Chart not found](https://raw.githubusercontent.com/' + username + '/' + username + '/' + branch_name + '/charts/bar_graph.png) \n\n' - - if show_updated_date.lower() in truthy: - now = datetime.datetime.utcnow() - d1 = now.strftime(updated_date_format) - stats = stats + "\n Last Updated on " + d1 + " UTC" - - return stats - - -# def star_me(): -# requests.put("https://api.github.com/user/starred/anmol098/waka-readme-stats", headers=headers) - - -def decode_readme(data: str): - '''Decode the contents of old readme''' - decoded_bytes = base64.b64decode(data) - return str(decoded_bytes, 'utf-8') - - -def generate_new_readme(stats: str, readme: str): - '''Generate a new Readme.md''' - stats_in_readme = f"{START_COMMENT}\n{stats}\n{END_COMMENT}" - return re.sub(listReg, stats_in_readme, readme) - - -if __name__ == '__main__': - try: - start_time = datetime.datetime.now().timestamp() * 1000 - if ghtoken is None: - raise Exception('Token not available') - g = Github(ghtoken) - headers = {"Authorization": "Bearer " + ghtoken} - user_data = run_query(userInfoQuery) # Execute the query - if "errors" in user_data: - raise Exception(user_data) - username = user_data["data"]["viewer"]["login"] - id = user_data["data"]["viewer"]["id"] - email = user_data["data"]["viewer"]["email"] - print("Username " + username) - repo = g.get_repo(f"{username}/{username}") - contents = repo.get_readme() - try: - with open(os.path.join(os.path.dirname(__file__), 'translation.json'), encoding='utf-8') as config_file: - data = json.load(config_file) - translate = data[locale] - except Exception as e: - print("Cannot find the Locale choosing default to english") - translate = data['en'] - waka_stats = get_stats(g) - # star_me() - rdmd = decode_readme(contents.content) - new_readme = generate_new_readme(stats=waka_stats, readme=rdmd) - if commit_by_me.lower() in truthy: - committer = InputGitAuthor(username or commit_username, email or commit_email) - else: - committer = InputGitAuthor( - commit_username or 'readme-bot', - commit_email or '41898282+github-actions[bot]@users.noreply.github.com' - ) - if new_readme != rdmd: - try: - repo.update_file(path=contents.path, message=commit_message, - content=new_readme, sha=contents.sha, branch=branchName, - committer=committer) - except: - repo.update_file(path=contents.path, message=commit_message, - content=new_readme, sha=contents.sha, branch='main', - committer=committer) - print("Readme updated") - end_time = datetime.datetime.now().timestamp() * 1000 - print("Program processed in {} miliseconds.".format(round(end_time - start_time, 0))) - except Exception as e: - traceback.print_exc() - print("Exception Occurred " + str(e)) diff --git a/requirements.txt b/requirements.txt index 8f0244c..b987f54 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,30 +1,8 @@ -attrs==20.3.0 -certifi==2020.12.5 -chardet==4.0.0 -cycler==0.10.0 -Deprecated==1.2.12 -entrypoints==0.3 -humanize==3.3.0 -idna==2.10 -Jinja2==2.11.3 -jsonschema==3.2.0 -kiwisolver==1.3.1 -MarkupSafe==1.1.1 +PyGithub==1.54.1 matplotlib==3.6.3 numpy==1.24.2 -Pillow==8.2.0 -portpicker==1.3.1 -PyGithub==1.54.1 -PyJWT==1.7.1 -pyparsing==2.4.7 -pyrsistent==0.17.3 -python-dateutil==2.8.1 python-dotenv==0.17.0 pytz==2021.1 -requests==2.25.1 -selenium==3.141.0 -six==1.15.0 -toolz==0.11.1 -tornado==6.1 -urllib3==1.26.5 -wrapt==1.12.1 +humanize==3.3.0 +httpx==0.23.3 +PyYAML==6.0 diff --git a/sources/colors.json b/sources/colors.json new file mode 100644 index 0000000..e69de29 diff --git a/sources/download_manager.py b/sources/download_manager.py new file mode 100644 index 0000000..cdd7280 --- /dev/null +++ b/sources/download_manager.py @@ -0,0 +1,202 @@ +from hashlib import md5 +from json import dumps +from string import Template +from typing import Awaitable, Dict, Callable, Optional + +from httpx import AsyncClient +from yaml import safe_load +from github import AuthenticatedUser + + +GITHUB_API_QUERIES = { + "repositories_contributed_to": """ +{ + user(login: "$username") { + repositoriesContributedTo(last: 100, includeUserRepositories: true) { + nodes { + isFork + name + owner { + login + } + } + } + } +}""", + "repository_committed_dates": """ +{ + repository(owner: "$owner", name: "$name") { + defaultBranchRef { + target { + ... on Commit { + history(first: 100, author: { id: "$id" }) { + edges { + node { + committedDate + } + } + } + } + } + } + } +}""", + "user_repository_list": """ +{ + user(login: "$username") { + repositories(orderBy: {field: CREATED_AT, direction: ASC}, last: 100, affiliations: [OWNER, COLLABORATOR], isFork: false) { + edges { + node { + primaryLanguage { + name + } + name + owner { + login + } + } + } + } + } +} +""", + "repository_commit_list": """ +{ + repository(owner: "$owner", name: "$name") { + refs(refPrefix: "refs/heads/", orderBy: {direction: DESC, field: TAG_COMMIT_DATE}, first: 100) { + edges { + node { + ... on Ref { + target { + ... on Commit { + history(first: 100, author: { id: "$id" }) { + edges { + node { + ... on Commit { + additions + deletions + committedDate + } + } + } + } + } + } + } + } + } + } + } +} +""" +} + + +async def init_download_manager(waka_key: str, github_key: str, user: AuthenticatedUser): + """ + Initialize download manager: + - Setup headers for GitHub GraphQL requests. + - Launch static queries in background. + :param waka_key: WakaTime API token. + :param github_key: GitHub API token. + :param user: GitHub current user info. + """ + await DownloadManager.load_remote_resources({ + "linguist": "https://cdn.jsdelivr.net/gh/github/linguist@master/lib/linguist/languages.yml", + "waka_latest": f"https://wakatime.com/api/v1/users/current/stats/last_7_days?api_key={waka_key}", + "waka_all": f"https://wakatime.com/api/v1/users/current/all_time_since_today?api_key={waka_key}", + "github_stats": f"https://github-contributions.vercel.app/api/v1/{user.login}" + }, { + "Authorization": f"Bearer {github_key}" + }) + + +class DownloadManager: + """ + Class for handling and caching all kinds of requests. + There considered to be two types of queries: + - Static queries: queries that don't require many arguments that should be executed once + Example: queries to WakaTime API or to GitHub linguist + - Dynamic queries: queries that require many arguments and should be executed multiple times + Example: GraphQL queries to GitHub API + DownloadManager launches all static queries asynchronously upon initialization and caches their results. + It also executes dynamic queries upon request and caches result. + """ + _client = AsyncClient(timeout=60.0) + _REMOTE_RESOURCES_CACHE = dict() + + @staticmethod + async def load_remote_resources(resources: Dict[str, str], github_headers: Dict[str, str]): + """ + Prepare DownloadManager to launch GitHub API queries and launch all static queries. + :param resources: Dictionary of static queries, "IDENTIFIER": "URL". + :param github_headers: Dictionary of headers for GitHub API queries. + """ + for resource, url in resources.items(): + DownloadManager._REMOTE_RESOURCES_CACHE[resource] = DownloadManager._client.get(url) + DownloadManager._client.headers = github_headers + + @staticmethod + async def _get_remote_resource(resource: str, convertor: Optional[Callable[[bytes], Dict]]) -> Dict: + """ + Receive execution result of static query, wait for it if necessary. + If the query wasn't cached previously, cache it. + NB! Caching is done before response parsing - to throw exception on accessing cached erroneous response. + :param resource: Static query identifier. + :param convertor: Optional function to convert `response.contents` to dict. + By default `response.json()` is used. + :return: Response dictionary. + """ + if isinstance(DownloadManager._REMOTE_RESOURCES_CACHE[resource], Awaitable): + res = await DownloadManager._REMOTE_RESOURCES_CACHE[resource] + DownloadManager._REMOTE_RESOURCES_CACHE[resource] = res + if res.status_code == 200: + if convertor is None: + return res.json() + else: + return convertor(res.content) + else: + raise Exception(f"Query '{res.url}' failed to run by returning code of {res.status_code}: {res.json()}") + + @staticmethod + async def get_remote_json(resource: str) -> Dict: + """ + Shortcut for `_get_remote_resource` to return JSON response data. + :param resource: Static query identifier. + :return: Response JSON dictionary. + """ + return await DownloadManager._get_remote_resource(resource, None) + + @staticmethod + async def get_remote_yaml(resource: str) -> Dict: + """ + Shortcut for `_get_remote_resource` to return YAML response data. + :param resource: Static query identifier. + :return: Response YAML dictionary. + """ + return await DownloadManager._get_remote_resource(resource, safe_load) + + @staticmethod + async def get_remote_graphql(query: str, **kwargs) -> Dict: + """ + Execute GitHub GraphQL API query. + The queries are defined in `GITHUB_API_QUERIES`, all parameters should be passed as kwargs. + If the query wasn't cached previously, cache it. Cache query by its identifier + parameters hash. + NB! Caching is done before response parsing - to throw exception on accessing cached erroneous response. + Parse and return response as JSON. + :param query: Dynamic query identifier. + :param kwargs: Parameters for substitution of variables in dynamic query. + :return: Response JSON dictionary. + """ + key = f"{query}_{md5(dumps(kwargs, sort_keys=True).encode('utf-8')).digest()}" + if key not in DownloadManager._REMOTE_RESOURCES_CACHE: + res = await DownloadManager._client.post("https://api.github.com/graphql", json={ + "query": Template(GITHUB_API_QUERIES[query]).substitute(kwargs) + }) + DownloadManager._REMOTE_RESOURCES_CACHE[key] = res + else: + res = DownloadManager._REMOTE_RESOURCES_CACHE[key] + if res.status_code == 200: + return res.json() + else: + raise Exception(f"Query '{query}' failed to run by returning code of {res.status_code}: {res.json()}") diff --git a/sources/loc.py b/sources/loc.py new file mode 100644 index 0000000..7b5cfb1 --- /dev/null +++ b/sources/loc.py @@ -0,0 +1,76 @@ +import re +from asyncio import sleep + +from github import Github, InputGitAuthor, AuthenticatedUser +import datetime + +from download_manager import DownloadManager +from make_bar_graph import build_graph + + +class LinesOfCode: + + def __init__(self, user: AuthenticatedUser, ghtoken, repositoryData, ignored_repos): + self.g = Github(ghtoken) + self.user = user + self.repositoryData = repositoryData + self.ignored_repos = ignored_repos + + async def calculateLoc(self): + result = self.repositoryData + yearly_data = {} + total = len(result['data']['user']['repositories']['edges']) + for ind, repo in enumerate(result['data']['user']['repositories']['edges']): + if repo['node']['name'] not in self.ignored_repos: + print(f"{ind}/{total}", "Retrieving repo:", repo['node']["owner"]["login"], repo['node']['name']) + await self.getCommitStat(repo['node'], yearly_data) + await sleep(0.7) + return yearly_data + + async def plotLoc(self, yearly_data): + await build_graph(yearly_data) + self.pushChart() + + def getQuarter(self, timeStamp): + month = datetime.datetime.fromisoformat(timeStamp).month + if month >= 1 and month <= 3: + return 1 + elif month >= 4 and month <= 6: + return 2 + elif month >= 7 and month <= 9: + return 3 + elif month >= 10 and month <= 12: + return 4 + + async def getCommitStat(self, repoDetails, yearly_data): + commit_data = await DownloadManager.get_remote_graphql("repository_commit_list", owner=repoDetails["owner"]["login"], name=repoDetails['name'], id=self.user.node_id) + + if commit_data["data"]["repository"] is None: + print("\tSkipping:", repoDetails['name']) + return + + for commit in [commit["node"] for branch in commit_data["data"]["repository"]["refs"]["edges"] for commit in branch["node"]["target"]["history"]["edges"]]: + date = re.search(r'\d+-\d+-\d+', commit["committedDate"]).group(0) + curr_year = datetime.datetime.fromisoformat(date).year + quarter = self.getQuarter(date) + + if repoDetails['primaryLanguage'] is not None: + if curr_year not in yearly_data: + yearly_data[curr_year] = {} + if quarter not in yearly_data[curr_year]: + yearly_data[curr_year][quarter] = {} + if repoDetails['primaryLanguage']['name'] not in yearly_data[curr_year][quarter]: + yearly_data[curr_year][quarter][repoDetails['primaryLanguage']['name']] = 0 + yearly_data[curr_year][quarter][repoDetails['primaryLanguage']['name']] += (commit["additions"] - commit["deletions"]) + + + def pushChart(self): + repo = self.g.get_repo(f"{self.user.login}/{self.user.login}") + committer = InputGitAuthor('readme-bot', '41898282+github-actions[bot]@users.noreply.github.com') + with open('bar_graph.png', 'rb') as input_file: + data = input_file.read() + try: + contents = repo.get_contents("charts/bar_graph.png") + repo.update_file(contents.path, "Charts Updated", data, contents.sha, committer=committer) + except Exception as e: + repo.create_file("charts/bar_graph.png", "Charts Added", data, committer=committer) diff --git a/sources/main.py b/sources/main.py new file mode 100644 index 0000000..cbc802c --- /dev/null +++ b/sources/main.py @@ -0,0 +1,457 @@ +''' +Readme Development Metrics With waka time progress +''' +import re +import os +import base64 +from asyncio import run +from typing import Dict + +from pytz import timezone +import pytz +from github import Github, InputGitAuthor, AuthenticatedUser +import datetime + +from download_manager import init_download_manager, DownloadManager +from loc import LinesOfCode +import humanize +from urllib.parse import quote +import json +import math + +from dotenv import load_dotenv + +load_dotenv() + +START_COMMENT = f'' +END_COMMENT = f'' +listReg = f"{START_COMMENT}[\\s\\S]+{END_COMMENT}" + +waka_key = os.getenv('INPUT_WAKATIME_API_KEY') +ghtoken = os.getenv('INPUT_GH_TOKEN') +branchName = os.getenv('INPUT_PUSH_BRANCH_NAME') +showTimeZone = os.getenv('INPUT_SHOW_TIMEZONE') +showProjects = os.getenv('INPUT_SHOW_PROJECTS') +showEditors = os.getenv('INPUT_SHOW_EDITORS') +showOs = os.getenv('INPUT_SHOW_OS') +showCommit = os.getenv('INPUT_SHOW_COMMIT') +showLanguage = os.getenv('INPUT_SHOW_LANGUAGE') +show_loc = os.getenv('INPUT_SHOW_LINES_OF_CODE') +show_days_of_week = os.getenv('INPUT_SHOW_DAYS_OF_WEEK') +showLanguagePerRepo = os.getenv('INPUT_SHOW_LANGUAGE_PER_REPO') +showLocChart = os.getenv('INPUT_SHOW_LOC_CHART') +show_profile_view = os.getenv('INPUT_SHOW_PROFILE_VIEWS') +show_short_info = os.getenv('INPUT_SHOW_SHORT_INFO') +locale = os.getenv('INPUT_LOCALE') +commit_by_me = os.getenv('INPUT_COMMIT_BY_ME') +ignored_repos_name = str(os.getenv('INPUT_IGNORED_REPOS') or '').replace(' ', '').split(',') +show_updated_date = os.getenv('INPUT_SHOW_UPDATED_DATE') +updated_date_format = os.getenv('INPUT_UPDATED_DATE_FORMAT') +commit_message = os.getenv('INPUT_COMMIT_MESSAGE') +commit_username = os.getenv('INPUT_COMMIT_USERNAME') +commit_email = os.getenv('INPUT_COMMIT_EMAIL') +show_total_code_time = os.getenv('INPUT_SHOW_TOTAL_CODE_TIME') +symbol_version = os.getenv('INPUT_SYMBOL_VERSION').strip() +show_waka_stats = 'y' + +truthy = ['true', '1', 't', 'y', 'yes'] + +translate: Dict[str, str] +user: AuthenticatedUser + + +def millify(n): + millnames = ['', ' Thousand', ' Million', ' Billion', ' Trillion'] + n = float(n) + millidx = max(0, min(len(millnames) - 1, + int(math.floor(0 + if n == 0 + else math.log10(abs(n)) / 3)))) + + return '{:.0f}{}'.format(n / 10 ** (3 * millidx), millnames[millidx]) + + +def make_graph(percent: float): + '''Make progress graph from API graph''' + if (symbol_version == '1'): # version 1 + done_block = '█' + empty_block = '░' + elif (symbol_version == '2'): # version 2 + done_block = '⣿' + empty_block = '⣀' + elif (symbol_version == '3'): # version 3 + done_block = '⬛' + empty_block = '⬜' + else: + done_block = '█' # default is version 1 + empty_block = '░' + + pc_rnd = round(percent) + return f"{done_block * int(pc_rnd / 4)}{empty_block * int(25 - int(pc_rnd / 4))}" + + +def make_list(data: list): + '''Make List''' + data_list = [] + for l in data[:5]: + ln = len(l['name']) + ln_text = len(l['text']) + percent = "{:05.2f}".format(float(l['percent'])) + op = f"{l['name'][:25]}{' ' * (25 - ln)}{l['text']}{' ' * (20 - ln_text)}{make_graph(l['percent'])} {percent} % " + data_list.append(op) + return '\n'.join(data_list) + + +def make_commit_list(data: list): + '''Make List''' + data_list = [] + for l in data[:7]: + ln = len(l['name']) + ln_text = len(l['text']) + percent = "{:05.2f}".format(float(l['percent'])) + op = f"{l['name']}{' ' * ((15 - ln) + (11 - ln_text))}{l['text']}{' ' * (7)}{make_graph(l['percent'])} {percent} % " + data_list.append(op) + return '\n'.join(data_list) + + +async def generate_commit_list(tz): + string = '' + + result = await DownloadManager.get_remote_graphql("repositories_contributed_to", username=user.login) + nodes = result["data"]["user"]["repositoriesContributedTo"]["nodes"] + repos = [d for d in nodes if d['isFork'] is False] + + morning = 0 # 6 - 12 + daytime = 0 # 12 - 18 + evening = 0 # 18 - 24 + night = 0 # 0 - 6 + + Monday = 0 + Tuesday = 0 + Wednesday = 0 + Thursday = 0 + Friday = 0 + Saturday = 0 + Sunday = 0 + + for repository in repos: + result = await DownloadManager.get_remote_graphql("repository_committed_dates", owner=repository["owner"]["login"], name=repository["name"], id=user.node_id) + committed_dates = result["data"]["repository"]["defaultBranchRef"]["target"]["history"]["edges"] + for committedDate in committed_dates: + date = datetime.datetime.strptime(committedDate["node"]["committedDate"], "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=pytz.utc).astimezone(timezone(tz)) + hour = date.hour + weekday = date.strftime('%A') + if 6 <= hour < 12: + morning += 1 + if 12 <= hour < 18: + daytime += 1 + if 18 <= hour < 24: + evening += 1 + if 0 <= hour < 6: + night += 1 + + if weekday == "Monday": + Monday += 1 + if weekday == "Tuesday": + Tuesday += 1 + if weekday == "Wednesday": + Wednesday += 1 + if weekday == "Thursday": + Thursday += 1 + if weekday == "Friday": + Friday += 1 + if weekday == "Saturday": + Saturday += 1 + if weekday == "Sunday": + Sunday += 1 + + sumAll = morning + daytime + evening + night + sum_week = Sunday + Monday + Tuesday + Friday + Saturday + Wednesday + Thursday + title = translate['I am an Early'] if morning + daytime >= evening + night else translate['I am a Night'] + one_day = [ + {"name": "🌞 " + translate['Morning'], "text": str(morning) + " commits", + "percent": round((morning / sumAll) * 100, 2)}, + {"name": "🌆 " + translate['Daytime'], "text": str(daytime) + " commits", + "percent": round((daytime / sumAll) * 100, 2)}, + {"name": "🌃 " + translate['Evening'], "text": str(evening) + " commits", + "percent": round((evening / sumAll) * 100, 2)}, + {"name": "🌙 " + translate['Night'], "text": str(night) + " commits", + "percent": round((night / sumAll) * 100, 2)}, + ] + dayOfWeek = [ + {"name": translate['Monday'], "text": str(Monday) + " commits", "percent": round((Monday / sum_week) * 100, 2)}, + {"name": translate['Tuesday'], "text": str(Tuesday) + " commits", + "percent": round((Tuesday / sum_week) * 100, 2)}, + {"name": translate['Wednesday'], "text": str(Wednesday) + " commits", + "percent": round((Wednesday / sum_week) * 100, 2)}, + {"name": translate['Thursday'], "text": str(Thursday) + " commits", + "percent": round((Thursday / sum_week) * 100, 2)}, + {"name": translate['Friday'], "text": str(Friday) + " commits", "percent": round((Friday / sum_week) * 100, 2)}, + {"name": translate['Saturday'], "text": str(Saturday) + " commits", + "percent": round((Saturday / sum_week) * 100, 2)}, + {"name": translate['Sunday'], "text": str(Sunday) + " commits", "percent": round((Sunday / sum_week) * 100, 2)}, + ] + + string = string + '**' + title + '** \n\n' + '```text\n' + make_commit_list(one_day) + '\n\n```\n' + + if show_days_of_week.lower() in truthy: + max_element = { + 'percent': 0 + } + + for day in dayOfWeek: + if day['percent'] > max_element['percent']: + max_element = day + days_title = translate['I am Most Productive on'] % max_element['name'] + string = string + '📅 **' + days_title + '** \n\n' + '```text\n' + make_commit_list(dayOfWeek) + '\n\n```\n' + + return string + + +async def get_waka_time_stats(): + stats = '' + no_activity = translate["No Activity Tracked This Week"] + + data = await DownloadManager.get_remote_json("waka_latest") + if showCommit.lower() in truthy: + stats = stats + await generate_commit_list(data['data']['timezone']) + '\n\n' + + if showTimeZone.lower() in truthy or showLanguage.lower() in truthy or showEditors.lower() in truthy or showProjects.lower() in truthy or showOs.lower() in truthy: + stats += '📊 **' + translate['This Week I Spend My Time On'] + '** \n\n' + stats += '```text\n' + + if showTimeZone.lower() in truthy: + tzone = data['data']['timezone'] + stats = stats + '⌚︎ ' + translate['Timezone'] + ': ' + tzone + '\n\n' + + if showLanguage.lower() in truthy: + if len(data['data']['languages']) == 0: + lang_list = no_activity + else: + lang_list = make_list(data['data']['languages']) + stats = stats + '💬 ' + translate['Languages'] + ': \n' + lang_list + '\n\n' + + if showEditors.lower() in truthy: + if len(data['data']['editors']) == 0: + edit_list = no_activity + else: + edit_list = make_list(data['data']['editors']) + stats = stats + '🔥 ' + translate['Editors'] + ': \n' + edit_list + '\n\n' + + if showProjects.lower() in truthy: + if len(data['data']['projects']) == 0: + project_list = no_activity + else: + # Re-order the project list by percentage + data['data']['projects'] = sorted(data['data']['projects'], key=lambda x: x["percent"], + reverse=True) + project_list = make_list(data['data']['projects']) + stats = stats + '🐱‍💻 ' + translate['Projects'] + ': \n' + project_list + '\n\n' + + if showOs.lower() in truthy: + if len(data['data']['operating_systems']) == 0: + os_list = no_activity + else: + os_list = make_list(data['data']['operating_systems']) + stats = stats + '💻 ' + translate['operating system'] + ': \n' + os_list + '\n\n' + + stats += '```\n\n' + + return stats + + +def generate_language_per_repo(result): + language_count = {} + total = 0 + for repo in result['data']['user']['repositories']['edges']: + if repo['node']['primaryLanguage'] is None: + continue + language = repo['node']['primaryLanguage']['name'] + total += 1 + if language not in language_count.keys(): + language_count[language] = {} + language_count[language]['count'] = 1 + else: + language_count[language]['count'] = language_count[language]['count'] + 1 + data = [] + sorted_labels = list(language_count.keys()) + sorted_labels.sort(key=lambda x: language_count[x]['count'], reverse=True) + most_language_repo = sorted_labels[0] + for label in sorted_labels: + percent = round(language_count[label]['count'] / total * 100, 2) + extension = " repos" + if language_count[label]['count'] == 1: + extension = " repo" + data.append({ + "name": label, + "text": str(language_count[label]['count']) + extension, + "percent": percent + }) + + title = translate['I Mostly Code in'] % most_language_repo + return '**' + title + '** \n\n' + '```text\n' + make_list(data) + '\n\n```\n' + + +async def get_yearly_data(): + repository_list = await DownloadManager.get_remote_graphql("user_repository_list", username=user.login, id=user.node_id) + loc = LinesOfCode(user, ghtoken, repository_list, ignored_repos_name) + yearly_data = await loc.calculateLoc() + if showLocChart.lower() in truthy: + await loc.plotLoc(yearly_data) + return yearly_data + + +async def get_line_of_code() -> str: + repositoryList = await DownloadManager.get_remote_graphql("user_repository_list", username=user.login, id=user.node_id) + loc = LinesOfCode(user, ghtoken, repositoryList, ignored_repos_name) + yearly_data = await loc.calculateLoc() + total_loc = sum( + [yearly_data[year][quarter][lang] for year in yearly_data for quarter in yearly_data[year] for lang in + yearly_data[year][quarter]]) + return millify(int(total_loc)) + + +async def get_short_info(): + string = '**🐱 ' + translate['My GitHub Data'] + '** \n\n' + if user.disk_usage is None: + disk_usage = humanize.naturalsize(0) + print("Please add new github personal access token with user permission") + else: + disk_usage = humanize.naturalsize(user.disk_usage) + data = await DownloadManager.get_remote_json("github_stats") + if len(data['years']) > 0: + this_year_data = data['years'][0] + total = this_year_data['total'] + year = this_year_data['year'] + string += '> 🏆 ' + translate['Contributions in the year'] % (humanize.intcomma(total), year) + '\n > \n' + + string += '> 📦 ' + translate["Used in GitHub's Storage"] % disk_usage + ' \n > \n' + is_hireable = user.hireable + public_repo = user.public_repos + private_repo = user.owned_private_repos + if private_repo is None: + private_repo = 0 + if is_hireable: + string += "> 💼 " + translate["Opted to Hire"] + "\n > \n" + else: + string += "> 🚫 " + translate["Not Opted to Hire"] + "\n > \n" + + string += '> 📜 ' + string += translate['public repositories'] % public_repo + " " + '\n > \n' if public_repo != 1 else translate[ + 'public repository'] % public_repo + " " + '\n > \n' + string += '> 🔑 ' + string += translate['private repositories'] % private_repo + " " + ' \n > \n' if private_repo != 1 else translate[ + 'private repository'] % private_repo + " " + '\n > \n' + + return string + + +async def get_stats(github) -> str: + '''Gets API data and returns markdown progress''' + + stats = '' + repositoryList = await DownloadManager.get_remote_graphql("user_repository_list", username=user.login, id=user.node_id) + + if show_loc.lower() in truthy or showLocChart.lower() in truthy: + # This condition is written to calculate the lines of code because it is heavy process soo needs to be calculate once this will reduce the execution time + await get_yearly_data() + + if show_total_code_time.lower() in truthy: + data = await DownloadManager.get_remote_json("waka_all") + stats += '![Code Time](http://img.shields.io/badge/' + quote( + str("Code Time")) + '-' + quote(str( + data['data']['text'])) + '-blue)\n\n' + + if show_profile_view.lower() in truthy: + data = github.get_repo(f"{user.login}/{user.login}").get_views_traffic(per="week") + stats += '![Profile Views](http://img.shields.io/badge/' + quote(str(translate['Profile Views'])) + '-' + str( + data['count']) + '-blue)\n\n' + + if show_loc.lower() in truthy: + stats += '![Lines of code](https://img.shields.io/badge/' + quote( + str(translate['From Hello World I have written'])) + '-' + quote( + str(await get_line_of_code())) + '%20' + quote(str(translate['Lines of code'])) + '-blue)\n\n' + + if show_short_info.lower() in truthy: + stats += await get_short_info() + + if show_waka_stats.lower() in truthy: + stats += await get_waka_time_stats() + + if showLanguagePerRepo.lower() in truthy: + stats = stats + generate_language_per_repo(repositoryList) + '\n\n' + + if showLocChart.lower() in truthy: + stats += '**' + translate['Timeline'] + '**\n\n' + branch_name = github.get_repo(f'{user.login}/{user.login}').default_branch + stats = stats + '![Chart not found](https://raw.githubusercontent.com/' + user.login + '/' + user.login + '/' + branch_name + '/charts/bar_graph.png) \n\n' + + if show_updated_date.lower() in truthy: + now = datetime.datetime.utcnow() + d1 = now.strftime(updated_date_format) + stats = stats + "\n Last Updated on " + d1 + " UTC" + + return stats + + +def decode_readme(data: str): + '''Decode the contents of old readme''' + decoded_bytes = base64.b64decode(data) + return str(decoded_bytes, 'utf-8') + + +def generate_new_readme(stats: str, readme: str): + '''Generate a new Readme.md''' + stats_in_readme = f"{START_COMMENT}\n{stats}\n{END_COMMENT}" + return re.sub(listReg, stats_in_readme, readme) + + +async def main(): + global translate, user + + if ghtoken is None: + raise Exception('Token not available') + user = Github(ghtoken).get_user() + print(f"Current user: {user.login}") + await init_download_manager(waka_key, ghtoken, user) + + try: + with open(os.path.join(os.path.dirname(__file__), 'translation.json'), encoding='utf-8') as config_file: + data = json.load(config_file) + translate = data[locale] + except Exception as e: + print("Cannot find the Locale choosing default to english") + translate = data['en'] + + g = Github(ghtoken) + waka_stats = await get_stats(g) + + repo = g.get_repo(f"{user.login}/{user.login}") + contents = repo.get_readme() + rdmd = decode_readme(contents.content) + new_readme = generate_new_readme(stats=waka_stats, readme=rdmd) + + if commit_by_me.lower() in truthy: + committer = InputGitAuthor(user.login or commit_username, user.email or commit_email) + else: + committer = InputGitAuthor( + commit_username or 'readme-bot', + commit_email or '41898282+github-actions[bot]@users.noreply.github.com' + ) + if new_readme != rdmd: + try: + repo.update_file(path=contents.path, message=commit_message, + content=new_readme, sha=contents.sha, branch=branchName, + committer=committer) + except: + repo.update_file(path=contents.path, message=commit_message, + content=new_readme, sha=contents.sha, branch='main', + committer=committer) + print("Readme updated") + + +if __name__ == '__main__': + start_time = datetime.datetime.now().timestamp() * 1000 + run(main()) + end_time = datetime.datetime.now().timestamp() * 1000 + print(f"Program processed in {round(end_time - start_time, 0)} miliseconds.") diff --git a/make_bar_graph.py b/sources/make_bar_graph.py similarity index 91% rename from make_bar_graph.py rename to sources/make_bar_graph.py index 207f402..1081102 100644 --- a/make_bar_graph.py +++ b/sources/make_bar_graph.py @@ -6,11 +6,13 @@ import numpy as np import matplotlib.patches as mpatches import matplotlib.pyplot as plt +from download_manager import DownloadManager + MAX_LANGUAGES = 5 -def build_graph(yearly_data: Dict) -> str: +async def build_graph(yearly_data: Dict) -> str: """ Draws graph of lines of code written by user by quarters of years. Picks top `MAX_LANGUAGES` languages from each quarter only. @@ -18,8 +20,7 @@ def build_graph(yearly_data: Dict) -> str: :param yearly_data: GitHub user yearly data. :return: String, path to graph file. """ - with open(join(dirname(__file__), "colors.json")) as f: - colors = load(f) + colors = await DownloadManager.get_remote_yaml("linguist") languages_all_loc = dict() years = len(yearly_data.keys()) diff --git a/translation.json b/sources/translation.json similarity index 100% rename from translation.json rename to sources/translation.json