├── .dockerignore
├── .gitattributes
├── .github
├── dependabot.yml
├── preview
│ └── user-statistician.png
└── workflows
│ ├── build.yml
│ ├── codeql-analysis.yml
│ ├── generate-international-samples.yml
│ ├── generate-samples.yml
│ ├── major-release-num.yml
│ ├── manual-theme-sample.yml
│ └── stale.yml
├── .gitignore
├── CHANGELOG.md
├── Dockerfile
├── LICENSE
├── README.md
├── action.yml
├── octicons
├── README.md
├── archive-16.svg
├── comment-discussion-16.svg
├── eye-16.svg
├── git-commit-16.svg
├── git-pull-request-16.svg
├── heart-16.svg
├── issue-opened-16.svg
├── lock-16.svg
├── mark-github-16.svg
├── people-16.svg
├── person-add-16.svg
├── repo-16.svg
├── repo-forked-16.svg
├── repo-push-16.svg
├── repo-template-16.svg
├── ruby-16.svg
└── star-16.svg
├── quickstart
├── README.md
├── all-defaults.yml
├── contributions.yml
├── dark-dimmed.yml
├── dark.yml
├── languages.yml
├── multiple-stats-cards.yml
└── repositories.yml
├── src
├── ColorUtil.py
├── Colors.py
├── PieChart.py
├── StatConfig.py
├── Statistician.py
├── StatsImageGenerator.py
├── TextLength.py
├── UserStatistician.py
├── locales
│ ├── bn.json
│ ├── cs.json
│ ├── de.json
│ ├── el.json
│ ├── en.json
│ ├── es.json
│ ├── fa.json
│ ├── fi.json
│ ├── fr.json
│ ├── hi.json
│ ├── hu.json
│ ├── hy.json
│ ├── id.json
│ ├── it.json
│ ├── ja.json
│ ├── ko.json
│ ├── lt.json
│ ├── ml.json
│ ├── nl.json
│ ├── no.json
│ ├── or.json
│ ├── pl.json
│ ├── pt.json
│ ├── ro.json
│ ├── ru.json
│ ├── sat.json
│ ├── sr.json
│ ├── sv.json
│ ├── th.json
│ ├── tl.json
│ ├── tr.json
│ └── uk.json
└── queries
│ ├── basicstats.graphql
│ ├── reposContributedTo.graphql
│ ├── repostats.graphql
│ └── singleYearQueryFragment.graphql
├── tests
└── tests.py
└── util
├── CharacterWidths.py
├── default-widths.json
├── default-widths.py
└── refactor-locales-to-json.py
/.dockerignore:
--------------------------------------------------------------------------------
1 | *
2 | !Dockerfile
3 | !src
4 |
5 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.graphql linguist-detectable
2 | src/locales/*.json linguist-detectable
3 | quickstart/*.yml linguist-detectable
4 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "docker"
9 | directory: "/"
10 | target-branch: "main"
11 | schedule:
12 | interval: "daily"
13 | - package-ecosystem: "github-actions"
14 | directory: "/"
15 | target-branch: "main"
16 | schedule:
17 | interval: "daily"
18 |
19 |
--------------------------------------------------------------------------------
/.github/preview/user-statistician.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cicirello/user-statistician/4c6b9b1317921fe8bf278b45639755748f77d58d/.github/preview/user-statistician.png
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | paths-ignore: [ '**.svg', '**.md' ]
7 | pull_request:
8 | branches: [ main ]
9 |
10 | permissions:
11 | contents: read
12 |
13 | jobs:
14 |
15 | build:
16 |
17 | runs-on: ubuntu-latest
18 |
19 | steps:
20 | - uses: actions/checkout@v4
21 |
22 | - name: Setup Python
23 | uses: actions/setup-python@v5
24 | with:
25 | python-version: '3.12'
26 |
27 | - name: Run Python unit tests
28 | run: |
29 | python3 -u -m unittest tests/tests.py
30 | env:
31 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
32 |
33 | - name: Verify that the Docker image for the action builds
34 | run: docker build . --file Dockerfile
35 |
36 | - name: Integration test
37 | id: integration
38 | uses: ./
39 | with:
40 | colors: dark
41 | commit-and-push: false
42 | featured-repository: Chips-n-Salsa
43 | animated-language-chart: true
44 | locale: en
45 | #fail-on-error: false
46 | #category-order: general, repositories, languages, contributions
47 | env:
48 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
49 |
50 | - name: Output the outputs of the integration test of the action
51 | run: |
52 | echo "exit-code = ${{ steps.integration.outputs.exit-code }}"
53 |
54 | - name: Upload generated SVG as a workflow artifact for inspection if necessary
55 | uses: actions/upload-artifact@v4
56 | with:
57 | name: generated-image
58 | path: images/userstats.svg
59 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ main ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ main ]
20 | schedule:
21 | - cron: '34 20 * * 1'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'python' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
37 | # Learn more:
38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
39 |
40 | steps:
41 | - name: Checkout repository
42 | uses: actions/checkout@v4
43 |
44 | # Initializes the CodeQL tools for scanning.
45 | - name: Initialize CodeQL
46 | uses: github/codeql-action/init@v3
47 | with:
48 | languages: ${{ matrix.language }}
49 | # If you wish to specify custom queries, you can do so here or in a config file.
50 | # By default, queries listed here will override any specified in a config file.
51 | # Prefix the list here with "+" to use these queries and those in the config file.
52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
53 |
54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
55 | # If this step fails, then you should remove it and run the build manually (see below)
56 | - name: Autobuild
57 | uses: github/codeql-action/autobuild@v3
58 |
59 | # ℹ️ Command-line programs to run using the OS shell.
60 | # 📚 https://git.io/JvXDl
61 |
62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
63 | # and modify them (or add more) to build your code if your project
64 | # uses a compiled language
65 |
66 | #- run: |
67 | # make bootstrap
68 | # make release
69 |
70 | - name: Perform CodeQL Analysis
71 | uses: github/codeql-action/analyze@v3
72 |
--------------------------------------------------------------------------------
/.github/workflows/generate-international-samples.yml:
--------------------------------------------------------------------------------
1 | name: international samples
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | samples:
8 |
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | - uses: actions/checkout@v4
13 | with:
14 | ref: samples
15 |
16 | - name: Sample German
17 | uses: cicirello/user-statistician@v1
18 | with:
19 | colors: light
20 | locale: de
21 | image-file: images/de.svg
22 | env:
23 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
24 |
25 | - name: Sample Italian
26 | uses: cicirello/user-statistician@v1
27 | with:
28 | colors: dark
29 | locale: it
30 | image-file: images/it.svg
31 | env:
32 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
33 |
--------------------------------------------------------------------------------
/.github/workflows/generate-samples.yml:
--------------------------------------------------------------------------------
1 | name: samples
2 |
3 | on:
4 | schedule:
5 | - cron: '0 1 * * 6'
6 | workflow_dispatch:
7 |
8 | jobs:
9 | samples:
10 |
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - uses: actions/checkout@v4
15 | with:
16 | ref: samples
17 |
18 | - name: Sample light
19 | uses: cicirello/user-statistician@v1
20 | with:
21 | colors: light
22 | image-file: images/light.svg
23 | # Using custom-title is not necessary here in general.
24 | # I'm just using it so that the sample has a more generic name
25 | # of user rather than my name. The format this uses is identical
26 | # to if this input was not used, but with my name replaced by a
27 | # generic name.
28 | custom-title: Firstname Lastname's GitHub Activity
29 | env:
30 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
31 |
32 | - name: Sample dark
33 | uses: cicirello/user-statistician@v1
34 | with:
35 | colors: dark
36 | image-file: images/dark.svg
37 | include-title: false
38 | env:
39 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
40 |
41 | - name: Sample dark-dimmed
42 | uses: cicirello/user-statistician@v1
43 | with:
44 | colors: dark-dimmed
45 | image-file: images/dark-dimmed.svg
46 | custom-title: My GitHub Statistics
47 | hide-keys: joined, mostStarred, mostForked, followers, following, private
48 | max-languages: 100
49 | animated-language-chart: true
50 | env:
51 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
52 |
--------------------------------------------------------------------------------
/.github/workflows/major-release-num.yml:
--------------------------------------------------------------------------------
1 | name: Move Major Release Tag
2 |
3 | on:
4 | release:
5 | types: [created]
6 |
7 | jobs:
8 | movetag:
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | - uses: actions/checkout@v4
13 |
14 | - name: Get major version num and update tag
15 | run: |
16 | VERSION=${GITHUB_REF#refs/tags/}
17 | MAJOR=${VERSION%%.*}
18 | git config --global user.name 'Vincent A Cicirello'
19 | git config --global user.email 'cicirello@users.noreply.github.com'
20 | git tag -fa ${MAJOR} -m "Update major version tag"
21 | git push origin ${MAJOR} --force
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.github/workflows/manual-theme-sample.yml:
--------------------------------------------------------------------------------
1 | name: Generate Theme Sample
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | THEME:
7 | description: 'Theme Key'
8 | required: true
9 |
10 | jobs:
11 | theme-sample:
12 |
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@v4
17 | with:
18 | ref: samples
19 |
20 | - name: Theme sample
21 | uses: cicirello/user-statistician@v1
22 | with:
23 | colors: ${{ github.event.inputs.THEME }}
24 | image-file: images/${{ github.event.inputs.THEME }}.svg
25 | # Using custom-title is not necessary here in general.
26 | # I'm just using it so that the sample has a more generic name
27 | # of user rather than my name. The format this uses is identical
28 | # to if this input was not used, but with my name replaced by a
29 | # generic name.
30 | custom-title: Firstname Lastname's GitHub Activity
31 | env:
32 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
33 |
--------------------------------------------------------------------------------
/.github/workflows/stale.yml:
--------------------------------------------------------------------------------
1 | name: 'Close stale PRs'
2 | on:
3 | schedule:
4 | - cron: '45 1 * * *'
5 |
6 | permissions:
7 | pull-requests: write
8 |
9 | jobs:
10 | stale:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/stale@v9
14 | with:
15 | stale-pr-message: 'This PR has been automarked as stale for 30 days of inactivity, and will autoclose if still inactive in 5 days.'
16 | close-pr-message: 'Autoclosing this stale PR.'
17 | days-before-stale: 30
18 | days-before-close: 5
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__/
2 | tests/__pycache__/
3 | *.pyc
4 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## [Unreleased] - 2025-05-19
8 |
9 | ### Added
10 |
11 | ### Changed
12 |
13 | ### Deprecated
14 |
15 | ### Removed
16 |
17 | ### Fixed
18 |
19 | ### Dependencies
20 | * Bump cicirello/pyaction from 4.32.0 to 4.33.0
21 |
22 | ### CI/CD
23 |
24 | ### Other
25 |
26 |
27 | ## [1.24.0] - 2024-10-14
28 |
29 | ### Added
30 | * Translation to Czech (`locale: cs`) in #259 (@Sarthak027)
31 | * Translation to Greek (`locale: el`) in #260 (@Debasmita54)
32 |
33 | ### Dependencies
34 | * Bump cicirello/pyaction from 4.29.0 to 4.32.0
35 |
36 |
37 | ## [1.23.0] - 2024-05-13
38 |
39 | ### Added
40 | * An action input, `commit-message`, to enable customizing the commit message
41 |
42 | ### Dependencies
43 | * Bump cicirello/pyaction from 4.27.0 to 4.29.0
44 |
45 |
46 | ## [1.22.1] - 2023-12-08
47 |
48 | ### Fixed
49 | * Eliminated adjustment for watching own repositories from the "Watched By" stat for consistency with other stats that don't make such an adjustment such as the star count.
50 |
51 | ### Dependencies
52 | * Bump cicirello/pyaction from 4.25.0 to 4.27.0
53 |
54 |
55 | ## [1.22.0] - 2023-10-18
56 |
57 | ### Added
58 | * Translation to Armenian (`locale: hy`) in #240 (@JairTorres1003).
59 |
60 | ### Dependencies
61 | * Bump cicirello/pyaction from 4.24.0 to 4.25.0 (which includes bumping Python to 3.12).
62 |
63 | ### CI/CD
64 | * Bump Python to 3.12 in CI/CD workflows when running unit tests (@cicirello).
65 |
66 |
67 | ## [1.21.0] - 2023-10-04
68 |
69 | ### Added
70 | * Translation to Tagalog (`locale: tl`) in #227 (@digracesion).
71 | * Translation to Swedish (`locale: sv`) in #230 (@Viveksati5143).
72 | * Translation to Persian (`locale: fa`) in #232 (@AshkanArabim).
73 | * Translation to Malayalam (`locale: ml`) in #235 (@Sarthak027).
74 | * Translation to Finnish (`locale: fi`) in #236 (@Sadeedpv).
75 |
76 | ### Dependencies
77 | * Bump cicirello/pyaction from 4.22.0 to 4.24.0
78 |
79 | ### Other
80 | * Updated the quickstart / sample workflows to the latest version of actions/checkout.
81 |
82 |
83 | ## [1.20.5] - 2023-09-07
84 |
85 | ### Fixed
86 | * Resolved issue with failing to commit and push, a bug introduced in v1.20.3.
87 |
88 |
89 | ## [1.20.4] - 2023-09-07
90 |
91 | ### Fixed
92 | * Refactored everything locale related to extract definitions of locales from Python dictionaries into JSON files to make it easier to contribute additional language translations.
93 |
94 |
95 | ## [1.20.3] - 2023-09-06
96 |
97 | ### Fixed
98 | * Get repository owner (user for stats image) from GitHub Actions environment variables #210 (fixes issue related to update to GitHub CLI #209 determining owner of repository).
99 |
100 | ### Dependencies
101 | * Bump cicirello/pyaction from 4.14.0 to 4.22.0
102 |
103 |
104 | ## [1.20.2] - 2022-12-30
105 |
106 | ### Fixed
107 | * Better match background for GitHub-inspired themes, using GitHub's canvas.default instead of canvas.inset.
108 |
109 |
110 | ## [1.20.1] - 2022-12-30
111 |
112 | ### Fixed
113 | * Improved Russian Translation in #203, contributed by @mrtnvgr.
114 |
115 | ### Dependencies
116 | * Bump cicirello/pyaction from 4.12.0 to 4.14.0
117 |
118 |
119 | ## [1.20.0] - 2022-10-25
120 |
121 | ### Added
122 | * Translation to Odia (`locale: or`) in #186, contributed by @Prasanta-Hembram.
123 |
124 | ### Fixed
125 | * Some users may be using the action on a self-hosted runner not yet updated to a version supporting the
126 | new GitHub Actions `GITHUB_OUTPUT` env file. This patch adds backwards compatibility for that case by
127 | falling back to the deprecated `set-output` if `GITHUB_OUTPUT` doesn't exist. #190 (@cicirello).
128 |
129 | ### Dependencies
130 | * Bump cicirello/pyaction from 4.11.0 to 4.12.0, including upgrading Python within the Docker container to 3.11.
131 |
132 | ### CI/CD
133 | * Bump Python to 3.11 in CI/CD workflows.
134 |
135 |
136 | ## [1.19.0] - 2022-10-20
137 |
138 | ### Added
139 | * Translation to Santali (`locale: sat`) in #178, contributed by @Prasanta-Hembram.
140 | * Translation to Serbian (`locale: sr`) in #182, contributed by @keen003.
141 |
142 | ### Fixed
143 | * Replaced use of GitHub Action's deprecated `set-output` with the new `$GITHUB_OUTPUT` env file,
144 | in #184 (@cicirello).
145 |
146 | ### Dependencies
147 | * Bump cicirello/pyaction from 4.10.0 to 4.11.0
148 |
149 |
150 | ## [1.18.0] - 2022-10-12
151 |
152 | ### Added
153 | * Translation to Hungarian (`locale: hu`) in #172, contributed by @jpacsai.
154 |
155 | ### Dependencies
156 | * Bump cicirello/pyaction from 4.9.0 to 4.10.0
157 |
158 |
159 | ## [1.17.0] - 2022-10-05
160 |
161 | ### Added
162 | * Increased internationalization with the addition of new locales:
163 | * Dutch (`locale: nl`) in #166, contributed by @lovelacecoding.
164 | * Norwegian (`locale: no`) in #167, contributed by @rubjo.
165 | * Romanian (`locale: ro`) in #164, contributed by @donheshanthaka.
166 | * Thai (`locale: th`) in #165, contributed by @Slowlife01 and updated by @thititongumpun.
167 |
168 | ### Dependencies
169 | * Bump cicirello/pyaction from 4.8.1 to 4.9.0.
170 |
171 |
172 | ## [1.16.1] - 2022-09-09
173 |
174 | ### Fixed
175 | * Corrected minor error in language chart radius calculation that was causing too small margin around chart for users with long names.
176 |
177 |
178 | ## [1.16.0] - 2022-09-08
179 |
180 | ### Added
181 | * New themes, including
182 | * halloween - A dark theme for use around Halloween
183 | * halloween-light - A light theme for use around Halloween
184 | * batty - A light theme for use around Halloween
185 | * Additional icon options for the icon in top corners, including:
186 | * pumpkin
187 | * bat
188 |
189 | ### Dependencies
190 | * Bump cicirello/pyaction from 4.7.1 to 4.8.1, including upgrading Python within the Docker container to 3.10.7
191 |
192 |
193 | ## [1.15.1] - 2022-08-24
194 |
195 | ### Fixed
196 | * Decreased the size of icon in top corners for better visual appearance.
197 |
198 |
199 | ## [1.15.0] - 2022-08-19
200 |
201 | ### Added
202 | * Icons in upper corners surrounding the title, with the following features:
203 | * Theme-defined icons, initially the GitHub Octocat from Octicons for current built-in themes.
204 | * Input `top-icon` to enable overriding, such as disabling the icons, or setting a different one.
205 | * For now, `top-icon` is limited to the GitHub Octocat or nothing (additional options planned).
206 |
207 | ### Dependencies
208 | * Bump cicirello/pyaction from 4.4.0 to 4.7.1
209 |
210 |
211 | ## [1.14.0] - 2022-06-08
212 |
213 | ### Changed
214 | * Centered title.
215 | * Bumped base docker image cicirello/pyaction from 4.3.1 to 4.4.0.
216 |
217 | ### Fixed
218 | * Minor label edit.
219 |
220 |
221 | ## [1.13.0] - 2022-05-02
222 |
223 | ### Added
224 | * New themes added to correspond to all of GitHub's themes, including:
225 | * dark-high-contrast
226 | * light-high-contrast
227 | * dark-colorblind
228 | * light-colorblind
229 | * dark-tritanopia
230 | * light-tritanopia
231 |
232 | ### Changed
233 | * Bumped base Docker image cicirello/pyaction from 4.2.0 to 4.3.1.
234 |
235 | ### Fixed
236 | * Fixed margin calculation when most starred, most forked, or featured repo has long name.
237 | * Adjusted existing themes (dark, light, dark-dimmed) based on newer versions of corresponding GitHub themes.
238 |
239 |
240 | ## [1.12.3] - 2022-02-22
241 |
242 | ### Changed
243 | * Switched to specific release of base Docker image to avoid accidental breaking changes
244 | in base Docker image.
245 |
246 | ### Fixed
247 | * Total count of repositories (other than own) contributed to will now show as
248 | a blank spot on the SVG. Previously reported values were highly inaccurate, and
249 | cannot be computed accurately at the present time due to unavailability of
250 | necessary data from the GitHub GraphQL API.
251 |
252 |
253 | ## [1.12.2] - 2022-02-18
254 |
255 | ### Fixed
256 | * Suppressed Python's pycache on imports (fixes Issue #107).
257 |
258 |
259 | ## [1.12.1] - 2022-02-17
260 |
261 | ### Changed
262 | * Refactored text length calculation.
263 |
264 |
265 | ## [1.12.0] - 2021-11-04
266 |
267 | ### Added
268 | * Increased internationalization support with the addition of new locales:
269 | * Ukrainian (`locale: uk`) via [PR#102](https://github.com/cicirello/user-statistician/pull/102).
270 |
271 | ### Fixed
272 | * Added missing `lang` and `xml:lang` attributes to the opening svg tag to report the
273 | language of the content of the SVG to provide better support for visually impaired
274 | users who use a screen reader.
275 |
276 |
277 | ## [1.11.0] - 2021-10-13
278 |
279 | ### Added
280 | * Increased internationalization support with the addition of new locales:
281 | * Lithuanian (`locale: lt`) via [PR#98](https://github.com/cicirello/user-statistician/pull/98).
282 | * Japanese (`locale: ja`) via [PR#89](https://github.com/cicirello/user-statistician/pull/89).
283 | * Turkish (`locale: tr`) via [PR#90](https://github.com/cicirello/user-statistician/pull/90).
284 |
285 |
286 | ## [1.10.0] - 2021-10-06
287 |
288 | ### Added
289 | * Increased internationalization support with the addition of new locales:
290 | * Korean (`locale: ko`) via [PR#93](https://github.com/cicirello/user-statistician/pull/93).
291 |
292 | ### Fixed
293 | * The total column for the number of repositories (owned by someone else) that the user has
294 | contributed to, at the present time, cannot be computed exactly due to limitations in the
295 | GitHub API. The relevant queries seem to exclude older contribTo data. To account for this,
296 | that value is now listed as a lower bound (e.g., instead of a number like 7, it is listed
297 | as ≥7). This is the only stat affected by this.
298 |
299 |
300 | ## [1.9.0] - 2021-10-04
301 |
302 | ### Added
303 | * Increased internationalization support with the addition of new locales:
304 | * Portuguese (`locale: pt`) via [PR#69](https://github.com/cicirello/user-statistician/pull/69).
305 | * Bahasa Indonesia (`locale: id`) via [PR#71](https://github.com/cicirello/user-statistician/pull/71).
306 | * French (`locale: fr`) via [PR#77](https://github.com/cicirello/user-statistician/pull/77).
307 | * Spanish (`locale: es`) via [PR#79](https://github.com/cicirello/user-statistician/pull/79).
308 | * Russian (`locale: ru`) via [PR#80](https://github.com/cicirello/user-statistician/pull/80).
309 | * Hindi (`locale: hi`) via [PR#81](https://github.com/cicirello/user-statistician/pull/81).
310 | * Polish (`locale: pl`) via [PR#78](https://github.com/cicirello/user-statistician/pull/78).
311 | * Bengali (`locale: bn`) via [PR#92](https://github.com/cicirello/user-statistician/pull/92).
312 |
313 | ### Changed
314 | * Minor refactoring to improve code maintainability
315 |
316 |
317 | ## [1.8.1] - 2021-09-02
318 |
319 | ### Fixed
320 | * Improved visual consistency of fonts across browsers
321 |
322 |
323 | ## [1.8.0] - 2021-08-31
324 |
325 | ### Added
326 | * German locale: German translations of title template, headings, labels,
327 | etc for locale code `de`.
328 |
329 | ### Changed
330 | * Improved precision of fonts if the SVG is scaled.
331 | * Minor adjustment to margins.
332 |
333 |
334 | ## [1.7.1] - 2021-08-30
335 |
336 | ### Fixed
337 | * The width of the SVG is now set based on the content, including
338 | factoring in the effects of different locales where headings, and
339 | labels may be longer. Note that the `image-width` input can still
340 | be used to set a larger width. The action will now use the larger
341 | of the user-defined value of `image-width`, or the width necessary
342 | to accommodate the content.
343 |
344 |
345 | ## [1.7.0] - 2021-08-28
346 |
347 | ### Added
348 | * Italian locale: Italian translations of title template, headings, labels,
349 | etc for locale code `it`.
350 |
351 | ### Fixed
352 | * Added missing UTF-8 encoding when writing the SVG to fix issue with
353 | characters needed for some language translations.
354 | * Fixed exception in case when user stores the SVG at root of repo.
355 |
356 |
357 | ## [1.6.0] - 2021-08-09
358 |
359 | ### Added
360 | * User adjustable width, via a new action input `image-width`.
361 |
362 | ### Changed
363 | * Revised SVG generation to eliminate unnecessary SVG tags surrounding
364 | icon paths and language chart. This is a non-functional change. The SVG
365 | tags referred to here are not incorrect, but they are not needed. By changing
366 | SVG generation to not insert them, DOM size is decreased (possibly decreasing
367 | rendering time), and file size is decreased, possibly speeding up download time.
368 |
369 |
370 | ## [1.5.0] - 2021-08-06
371 |
372 | ### Added
373 | * A new action input, `featured-repository`, that enables the user of the action
374 | to (optionally) specify a repository to feature in the General Stats and Info
375 | section of the SVG. For example, perhaps they have a repository that they feel
376 | is a better representative of their work than their most starred and most forked
377 | repositories.
378 | * An option to animate the language distribution chart, a continuous rotation of the
379 | pie chart. This feature is disabled by default. It is controlled by a pair of new inputs:
380 | `animated-language-chart` and `language-animation-speed`.
381 |
382 | ### Fixed
383 | * Corrected bug in edge case when user only owns forks, which had been causing the
384 | action to fail with an exception.
385 |
386 |
387 | ## [1.4.0] - 2021-08-04
388 |
389 | ### Added
390 | * Most starred repo added to General Stats and Info section of SVG.
391 | * Most forked repo added to General Stats and Info section of SVG.
392 |
393 | ### Changed
394 | * "General User Stats" section renamed to "General Stats and Info" to better reflect
395 | the addition of Most Starred and Most Forked.
396 |
397 |
398 | ## [1.3.0] - 2021-07-29
399 |
400 | ### Added
401 | * The ability to exclude specific repositories from the language
402 | distribution chart, controlled by a new action input `language-repository-exclusions`,
403 | which is a list of repositories to exclude from the language stats.
404 |
405 | ### Changed
406 | * Revised the Quickstart workflows to include pushing the workflow file to
407 | the events that runs the workflow to make it even easier for a user to get started.
408 |
409 |
410 | ## [1.2.0] - 2021-07-23
411 |
412 | ### Added
413 | * The year user joined GitHub is now in General User Stats section of card.
414 | * New action input, `category-order`, which allows user to customize the order
415 | of the categories of stats.
416 |
417 | ### Changed
418 | * Minified SVG during generation (removed unnecessary characters like new lines,
419 | and a couple empty text tags). This doesn't change the contents or appearance
420 | of the SVG.
421 |
422 |
423 | ## [1.1.1] - 2021-07-22
424 |
425 | ### Fixed
426 | * Fixed minor bug in handling of failOnError input.
427 |
428 |
429 | ## [1.1.0] - 2021-07-20
430 |
431 | ### Added
432 | * Language Distribution section added to the card:
433 | * Languages section of the stats card that summarizes the distribution
434 | of languages for the public repositories owned by the user. This is intended
435 | to be the equivalent of the languages graph that GitHub generates for each
436 | individual repository, except for the combination of all of the user's
437 | repositories. The distribution is visualized, however, with a pie chart, rather
438 | than the simple line chart.
439 | * The language distribution calculation features a user customizable number
440 | of languages to display. Any extra languages beyond what the user specifies
441 | are summarized into a single "Other" item (much like the "Other" that appears
442 | in GitHub's language graphs in a repository for low percentage languages).
443 | * By default, the language distribution auto-calibrates the number of languages
444 | based on the percentages. Specifically, all languages that individually account for
445 | less than one percent are combined into an "Other" item.
446 |
447 | ### Changed
448 | * Text and title colors in built-in themes (light, dark, and dark-dimmed)
449 | changed slightly for accessibility (changed to ensure text and background
450 | have contrast ratio of at least 4.5, and title and background have contrast
451 | ratio of at least 4.5). Test cases will enforce this criteria on any themes
452 | that may be contributed in the future (but not on a user's own custom colors).
453 | * Increased width of image slightly for better visual appearance of data portion
454 | with label portion (e.g., right half with data is same width as left half with
455 | labels).
456 | * Changed default title template to better reflect content of the stats card.
457 |
458 |
459 | ## [1.0.2] - 2021-07-15
460 |
461 | ### Fixed
462 | * Corrected all-time count of repositories contributed to that are owned by others.
463 |
464 |
465 | ## [1.0.1] - 2021-07-14
466 |
467 | ### Fixed
468 | * Changed the author of commits to the github-actions bot
469 | to avoid artificially inflating the user of the action's
470 | commit count.
471 |
472 |
473 | ## [1.0.0] - 2021-07-13
474 |
475 | ### Added
476 | * This is the initial release. The user-statistician is a GitHub
477 | Action that generates a detailed visual summary of your activity
478 | on GitHub in the form of an SVG, suitable to display on your GitHub
479 | Profile README. It runs entirely on GitHub, and is designed to run on
480 | a schedule, pushing an updated user stats SVG to your profile repo.
481 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021-2025 Vincent A. Cicirello
2 | # https://www.cicirello.org/
3 | # Licensed under the MIT License
4 |
5 | # The base image is pyaction, which is python slim, plus the GitHub CLI (gh).
6 | FROM ghcr.io/cicirello/pyaction:4.33.0
7 |
8 | # Copy the GraphQl queries and python source into the container.
9 | COPY src /
10 |
11 | # Set the entrypoint.
12 | ENTRYPOINT ["/UserStatistician.py"]
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Vincent A. Cicirello
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/action.yml:
--------------------------------------------------------------------------------
1 | # user-statistician: Github action for generating a user stats card
2 | #
3 | # Copyright (c) 2021-2024 Vincent A Cicirello
4 | # https://www.cicirello.org/
5 | #
6 | # MIT License
7 | #
8 | # Permission is hereby granted, free of charge, to any person obtaining a copy
9 | # of this software and associated documentation files (the "Software"), to deal
10 | # in the Software without restriction, including without limitation the rights
11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | # copies of the Software, and to permit persons to whom the Software is
13 | # furnished to do so, subject to the following conditions:
14 | #
15 | # The above copyright notice and this permission notice shall be included in all
16 | # copies or substantial portions of the Software.
17 | #
18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 | # SOFTWARE.
25 | #
26 | name: 'user-statistician'
27 | description: "Generate a GitHub stats SVG for your GitHub Profile README in GitHub Actions"
28 | branding:
29 | icon: 'book-open'
30 | color: 'green'
31 | inputs:
32 | image-file:
33 | description: 'Name and path of the image file to generate, relative to root of repository'
34 | required: false
35 | default: images/userstats.svg
36 | include-title:
37 | description: 'Include a title in the image'
38 | required: false
39 | default: true
40 | custom-title:
41 | description: 'Define a custom title for the image'
42 | required: false
43 | default: ''
44 | colors:
45 | description: 'The color theme or list of custom colors'
46 | required: false
47 | default: light
48 | hide-keys:
49 | description: 'A list of statistics to hide specified by their key'
50 | required: false
51 | default: ''
52 | fail-on-error:
53 | description: 'Choose whether to fail the workflow if there is an error'
54 | required: false
55 | default: true
56 | commit-and-push:
57 | description: 'Commits and pushes the generated image'
58 | required: false
59 | default: true
60 | locale:
61 | description: 'One of the supported ISO 639-1 (two character) or ISO 639-2 (three character) language codes'
62 | required: false
63 | default: en
64 | border-radius:
65 | description: 'The radius of the border'
66 | required: false
67 | default: 6
68 | show-border:
69 | description: 'Controls whether the stats SVG has a border'
70 | required: false
71 | default: true
72 | small-title:
73 | description: 'Controls size of title'
74 | required: false
75 | default: false
76 | max-languages:
77 | description: 'Controls maximum number of languages to list separately'
78 | required: false
79 | default: auto
80 | category-order:
81 | description: 'List of keys for the categories in order of appearance.'
82 | required: false
83 | default: general, repositories, contributions, languages
84 | language-repository-exclusions:
85 | description: 'List of repositories to exclude from language stats.'
86 | required: false
87 | default: ''
88 | featured-repository:
89 | description: 'Name of a repository to feature in the General Stats and Info section'
90 | required: false
91 | default: ''
92 | animated-language-chart:
93 | description: 'Boolean controlling whether the language chart is animated'
94 | required: false
95 | default: false
96 | language-animation-speed:
97 | description: 'The time for one full rotation in seconds'
98 | required: false
99 | default: 10
100 | image-width:
101 | description: 'The minimum width of the SVG in pixels'
102 | required: false
103 | default: 0
104 | top-icon:
105 | description: 'Icon displayed at top of SVG to left and right of title'
106 | required: false
107 | default: default
108 | commit-message:
109 | description: 'The commit message'
110 | required: false
111 | default: 'Automated change by https://github.com/cicirello/user-statistician'
112 | outputs:
113 | exit-code:
114 | description: '0 if successful or non-zero if unsuccessful'
115 | runs:
116 | using: 'docker'
117 | image: 'Dockerfile'
118 | args:
119 | - ${{ inputs.image-file }}
120 | - ${{ inputs.include-title }}
121 | - ${{ inputs.custom-title }}
122 | - ${{ inputs.colors }}
123 | - ${{ inputs.hide-keys }}
124 | - ${{ inputs.fail-on-error }}
125 | - ${{ inputs.commit-and-push }}
126 | - ${{ inputs.locale }}
127 | - ${{ inputs.border-radius }}
128 | - ${{ inputs.show-border }}
129 | - ${{ inputs.small-title }}
130 | - ${{ inputs.max-languages }}
131 | - ${{ inputs.category-order }}
132 | - ${{ inputs.language-repository-exclusions }}
133 | - ${{ inputs.featured-repository }}
134 | - ${{ inputs.animated-language-chart }}
135 | - ${{ inputs.language-animation-speed }}
136 | - ${{ inputs.image-width }}
137 | - ${{ inputs.top-icon }}
138 | - ${{ inputs.commit-message }}
139 |
--------------------------------------------------------------------------------
/octicons/README.md:
--------------------------------------------------------------------------------
1 | # Icons Used in the Images
2 |
3 | The icons contained in this directory are used in the images
4 | generated by this action. They are
5 | from [GitHub's Octicons](https://github.com/primer/octicons),
6 | and are copyright (c) GitHub, Inc, and licensed by GitHub under
7 | the MIT license.
8 |
9 | This directory contains the unaltered Octicons in use by this
10 | action, as a reference point. Some of these may or may not have
11 | been altered as used by the action.
12 |
13 |
--------------------------------------------------------------------------------
/octicons/archive-16.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/octicons/comment-discussion-16.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/octicons/eye-16.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/octicons/git-commit-16.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/octicons/git-pull-request-16.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/octicons/heart-16.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/octicons/issue-opened-16.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/octicons/lock-16.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/octicons/mark-github-16.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/octicons/people-16.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/octicons/person-add-16.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/octicons/repo-16.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/octicons/repo-forked-16.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/octicons/repo-push-16.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/octicons/repo-template-16.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/octicons/ruby-16.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/octicons/star-16.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/quickstart/README.md:
--------------------------------------------------------------------------------
1 | # Quickstart
2 |
3 | This directory contains several ready-to-use workflows for a few
4 | of the more common anticipated settings. The idea is to make it
5 | as easy as possible for you to try out the action. You can start
6 | with one of these and then customize to your liking.
7 |
8 | ## How to Use
9 |
10 | To use one of these workflows, do the following:
11 | 1. In your GitHub profile repository (repository with
12 | same name as your username), create a directory `.github/workflows`
13 | if you don't already have this.
14 | 2. Pick one of the provided workflows (see the [list](#workflow-list) below).
15 | 3. Download your chosen workflow and commit it to your `.github/workflows`
16 | directory within your profile repository.
17 | 4. If you didn't change the name of the file, then it will run when you push
18 | it to your repository since it has been configured to run on a push to
19 | that filename (assuming your branch is either named `main` or `master`). If
20 | you changed the name of the file, then edit the `paths`
21 | attribute on the `push` event with the new name of the workflow file.
22 | 5. The workflow is configured to run on a schedule daily. You can change the schedule to
23 | your liking.
24 | 6. You can also choose to run it manually because it is also configured on the
25 | `workflow_dispatch` event. To do this,
26 | navigate to the `Actions` tab for your profile repository. Select the
27 | workflow from the list of workflows on the left. You'll notice that
28 | it indicates: "This workflow has a workflow_dispatch event trigger."
29 | To the right of that click the "Run workflow" button to run the workflow
30 | manually.
31 | 7. You'll find the SVG in the images directory (which the action creates
32 | if it doesn't already exist).
33 | 8. Add a link to it in the `README.md` in your profile repository. If you
34 | used one of these workflows as is, without using the inputs to change
35 | the file name of the image, then you can add the image to your profile
36 | with the following Markdown:
37 |
38 | ```markdown
39 | 
40 | ```
41 |
42 | Although not required, it is appreciated if you instead link the image to this repository
43 | so that others know how you generated it, with the following markdown:
44 |
45 | ```markdown
46 | [](https://github.com/cicirello/user-statistician)
47 | ```
48 |
49 | ## Workflow List
50 |
51 | The ready-to-use workflows are as follows:
52 | * [all-defaults.yml](all-defaults.yml): This runs the action
53 | on a daily schedule using all of the default settings, which is a
54 | light color theme.
55 | * [dark.yml](dark.yml): This runs the action
56 | on a daily schedule with a dark color theme, but otherwise uses
57 | all of the default settings.
58 | * [dark-dimmed.yml](dark-dimmed.yml): This runs the action
59 | on a daily schedule with a dark-dimmed color theme, but otherwise uses
60 | all of the default settings.
61 | * [contributions.yml](contributions.yml): This runs the action
62 | on a daily schedule, only generating the contribution stats (hiding
63 | the other sections), with a dark-dimmed theme.
64 | * [repositories.yml](repositories.yml): This runs the action
65 | on a daily schedule, only generating the repositories stats (hiding
66 | the other sections), with a dark-dimmed theme.
67 | * [languages.yml](languages.yml): This runs the action
68 | on a daily schedule, only generating the languages distribution chart
69 | (hiding the other sections), with a dark theme.
70 | * [multiple-stats-cards.yml](multiple-stats-cards.yml): This runs the
71 | action on a daily schedule, generating three separate SVGs, one for
72 | the contribution stats, one for the repositories stats, and one for
73 | the language distribution chart. It uses the dark theme for all three.
74 | Note that if you use this one, you'll have three images to insert into
75 | your profile readme.
76 |
--------------------------------------------------------------------------------
/quickstart/all-defaults.yml:
--------------------------------------------------------------------------------
1 | # This workflow configures the user-statistician with all of
2 | # the defaults, which is a light theme, nothing hidden, etc.
3 | # It is configured on a daily schedule at 3am.
4 |
5 | name: user-statistician
6 |
7 | on:
8 | schedule:
9 | - cron: '0 3 * * *'
10 | push:
11 | branches: [ main, master ]
12 | paths: [ '.github/workflows/all-defaults.yml' ]
13 | workflow_dispatch:
14 |
15 | jobs:
16 | stats:
17 | runs-on: ubuntu-latest
18 |
19 | steps:
20 | - uses: actions/checkout@v4
21 |
22 | - name: Generate the user stats image
23 | uses: cicirello/user-statistician@v1
24 | env:
25 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
26 |
--------------------------------------------------------------------------------
/quickstart/contributions.yml:
--------------------------------------------------------------------------------
1 | # This workflow configures the user-statistician to
2 | # generate only the contributions stats, using
3 | # the dark-dimmed color theme. It is configured on a daily
4 | # schedule at 3am.
5 |
6 | name: user-statistician
7 |
8 | on:
9 | schedule:
10 | - cron: '0 3 * * *'
11 | push:
12 | branches: [ main, master ]
13 | paths: [ '.github/workflows/contributions.yml' ]
14 | workflow_dispatch:
15 |
16 | jobs:
17 | stats:
18 | runs-on: ubuntu-latest
19 |
20 | steps:
21 | - uses: actions/checkout@v4
22 |
23 | - name: Generate the user stats image
24 | uses: cicirello/user-statistician@v1
25 | with:
26 | colors: dark-dimmed
27 | hide-keys: general, languages, repositories
28 | env:
29 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
30 |
--------------------------------------------------------------------------------
/quickstart/dark-dimmed.yml:
--------------------------------------------------------------------------------
1 | # This workflow configures the user-statistician with the
2 | # dark-dimmed color theme, but otherwise uses all of
3 | # the defaults. It is configured on a daily schedule at 3am.
4 |
5 | name: user-statistician
6 |
7 | on:
8 | schedule:
9 | - cron: '0 3 * * *'
10 | push:
11 | branches: [ main, master ]
12 | paths: [ '.github/workflows/dark-dimmed.yml' ]
13 | workflow_dispatch:
14 |
15 | jobs:
16 | stats:
17 | runs-on: ubuntu-latest
18 |
19 | steps:
20 | - uses: actions/checkout@v4
21 |
22 | - name: Generate the user stats image
23 | uses: cicirello/user-statistician@v1
24 | with:
25 | colors: dark-dimmed
26 | env:
27 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
28 |
--------------------------------------------------------------------------------
/quickstart/dark.yml:
--------------------------------------------------------------------------------
1 | # This workflow configures the user-statistician with the
2 | # dark color theme, but otherwise uses all of
3 | # the defaults. It is configured on a daily schedule at 3am.
4 |
5 | name: user-statistician
6 |
7 | on:
8 | schedule:
9 | - cron: '0 3 * * *'
10 | push:
11 | branches: [ main, master ]
12 | paths: [ '.github/workflows/dark.yml' ]
13 | workflow_dispatch:
14 |
15 | jobs:
16 | stats:
17 | runs-on: ubuntu-latest
18 |
19 | steps:
20 | - uses: actions/checkout@v4
21 |
22 | - name: Generate the user stats image
23 | uses: cicirello/user-statistician@v1
24 | with:
25 | colors: dark
26 | env:
27 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
28 |
--------------------------------------------------------------------------------
/quickstart/languages.yml:
--------------------------------------------------------------------------------
1 | # This workflow configures the user-statistician to
2 | # generate only the language distribution chart, using
3 | # the dark color theme. It is configured on a daily
4 | # schedule at 3am.
5 |
6 | name: user-statistician
7 |
8 | on:
9 | schedule:
10 | - cron: '0 3 * * *'
11 | push:
12 | branches: [ main, master ]
13 | paths: [ '.github/workflows/languages.yml' ]
14 | workflow_dispatch:
15 |
16 | jobs:
17 | stats:
18 | runs-on: ubuntu-latest
19 |
20 | steps:
21 | - uses: actions/checkout@v4
22 |
23 | - name: Generate the user stats image
24 | uses: cicirello/user-statistician@v1
25 | with:
26 | colors: dark
27 | hide-keys: general, contributions, repositories
28 | env:
29 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
30 |
--------------------------------------------------------------------------------
/quickstart/multiple-stats-cards.yml:
--------------------------------------------------------------------------------
1 | # This workflow configures the user-statistician to
2 | # generate three SVGs, one each for contribution stats,
3 | # repositories stats, and the language distribution chart, using
4 | # the dark color theme. It uses the image-file input to assign each
5 | # SVG a unique filename. It is configured on a daily
6 | # schedule at 3am.
7 |
8 | name: user-statistician
9 |
10 | on:
11 | schedule:
12 | - cron: '0 3 * * *'
13 | push:
14 | branches: [ main, master ]
15 | paths: [ '.github/workflows/multiple-stats-cards.yml' ]
16 | workflow_dispatch:
17 |
18 | jobs:
19 | stats:
20 | runs-on: ubuntu-latest
21 |
22 | steps:
23 | - uses: actions/checkout@v4
24 |
25 | - name: Generate the languages distribution
26 | uses: cicirello/user-statistician@v1
27 | with:
28 | image-file: images/languages.svg
29 | colors: dark
30 | hide-keys: general, contributions, repositories
31 | env:
32 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
33 |
34 | - name: Generate the contributions stats
35 | uses: cicirello/user-statistician@v1
36 | with:
37 | image-file: images/contribs.svg
38 | colors: dark
39 | hide-keys: general, languages, repositories
40 | env:
41 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
42 |
43 | - name: Generate the repositories stats
44 | uses: cicirello/user-statistician@v1
45 | with:
46 | image-file: images/repos.svg
47 | colors: dark
48 | hide-keys: general, contributions, languages
49 | env:
50 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
51 |
--------------------------------------------------------------------------------
/quickstart/repositories.yml:
--------------------------------------------------------------------------------
1 | # This workflow configures the user-statistician to
2 | # generate only the repositories stats, using
3 | # the dark-dimmed color theme. It is configured on a daily
4 | # schedule at 3am.
5 |
6 | name: user-statistician
7 |
8 | on:
9 | schedule:
10 | - cron: '0 3 * * *'
11 | push:
12 | branches: [ main, master ]
13 | paths: [ '.github/workflows/repositories.yml' ]
14 | workflow_dispatch:
15 |
16 | jobs:
17 | stats:
18 | runs-on: ubuntu-latest
19 |
20 | steps:
21 | - uses: actions/checkout@v4
22 |
23 | - name: Generate the user stats image
24 | uses: cicirello/user-statistician@v1
25 | with:
26 | colors: dark-dimmed
27 | hide-keys: general, languages, contributions
28 | env:
29 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
30 |
--------------------------------------------------------------------------------
/src/ColorUtil.py:
--------------------------------------------------------------------------------
1 | # user-statistician: Github action for generating a user stats card
2 | #
3 | # Copyright (c) 2021-2023 Vincent A Cicirello
4 | # https://www.cicirello.org/
5 | #
6 | # MIT License
7 | #
8 | # Permission is hereby granted, free of charge, to any person obtaining a copy
9 | # of this software and associated documentation files (the "Software"), to deal
10 | # in the Software without restriction, including without limitation the rights
11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | # copies of the Software, and to permit persons to whom the Software is
13 | # furnished to do so, subject to the following conditions:
14 | #
15 | # The above copyright notice and this permission notice shall be included in all
16 | # copies or substantial portions of the Software.
17 | #
18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 | # SOFTWARE.
25 | #
26 |
27 | def isValidColor(color):
28 | """Checks if a color is a valid color.
29 |
30 | Keyword arguments:
31 | color - The color to check, either in hex or as
32 | a named color or as an rgba().
33 | """
34 | validHexDigits = set("0123456789abcdefABCDEF")
35 | color = color.strip()
36 | if color.startswith("#"):
37 | if len(color) != 4 and len(color) != 7:
38 | return False
39 | return all(c in validHexDigits for c in color[1:])
40 | elif color.startswith("rgba("):
41 | return strToRGBA(color) != None
42 | else:
43 | return color in _namedColors
44 |
45 | def strToRGBA(color):
46 | """Converts a str specifying rgba color to
47 | r, g, b, and a channels. Returns (r, g, b, a) is valid
48 | and otherwise returns None.
49 |
50 | Keyword arguments:
51 | color - a str of the form: rgba(r,g,b,a).
52 | """
53 | if color.startswith("rgba("):
54 | last = color.find(")")
55 | if last > 5:
56 | rgba = color[5:last].split(",")
57 | if len(rgba) == 4:
58 | try:
59 | r = int(rgba[0])
60 | g = int(rgba[1])
61 | b = int(rgba[2])
62 | a = float(rgba[3])
63 | except ValueError:
64 | return None
65 | r = min(r, 255)
66 | g = min(g, 255)
67 | b = min(b, 255)
68 | r = max(r, 0)
69 | g = max(g, 0)
70 | b = max(b, 0)
71 | a = max(a, 0.0)
72 | a = min(a, 1.0)
73 | return r, g, b, a
74 | return None
75 |
76 | def highContrastingColor(color):
77 | """Computes a highly contrasting color.
78 | Specifically, maximizes the contrast ratio. Contrast ratio is
79 | (L1 + 0.05) / (L2 + 0.05), where L1 and L2 are the luminances
80 | of the colors and L1 is the larger luminance. Returns None
81 | if color is not a valid hex color or named color.
82 |
83 | Keyword arguments:
84 | color - The color to contrast with, in hex or as a named color.
85 | """
86 | L = luminance(color)
87 | if L == None:
88 | return None
89 | if (L + 0.05) / 0.05 >= 1.05 / (L + 0.05):
90 | return "#000000" # black
91 | else:
92 | return "#ffffff" # white
93 |
94 | def contrastRatio(c1, c2):
95 | """Computes contrast ratio of a pair of colors.
96 | Returns the contrast ratio provided both colors are valid,
97 | and otherwise returns None.
98 |
99 | Keyword arguments:
100 | c1 - Color 1, in hex or as a named color.
101 | c2 - Color 2, in hex or as a named color.
102 | """
103 | L1 = luminance(c1)
104 | L2 = luminance(c2)
105 | if L1 == None or L2 == None:
106 | return None
107 | if L1 < L2:
108 | L1, L2 = L2, L1
109 | return (L1 + 0.05) / (L2 + 0.05)
110 |
111 | def luminance(color):
112 | """Calculates the luminance of a color. Returns None
113 | if color is not a valid hex color or named color.
114 |
115 | Keyword arguments:
116 | color - The color, either in hex or as a named color.
117 | """
118 | color = color.strip()
119 | if color.startswith("rgba("):
120 | rgba = strToRGBA(color)
121 | if rgba != None:
122 | r, g, b, a = rgba
123 | else:
124 | return None
125 | else:
126 | if not color.startswith("#"):
127 | if color not in _namedColors:
128 | return None
129 | color = _namedColors[color]
130 | if len(color) == 4:
131 | r = color[1] + color[1]
132 | g = color[2] + color[2]
133 | b = color[3] + color[3]
134 | elif len(color) == 7:
135 | r = color[1:3]
136 | g = color[3:5]
137 | b = color[5:7]
138 | else:
139 | return None
140 | r = int(r, base=16)
141 | g = int(g, base=16)
142 | b = int(b, base=16)
143 | r = _sRGBtoLin(r / 255)
144 | g = _sRGBtoLin(g / 255)
145 | b = _sRGBtoLin(b / 255)
146 | return 0.2126 * r + 0.7152 * g + 0.0722 * b
147 |
148 | def _sRGBtoLin(c):
149 | """Transformation from
150 | https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_Colors_and_Luminance
151 |
152 | Keyword arguments:
153 | c - A color channel (i.e., r, g, or b)
154 | """
155 | if c <= 0.04045:
156 | return c / 12.92
157 | else :
158 | return ((c + 0.055)/1.055) ** 2.4
159 |
160 | # The SVG, CSS, etc named colors with their hex.
161 | # See https://developer.mozilla.org/en-US/docs/Web/CSS/color_value.
162 | # We need the hex that corresponds to the names in the event that the
163 | # user specifies a color by name so that we can compute a high contrast
164 | # color relative to background for the "Other" language category, or
165 | # for languages without colors defined by Linguist.
166 | _namedColors = {
167 | "aliceblue" : "#f0f8ff",
168 | "antiquewhite" : "#faebd7",
169 | "aqua" : "#00ffff",
170 | "aquamarine" : "#7fffd4",
171 | "azure" : "#f0ffff",
172 | "beige" : "#f5f5dc",
173 | "bisque" : "#ffe4c4",
174 | "black" : "#000000",
175 | "blanchedalmond" : "#ffebcd",
176 | "blue" : "#0000ff",
177 | "blueviolet" : "#8a2be2",
178 | "brown" : "#a52a2a",
179 | "burlywood" : "#deb887",
180 | "cadetblue" : "#5f9ea0",
181 | "chartreuse" : "#7fff00",
182 | "chocolate" : "#d2691e",
183 | "coral" : "#ff7f50",
184 | "cornflowerblue" : "#6495ed",
185 | "cornsilk" : "#fff8dc",
186 | "crimson" : "#dc143c",
187 | "cyan" : "#00ffff",
188 | "darkblue" : "#00008b",
189 | "darkcyan" : "#008b8b",
190 | "darkgoldenrod" : "#b8860b",
191 | "darkgray" : "#a9a9a9",
192 | "darkgreen" : "#006400",
193 | "darkgrey" : "#a9a9a9",
194 | "darkkhaki" : "#bdb76b",
195 | "darkmagenta" : "#8b008b",
196 | "darkolivegreen" : "#556b2f",
197 | "darkorange" : "#ff8c00",
198 | "darkorchid" : "#9932cc",
199 | "darkred" : "#8b0000",
200 | "darksalmon" : "#e9967a",
201 | "darkseagreen" : "#8fbc8f",
202 | "darkslateblue" : "#483d8b",
203 | "darkslategray" : "#2f4f4f",
204 | "darkslategrey" : "#2f4f4f",
205 | "darkturquoise" : "#00ced1",
206 | "darkviolet" : "#9400d3",
207 | "deeppink" : "#ff1493",
208 | "deepskyblue" : "#00bfff",
209 | "dimgray" : "#696969",
210 | "dimgrey" : "#696969",
211 | "dodgerblue" : "#1e90ff",
212 | "firebrick" : "#b22222",
213 | "floralwhite" : "#fffaf0",
214 | "forestgreen" : "#228b22",
215 | "fuchsia" : "#ff00ff",
216 | "gainsboro" : "#dcdcdc",
217 | "ghostwhite" : "#f8f8ff",
218 | "goldenrod" : "#daa520",
219 | "gold" : "#ffd700",
220 | "gray" : "#808080",
221 | "green" : "#008000",
222 | "greenyellow" : "#adff2f",
223 | "grey" : "#808080",
224 | "honeydew" : "#f0fff0",
225 | "hotpink" : "#ff69b4",
226 | "indianred" : "#cd5c5c",
227 | "indigo" : "#4b0082",
228 | "ivory" : "#fffff0",
229 | "khaki" : "#f0e68c",
230 | "lavenderblush" : "#fff0f5",
231 | "lavender" : "#e6e6fa",
232 | "lawngreen" : "#7cfc00",
233 | "lemonchiffon" : "#fffacd",
234 | "lightblue" : "#add8e6",
235 | "lightcoral" : "#f08080",
236 | "lightcyan" : "#e0ffff",
237 | "lightgoldenrodyellow" : "#fafad2",
238 | "lightgray" : "#d3d3d3",
239 | "lightgreen" : "#90ee90",
240 | "lightgrey" : "#d3d3d3",
241 | "lightpink" : "#ffb6c1",
242 | "lightsalmon" : "#ffa07a",
243 | "lightseagreen" : "#20b2aa",
244 | "lightskyblue" : "#87cefa",
245 | "lightslategray" : "#778899",
246 | "lightslategrey" : "#778899",
247 | "lightsteelblue" : "#b0c4de",
248 | "lightyellow" : "#ffffe0",
249 | "lime" : "#00ff00",
250 | "limegreen" : "#32cd32",
251 | "linen" : "#faf0e6",
252 | "magenta" : "#ff00ff",
253 | "maroon" : "#800000",
254 | "mediumaquamarine" : "#66cdaa",
255 | "mediumblue" : "#0000cd",
256 | "mediumorchid" : "#ba55d3",
257 | "mediumpurple" : "#9370db",
258 | "mediumseagreen" : "#3cb371",
259 | "mediumslateblue" : "#7b68ee",
260 | "mediumspringgreen" : "#00fa9a",
261 | "mediumturquoise" : "#48d1cc",
262 | "mediumvioletred" : "#c71585",
263 | "midnightblue" : "#191970",
264 | "mintcream" : "#f5fffa",
265 | "mistyrose" : "#ffe4e1",
266 | "moccasin" : "#ffe4b5",
267 | "navajowhite" : "#ffdead",
268 | "navy" : "#000080",
269 | "oldlace" : "#fdf5e6",
270 | "olive" : "#808000",
271 | "olivedrab" : "#6b8e23",
272 | "orange" : "#ffa500",
273 | "orangered" : "#ff4500",
274 | "orchid" : "#da70d6",
275 | "palegoldenrod" : "#eee8aa",
276 | "palegreen" : "#98fb98",
277 | "paleturquoise" : "#afeeee",
278 | "palevioletred" : "#db7093",
279 | "papayawhip" : "#ffefd5",
280 | "peachpuff" : "#ffdab9",
281 | "peru" : "#cd853f",
282 | "pink" : "#ffc0cb",
283 | "plum" : "#dda0dd",
284 | "powderblue" : "#b0e0e6",
285 | "purple" : "#800080",
286 | "rebeccapurple" : "#663399",
287 | "red" : "#ff0000",
288 | "rosybrown" : "#bc8f8f",
289 | "royalblue" : "#4169e1",
290 | "saddlebrown" : "#8b4513",
291 | "salmon" : "#fa8072",
292 | "sandybrown" : "#f4a460",
293 | "seagreen" : "#2e8b57",
294 | "seashell" : "#fff5ee",
295 | "sienna" : "#a0522d",
296 | "silver" : "#c0c0c0",
297 | "skyblue" : "#87ceeb",
298 | "slateblue" : "#6a5acd",
299 | "slategray" : "#708090",
300 | "slategrey" : "#708090",
301 | "snow" : "#fffafa",
302 | "springgreen" : "#00ff7f",
303 | "steelblue" : "#4682b4",
304 | "tan" : "#d2b48c",
305 | "teal" : "#008080",
306 | "thistle" : "#d8bfd8",
307 | "tomato" : "#ff6347",
308 | "turquoise" : "#40e0d0",
309 | "violet" : "#ee82ee",
310 | "wheat" : "#f5deb3",
311 | "white" : "#ffffff",
312 | "whitesmoke" : "#f5f5f5",
313 | "yellow" : "#ffff00",
314 | "yellowgreen" : "#9acd32"
315 | }
316 |
--------------------------------------------------------------------------------
/src/Colors.py:
--------------------------------------------------------------------------------
1 | #
2 | # user-statistician: Github action for generating a user stats card
3 | #
4 | # Copyright (c) 2021-2022 Vincent A Cicirello
5 | # https://www.cicirello.org/
6 | #
7 | # MIT License
8 | #
9 | # Permission is hereby granted, free of charge, to any person obtaining a copy
10 | # of this software and associated documentation files (the "Software"), to deal
11 | # in the Software without restriction, including without limitation the rights
12 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | # copies of the Software, and to permit persons to whom the Software is
14 | # furnished to do so, subject to the following conditions:
15 | #
16 | # The above copyright notice and this permission notice shall be included in all
17 | # copies or substantial portions of the Software.
18 | #
19 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | # SOFTWARE.
26 | #
27 |
28 |
29 |
30 | # Notes on the included themes:
31 | #
32 | # The light, dark, and dark-dimmed themes are based on
33 | # GitHub's themes, and color-palette (see
34 | # https://primer.style/primitives/colors).
35 | #
36 | # Specifically, from the link above we use:
37 | # * background color (bg): canvas.default
38 | # * border color: accent.muted
39 | # * icons: accent.emphasis
40 | # * text: fg.default
41 | # * title: accent.fg
42 | #
43 | # Notes to Potential Contributors:
44 | #
45 | # (1) For those who want to contribute a theme,
46 | # please check the combination of your background
47 | # color with text color, and background with title
48 | # color for accessibility at this site,
49 | # https://colorable.jxnblk.com/, and make sure the
50 | # combination has a rating of at least AA. You can also
51 | # simply run the test cases, which will automatically
52 | # verify that the text color and the background color have
53 | # a contrast ratio of at least 4.5:1, which is AA.
54 | # The contrast ratio between the background and title
55 | # colors should also be at least 4.5:1 (also enforced by test cases).
56 | #
57 | # (2) Before contributing a new color theme, ask yourself
58 | # whether it will likely have broad appeal or a narrow
59 | # audience. For example, if it is just the color palette
60 | # of your personal website or blog, then a theme may not
61 | # be necessary. You can simply use the colors input for
62 | # your usage.
63 | #
64 | # (3) Is it similar to one of the existing themes? Or does it
65 | # provide users with something truly new to choose from?
66 | #
67 | # (4) Please add the new theme alphabetized by theme name.
68 | #
69 | # (5) Include a comment with your GitHub userid indicating you
70 | # are the contributor of the theme (see the existing themes).
71 | #
72 | # (6) You can use either 3-digit hex, 6-digit hex, or named colors.
73 | #
74 | # (7) The existing test cases will automatically test that your
75 | # colors are valid hex, or valid named colors.
76 | # See https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
77 | # for list of named colors.
78 |
79 | colorMapping = {
80 | # Contributor: cicirello (Halloween related themes)
81 | "batty" : {
82 | "bg" : "#F6FAFD",
83 | "border" : "#C0C3C6",
84 | "icons" : "#151515",
85 | "text" : "#535353",
86 | "title" : "#151515",
87 | "title-icon" : "bat"
88 | },
89 |
90 | # Contributor: cicirello (part of initial theme set)
91 | "dark" : {
92 | "bg" : "#0d1117",
93 | "border" : "rgba(56,139,253,0.4)",
94 | "icons" : "#1f6feb",
95 | "text" : "#c9d1d9",
96 | "title" : "#58a6ff",
97 | "title-icon" : "github"
98 | },
99 |
100 | # Contributor: cicirello (updated theme set)
101 | "dark-colorblind" : {
102 | "bg" : "#0d1117",
103 | "border" : "rgba(56,139,253,0.4)",
104 | "icons" : "#1f6feb",
105 | "text" : "#c9d1d9",
106 | "title" : "#58a6ff",
107 | "title-icon" : "github"
108 | },
109 |
110 | # Contributor: cicirello (part of initial theme set)
111 | "dark-dimmed" : {
112 | "bg" : "#22272e",
113 | "border" : "rgba(65,132,228,0.4)",
114 | "icons" : "#316dca",
115 | "text" : "#adbac7",
116 | "title" : "#539bf5",
117 | "title-icon" : "github"
118 | },
119 |
120 | # Contributor: cicirello (updated theme set)
121 | "dark-high-contrast" : {
122 | "bg" : "#0a0c10",
123 | "border" : "#409eff",
124 | "icons" : "#409eff",
125 | "text" : "#f0f3f6",
126 | "title" : "#71b7ff",
127 | "title-icon" : "github"
128 | },
129 |
130 | # Contributor: cicirello (updated theme set)
131 | "dark-tritanopia" : {
132 | "bg" : "#0d1117",
133 | "border" : "rgba(56,139,253,0.4)",
134 | "icons" : "#1f6feb",
135 | "text" : "#c9d1d9",
136 | "title" : "#58a6ff",
137 | "title-icon" : "github"
138 | },
139 |
140 | # Contributor: cicirello (Halloween related themes)
141 | "halloween" : {
142 | "bg" : "#090B06",
143 | "border" : "#F5D913",
144 | "icons" : "#F46D0E",
145 | "text" : "#EB912D",
146 | "title" : "#F46D0E",
147 | "title-icon" : "pumpkin"
148 | },
149 |
150 | # Contributor: cicirello (Halloween related themes)
151 | "halloween-light" : {
152 | "bg" : "#FFFDE9",
153 | "border" : "#E1DF81",
154 | "icons" : "#BA440B",
155 | "text" : "#50391F",
156 | "title" : "#BA440B",
157 | "title-icon" : "pumpkin"
158 | },
159 |
160 | # Contributor: cicirello (part of initial theme set)
161 | "light" : {
162 | "bg" : "#ffffff",
163 | "border" : "rgba(84,174,255,0.4)",
164 | "icons" : "#0969da",
165 | "text" : "#24292f",
166 | "title" : "#0969da",
167 | "title-icon" : "github"
168 | },
169 |
170 | # Contributor: cicirello (updated theme set)
171 | "light-colorblind" : {
172 | "bg" : "#ffffff",
173 | "border" : "rgba(84,174,255,0.4)",
174 | "icons" : "#0969da",
175 | "text" : "#24292f",
176 | "title" : "#0969da",
177 | "title-icon" : "github"
178 | },
179 |
180 | # Contributor: cicirello (updated theme set)
181 | "light-high-contrast" : {
182 | "bg" : "#ffffff",
183 | "border" : "#368cf9",
184 | "icons" : "#0349b4",
185 | "text" : "#0E1116",
186 | "title" : "#0349b4",
187 | "title-icon" : "github"
188 | },
189 |
190 | # Contributor: cicirello (updated theme set)
191 | "light-tritanopia" : {
192 | "bg" : "#ffffff",
193 | "border" : "rgba(84,174,255,0.4)",
194 | "icons" : "#0969da",
195 | "text" : "#24292f",
196 | "title" : "#0969da",
197 | "title-icon" : "github"
198 | },
199 | }
200 |
201 | # These are template strings for the icons available for the title line.
202 | # Each color theme has an associated icon. User can also override the default
203 | # for the theme by name.
204 | #
205 | # The template strings each have up to 4 inputs, {0}, {1}, {2}, and {3}.
206 | # {0} is the width/height, i.e., it is square.
207 | # {1} is the x position in pixels.
208 | # {2} is the y position in pixels.
209 | # {3}, if present, is the fill color, which will be populated with the high
210 | # contrasting color relative to the background (e.g., for github, it will
211 | # either be white or black depending upon background, which is consistent
212 | # with GitHub's logo usage guidelines.
213 | iconTemplates = {
214 | "github" : """""",
215 | "pumpkin" : """""",
216 | "bat" : """""",
217 | }
218 |
--------------------------------------------------------------------------------
/src/PieChart.py:
--------------------------------------------------------------------------------
1 | #
2 | # user-statistician: Github action for generating a user stats card
3 | #
4 | # Copyright (c) 2021-2023 Vincent A Cicirello
5 | # https://www.cicirello.org/
6 | #
7 | # MIT License
8 | #
9 | # Permission is hereby granted, free of charge, to any person obtaining a copy
10 | # of this software and associated documentation files (the "Software"), to deal
11 | # in the Software without restriction, including without limitation the rights
12 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | # copies of the Software, and to permit persons to whom the Software is
14 | # furnished to do so, subject to the following conditions:
15 | #
16 | # The above copyright notice and this permission notice shall be included in all
17 | # copies or substantial portions of the Software.
18 | #
19 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | # SOFTWARE.
26 | #
27 |
28 | import math
29 |
30 | _headerTemplate = '")
98 | return "".join(components)
99 |
--------------------------------------------------------------------------------
/src/StatConfig.py:
--------------------------------------------------------------------------------
1 | #
2 | # user-statistician: Github action for generating a user stats card
3 | #
4 | # Copyright (c) 2021-2023 Vincent A Cicirello
5 | # https://www.cicirello.org/
6 | #
7 | # MIT License
8 | #
9 | # Permission is hereby granted, free of charge, to any person obtaining a copy
10 | # of this software and associated documentation files (the "Software"), to deal
11 | # in the Software without restriction, including without limitation the rights
12 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | # copies of the Software, and to permit persons to whom the Software is
14 | # furnished to do so, subject to the following conditions:
15 | #
16 | # The above copyright notice and this permission notice shall be included in all
17 | # copies or substantial portions of the Software.
18 | #
19 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | # SOFTWARE.
26 | #
27 |
28 | import json
29 |
30 | # The locale keys are ISO 639-1 two-character language codes
31 | # (see: https://www.loc.gov/standards/iso639-2/php/English_list.php).
32 | # If you are contributing a new locale, please add code in alphabetical
33 | # order below.
34 | supportedLocales = {
35 | "bn",
36 | "cs",
37 | "de",
38 | "el",
39 | "en",
40 | "es",
41 | "fa",
42 | "fi",
43 | "fr",
44 | "hi",
45 | "hu",
46 | "hy",
47 | "id",
48 | "it",
49 | "ja",
50 | "ko",
51 | "lt",
52 | "ml",
53 | "nl",
54 | "no",
55 | "or",
56 | "pl",
57 | "pt",
58 | "ro",
59 | "ru",
60 | "sat",
61 | "sr",
62 | "sv",
63 | "th",
64 | "tl",
65 | "tr",
66 | "uk",
67 | }
68 |
69 | # The default order for the categories of stats on the SVG
70 | categoryOrder = ["general", "repositories", "contributions", "languages"]
71 |
72 | # Mapping from category key to list of stats keys in the
73 | # order they should appear.
74 | statsByCategory = {
75 | "general" : [
76 | "joined",
77 | "featured",
78 | "mostStarred",
79 | "mostForked",
80 | "followers",
81 | "sponsors",
82 | "following",
83 | "sponsoring"
84 | ],
85 | "repositories" : [
86 | "public",
87 | "starredBy",
88 | "forkedBy",
89 | "watchedBy",
90 | "templates",
91 | "archived"
92 | ],
93 | "contributions" : [
94 | "commits",
95 | "issues",
96 | "prs",
97 | "reviews",
98 | "contribTo",
99 | "private"
100 | ],
101 | "languages" : []
102 | }
103 |
104 | _locale_directory = "/locales/"
105 |
106 | def loadLocale(locale) :
107 | """Loads the specified locale.
108 |
109 | Keyword arguments:
110 | locale - The locale code to load.
111 | """
112 | with open(_locale_directory + locale + ".json", "r", encoding="utf8") as f:
113 | return json.load(f)
114 |
115 | # ADDITIONAL LICENSE NOTES
116 | #
117 | # GitHub's Octicons:
118 | # The paths defining the icons used in the action, as specified in the
119 | # Python dictionary icons below, are derived from GitHub's Octicons
120 | # (https://github.com/primer/octicons), and are copyright (c) GitHub, Inc,
121 | # and licensed by GitHub under the MIT license.
122 |
123 | # Dictionary of icon paths for the supported statistics.
124 | icons = {
125 | "joined" : '',
126 | "featured" : '',
127 | "mostStarred" : '',
128 | "mostForked" : '',
129 | "followers" : '',
130 | "following" : '',
131 | "sponsors" : '',
132 | "sponsoring" : '',
133 | "public" : '',
134 | "starredBy" : '',
135 | "forkedBy" : '',
136 | "watchedBy" : '',
137 | "templates" : '',
138 | "archived" : '',
139 | "commits" : '',
140 | "issues" : '',
141 | "prs" : '',
142 | "reviews" : '',
143 | "contribTo" : '',
144 | "private" : '',
145 | }
146 |
--------------------------------------------------------------------------------
/src/Statistician.py:
--------------------------------------------------------------------------------
1 | #
2 | # user-statistician: Github action for generating a user stats card
3 | #
4 | # Copyright (c) 2021-2023 Vincent A Cicirello
5 | # https://www.cicirello.org/
6 | #
7 | # MIT License
8 | #
9 | # Permission is hereby granted, free of charge, to any person obtaining a copy
10 | # of this software and associated documentation files (the "Software"), to deal
11 | # in the Software without restriction, including without limitation the rights
12 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | # copies of the Software, and to permit persons to whom the Software is
14 | # furnished to do so, subject to the following conditions:
15 | #
16 | # The above copyright notice and this permission notice shall be included in all
17 | # copies or substantial portions of the Software.
18 | #
19 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | # SOFTWARE.
26 | #
27 |
28 | import json
29 | import subprocess
30 | import os
31 |
32 | def set_outputs(names_values):
33 | """Sets the GitHub Action outputs.
34 |
35 | Keyword arguments:
36 | names_values - Dictionary of output names with values
37 | """
38 | if "GITHUB_OUTPUT" in os.environ:
39 | with open(os.environ["GITHUB_OUTPUT"], "a") as f:
40 | for name, value in names_values.items():
41 | print("{0}={1}".format(name, value), file=f)
42 | else: # Fall-back to deprecated set-output for self-hosted runners
43 | for name, value in names_values.items():
44 | print("::set-output name={0}::{1}".format(name, value))
45 |
46 | class Statistician:
47 | """The Statistician class executes GitHub GraphQl queries,
48 | and parses the query results.
49 | """
50 |
51 | __slots__ = [
52 | '_contributionYears',
53 | '_user',
54 | '_contrib',
55 | '_repo',
56 | '_login',
57 | '_name',
58 | '_languages',
59 | '_autoLanguages',
60 | '_maxLanguages',
61 | '_languageRepoExclusions',
62 | '_featuredRepo'
63 | ]
64 |
65 | def __init__(
66 | self,
67 | fail,
68 | autoLanguages,
69 | maxLanguages,
70 | languageRepoExclusions,
71 | featuredRepo):
72 | """The initializer executes the queries and parses the results.
73 | Upon completion of the intitializer, the user statistics will
74 | be available.
75 |
76 | Keyword arguments:
77 | fail - If True, the workflow will fail if there are errors.
78 | autoLanguages - If True, the number of displayed languages is chosen based on data,
79 | regardless of value of maxLanguages.
80 | maxLanguages - The maximum number of languages to display. Must be at least 1. If less than
81 | 1, it treats it as if it was 1.
82 | languageRepoExclusions - A set of repositories to exclude from language stats
83 | """
84 | self._autoLanguages = autoLanguages
85 | self._maxLanguages = maxLanguages if maxLanguages >= 1 else 1
86 | self._languageRepoExclusions = languageRepoExclusions
87 | self._featuredRepo = featuredRepo
88 | self.ghDisableInteractivePrompts()
89 | basicStatsQuery = self.loadQuery("/queries/basicstats.graphql",
90 | fail)
91 | additionalRepoStatsQuery = self.loadQuery("/queries/repostats.graphql",
92 | fail)
93 | oneYearContribTemplate = self.loadQuery("/queries/singleYearQueryFragment.graphql",
94 | fail)
95 | reposContributedTo = self.loadQuery("/queries/reposContributedTo.graphql",
96 | fail)
97 |
98 | self.parseStats(
99 | self.executeQuery(basicStatsQuery,
100 | failOnError=fail),
101 | self.executeQuery(additionalRepoStatsQuery,
102 | needsPagination=True,
103 | failOnError=fail),
104 | self.executeQuery(reposContributedTo,
105 | needsPagination=True,
106 | failOnError=fail)
107 | )
108 | self.parsePriorYearStats(
109 | self.executeQuery(
110 | self.createPriorYearStatsQuery(self._contributionYears, oneYearContribTemplate),
111 | failOnError=fail
112 | )
113 | )
114 |
115 | def getStatsByKey(self, key):
116 | """Gets a category of stats by key.
117 |
118 | Keyword arguments:
119 | key - A category key.
120 | """
121 | if key == "general":
122 | return self._user
123 | elif key == "repositories":
124 | return self._repo
125 | elif key == "contributions":
126 | return self._contrib
127 | elif key == "languages":
128 | return self._languages
129 | else:
130 | return None # passed an invalid key
131 |
132 | def loadQuery(self, queryFilepath, failOnError=True):
133 | """Loads a graphql query.
134 |
135 | Keyword arguments:
136 | queryFilepath - The file with path of the query.
137 | failOnError - If True, the workflow will fail if there is an error loading the
138 | query; and if False, this action will quietly exit with no error code. In
139 | either case, an error message will be logged to the console.
140 | """
141 | try:
142 | with open(queryFilepath, 'r') as file:
143 | return file.read()
144 | except IOError:
145 | print("Error (1): Failed to open query file:", queryFilePath)
146 | set_outputs({"exit-code" : 1})
147 | exit(1 if failOnError else 0)
148 |
149 | def parseStats(self, basicStats, repoStats, reposContributedToStats):
150 | """Parses the user statistics.
151 |
152 | Keyword arguments:
153 | basicStats - The results of the basic stats query.
154 | repoStats - The results of the repo stats query.
155 | """
156 | # Extract username (i.e., login) and fullname.
157 | # Name needed for title of statistics card, and username
158 | # needed if we support committing stats card.
159 | self._login = basicStats["data"]["user"]["login"]
160 | self._name = basicStats["data"]["user"]["name"]
161 |
162 | # The name field is nullable, so use the login id if
163 | # user's public name is null.
164 | if self._name == None:
165 | self._name = self._login
166 |
167 | # Extract most recent year data from query results
168 | pastYearData = basicStats["data"]["user"]["contributionsCollection"]
169 |
170 | # Extract repositories contributes to (owned by others) in past year
171 | pastYearData[
172 | "repositoriesContributedTo"] = basicStats[
173 | "data"]["user"]["repositoriesContributedTo"]["totalCount"]
174 |
175 | # Extract list of contribution years
176 | self._contributionYears = pastYearData["contributionYears"]
177 | # Just reorganizing data for clarity
178 | del pastYearData["contributionYears"]
179 |
180 | # Extract followed and following counts
181 | self._user = {}
182 | self._user["followers"] = [
183 | basicStats["data"]["user"]["followers"]["totalCount"] ]
184 | self._user["following"] = [
185 | basicStats["data"]["user"]["following"]["totalCount"] ]
186 | self._user["joined"] = [ min(self._contributionYears) ]
187 |
188 | # Extract sponsors and sponsoring counts
189 | self._user["sponsors"] = [
190 | basicStats["data"]["user"]["sponsorshipsAsMaintainer"]["totalCount"] ]
191 | self._user["sponsoring"] = [
192 | basicStats["data"]["user"]["sponsorshipsAsSponsor"]["totalCount"] ]
193 |
194 | #
195 | if self._featuredRepo != None:
196 | self._user["featured"] = [ self._featuredRepo ]
197 |
198 | # Extract all time counts of issues and pull requests
199 | issues = basicStats["data"]["user"]["issues"]["totalCount"]
200 | pullRequests = basicStats["data"]["user"]["pullRequests"]["totalCount"]
201 |
202 | # Reorganize for simplicity
203 | repoStats = list(map(lambda x : x["data"]["user"]["repositories"], repoStats))
204 | reposContributedToStats = list(
205 | map(lambda x : x["data"]["user"]["topRepositories"], reposContributedToStats))
206 |
207 | # This is the count of owned repos, including all public,
208 | # but may or may not include all private depending upon token used to authenticate.
209 | ownedRepositories = repoStats[0]["totalCount"]
210 |
211 | # Count num repos owned by someone else that the user has contributed to
212 | # NOTE: It doesn't appear that it is currently possible through any query
213 | # or combination of queries to actually compute this other than for the most recent
214 | # year's data. Keeping the query in, but changing to leave that stat blank in
215 | # the SVG.
216 | repositoriesContributedTo = sum(
217 | 1 for page in reposContributedToStats if page[
218 | "nodes"] != None for repo in page[
219 | "nodes"] if repo["owner"]["login"] != self._login)
220 |
221 | self._contrib = {
222 | "commits" : [pastYearData["totalCommitContributions"], 0],
223 | "issues" : [pastYearData["totalIssueContributions"], issues],
224 | "prs" : [pastYearData["totalPullRequestContributions"], pullRequests],
225 | "reviews" : [pastYearData["totalPullRequestReviewContributions"], 0],
226 | # See comment above for reason for this change.
227 | #"contribTo" : [pastYearData["repositoriesContributedTo"], repositoriesContributedTo],
228 | "contribTo" : [pastYearData["repositoriesContributedTo"]],
229 | "private" : [pastYearData["restrictedContributionsCount"], 0]
230 | }
231 |
232 | # The "nodes" field is nullable so make sure the user owns at least 1 repo.
233 | if repoStats[0]["totalCount"] > 0:
234 | # Note that the explicit checks of, if page["nodes"] != None, are
235 | # precautionary since the above check of totalCount should be sufficient
236 | # to protect against a null list of repos.
237 |
238 | # Count stargazers, forks of my repos, and watchers
239 | stargazers = sum(
240 | repo["stargazerCount"] for page in repoStats if page[
241 | "nodes"] != None for repo in page[
242 | "nodes"] if not repo["isPrivate"] and not repo["isFork"])
243 | forksOfMyRepos = sum(
244 | repo["forkCount"] for page in repoStats if page[
245 | "nodes"] != None for repo in page[
246 | "nodes"] if not repo["isPrivate"] and not repo["isFork"])
247 | stargazersAll = sum(
248 | repo["stargazerCount"] for page in repoStats if page[
249 | "nodes"] != None for repo in page[
250 | "nodes"] if not repo["isPrivate"])
251 | forksOfMyReposAll = sum(
252 | repo["forkCount"] for page in repoStats if page[
253 | "nodes"] != None for repo in page[
254 | "nodes"] if not repo["isPrivate"])
255 |
256 | # Find repos with most stars and most forks
257 | try:
258 | mostStars = max(
259 | (repo for page in repoStats if page[
260 | "nodes"] != None for repo in page[
261 | "nodes"] if not repo["isPrivate"] and not repo["isFork"]),
262 | key=lambda x : x["stargazerCount"])["name"]
263 | self._user["mostStarred"] = [ mostStars ]
264 | except ValueError:
265 | pass
266 |
267 | try:
268 | mostForks = max(
269 | (repo for page in repoStats if page[
270 | "nodes"] != None for repo in page["nodes"] if not repo[
271 | "isPrivate"] and not repo["isFork"]),
272 | key=lambda x : x["forkCount"])["name"]
273 | self._user["mostForked"] = [ mostForks ]
274 | except ValueError:
275 | pass
276 |
277 | # Compute number of watchers
278 | watchers = sum(
279 | repo["watchers"]["totalCount"] for page in repoStats if page[
280 | "nodes"] != None for repo in page["nodes"] if not repo["isPrivate"])
281 |
282 | watchersNonForks = sum(
283 | repo["watchers"]["totalCount"] for page in repoStats if page[
284 | "nodes"] != None for repo in page["nodes"] if not repo[
285 | "isPrivate"] and not repo["isFork"])
286 |
287 | # Count of private repos (not accurate since depends on token used to authenticate query,
288 | # however, all those here are included in count of owned repos.
289 | privateCount = sum(
290 | 1 for page in repoStats if page[
291 | "nodes"] != None for repo in page["nodes"] if repo["isPrivate"])
292 |
293 | publicAll = ownedRepositories - privateCount
294 |
295 | # Counts of archived repos
296 | publicNonForksArchivedCount = sum(
297 | 1 for page in repoStats if page[
298 | "nodes"] != None for repo in page["nodes"] if repo[
299 | "isArchived"] and not repo["isPrivate"] and not repo["isFork"])
300 | publicArchivedCount = sum(
301 | 1 for page in repoStats if page[
302 | "nodes"] != None for repo in page["nodes"] if repo[
303 | "isArchived"] and not repo["isPrivate"])
304 |
305 | # Counts of template repos
306 | publicNonForksTemplatesCount = sum(
307 | 1 for page in repoStats if page[
308 | "nodes"] != None for repo in page["nodes"] if repo[
309 | "isTemplate"] and not repo["isPrivate"] and not repo["isFork"])
310 | publicTemplatesCount = sum(
311 | 1 for page in repoStats if page[
312 | "nodes"] != None for repo in page["nodes"] if repo[
313 | "isTemplate"] and not repo["isPrivate"])
314 |
315 | # Count of public non forks owned by user
316 | publicNonForksCount = ownedRepositories - sum(
317 | 1 for page in repoStats if page["nodes"] != None for repo in page[
318 | "nodes"] if repo["isPrivate"] or repo["isFork"])
319 |
320 | # Compute language distribution
321 | totalSize, languageData = self.summarizeLanguageStats(repoStats)
322 | else:
323 | # if no owned repos then set all repo related stats to 0
324 | stargazers = 0
325 | forksOfMyRepos = 0
326 | stargazersAll = 0
327 | forksOfMyReposAll = 0
328 | watchers = 0
329 | watchersNonForks = 0
330 | privateCount = 0
331 | publicAll = 0
332 | publicNonForksArchivedCount = 0
333 | publicArchivedCount = 0
334 | publicNonForksCount = 0
335 | publicNonForksTemplatesCount = 0
336 | publicTemplatesCount = 0
337 | totalSize, languageData = 0, {}
338 |
339 | self._repo = {
340 | "public" : [publicNonForksCount, publicAll],
341 | "starredBy" : [stargazers, stargazersAll],
342 | "forkedBy" : [forksOfMyRepos, forksOfMyReposAll],
343 | "watchedBy" : [watchersNonForks, watchers],
344 | "archived" : [publicNonForksArchivedCount, publicArchivedCount],
345 | "templates" : [publicNonForksTemplatesCount, publicTemplatesCount]
346 | }
347 |
348 | self._languages = self.organizeLanguageStats(totalSize, languageData)
349 |
350 | def organizeLanguageStats(self, totalSize, languageData):
351 | """Computes a list of languages and percentages in decreasing order
352 | by percentage.
353 |
354 | Keyword arguments:
355 | totalSize - total size of all code with language detection data
356 | languageData - the summarized language totals, colors, etc
357 | """
358 | if totalSize == 0:
359 | return { "totalSize" : 0, "languages" : [] }
360 | else:
361 | languages = [ (name, data) for name, data in languageData.items() ]
362 | languages.sort(key = lambda L : L[1]["size"], reverse=True)
363 | if self._autoLanguages :
364 | for i, L in enumerate(languages):
365 | if L[1]["percentage"] < 0.01:
366 | self._maxLanguages = i
367 | break
368 | if len(languages) > self._maxLanguages:
369 | self.combineLanguages(languages, self._maxLanguages, totalSize)
370 | self.checkColors(languages)
371 | return { "totalSize" : totalSize, "languages" : languages }
372 |
373 | def combineLanguages(self, languages, maxLanguages, totalSize):
374 | """Combines lowest percentage languages into an Other.
375 |
376 | Keyword arguments:
377 | languages - Sorted list of languages (sorted by size).
378 | maxLanguages - The maximum number of languages to keep as is.
379 | """
380 | if len(languages) > self._maxLanguages:
381 | combinedSize = sum(L[1]["size"] for L in languages[maxLanguages:])
382 | languages[maxLanguages] = (
383 | "Other",
384 | { "color" : None,
385 | "size" : combinedSize,
386 | "percentage" : combinedSize / totalSize
387 | }
388 | )
389 | del languages[maxLanguages+1:]
390 |
391 | def checkColors(self, languages):
392 | """Make sure all languages have colors, and assign shades of gray to
393 | those that don't.
394 |
395 | Keyword arguments:
396 | languages - Sorted list of languages (sorted by size).
397 | """
398 | # Not all languages have colors assigned by GitHub's Linguist.
399 | # In such cases, we alternate between these two shades of gray.
400 | colorsForLanguagesWithoutColors = [ "#959da5", "#d1d5da" ]
401 | index = 0
402 | for L in languages:
403 | if L[1]["color"] == None:
404 | L[1]["color"] = colorsForLanguagesWithoutColors[index]
405 | index = (index + 1) % 2
406 |
407 | def summarizeLanguageStats(self, repoStats):
408 | """Summarizes the language distibution of the user's owned repositories.
409 |
410 | Keyword arguments:
411 | repoStats - The results of the repo stats query.
412 | """
413 | totalSize = 0
414 | languageData = {}
415 | for page in repoStats:
416 | if page["nodes"] != None:
417 | for repo in page["nodes"]:
418 | if (not repo["isPrivate"] and not repo["isFork"] and
419 | (repo["name"].lower() not in self._languageRepoExclusions)):
420 | totalSize += repo["languages"]["totalSize"]
421 | if repo["languages"]["edges"] != None :
422 | for L in repo["languages"]["edges"]:
423 | name = L["node"]["name"]
424 | if name in languageData:
425 | languageData[name]["size"] += L["size"]
426 | else:
427 | languageData[name] = {
428 | "color" : L["node"]["color"],
429 | "size" : L["size"]
430 | }
431 | for L in languageData:
432 | languageData[L]["percentage"] = languageData[L]["size"] / totalSize
433 | return totalSize, languageData
434 |
435 | def createPriorYearStatsQuery(self, yearList, oneYearContribTemplate):
436 | """Generates the query for prior year stats.
437 |
438 | Keyword arguments:
439 | yearList - a list of the years when the user had contributions,
440 | obtained by one of the other queries.
441 | oneYearContribTemplate - a string template of the part of a query
442 | for one year.
443 | """
444 | query = "query($owner: String!) {\n user(login: $owner) {\n"
445 | for y in yearList:
446 | query += oneYearContribTemplate.format(y)
447 | query += " }\n}\n"
448 | return query
449 |
450 | def parsePriorYearStats(self, queryResults):
451 | """Parses one year of commits, PR reviews, and restricted contributions.
452 |
453 | Keyword arguments:
454 | queryResults - The results of the query.
455 | """
456 | queryResults = queryResults["data"]["user"]
457 | self._contrib["commits"][1] = sum(
458 | stats["totalCommitContributions"] for k, stats in queryResults.items())
459 | self._contrib["reviews"][1] = sum(
460 | stats["totalPullRequestReviewContributions"] for k, stats in queryResults.items())
461 | self._contrib["private"][1] = sum(
462 | stats["restrictedContributionsCount"] for k, stats in queryResults.items())
463 |
464 | def executeQuery(self, query, needsPagination=False, failOnError=True):
465 | """Executes a GitHub GraphQl query using the GitHub CLI (gh).
466 |
467 | Keyword arguments:
468 | query - The query as a string.
469 | needsPagination - Pass True to enable pagination of query results.
470 | failOnError - If True, the workflow will fail if there is an error executing the
471 | query; and if False, this action will quietly exit with no error code. In
472 | either case, an error message will be logged to the console.
473 | """
474 | if "GITHUB_REPOSITORY_OWNER" in os.environ:
475 | owner = os.environ["GITHUB_REPOSITORY_OWNER"]
476 | else:
477 | print("Error (7): Could not determine the repository owner.")
478 | set_outputs({"exit-code" : 7})
479 | exit(7 if failOnError else 0)
480 | arguments = [
481 | 'gh', 'api', 'graphql',
482 | '-F', 'owner=' + owner,
483 | '--cache', '1h',
484 | '-f', 'query=' + query
485 | ]
486 | if needsPagination:
487 | arguments.insert(5, '--paginate')
488 | result = subprocess.run(
489 | arguments,
490 | stdout=subprocess.PIPE,
491 | universal_newlines=True
492 | ).stdout.strip()
493 | numPages = result.count('"data"')
494 | if numPages == 0:
495 | # Check if any error details
496 | result = json.loads(result) if len(result) > 0 else ""
497 | if "errors" in result:
498 | print("Error (2): GitHub api Query failed with error:")
499 | print(result["errors"])
500 | code = 2
501 | else:
502 | print("Error (3): Something unexpected occurred during GitHub API query.")
503 | code = 3
504 | set_outputs({"exit-code" : code})
505 | exit(code if failOnError else 0)
506 | elif needsPagination:
507 | if (numPages > 1):
508 | result = result.replace('}{"data"', '},{"data"')
509 | result = "[" + result + "]"
510 | result = json.loads(result)
511 | failed = False
512 | errorMessage = None
513 | if ((not needsPagination) and
514 | (("data" not in result) or result["data"] == None)):
515 | failed = True
516 | if "errors" in result:
517 | errorMessage = result["errors"]
518 | elif needsPagination and ("data" not in result[0] or result[0]["data"] == None):
519 | failed = True
520 | if "errors" in result[0] :
521 | errorMessage = result[0]["errors"]
522 | if failed:
523 | print("Error (6): No data returned.")
524 | if errorMessage != None:
525 | print(errorMessage)
526 | set_outputs({"exit-code" : 6})
527 | exit(6 if failOnError else 0)
528 | return result
529 |
530 | def ghDisableInteractivePrompts(self):
531 | """Disable gh's interactive prompts. This is probably unnecessary,
532 | as all of our testing so far, the queries run fine and don't produce any
533 | prompts. Disabling as a precaution in case some unexpected condition occurs
534 | that generates a prompt, so we don't accidentally leave a workflow waiting for
535 | user itneraction.
536 | """
537 | result = subprocess.run(
538 | ["gh", "config", "set", "prompt", "disabled"],
539 | stdout=subprocess.PIPE,
540 | universal_newlines=True
541 | ).stdout.strip()
542 |
543 |
--------------------------------------------------------------------------------
/src/StatsImageGenerator.py:
--------------------------------------------------------------------------------
1 | #
2 | # user-statistician: Github action for generating a user stats card
3 | #
4 | # Copyright (c) 2021-2023 Vincent A Cicirello
5 | # https://www.cicirello.org/
6 | #
7 | # MIT License
8 | #
9 | # Permission is hereby granted, free of charge, to any person obtaining a copy
10 | # of this software and associated documentation files (the "Software"), to deal
11 | # in the Software without restriction, including without limitation the rights
12 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | # copies of the Software, and to permit persons to whom the Software is
14 | # furnished to do so, subject to the following conditions:
15 | #
16 | # The above copyright notice and this permission notice shall be included in all
17 | # copies or substantial portions of the Software.
18 | #
19 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | # SOFTWARE.
26 | #
27 |
28 | from StatConfig import statsByCategory, loadLocale, icons
29 | from PieChart import svgPieChart
30 | from Colors import iconTemplates
31 | from ColorUtil import highContrastingColor
32 | from TextLength import calculateTextLength, calculateTextLength110Weighted
33 | import math
34 |
35 | class StatsImageGenerator:
36 | """Generates an svg image from the collected stats."""
37 |
38 | headerTemplate = '\n")
572 |
573 |
--------------------------------------------------------------------------------
/src/UserStatistician.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env -S python3 -B
2 | #
3 | # user-statistician: Github action for generating a user stats card
4 | #
5 | # Copyright (c) 2021-2024 Vincent A Cicirello
6 | # https://www.cicirello.org/
7 | #
8 | # MIT License
9 | #
10 | # Permission is hereby granted, free of charge, to any person obtaining a copy
11 | # of this software and associated documentation files (the "Software"), to deal
12 | # in the Software without restriction, including without limitation the rights
13 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | # copies of the Software, and to permit persons to whom the Software is
15 | # furnished to do so, subject to the following conditions:
16 | #
17 | # The above copyright notice and this permission notice shall be included in all
18 | # copies or substantial portions of the Software.
19 | #
20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 | # SOFTWARE.
27 | #
28 |
29 | from Statistician import Statistician, set_outputs
30 | from Colors import colorMapping, iconTemplates
31 | from StatsImageGenerator import StatsImageGenerator
32 | from StatConfig import supportedLocales, categoryOrder
33 | import sys
34 | import os
35 | import subprocess
36 |
37 | def writeImageToFile(filename, image, failOnError):
38 | """Writes the image to a file, creating any
39 | missing directories from the path.
40 |
41 | Keyword arguments:
42 | filename - The filename for the image, with complete path.
43 | image - A string containing the image.
44 | failOnError - If True, the workflow will fail if there is an error
45 | writing the image to a file; and if False, this action will quietly
46 | exit with no error code. In either case, an error message will be
47 | logged to the console.
48 | """
49 | # Since we're running in a docker container, everything runs
50 | # as root. We need this umask call so we'll have write permissions
51 | # once the action finished and we're outside the container again.
52 | os.umask(0)
53 | # Create the directory if it doesn't exist.
54 | directoryName = os.path.dirname(filename)
55 | if len(directoryName) > 0:
56 | os.makedirs(directoryName, exist_ok=True, mode=0o777)
57 | try:
58 | # Write the image to a file
59 | with open(filename, "wb") as file:
60 | image = image.encode(encoding="UTF-8")
61 | file.write(image)
62 | except IOError:
63 | print("Error (4): An error occurred while writing the image to a file.")
64 | set_outputs({"exit-code" : 4})
65 | exit(4 if failOnError else 0)
66 |
67 | def executeCommand(arguments):
68 | """Execute a subprocess and return result and exit code.
69 |
70 | Keyword arguments:
71 | arguments - The arguments for the command.
72 | """
73 | result = subprocess.run(
74 | arguments,
75 | stdout=subprocess.PIPE,
76 | universal_newlines=True
77 | )
78 | return result.stdout.strip(), result.returncode
79 |
80 | def commitAndPush(filename, name, login, failOnError, commit_message):
81 | """Commits and pushes the image.
82 |
83 | Keyword arguments:
84 | filename - The path to the image.
85 | name - The user's name.
86 | login - The user's login id.
87 | failOnError - Boolean controlling whether or not to fail the run if an error occurs.
88 | commit_message - Message for the commit
89 | """
90 | # Resolve issue related to user in Docker container vs owner of repository
91 | executeCommand(
92 | ["git", "config", "--global", "--add", "safe.directory", "/github/workspace"])
93 | # Make sure this isn't being run during a pull-request.
94 | result = executeCommand(
95 | ["git", "symbolic-ref", "-q", "HEAD"])
96 | if result[1] == 0:
97 | # Check if the image changed
98 | result = executeCommand(
99 | ["git", "status", "--porcelain", filename])
100 | if len(result[0]) > 0:
101 | # Commit and push
102 | executeCommand(
103 | ["git", "config", "--global", "user.name", name])
104 | executeCommand(
105 | ["git", "config", "--global", "user.email",
106 | login + '@users.noreply.github.com'])
107 | executeCommand(["git", "add", filename])
108 | executeCommand(["git", "commit", "-m",
109 | commit_message,
110 | filename])
111 | r = executeCommand(["git", "push"])
112 | if r[1] != 0:
113 | print("Error (5): push failed.")
114 | set_outputs({"exit-code" : 5})
115 | exit(5 if failOnError else 0)
116 |
117 |
118 | if __name__ == "__main__":
119 |
120 | imageFilenameWithPath = sys.argv[1].strip()
121 |
122 | includeTitle = sys.argv[2].strip().lower() == "true"
123 |
124 | customTitle = sys.argv[3].strip()
125 | if len(customTitle) == 0 or not includeTitle:
126 | customTitle = None
127 |
128 | colors = sys.argv[4].strip().replace(",", " ").split()
129 | if len(colors) == 1 and colors[0] in colorMapping:
130 | # get theme colors
131 | colors = colorMapping[colors[0]]
132 | elif len(colors) < 4:
133 | # default to light theme if invalid number of colors passed
134 | colors = colorMapping["light"]
135 | else:
136 | colors = {
137 | "title-icon" : "github",
138 | "bg" : colors[0],
139 | "border" : colors[1],
140 | "icons" : colors[2],
141 | "title" : colors[3],
142 | "text" : colors[4] if len(colors) > 4 else colors[3]
143 | }
144 |
145 | exclude = set(sys.argv[5].strip().replace(",", " ").split())
146 |
147 | failOnError = sys.argv[6].strip().lower() == "true"
148 |
149 | commit = sys.argv[7].strip().lower() == "true"
150 |
151 | locale = sys.argv[8].strip().lower()
152 | if locale not in supportedLocales:
153 | locale = "en"
154 |
155 | radius = int(sys.argv[9])
156 |
157 | showBorder = sys.argv[10].strip().lower() == "true"
158 | if not showBorder :
159 | radius = 0
160 | colors["border"] = colors["bg"]
161 |
162 | smallTitle = sys.argv[11].strip().lower() == "true"
163 | if smallTitle :
164 | titleSize = 16
165 | else :
166 | titleSize = 18
167 |
168 | maxLanguages = sys.argv[12].strip().lower()
169 | autoLanguages = maxLanguages == "auto"
170 | if not autoLanguages:
171 | maxLanguages = int(maxLanguages)
172 | else:
173 | maxLanguages = 1000 # doesn't really matter, but should be an int
174 |
175 | categories = sys.argv[13].strip().replace(",", " ").lower().split()
176 | validCategoryKeys = set(categoryOrder)
177 | categories = [ c for c in categories if c in validCategoryKeys]
178 | if len(categories) == 0 :
179 | categories = categoryOrder
180 |
181 | languageRepoExclusions = set(
182 | sys.argv[14].strip().replace(",", " ").lower().split())
183 |
184 | featuredRepo = sys.argv[15].strip()
185 | if len(featuredRepo) == 0:
186 | featuredRepo = None
187 |
188 | animateLanguageChart = sys.argv[16].strip().lower() == "true"
189 | animationSpeed = int(sys.argv[17].strip())
190 |
191 | width = int(sys.argv[18].strip())
192 |
193 | topIcon = sys.argv[19].strip().lower()
194 | if topIcon == "none":
195 | colors.pop("title-icon", None)
196 | elif topIcon != "default" and topIcon in iconTemplates:
197 | colors["title-icon"] = topIcon
198 |
199 | commit_message = sys.argv[20].strip()
200 |
201 | stats = Statistician(
202 | failOnError,
203 | autoLanguages,
204 | maxLanguages,
205 | languageRepoExclusions,
206 | featuredRepo
207 | )
208 | generator = StatsImageGenerator(
209 | stats,
210 | colors,
211 | locale,
212 | radius,
213 | titleSize,
214 | categories,
215 | animateLanguageChart,
216 | animationSpeed,
217 | width,
218 | customTitle,
219 | includeTitle,
220 | exclude
221 | )
222 | image = generator.generateImage()
223 | writeImageToFile(imageFilenameWithPath, image, failOnError)
224 |
225 | if commit:
226 | commitAndPush(
227 | imageFilenameWithPath,
228 | "github-actions",
229 | "41898282+github-actions[bot]",
230 | failOnError,
231 | commit_message)
232 |
233 | set_outputs({"exit-code" : 0})
234 |
235 |
--------------------------------------------------------------------------------
/src/locales/bn.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "{0} এর গিটহাব কার্যকলাপ",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "সাধারণ পরিসংখ্যান এবং তথ্য",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "সংগ্রহস্থল",
11 | "column-one": "অ-কাঁটা",
12 | "column-two": "সব"
13 | },
14 | "contributions": {
15 | "heading": "অবদানসমূহ",
16 | "column-one": "বিগত বছর",
17 | "column-two": "মোট"
18 | },
19 | "languages": {
20 | "heading": "প্রকাশ্য ভান্ডারে ভাষা বিতরণ",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "যোগদানের বছর",
27 | "featured": "বৈশিষ্ট্যযুক্ত রেপো",
28 | "mostStarred": "সর্বাধিক তারকা প্রাপ্ত রেপো",
29 | "mostForked": "সর্বাধিক ফর্কড রেপো",
30 | "followers": "অনুসারী",
31 | "following": "অনুসরণ করছে",
32 | "sponsors": "পৃষ্ঠপোষক",
33 | "sponsoring": "পৃষ্ঠপোষকতা",
34 | "public": "ভাণ্ডার মালিকানাধীন",
35 | "starredBy": "তারকা প্রদান করেছে",
36 | "forkedBy": "ফোর্ক করেছে",
37 | "watchedBy": "দেখেছেন",
38 | "templates": "টেমপ্লেট সমুহ",
39 | "archived": "সংরক্ষণাগারভুক্ত",
40 | "commits": "কমিট করে",
41 | "issues": "ইস্যু",
42 | "prs": "অনুরোধগুলি টানুন",
43 | "reviews": "অনুরোধ টানার পর্যালোচনাগুলি",
44 | "contribTo": "অবদান",
45 | "private": "ব্যক্তিগত অবদান"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/cs.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "{0}ova GitHub aktivita",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "Obecné statistiky a informace",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Repozitáře",
11 | "column-one": "Neodvozené",
12 | "column-two": "Vše"
13 | },
14 | "contributions": {
15 | "heading": "Příspěvky",
16 | "column-one": "Minulý rok",
17 | "column-two": "Celkem"
18 | },
19 | "languages": {
20 | "heading": "Distribuce jazyků ve veřejných repozitářích",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "Rok připojení",
27 | "featured": "Vyznačený repozitář",
28 | "mostStarred": "Nejvíce hvězdičkový repozitář",
29 | "mostForked": "Nejvíce odvozený repozitář",
30 | "followers": "Následovníci",
31 | "following": "Sleduji",
32 | "sponsors": "Sponzoři",
33 | "sponsoring": "Sponzorování",
34 | "public": "Mé repozitáře",
35 | "starredBy": "Hvězdičkované od",
36 | "forkedBy": "Odvozené od",
37 | "watchedBy": "Sledované od",
38 | "templates": "Šablony",
39 | "archived": "Archivované",
40 | "commits": "Závazky",
41 | "issues": "Problémy",
42 | "prs": "Žádosti o sloučení",
43 | "reviews": "Recenze žádostí o sloučení",
44 | "contribTo": "Přispěno do",
45 | "private": "Soukromé příspěvky"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/locales/de.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "{0}s GitHub Aktivität",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "Allgemeine Statistiken und Informationen",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Repositories",
11 | "column-one": "Non-Forks",
12 | "column-two": "Alle"
13 | },
14 | "contributions": {
15 | "heading": "Beiträge",
16 | "column-one": "Letztes Jahr",
17 | "column-two": "Gesamt"
18 | },
19 | "languages": {
20 | "heading": "Verteilung der Sprachen in Öffentlichen Repositories",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "Beitrittsdatum",
27 | "featured": "Vorgestelltes Repo",
28 | "mostStarred": "Meistmarkiertes Repo",
29 | "mostForked": "Meistgeforktes Repo",
30 | "followers": "Follower",
31 | "following": "Folgt",
32 | "sponsors": "Sponsoren",
33 | "sponsoring": "Sponsoring",
34 | "public": "Eigene Repositories",
35 | "starredBy": "Markiert Von",
36 | "forkedBy": "Geforkt Von",
37 | "watchedBy": "Verfolgt Von",
38 | "templates": "Vorlagen",
39 | "archived": "Archiviert",
40 | "commits": "Commits",
41 | "issues": "Issues",
42 | "prs": "Pull Requests",
43 | "reviews": "Überprüfungen von Pull Requests",
44 | "contribTo": "Beigetragen Zu",
45 | "private": "Private Beiträge"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/el.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "Δραστηριότητα GitHub του {0}",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "Γενικά Στατιστικά και Πληροφορίες",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Αποθετήρια",
11 | "column-one": "Μη Forks",
12 | "column-two": "Όλα"
13 | },
14 | "contributions": {
15 | "heading": "Συνεισφορές",
16 | "column-one": "Τελευταίος Χρόνος",
17 | "column-two": "Σύνολο"
18 | },
19 | "languages": {
20 | "heading": "Κατανομή Γλωσσών στα Δημόσια Αποθετήρια",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "Έτος Εγγραφής",
27 | "featured": "Προτεινόμενο Αποθετήριο",
28 | "mostStarred": "Αποθετήριο με τα Περισσότερα Αστέρια",
29 | "mostForked": "Αποθετήριο με τα Περισσότερα Forks",
30 | "followers": "Ακόλουθοι",
31 | "following": "Ακολουθεί",
32 | "sponsors": "Χορηγοί",
33 | "sponsoring": "Χορηγεί",
34 | "public": "Τα Αποθετήρια Μου",
35 | "starredBy": "Προτιμήθηκε Από",
36 | "forkedBy": "Fork Από",
37 | "watchedBy": "Παρακολουθήθηκε Από",
38 | "templates": "Πρότυπα",
39 | "archived": "Αρχειοθετημένα",
40 | "commits": "Commits",
41 | "issues": "Θέματα",
42 | "prs": "Αιτήματα Έλξης",
43 | "reviews": "Αξιολογήσεις Αιτημάτων Έλξης",
44 | "contribTo": "Συνεισφορά Σε",
45 | "private": "Ιδιωτικές Συνεισφορές"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/locales/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "{0}'s GitHub Activity",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "General Stats and Info",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Repositories",
11 | "column-one": "Non-Forks",
12 | "column-two": "All"
13 | },
14 | "contributions": {
15 | "heading": "Contributions",
16 | "column-one": "Past Year",
17 | "column-two": "Total"
18 | },
19 | "languages": {
20 | "heading": "Language Distribution in Public Repositories",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "Year Joined",
27 | "featured": "Featured Repo",
28 | "mostStarred": "Most Starred Repo",
29 | "mostForked": "Most Forked Repo",
30 | "followers": "Followers",
31 | "following": "Following",
32 | "sponsors": "Sponsors",
33 | "sponsoring": "Sponsoring",
34 | "public": "My Repositories",
35 | "starredBy": "Starred By",
36 | "forkedBy": "Forked By",
37 | "watchedBy": "Watched By",
38 | "templates": "Templates",
39 | "archived": "Archived",
40 | "commits": "Commits",
41 | "issues": "Issues",
42 | "prs": "Pull Requests",
43 | "reviews": "Pull Request Reviews",
44 | "contribTo": "Contributed To",
45 | "private": "Private Contributions"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/es.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "Actividad en GitHub de {0}",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "Estadísticas generales e información",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Repositorios",
11 | "column-one": "No bifurcados",
12 | "column-two": "Todos"
13 | },
14 | "contributions": {
15 | "heading": "Contribuciones",
16 | "column-one": "Año pasado",
17 | "column-two": "Total"
18 | },
19 | "languages": {
20 | "heading": "Distribución de lenguajes en repositorios públicos",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "Año de ingreso",
27 | "featured": "Repositorio destacado",
28 | "mostStarred": "Repositorio con más estrellas",
29 | "mostForked": "Repositorio más bifurcado",
30 | "followers": "Seguidores",
31 | "following": "Siguiendo",
32 | "sponsors": "Patrocinadores",
33 | "sponsoring": "Patrocinando",
34 | "public": "Repositorios propios",
35 | "starredBy": "Con estrella por",
36 | "forkedBy": "Bifurcado por",
37 | "watchedBy": "Visto por",
38 | "templates": "Plantillas",
39 | "archived": "Archivado",
40 | "commits": "Commits",
41 | "issues": "Problemas",
42 | "prs": "Pull Requests",
43 | "reviews": "Revisiones de Pull Requests",
44 | "contribTo": "Contribuido a",
45 | "private": "Contribuciones privadas"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/fa.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "عملکرد گیتهاب {0}",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "آمار و اطلاعات کلی",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "ریپازیتوری ها",
11 | "column-one": "فورک نشده",
12 | "column-two": "همه"
13 | },
14 | "contributions": {
15 | "heading": "همکاری ها",
16 | "column-one": "سال گذشته",
17 | "column-two": "مجموع"
18 | },
19 | "languages": {
20 | "heading": "پراکندگی زبان ها در ریپو های عمومی",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "سال عضویت",
27 | "featured": "ریپازیتوری برجسته",
28 | "mostStarred": "ریپو با بیشترین ستاره",
29 | "mostForked": "ریپو با بیشترین فورک",
30 | "followers": "دنبال کنندگان",
31 | "following": "دنبال میکند",
32 | "sponsors": "اسپانسر ها",
33 | "sponsoring": "اسپانسر میکند",
34 | "public": "ریپازیتوری ها",
35 | "starredBy": "ستاره کنندگان",
36 | "forkedBy": "فورک کنندگان",
37 | "watchedBy": "بینندگان",
38 | "templates": "قالب ها",
39 | "archived": "آرشیو ها",
40 | "commits": "کامیت ها",
41 | "issues": "موضوعات",
42 | "prs": "درخواست های کشش",
43 | "reviews": "مرور درخواست های کشش",
44 | "contribTo": "همکاری کرده با",
45 | "private": "همکاری های خصوصی"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/fi.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "Käyttäjän {0} GitHub-toiminto",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "Yleiset tilastot ja tiedot",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Tietovarastot",
11 | "column-one": "Ei-haarukat",
12 | "column-two": "Kaikki"
13 | },
14 | "contributions": {
15 | "heading": "Avustukset",
16 | "column-one": "Viime vuosi",
17 | "column-two": "Kaikki yhteensä"
18 | },
19 | "languages": {
20 | "heading": "Kielten jakelu julkisissa arkistoissa",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "Vuosi liittyi",
27 | "featured": "Suositeltu Repo",
28 | "mostStarred": "Tähdellä merkityin Repo",
29 | "mostForked": "Useimmat Forked Repo",
30 | "followers": "Seuraajat",
31 | "following": "Seurata",
32 | "sponsors": "Sponsorit",
33 | "sponsoring": "Sponsorointi",
34 | "public": "Omat arkistot",
35 | "starredBy": "tähdellä",
36 | "forkedBy": "Haaroittunut",
37 | "watchedBy": "Katsonut",
38 | "templates": "Mallit",
39 | "archived": "Arkistoitu",
40 | "commits": "Sitoutuu",
41 | "issues": "ongelmia",
42 | "prs": "Vedä pyyntöjä",
43 | "reviews": "Pyydä arvosteluja",
44 | "contribTo": "Osallistunut",
45 | "private": "Yksityiset lahjoitukset"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/locales/fr.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "Activité GitHub de {0}",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "Statistiques Générales et Info",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Dépôts",
11 | "column-one": "Non clonés",
12 | "column-two": "Tout"
13 | },
14 | "contributions": {
15 | "heading": "Contributions",
16 | "column-one": "Dernière année",
17 | "column-two": "Total"
18 | },
19 | "languages": {
20 | "heading": "Répartition des langages dans les dépôts publiques",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "Année d'adhésion",
27 | "featured": "Dépôt en vedette",
28 | "mostStarred": "Dépôt le plus étoilé",
29 | "mostForked": "Dépôt le plus cloné",
30 | "followers": "Abonnés",
31 | "following": "Abonnements",
32 | "sponsors": "Sponsors",
33 | "sponsoring": "Sponsorise",
34 | "public": "Dépôts possédés",
35 | "starredBy": "Étoilé par",
36 | "forkedBy": "Cloné par",
37 | "watchedBy": "Regardé par",
38 | "templates": "Modèles",
39 | "archived": "Archivé",
40 | "commits": "Commits",
41 | "issues": "Issues",
42 | "prs": "Pull Requests",
43 | "reviews": "Révision de Pull Request",
44 | "contribTo": "Contribué à",
45 | "private": "Contributions privées"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/hi.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "{0} की गिटहब गतिविधि",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "साधारण सांख्यिकी और सूचना",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "भंडार",
11 | "column-one": "गैर-फोर्क",
12 | "column-two": "सभी"
13 | },
14 | "contributions": {
15 | "heading": "योगदान",
16 | "column-one": "पिछला वर्ष",
17 | "column-two": "कुल"
18 | },
19 | "languages": {
20 | "heading": "सार्वजनिक भंडारों में भाषा वितरण",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "युक्त होने का वर्ष",
27 | "featured": "विशेष रुप से प्रदर्शित भंडार",
28 | "mostStarred": "सर्वाधिक तारांकित भंडार",
29 | "mostForked": "सर्वाधिक फोर्क भंडार",
30 | "followers": "समर्थक",
31 | "following": "अनुगामी",
32 | "sponsors": "प्रायोजक",
33 | "sponsoring": "प्रायोजन",
34 | "public": "अपना भंडार",
35 | "starredBy": "किसके द्वारा तारांकित",
36 | "forkedBy": "किसके द्वारा फोर्क किया गया",
37 | "watchedBy": "किसके द्वारा देखा गया",
38 | "templates": "आकार पट्ट",
39 | "archived": "संग्रहीत",
40 | "commits": "प्रतिबद्ध",
41 | "issues": "मुद्दे",
42 | "prs": "अनुरोध",
43 | "reviews": "अनुरोध समीक्षा",
44 | "contribTo": "योगदान",
45 | "private": "गुप्त योगदान"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/hu.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "{0} GitHub aktivitása",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "Általános statisztika és információ",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Repository-k",
11 | "column-one": "Non-Fork-ok",
12 | "column-two": "Mind"
13 | },
14 | "contributions": {
15 | "heading": "Kontribúciók",
16 | "column-one": "Elmúlt év",
17 | "column-two": "Összesen"
18 | },
19 | "languages": {
20 | "heading": "Nyelvek eloszlása nyilvános repository-kban",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "Csatlakozás éve",
27 | "featured": "Kiemelt repo",
28 | "mostStarred": "Legtöbbet csillagozott repo",
29 | "mostForked": "Legtöbbet fork-olt repo",
30 | "followers": "Követői",
31 | "following": "Követi",
32 | "sponsors": "Szponzorok",
33 | "sponsoring": "Szponzorál",
34 | "public": "Saját repository-k",
35 | "starredBy": "Csillagozta",
36 | "forkedBy": "Forkolta",
37 | "watchedBy": "Figyeli",
38 | "templates": "Sablonok",
39 | "archived": "Archiválva",
40 | "commits": "Commitok",
41 | "issues": "Issue-k",
42 | "prs": "Pull request-ek",
43 | "reviews": "Pull request review-k",
44 | "contribTo": "Kontribútolt",
45 | "private": "Privát kontribúciók"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/hy.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "{0}-ի GitHub գործունեությունը",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "Ընդհանուր Տեղեկություն և Տվյալներ",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Ռեպոզիտորիաներ",
11 | "column-one": "Ոչ պատառաքաղներ",
12 | "column-two": "Բոլորը"
13 | },
14 | "contributions": {
15 | "heading": "Ներդրումներ",
16 | "column-one": "Վերջին տարի",
17 | "column-two": "Ընդամենը"
18 | },
19 | "languages": {
20 | "heading": "Լեզուների բաշխում հանրային շտեմարաններում",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "Գրանցվել է Տարին",
27 | "featured": "Առաջարկվող ռեպո",
28 | "mostStarred": "Ամենաաստղային ռեպո",
29 | "mostForked": "Առավել ճեղքված պահոց",
30 | "followers": "Հետևորդներ",
31 | "following": "Հետևելով",
32 | "sponsors": "Հովանավորներ",
33 | "sponsoring": "Հովանավորություն",
34 | "public": "Իմ Ռեպոզիտորիաներ",
35 | "starredBy": "Աստղանշված է",
36 | "forkedBy": "Արտադրված է կողմից",
37 | "watchedBy": "Դիտել է",
38 | "templates": "Կաղապարներ",
39 | "archived": "Արխիվացված",
40 | "commits": "Պարտավորվում է",
41 | "issues": "Հարցեր",
42 | "prs": "Քաշեք հարցումներ",
43 | "reviews": "Քաշեք հարցումների վերանայումները",
44 | "contribTo": "Նպաստել է.",
45 | "private": "Մասնավոր ներդրումներ."
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/id.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "Aktivitas Github {0}",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "Info dan Status Umum",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Repositori",
11 | "column-one": "Non Fork",
12 | "column-two": "Semua"
13 | },
14 | "contributions": {
15 | "heading": "Kontribusi",
16 | "column-one": "Tahun Lalu",
17 | "column-two": "Total"
18 | },
19 | "languages": {
20 | "heading": "Distribusi Bahasa dalam Repositori Publik",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "Tahun Bergabung",
27 | "featured": "Repositori Unggulan",
28 | "mostStarred": "Repositori dengan Bintang Terbanyak",
29 | "mostForked": "Repositori dengan Fork Terbanyak",
30 | "followers": "Pengikut",
31 | "following": "Mengikuti",
32 | "sponsors": "Sponsor",
33 | "sponsoring": "Mensponsori",
34 | "public": "Repositori yang Dimiliki",
35 | "starredBy": "Diberikan bintang oleh",
36 | "forkedBy": "Di-fork oleh",
37 | "watchedBy": "Dilihat oleh",
38 | "templates": "Template",
39 | "archived": "Diarsipkan",
40 | "commits": "Commits",
41 | "issues": "Isu",
42 | "prs": "Pull Requests",
43 | "reviews": "Ulasan Pull Request",
44 | "contribTo": "Berkontribusi Ke",
45 | "private": "Kontribusi Pribadi"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/it.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "Attività GitHub di {0}",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "Statistiche Generali e Informazioni",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Repository",
11 | "column-one": "Non-Fork",
12 | "column-two": "Tutti"
13 | },
14 | "contributions": {
15 | "heading": "Contributi",
16 | "column-one": "Anno Scorso",
17 | "column-two": "Totale"
18 | },
19 | "languages": {
20 | "heading": "Distribuzione del Linguaggio nei Repository Pubblici",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "Anno di Iscrizione",
27 | "featured": "Repo in Primo Piano",
28 | "mostStarred": "Repo con più Stelle",
29 | "mostForked": "Repo con più Fork",
30 | "followers": "Seguaci",
31 | "following": "Seguendo",
32 | "sponsors": "Sponsors",
33 | "sponsoring": "Sponsorizza",
34 | "public": "Repository di Proprietà",
35 | "starredBy": "Stellato Da",
36 | "forkedBy": "Forkato Da",
37 | "watchedBy": "Seguito Da",
38 | "templates": "Modelli",
39 | "archived": "Archiviato",
40 | "commits": "Commits",
41 | "issues": "Problemi",
42 | "prs": "Richieste di Pull",
43 | "reviews": "Revisioni di Richieste di Pull",
44 | "contribTo": "Contribuito A",
45 | "private": "Contributi Privati"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/ja.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "{0}のgithubアクティビティ",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "一般的な統計と情報",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "リポジトリ",
11 | "column-one": "非フォーク",
12 | "column-two": "全て"
13 | },
14 | "contributions": {
15 | "heading": "貢献",
16 | "column-one": "昨年",
17 | "column-two": "合計"
18 | },
19 | "languages": {
20 | "heading": "公開リポジトリでの言語配布",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "入社年",
27 | "featured": "注目のリポジトリ",
28 | "mostStarred": "最もスター付きのリポジトリ",
29 | "mostForked": "最もフォークされたリポジトリ",
30 | "followers": "フォロワー",
31 | "following": "続く",
32 | "sponsors": "スポンサー",
33 | "sponsoring": "主催",
34 | "public": "所有リポジトリ",
35 | "starredBy": "主演",
36 | "forkedBy": "によるフォーク",
37 | "watchedBy": "によって見られた",
38 | "templates": "レンプレート",
39 | "archived": "記録",
40 | "commits": "専念",
41 | "issues": "問題",
42 | "prs": "プルリクエスト",
43 | "reviews": "プルリクエストレビュー",
44 | "contribTo": "に貢献しました",
45 | "private": "個人的な貢献"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/ko.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "{0}의 GitHub 활동",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "통계 및 정보",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "저장소",
11 | "column-one": "직접 만든(Non-Forks)",
12 | "column-two": "모두"
13 | },
14 | "contributions": {
15 | "heading": "기여",
16 | "column-one": "지난해",
17 | "column-two": "총"
18 | },
19 | "languages": {
20 | "heading": "공개 저장소 사용 언어 분포",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "가입 년도",
27 | "featured": "추천 저장소",
28 | "mostStarred": "Star를 가장 많이 받은 저장소",
29 | "mostForked": "Fork가 가장 많이된 저장소",
30 | "followers": "팔로워",
31 | "following": "팔로잉",
32 | "sponsors": "후원받은",
33 | "sponsoring": "후원하는",
34 | "public": "보유한 저장소",
35 | "starredBy": "받은 Star",
36 | "forkedBy": "Fork된 횟수",
37 | "watchedBy": "Watch된 횟수",
38 | "templates": "템플릿",
39 | "archived": "보관 처리된(Archived) 저장소",
40 | "commits": "커밋",
41 | "issues": "이슈",
42 | "prs": "풀 리퀘스트",
43 | "reviews": "리뷰",
44 | "contribTo": "기여 횟수",
45 | "private": "비공개"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/lt.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "{0} aktyvumas GitHub",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "Bendra statistika ir informacija",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Repozitorijos",
11 | "column-one": "Neklonuotos",
12 | "column-two": "Visos"
13 | },
14 | "contributions": {
15 | "heading": "Įnašai",
16 | "column-one": "Praeitais metais",
17 | "column-two": "Viso"
18 | },
19 | "languages": {
20 | "heading": "Kalbu pasiskirstymas viešosiose repozitorijose",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "Prisijungimo metai",
27 | "featured": "Siūloma repozitorija",
28 | "mostStarred": "Labiausiai pažymėta repozitorija",
29 | "mostForked": "Labiausiai klonuota repozitorija",
30 | "followers": "Sekėjai",
31 | "following": "Sekama",
32 | "sponsors": "Remėjai",
33 | "sponsoring": "Remiama",
34 | "public": "Priklausančios repozitorijos",
35 | "starredBy": "Pažymėta",
36 | "forkedBy": "Klonuota",
37 | "watchedBy": "Stebima",
38 | "templates": "Šablonai",
39 | "archived": "Archyvuota",
40 | "commits": "Commits",
41 | "issues": "Problemos",
42 | "prs": "Pull Prašymai",
43 | "reviews": "Pull prašymų peržiūros",
44 | "contribTo": "Prisidėjo prie",
45 | "private": "Privatūs įnašai"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/ml.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "{0}-ന്റെ GitHub പ്രവർത്തനം",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "പൊതുവായ സ്ഥിതിവിവരക്കണക്കുകളും വിവരങ്ങളും",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "ശേഖരങ്ങൾ",
11 | "column-one": "നോൺ ഫോർക്കുകൾ",
12 | "column-two": "എല്ലാം"
13 | },
14 | "contributions": {
15 | "heading": "സംഭാവനകൾ",
16 | "column-one": "കഴിഞ്ഞ വർഷം",
17 | "column-two": "ആകെ"
18 | },
19 | "languages": {
20 | "heading": "പൊതു സംഭരണികളിലെ ഭാഷാ വിതരണം",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "ചേർന്ന വർഷം",
27 | "featured": "ഫീച്ചർ ചെയ്ത റിപ്പോ",
28 | "mostStarred": "ഏറ്റവും കൂടുതൽ നക്ഷത്രമിട്ട റിപ്പോ",
29 | "mostForked": "മോസ്റ്റ് ഫോർക്ക്ഡ് റിപ്പോ",
30 | "followers": "അനുയായികൾ",
31 | "following": "പിന്തുടരുന്നു",
32 | "sponsors": "സ്പോൺസർമാർ",
33 | "sponsoring": "സ്പോൺസർ ചെയ്യുന്നു",
34 | "public": "എന്റെ ശേഖരങ്ങൾ",
35 | "starredBy": "അഭിനയിച്ചത്",
36 | "forkedBy": "ഫോർക്ക്ഡ് ബൈ",
37 | "watchedBy": "വീക്ഷിച്ചത്",
38 | "templates": "ടെംപ്ലേറ്റുകൾ",
39 | "archived": "ആർക്കൈവ് ചെയ്തു",
40 | "commits": "കമ്മിറ്റ് ചെയ്യുന്നു",
41 | "issues": "പ്രശ്നങ്ങൾ",
42 | "prs": "അഭ്യർത്ഥനകൾ വലിക്കുക",
43 | "reviews": "റിക്വസ്റ്റ് റിവ്യൂകൾ വലിക്കുക",
44 | "contribTo": "സമർപ്പിച്ചിരിക്കുന്നത്",
45 | "private": "സ്വകാര്യ സംഭാവനകൾ"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/nl.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "{0}'s GitHub activiteiten",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "Algemene statistieken en info",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Repositories",
11 | "column-one": "Non-Forks",
12 | "column-two": "Alles"
13 | },
14 | "contributions": {
15 | "heading": "Bijdragen",
16 | "column-one": "Dit jaar",
17 | "column-two": "Totaal"
18 | },
19 | "languages": {
20 | "heading": "Talen distributies in Publieke Repositories",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "Jaar van aanmelding",
27 | "featured": "Uitgelichte repository",
28 | "mostStarred": "Repository met meeste sterren",
29 | "mostForked": "Repository met meeste forks",
30 | "followers": "Volgers",
31 | "following": "Volgend",
32 | "sponsors": "Sponsoren",
33 | "sponsoring": "Gesponsord",
34 | "public": "Mijn Repositories",
35 | "starredBy": "Ster gegeven door",
36 | "forkedBy": "Geforkt door",
37 | "watchedBy": "Gevolgd door",
38 | "templates": "Sjablonen",
39 | "archived": "Gearchiveerd",
40 | "commits": "Commits",
41 | "issues": "Problemen",
42 | "prs": "Pull Requests",
43 | "reviews": "Pull Request Recensies",
44 | "contribTo": "Bijgedragen aan",
45 | "private": "Prive Bijdragen"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/no.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "{0}s GitHub-aktivitet",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "Generell statistikk og info",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Kodebaser",
11 | "column-one": "Ikke-forgreninger",
12 | "column-two": "Alle"
13 | },
14 | "contributions": {
15 | "heading": "Bidrag",
16 | "column-one": "Forrige år",
17 | "column-two": "Totalt"
18 | },
19 | "languages": {
20 | "heading": "Språkdistribusjon i offentlige kodebaser",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "Ble med i år",
27 | "featured": "Framhevet kodebase",
28 | "mostStarred": "Kodebase med flest stjerner",
29 | "mostForked": "Kodebase med flest forgreninger",
30 | "followers": "Følgere",
31 | "following": "Følger",
32 | "sponsors": "Sponsorer",
33 | "sponsoring": "Sponser",
34 | "public": "Mine kodebaser",
35 | "starredBy": "Stjernemerket av",
36 | "forkedBy": "Forgrenet av",
37 | "watchedBy": "Overvåket av",
38 | "templates": "Maler",
39 | "archived": "Arkivert",
40 | "commits": "Commits",
41 | "issues": "Saker",
42 | "prs": "Pull Requests",
43 | "reviews": "Pull Request-vurderinger",
44 | "contribTo": "Bidro til",
45 | "private": "Private bidrag"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/or.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "{0}ର GitHub କାର୍ଯ୍ୟକଳାପ",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "ସାଧାରଣ ପରିସଂଖ୍ୟାନ ଏବଂ ସୂଚନା",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "ସଂଗ୍ରହାଳୟ",
11 | "column-one": "ଅଣ-ଫର୍କସ୍",
12 | "column-two": "ସମସ୍ତ"
13 | },
14 | "contributions": {
15 | "heading": "ଅବଦାନ",
16 | "column-one": "ବିଗତ ବର୍ଷ",
17 | "column-two": "ମୋଟ"
18 | },
19 | "languages": {
20 | "heading": "ସର୍ବସାଧାରଣ ସଂଗ୍ରହାଳୟରେ ଭାଷା ବଣ୍ଟନ",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "ବର୍ଷ ଯୋଗଦାନ",
27 | "featured": "ବୈଶିଷ୍ଟ୍ୟ ରେପୋ",
28 | "mostStarred": "ସର୍ବାଧିକ ତାରକା ରେପୋ",
29 | "mostForked": "ଅଧିକାଂଶ ଫୋର୍କଡ୍ ରେପୋ",
30 | "followers": "ଅନୁସରଣକାରୀ",
31 | "following": "ନିମ୍ନଲିଖିତ",
32 | "sponsors": "ପ୍ରଯୋଜକ",
33 | "sponsoring": "ପ୍ରାୟୋଜକ",
34 | "public": "ମୋର ସଂଗ୍ରହାଳୟ",
35 | "starredBy": "ଷ୍ଟାର୍ ହୋଇଥିବା",
36 | "forkedBy": "ଦ୍ୱାରା କଣ୍ଟା ହୋଇଛି",
37 | "watchedBy": "ଦେଖିଲା",
38 | "templates": "ଟେମ୍ପଲେଟ୍",
39 | "archived": "ସଂଗୃହିତ",
40 | "commits": "ପ୍ରତିବଦ୍ଧତା",
41 | "issues": "ସମସ୍ୟାଗୁଡିକ",
42 | "prs": "ଅନୁରୋଧ ଟାଣନ୍ତୁ",
43 | "reviews": "ଅନୁରୋଧ ସମୀକ୍ଷାଗୁଡିକ ଟାଣନ୍ତୁ",
44 | "contribTo": "ଯୋଗଦାନ",
45 | "private": "ବ୍ୟକ୍ତିଗତ ଅବଦାନ"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/pl.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "Aktywność {0} na GitHubie",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "Ogólne statystyki i informacje",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Repozytoria",
11 | "column-one": "Non-Forks",
12 | "column-two": "Wszystkie"
13 | },
14 | "contributions": {
15 | "heading": "Kontrybucje",
16 | "column-one": "Ostatni rok",
17 | "column-two": "Wszystkie"
18 | },
19 | "languages": {
20 | "heading": "Rozkład języków w Repozytoriach Publicznych",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "Rok Dołączenia",
27 | "featured": "Polecane repozytorium",
28 | "mostStarred": "Repozytoria z największą ilością gwiazdek",
29 | "mostForked": "Najczęściej Forkowane Repozytoria",
30 | "followers": "Obserwujący",
31 | "following": "Obserwowani",
32 | "sponsors": "Sponsorzy",
33 | "sponsoring": "Sponsoring",
34 | "public": "Posiadane Repozytoria",
35 | "starredBy": "Polubione przez",
36 | "forkedBy": "Sforkowane przez",
37 | "watchedBy": "Obserwowane przez",
38 | "templates": "Szablony",
39 | "archived": "Zarchiwizowane",
40 | "commits": "Commity",
41 | "issues": "Problemy",
42 | "prs": "Pull Requesty",
43 | "reviews": "Recenzje Pull Requestów",
44 | "contribTo": "Kontrybuował Do",
45 | "private": "Prywatne Kontrybucje"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/pt.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "Atividade de {0} no GitHub",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "Estatísticas Gerais e Informações",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Repositórios",
11 | "column-one": "Sem Forks",
12 | "column-two": "Todos"
13 | },
14 | "contributions": {
15 | "heading": "Contribuições",
16 | "column-one": "Último ano",
17 | "column-two": "Total"
18 | },
19 | "languages": {
20 | "heading": "Distribuição de Linguagens em Repositórios Públicos",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "Ano de Inscrição",
27 | "featured": "Repositório em Primeiro Plano",
28 | "mostStarred": "Repositório com mais estrelas",
29 | "mostForked": "Repositório mais bifurcado",
30 | "followers": "Seguidores",
31 | "following": "A seguir",
32 | "sponsors": "Patrocinado",
33 | "sponsoring": "A patrocinar",
34 | "public": "Repositórios Possuídos",
35 | "starredBy": "Com Estrela De",
36 | "forkedBy": "Bifurcado Por",
37 | "watchedBy": "Visto Por",
38 | "templates": "Modelos",
39 | "archived": "Arquivados",
40 | "commits": "Commits",
41 | "issues": "Problemas",
42 | "prs": "Pull Requests",
43 | "reviews": "Avaliação de Pull Requests",
44 | "contribTo": "Contribuiu Para",
45 | "private": "Contribuições Privadas"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/ro.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "Activitatea GitHub a lui {0}",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "Statistici generale și informații",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Depozitele",
11 | "column-one": "Non-bifurcatii",
12 | "column-two": "Toate"
13 | },
14 | "contributions": {
15 | "heading": "Contribuții",
16 | "column-one": "Anul trecut",
17 | "column-two": "Total"
18 | },
19 | "languages": {
20 | "heading": "Distribuția limbii în arhivele publice",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "An alăturat",
27 | "featured": "Repo recomandate",
28 | "mostStarred": "Cel mai marcat Repo",
29 | "mostForked": "Repo cel mai bifurcat",
30 | "followers": "Urmaritori",
31 | "following": "Ca urmare a",
32 | "sponsors": "Sponsori",
33 | "sponsoring": "Sponsorizare",
34 | "public": "Arhivele mele",
35 | "starredBy": "Înscris de",
36 | "forkedBy": "Bifurcat de",
37 | "watchedBy": "Vizionat de",
38 | "templates": "Șabloane",
39 | "archived": "Arhivat",
40 | "commits": "Commits",
41 | "issues": "Probleme",
42 | "prs": "Solicitări de tragere",
43 | "reviews": "Recenzii Pull Request",
44 | "contribTo": "Contribuit la",
45 | "private": "Contribuții private"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/ru.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "Активность пользователя {0} на гитхабе",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "Общая статистика и информация",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Статистика репозиториев",
11 | "column-one": "Без форков",
12 | "column-two": "Все"
13 | },
14 | "contributions": {
15 | "heading": "Работа в репозиториях",
16 | "column-one": "За последний год",
17 | "column-two": "За все время"
18 | },
19 | "languages": {
20 | "heading": "Использование языков в общедоступных репозиториях",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "Год регистрации на гитхабе",
27 | "featured": "Избранное репо",
28 | "mostStarred": "Самое популярное репо",
29 | "mostForked": "Самое клонированное репо",
30 | "followers": "Подписчики",
31 | "following": "Подписан",
32 | "sponsors": "Спонсоры",
33 | "sponsoring": "Спонсирует",
34 | "public": "Собственные репозитории",
35 | "starredBy": "Добавили в избранное",
36 | "forkedBy": "Клонирован",
37 | "watchedBy": "Наблюдатели",
38 | "templates": "Шаблоны",
39 | "archived": "Заархивировано",
40 | "commits": "Коммиты",
41 | "issues": "Проблемы",
42 | "prs": "Пулл реквесты",
43 | "reviews": "Ревью пулл реквестов",
44 | "contribTo": "Участие в",
45 | "private": "Приватные изменения"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/sat.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "{0}ᱟᱜ ᱜᱤᱴᱦᱚᱵᱽ ᱠᱟᱹᱢᱤᱦᱚᱨᱟᱠᱚ",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "ᱥᱟᱫᱷᱟᱨᱚᱬ ᱵᱟᱛᱟᱣ ᱟᱨ ᱵᱤᱵᱨᱚᱬ",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "ᱜᱩᱫᱟᱢ",
11 | "column-one": "ᱵᱤᱱ ᱯᱷᱚᱨᱠ ᱠᱚ",
12 | "column-two": "ᱢᱩᱴ"
13 | },
14 | "contributions": {
15 | "heading": "ᱮᱱᱮᱢᱤᱭᱟᱹᱠᱚ",
16 | "column-one": "ᱪᱟᱞᱟᱣᱮᱱ ᱥᱮᱨᱢᱟᱸ",
17 | "column-two": "ᱢᱩᱴ"
18 | },
19 | "languages": {
20 | "heading": "ᱥᱟᱱᱟᱢ ᱜᱩᱫᱟᱢ ᱨᱮ ᱯᱟᱹᱨᱥᱤ ᱠᱚᱣᱟᱜ ᱯᱟᱥᱱᱟᱣ",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "ᱥᱮᱞᱮᱫ ᱥᱮᱨᱢᱟᱸ",
27 | "featured": "ᱵᱤᱥᱮᱥ ᱜᱩᱫᱟᱢ",
28 | "mostStarred": "ᱡᱷᱚᱛᱚ ᱠᱷᱚᱱ ᱰᱷᱮᱨ ᱪᱤᱱᱦᱟᱹ ᱦᱟᱜ ᱜᱩᱫᱟᱹᱢ",
29 | "mostForked": "ᱡᱟᱹᱥᱛᱤ ᱱᱚᱠᱚᱞ ᱠᱟᱱ ᱜᱚᱫᱟᱢ",
30 | "followers": "ᱯᱟᱧᱡᱟ ᱠᱩᱜ",
31 | "following": "ᱯᱟᱧᱡᱟ ᱮᱫᱟᱢ",
32 | "sponsors": "ᱨᱚᱠᱚᱢᱤᱭᱟᱹ",
33 | "sponsoring": "ᱨᱚᱠᱚᱢᱚᱜ ᱠᱟᱱᱟ",
34 | "public": "ᱤᱧᱟᱜ ᱜᱩᱫᱟᱢ ᱠᱚ",
35 | "starredBy": "ᱪᱤᱱᱦᱟᱹᱤᱭᱟᱹ",
36 | "forkedBy": "ᱱᱚᱠᱚᱞᱤᱭᱟᱹ",
37 | "watchedBy": "ᱛᱤᱱᱹᱜ ᱠᱚ ᱧᱮᱞ ᱠᱟᱫᱟ",
38 | "templates": "ᱪᱷᱟᱸᱪᱠᱚ",
39 | "archived": "ᱜᱟᱵᱟᱱᱮᱱᱟ",
40 | "commits": "ᱰᱟᱞᱟᱣᱠᱚ",
41 | "issues": "ᱯᱚᱞᱚᱡᱽᱠᱚ",
42 | "prs": "ᱚᱨ ᱱᱮᱦᱚᱨᱠᱚ",
43 | "reviews": "ᱚᱨ ᱱᱮᱦᱚᱨ ᱧᱮᱞᱯᱚᱨᱚᱠᱷ ᱠᱚ",
44 | "contribTo": "ᱮᱱᱮᱢ",
45 | "private": "ᱱᱤᱡᱚᱨᱟᱜ ᱩᱠᱩ ᱮᱱᱮᱢᱠᱚ"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/sr.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "{0} - Aktivnost na Githabu",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "Opšta statistika i informacije",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Repozitoriji",
11 | "column-one": "Ne-forkovani",
12 | "column-two": "Svi"
13 | },
14 | "contributions": {
15 | "heading": "Doprinosi",
16 | "column-one": "Prošla godina",
17 | "column-two": "Ukupno"
18 | },
19 | "languages": {
20 | "heading": "Zastupljenost jezika u javnim repozitorijima",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "Godina pristupa",
27 | "featured": "Izabrani repozitorij",
28 | "mostStarred": "Najviše zvezdica na repou",
29 | "mostForked": "Najviše forkovan repo",
30 | "followers": "Pratilaca",
31 | "following": "Prati",
32 | "sponsors": "Sponzori",
33 | "sponsoring": "Sponzoriše",
34 | "public": "Lični repozitoriji",
35 | "starredBy": "Dodeljenih zvezdica",
36 | "forkedBy": "Broj forkovanja",
37 | "watchedBy": "Pregledi",
38 | "templates": "Šabloni",
39 | "archived": "Arhive",
40 | "commits": "Komiti",
41 | "issues": "Problemi",
42 | "prs": "Pul zahtevi",
43 | "reviews": "Revizije pul zahteva",
44 | "contribTo": "Doprinosi",
45 | "private": "Privatni doprinosi"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/sv.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "{0}'s GitHub -aktivitet",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "Allmän statistik och information",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Förråd",
11 | "column-one": "Icke-gafflar",
12 | "column-two": "Allt"
13 | },
14 | "contributions": {
15 | "heading": "Bidrag",
16 | "column-one": "Förra året",
17 | "column-two": "totala"
18 | },
19 | "languages": {
20 | "heading": "Språkdistribution i offentliga arkiv",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "År ansluten",
27 | "featured": "Utvalda Repo",
28 | "mostStarred": "Mest stjärnklippta Repo",
29 | "mostForked": "Mest gaffelförsedda lagringsplatsen",
30 | "followers": "Anhängare",
31 | "following": "Följande",
32 | "sponsors": "Sponsorer",
33 | "sponsoring": "Sponsring",
34 | "public": "Förråd ägs",
35 | "starredBy": "Medverkat av",
36 | "forkedBy": "Gafflade av",
37 | "watchedBy": "Bevakad av",
38 | "templates": "Mallar",
39 | "archived": "Arkiverad",
40 | "commits": "Begår",
41 | "issues": "Frågor",
42 | "prs": "Pull-begäranden",
43 | "reviews": "Granskningar av pull-begäran",
44 | "contribTo": "Bidrog till",
45 | "private": "Privata bidrag"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/th.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "กิจกรรมของ {0} บน GitHub",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "สถิติและข้อมูลทั่วไป",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Repositories",
11 | "column-one": "ที่ไม่ใช่ Fork",
12 | "column-two": "รวมทั้งหมด"
13 | },
14 | "contributions": {
15 | "heading": "Contributions",
16 | "column-one": "ปีที่แล้ว",
17 | "column-two": "รวมทั้งหมด"
18 | },
19 | "languages": {
20 | "heading": "ภาษาที่ใช้ใน Repo สาธารณะ",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "ปีที่เข้าร่วม",
27 | "featured": "Repo ที่โดดเด่น",
28 | "mostStarred": "Repo ที่ติดดาวมากที่สุด",
29 | "mostForked": "Repo ที่มีการ Fork มากที่สุด",
30 | "followers": "ผู้ติดตาม",
31 | "following": "กำลังติดตาม",
32 | "sponsors": "ผู้สนับสนุน",
33 | "sponsoring": "กำลังสนับสนุน",
34 | "public": "Repo ทั้งหมดของฉัน",
35 | "starredBy": "ติดดาวทั้งหมด",
36 | "forkedBy": "มีการ Fork ทั้งหมด",
37 | "watchedBy": "ผู้ติดตาม",
38 | "templates": "เทมเพลตแม่แบบ",
39 | "archived": "เก็บถาวร",
40 | "commits": "คอมมิท",
41 | "issues": "ปัญหา",
42 | "prs": "Pull Requests",
43 | "reviews": "รีวิว Pull Request",
44 | "contribTo": "มีการช่วยไปแล้ว",
45 | "private": "Contributions ส่วนตัว"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/tl.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "Aktibidad sa GitHub ni {0}",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "Pangkalahatang Statistika at Impormasyon",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Mga Repositoryo",
11 | "column-one": "Mga Non-Fork",
12 | "column-two": "Lahat"
13 | },
14 | "contributions": {
15 | "heading": "Mga Kontribusyon",
16 | "column-one": "Nakaraang Taon",
17 | "column-two": "Kabuuan"
18 | },
19 | "languages": {
20 | "heading": "Pamamahagi ng Wika sa Pangkabuuang Repositoryo",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "Taon ng Pagsali",
27 | "featured": "Mga Naitampok na Repositoryo",
28 | "mostStarred": "Repositoryong may Pinakamaraming Bituin",
29 | "mostForked": "Repositoryong may Pinakamaraming Fork",
30 | "followers": "Mga Taga-subaybay",
31 | "following": "Mga Sinusubaybayan",
32 | "sponsors": "Mga Taga-suporta",
33 | "sponsoring": "Mga Sinusuportahan",
34 | "public": "Aking mga Repositoryo",
35 | "starredBy": "Binigyan ng Bituin Ni",
36 | "forkedBy": "Ni-Fork Ni",
37 | "watchedBy": "Inaabangan Ni",
38 | "templates": "Mga Template",
39 | "archived": "Tinabi",
40 | "commits": "Mga Commit",
41 | "issues": "Mga Isyu",
42 | "prs": "Mga Pull Request",
43 | "reviews": "Mga Naisuring Pull Request",
44 | "contribTo": "Nag-ambag Sa",
45 | "private": "Mga Pribadong Ambag"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/tr.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "{0}'in GitHub Etkinliği",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "Genel İstatistikler ve Bilgiler",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Depolar",
11 | "column-one": "Çatalsız",
12 | "column-two": "Tüm"
13 | },
14 | "contributions": {
15 | "heading": "Katkılar",
16 | "column-one": "Geçen sene",
17 | "column-two": "Total"
18 | },
19 | "languages": {
20 | "heading": "Genel Depolarda Dil Dağılımı",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "Katıldığı Yıl",
27 | "featured": "Öne Çıkan Repo",
28 | "mostStarred": "En Çok Yıldızlı Repo",
29 | "mostForked": "En Çatallı Repo",
30 | "followers": "Takipçiler",
31 | "following": "Takip etmek",
32 | "sponsors": "Sponsorlar",
33 | "sponsoring": "Sponsorluk",
34 | "public": "Sahip Olunan Depolar",
35 | "starredBy": "Tarafından yıldız",
36 | "forkedBy": "Tarafından çatallandı",
37 | "watchedBy": "İzleyen",
38 | "templates": "Sablonlar",
39 | "archived": "Arşivlenmiş",
40 | "commits": "Taahhütler",
41 | "issues": "Sorunlar",
42 | "prs": "Çekme İstekleri",
43 | "reviews": "İstek İncelemelerini Çekin",
44 | "contribTo": "Katkıda Bulunanlar",
45 | "private": "Özel Katkılar"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/locales/uk.json:
--------------------------------------------------------------------------------
1 | {
2 | "titleTemplate": "{0} активностей на GitHub",
3 | "categoryLabels": {
4 | "general": {
5 | "heading": "Загальна статистика та інформація",
6 | "column-one": null,
7 | "column-two": null
8 | },
9 | "repositories": {
10 | "heading": "Репозиторіїв",
11 | "column-one": "Без форків",
12 | "column-two": "Всі"
13 | },
14 | "contributions": {
15 | "heading": "Внески",
16 | "column-one": "За останній рік",
17 | "column-two": "Всього"
18 | },
19 | "languages": {
20 | "heading": "Використання мов у загальнодоступних репозиторіях",
21 | "column-one": null,
22 | "column-two": null
23 | }
24 | },
25 | "statLabels": {
26 | "joined": "Рік приєднання",
27 | "featured": "Вибрані ререпозиторії",
28 | "mostStarred": "Найпопулярніший репозиторій",
29 | "mostForked": "Найбільш клонований репозиторій",
30 | "followers": "Підписники",
31 | "following": "Підписки",
32 | "sponsors": "Спонсори",
33 | "sponsoring": "Спонсорство",
34 | "public": "Власні репозиторії",
35 | "starredBy": "Відмітили",
36 | "forkedBy": "Клонували",
37 | "watchedBy": "Підписники",
38 | "templates": "Шаблони",
39 | "archived": "Заархівовано",
40 | "commits": "Комміти",
41 | "issues": "Проблеми",
42 | "prs": "Пулл реквести",
43 | "reviews": "Огляди пулл реквестів",
44 | "contribTo": "Участь в",
45 | "private": "Приватна участь"
46 | }
47 | }
--------------------------------------------------------------------------------
/src/queries/basicstats.graphql:
--------------------------------------------------------------------------------
1 | query($owner: String!) {
2 | user(login: $owner) {
3 | contributionsCollection {
4 | totalCommitContributions
5 | totalIssueContributions
6 | totalPullRequestContributions
7 | totalPullRequestReviewContributions
8 | totalRepositoryContributions
9 | restrictedContributionsCount
10 | contributionYears
11 | }
12 | followers {
13 | totalCount
14 | }
15 | following {
16 | totalCount
17 | }
18 | issues {
19 | totalCount
20 | }
21 | login
22 | name
23 | pullRequests {
24 | totalCount
25 | }
26 | repositoriesContributedTo {
27 | totalCount
28 | }
29 | sponsorshipsAsMaintainer {
30 | totalCount
31 | }
32 | sponsorshipsAsSponsor {
33 | totalCount
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/queries/reposContributedTo.graphql:
--------------------------------------------------------------------------------
1 | query($owner: String!, $endCursor: String) {
2 | user(login: $owner) {
3 | topRepositories(first: 100, after: $endCursor, orderBy: {direction: DESC, field: UPDATED_AT}) {
4 | totalCount
5 | nodes {
6 | owner {
7 | login
8 | }
9 | }
10 | pageInfo {
11 | hasNextPage
12 | endCursor
13 | }
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/queries/repostats.graphql:
--------------------------------------------------------------------------------
1 | query($owner: String!, $endCursor: String) {
2 | user(login: $owner) {
3 | repositories(first: 100, after: $endCursor, ownerAffiliations: OWNER) {
4 | totalCount
5 | nodes {
6 | stargazerCount
7 | forkCount
8 | isArchived
9 | isFork
10 | isPrivate
11 | isTemplate
12 | name
13 | watchers {
14 | totalCount
15 | }
16 | languages(first: 100, orderBy: {direction: DESC, field: SIZE}) {
17 | totalCount
18 | totalSize
19 | edges {
20 | size
21 | node {
22 | color
23 | name
24 | }
25 | }
26 | }
27 | }
28 | pageInfo {
29 | hasNextPage
30 | endCursor
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/queries/singleYearQueryFragment.graphql:
--------------------------------------------------------------------------------
1 | year{0}: contributionsCollection(from: "{0}-01-01T00:00:00.001Z") {{
2 | totalCommitContributions
3 | totalPullRequestReviewContributions
4 | restrictedContributionsCount
5 | }}
6 |
--------------------------------------------------------------------------------
/util/CharacterWidths.py:
--------------------------------------------------------------------------------
1 | #
2 | # user-statistician: Github action for generating a user stats card
3 | #
4 | # Copyright (c) 2021-2022 Vincent A Cicirello
5 | # https://www.cicirello.org/
6 | #
7 | # MIT License
8 | #
9 | # Permission is hereby granted, free of charge, to any person obtaining a copy
10 | # of this software and associated documentation files (the "Software"), to deal
11 | # in the Software without restriction, including without limitation the rights
12 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | # copies of the Software, and to permit persons to whom the Software is
14 | # furnished to do so, subject to the following conditions:
15 | #
16 | # The above copyright notice and this permission notice shall be included in all
17 | # copies or substantial portions of the Software.
18 | #
19 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | # SOFTWARE.
26 | #
27 |
28 | import json
29 | import pprint
30 |
31 | if __name__ == "__main__" :
32 | with open("default-widths.json", "r") as f :
33 | defaultWidths = json.load(f)
34 | with open("default-widths.py", "wb") as f :
35 | heading = """########################################
36 | # The dict that follows is derived from
37 | # default-widths.json from
38 | # https://github.com/google/pybadges,
39 | # which is licensed under Apache-2.0.
40 | ########################################
41 | """
42 | formatted = pprint.pformat(defaultWidths, indent=0, compact=True)
43 | formatted = formatted.replace(" " * 21, "")
44 | formatted = formatted.replace(" " * 17, "")
45 | s = heading + "\ndefaultWidths = " + formatted
46 | f.write(s.encode(encoding="UTF-8"))
47 |
--------------------------------------------------------------------------------
/util/refactor-locales-to-json.py:
--------------------------------------------------------------------------------
1 | #
2 | # user-statistician: Github action for generating a user stats card
3 | #
4 | # Copyright (c) 2021-2023 Vincent A Cicirello
5 | # https://www.cicirello.org/
6 | #
7 | # MIT License
8 | #
9 | # Permission is hereby granted, free of charge, to any person obtaining a copy
10 | # of this software and associated documentation files (the "Software"), to deal
11 | # in the Software without restriction, including without limitation the rights
12 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | # copies of the Software, and to permit persons to whom the Software is
14 | # furnished to do so, subject to the following conditions:
15 | #
16 | # The above copyright notice and this permission notice shall be included in all
17 | # copies or substantial portions of the Software.
18 | #
19 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | # SOFTWARE.
26 | #
27 |
28 | from StatConfig import *
29 | import json
30 |
31 | if __name__ == "__main__":
32 | for locale in supportedLocales:
33 | combined = {
34 | "titleTemplate" : titleTemplates[locale],
35 | "categoryLabels" : categoryLabels[locale],
36 | "statLabels" : { key : value["label"][locale] for key, value in statLabels.items() }
37 | }
38 | print("Processing", locale)
39 | with open("locales/" + locale + ".json", "w", encoding='utf8') as f :
40 | json.dump(combined, f, ensure_ascii=False, indent=2)
41 |
42 |
--------------------------------------------------------------------------------