diff --git a/README.md b/README.md index ba4d2569a..da99c4777 100644 --- a/README.md +++ b/README.md @@ -109,10 +109,14 @@ because of their great performance and ease of use. Maintenance of this project is made possible by all the [contributors](https://github.com/zauberzeug/nicegui/graphs/contributors) and [sponsors](https://github.com/sponsors/zauberzeug). If you would like to support this project and have your avatar or company logo appear below, please [sponsor us](https://github.com/sponsors/zauberzeug). 💖 +
+ Consider this low-barrier form of contribution yourself. Your [support](https://github.com/sponsors/zauberzeug) is much appreciated. diff --git a/fetch_sponsors.py b/fetch_sponsors.py new file mode 100755 index 000000000..1bc0fad15 --- /dev/null +++ b/fetch_sponsors.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 +import json +import os +import re +from pathlib import Path + +import requests + +# NOTE: +# requires a GitHub token with the necessary permissions read:org and read:user +# call with `GITHUB_TOKEN=ghp_XXX ./fetch_sponsors.py` + + +response = requests.post( + 'https://api.github.com/graphql', + json={ + 'query': ''' + query($organization: String!) { + organization(login: $organization) { + sponsorshipsAsMaintainer(first: 100, includePrivate: true) { + nodes { + sponsorEntity { + ... on User { + login + name + url + avatarUrl + } + ... on Organization { + login + name + url + avatarUrl + } + } + tier { + monthlyPriceInDollars + name + isOneTime + } + createdAt + } + } + } + } + ''', + 'variables': {'organization': 'zauberzeug'}, + }, + headers={ + 'Authorization': f'token {os.getenv("GITHUB_TOKEN")}', + 'Accept': 'application/vnd.github.v3+json' + }, + timeout=10.0, +) +response.raise_for_status() +data = response.json() +if 'errors' in data: + raise RuntimeError(f'GitHub API Error: {data["errors"]}') + +sponsors = [] +for sponsor in data['data']['organization']['sponsorshipsAsMaintainer']['nodes']: + sponsor_entity = sponsor['sponsorEntity'] + tier = sponsor['tier'] + sponsors.append({ + 'login': sponsor_entity['login'], + 'name': sponsor_entity['name'], + 'url': sponsor_entity['url'], + 'avatar_url': sponsor_entity['avatarUrl'], + 'tier_name': tier['name'], + 'tier_amount': tier['monthlyPriceInDollars'], + 'tier_is_one_time': tier['isOneTime'], + 'created_at': sponsor['createdAt'], + }) +sponsors.sort(key=lambda s: s['created_at']) + +contributors = [] +page = 1 +while True: + contributors_response = requests.get( + f'https://api.github.com/repos/zauberzeug/nicegui/contributors?page={page}&per_page=100', + headers={ + 'Authorization': f'token {os.getenv("GITHUB_TOKEN")}', + 'Accept': 'application/vnd.github.v3+json' + }, + timeout=10.0, + ) + contributors_response.raise_for_status() + page_contributors = contributors_response.json() + if not page_contributors: + break + contributors.extend(page_contributors) + page += 1 + +print(f'Found {len(sponsors)} sponsors') +print(f'Total contributors for NiceGUI: {len(contributors)}') + +Path('website/sponsors.json').write_text(json.dumps({ + 'top': [s['login'] for s in sponsors if s['tier_amount'] >= 100 and not s['tier_is_one_time']], + 'total': len(sponsors), + 'contributors': len(contributors), +}, indent=2) + '\n') + +sponsor_html = '\n' +for sponsor in sponsors: + if sponsor['tier_amount'] >= 25 and not sponsor['tier_is_one_time']: + sponsor_html += f' \n' +sponsor_html += '
' +readme_path = Path('README.md') +readme_content = readme_path.read_text() +updated_content = re.sub( + r'.*?', + f'\n{sponsor_html}\n', + readme_content, + flags=re.DOTALL, +) +readme_path.write_text(updated_content) + +print('README.md and sponsors.json updated successfully.') diff --git a/website/main_page.py b/website/main_page.py index 6091dc0ee..b2f3d3b3e 100644 --- a/website/main_page.py +++ b/website/main_page.py @@ -1,3 +1,6 @@ +import json +from pathlib import Path + from nicegui import ui from . import documentation, example_card, svg @@ -5,6 +8,8 @@ from .header import add_head_html, add_header from .style import example_link, features, heading, link_target, section_heading, subtitle, title +SPONSORS = json.loads((Path(__file__).parent / 'sponsors.json').read_text()) + def create() -> None: """Create the content of the main page.""" @@ -159,6 +164,30 @@ def create() -> None: for example in examples: example_link(example) + with ui.column().classes('dark-box p-8 lg:p-16 my-16 bg-transparent border-y-2'): + with ui.column().classes('mx-auto items-center gap-y-8 gap-x-32 lg:flex-row'): + with ui.column().classes('max-lg:items-center max-lg:text-center'): + ui.markdown('NiceGUI is supported by') \ + .classes('text-2xl md:text-3xl font-medium') + with ui.row(align_items='center'): + assert SPONSORS['total'] > 0 + ui.markdown(f''' + our top {'sponsor' if SPONSORS['total'] == 1 else 'sponsors'} + ''') + for sponsor in SPONSORS['top']: + with ui.link(target=f'https://github.com/{sponsor}').classes('row items-center gap-2'): + ui.image(f'https://github.com/{sponsor}.png').classes('w-12 h-12 border') + ui.label(f'@{sponsor}') + ui.markdown(f''' + as well as {SPONSORS['total'] - len(SPONSORS['top'])} other [sponsors](https://github.com/sponsors/zauberzeug) + and {SPONSORS['contributors']} [contributors](https://github.com/zauberzeug/nicegui/graphs/contributors). + ''').classes('bold-links arrow-links') + with ui.link(target='https://github.com/sponsors/zauberzeug').style('color: black !important') \ + .classes('rounded-full mx-auto px-12 py-2 border-2 border-[#3e6a94] font-medium text-lg md:text-xl'): + with ui.row().classes('items-center gap-4'): + ui.icon('sym_o_favorite', color='#3e6a94') + ui.label('Become a sponsor').classes('text-[#3e6a94]') + with ui.row().classes('dark-box min-h-screen mt-16'): link_target('why') with ui.column().classes(''' diff --git a/website/sponsors.json b/website/sponsors.json new file mode 100644 index 000000000..15ee0d84c --- /dev/null +++ b/website/sponsors.json @@ -0,0 +1,7 @@ +{ + "top": [ + "daviborges666" + ], + "total": 18, + "contributors": 125 +} diff --git a/website/sponsors.py b/website/sponsors.py new file mode 100644 index 000000000..444be0fef --- /dev/null +++ b/website/sponsors.py @@ -0,0 +1 @@ +NUM_SPONSORS = 18