mirror of
https://github.com/Kludex/awesome-fastapi-projects.git
synced 2024-11-23 12:01:08 +00:00
Web App (#25)
* Set up the web project dependencies - Add linters, pre-commit, and GitHub Actions - Add a Makefile - Add a pyproject.toml * Fix pyupgrade job - Remove continue_on_error everywhere * Remove old code * Rename a GithubActions job * Change README * Adjust pre-commit and GitHub actions * Add tables and set up alembic * Set up tests * Extend tests * Add coverage config * Adjust the GithubActions workflow * Fix GithubActions workflow * Try fixing pyproject-fmt config * Fix formatting of pyproject.toml * Fix formatting of pyproject.toml * Add coverage report * Test listing the repositories * Add a working prototype of SourceGraph client * Add parsing of the SourceGraph SSE data * Fix tests * Ged rid of packages replaced by ruff * Fix waits in the SourceGraph client * Refactor the models and add a mapper - A new mapper allows to create database repositories from the SourceGraph data * Add mypy * Try fixing mypy action * Remove redundant configs * Exclude tests from type checking * Fix mypy pre-commit and GitHub action * Ignore factories * Make upserting possible for source graph data * Add logic for parsing the dependencies and populating the database * Add a database and a cron GitHub Action job * Try manually trigger a workflow * Bring back the old config * Add ReadTimeout for errors to retry for in SourceGraph client * Add typer * Adjust the docstrings * Update the database * Refactor and optimize scraping and dependencies parsing * Make scraping run on push for now * Add a unique constraint for the repo url and source graph repo id * Change the index columns in on_conflict statement for repo creation * Optimize dependencies parsing - Do not parse dependencies for a repo when revision did not change * Scraped repositories from Source Graph * Refactor scraping * Set up frontend * Scraped repositories from Source Graph * Add TODOs * Skip scraping when testing * Fix a test with updating the repos * Scraped repositories from Source Graph * Add some more TODOs * Scraped repositories from Source Graph * Add some more TODO comments * Add chadcn/ui * Scraped repositories from Source Graph * Create index.json * Scraped repositories from Source Graph * Add a draft of data table and display all the repos * Scraped repositories from Source Graph * Implement stars badges and description with overflow * Format the links to Github repos * Fix link clicking * Scraped repositories from Source Graph * Add simple pagination and stars column sorting * Scraped repositories from Source Graph * Implement basic searching * Scraped repositories from Source Graph * Implement a multiselect for dependencies * Scraped repositories from Source Graph * Implement actual filtering by dependencies * Scraped repositories from Source Graph * Add a workflow to deploy nextjs on github pages * Try fixing the deployment job * Enable static exports for app router * Fix uploading arifacts for nextjs job * Set base path to properly load JS and CSS * Fix the base path * Scraped repositories from Source Graph * Add header * Remove language version * Scraped repositories from Source Graph * Add some more TODOs * Scraped repositories from Source Graph * Adjust the pre-commit config * Fix pipelines * Scraped repositories from Source Graph * Add a footer * Create the indexes * Scraped repositories from Source Graph * Add more TODOs * Introduce minor footer adjustments * Adjust the scraping actions * Scraped repositories from Source Graph, parsed the dependencies, and generated the indexes * Implement query params state * Scraped repositories from Source Graph, parsed the dependencies, and generated the indexes * Do not commit query state on unmount * Scraped repositories from Source Graph, parsed the dependencies, and generated the indexes * Hopefully fix query states and multiselect input * Scraped repositories from Source Graph, parsed the dependencies, and generated the indexes * Extend the Makefile * Resolve most of TODOs * Resolve the conflicts with anyio version, bring back httpx * Adjust the Makefile and README.md * Fix a typo in README.md * Adjust readme * Fix the Makefile * Fix some stuff * Make some adjustments * Possibly fix failing scraping jobs * Load the repo project URL from env --------- Co-authored-by: vladfedoriuk <vladfedoriuk@users.noreply.github.com> Co-authored-by: Vladyslav Fedoriuk <vladyslav.fedoriuk@deployed.pl>
This commit is contained in:
parent
02fb674332
commit
2fdd348a15
68
.github/workflows/app.yaml
vendored
Normal file
68
.github/workflows/app.yaml
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
name: Python App Quality and Testing
|
||||
|
||||
on: [push]
|
||||
|
||||
|
||||
jobs:
|
||||
quality:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.11]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with: # https://github.com/actions/setup-python/blob/main/docs/advanced-usage.md#caching-packages
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: "pip"
|
||||
cache-dependency-path: "requirements/dev.txt"
|
||||
- name: Install dev dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install -r requirements/dev.txt
|
||||
- name: Lint with ruff
|
||||
run: |
|
||||
python -m ruff check --verbose --format=github .
|
||||
- name: Lint with mypy
|
||||
run: |
|
||||
python -m mypy --show-error-codes --pretty --show-column-numbers --show-error-context .
|
||||
- name: Lint with black
|
||||
run: |
|
||||
python -m black --check --verbose .
|
||||
- name: Lint with pyproject-fmt
|
||||
run: |
|
||||
python -m pyproject_fmt --check --indent=4 .
|
||||
test:
|
||||
needs: [quality]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.11]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with: # https://github.com/actions/setup-python/blob/main/docs/advanced-usage.md#caching-packages
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: "pip"
|
||||
cache-dependency-path: "requirements/test.txt"
|
||||
- name: Install test dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install -r requirements/test.txt
|
||||
- name: Test with pytest
|
||||
run: |
|
||||
python -m pytest -v -s --failed-first --cov=app --cov-report=xml --cov-branch
|
||||
- name: Generate Coverage Report
|
||||
run: |
|
||||
python -m coverage report -m
|
||||
- name: Upload coverage to Codecov
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
if: ${{ env.CODECOV_TOKEN }}
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
file: coverage.xml
|
||||
name: python ${{ matrix.python-version }}
|
120
.github/workflows/frontend.yaml
vendored
Normal file
120
.github/workflows/frontend.yaml
vendored
Normal file
|
@ -0,0 +1,120 @@
|
|||
# Sample workflow for building and deploying a Next.js site to GitHub Pages
|
||||
#
|
||||
# To get started with Next.js see: https://nextjs.org/docs/getting-started
|
||||
#
|
||||
name: Build and Deploy Next.js to GitHub Pages
|
||||
|
||||
on:
|
||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_run
|
||||
workflow_run:
|
||||
workflows:
|
||||
- "Scraping the repositories from Source Graph"
|
||||
- "Python App Quality and Testing"
|
||||
branches: [main]
|
||||
types:
|
||||
- completed
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
# https://docs.github.com/en/actions/using-workflows/manually-running-a-workflow
|
||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch
|
||||
workflow_dispatch:
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
||||
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
# Build job
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
# Use ``frontend`` as the working directory
|
||||
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_iddefaultsrun
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./frontend
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18.17.1]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: "pnpm"
|
||||
cache-dependency-path: "./frontend/pnpm-lock.yaml"
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v3
|
||||
with:
|
||||
# Automatically inject basePath in your Next.js configuration file and disable
|
||||
# server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized).
|
||||
#
|
||||
# You may remove this line if you want to manage the configuration yourself.
|
||||
static_site_generator: next
|
||||
- name: Build with Next.js
|
||||
run: pnpm next build
|
||||
- name: Static HTML export with Next.js
|
||||
run: pnpm next export
|
||||
- name: Upload artifact
|
||||
if: ${{ !env.ACT }} # skip during local actions testing
|
||||
uses: actions/upload-pages-artifact@v2
|
||||
with:
|
||||
path: ./frontend/out
|
||||
|
||||
# Lint job
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
# Use ``frontend`` as the working directory
|
||||
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_iddefaultsrun
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./frontend
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18.17.1]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: "pnpm"
|
||||
cache-dependency-path: "./frontend/pnpm-lock.yaml"
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
- name: Lint with ESLint
|
||||
run: pnpm lint
|
||||
- name: Lint with Prettier
|
||||
run: pnpm prettier:lint
|
||||
|
||||
# Deployment job
|
||||
deploy:
|
||||
if: ${{ !github.event.act }} # skip during local actions testing
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v2
|
51
.github/workflows/scraping.yaml
vendored
Normal file
51
.github/workflows/scraping.yaml
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
name: Scraping the repositories from Source Graph
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Trigger every day at midnight
|
||||
# https://crontab.guru/#0_0_*_*_*
|
||||
- cron: '0 0 * * *'
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
# https://docs.github.com/en/actions/using-workflows/manually-running-a-workflow
|
||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency: # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#concurrency
|
||||
group: "scraping"
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
scraping:
|
||||
if: ${{ !github.event.act }} # skip during local actions testing
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.11]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: "pip"
|
||||
cache-dependency-path: "requirements/base.txt"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install -r requirements/base.txt
|
||||
- name: Scrape the repositories
|
||||
run: |
|
||||
python -m app.scrape scrape-repos
|
||||
- name: Parse the dependencies
|
||||
run: |
|
||||
python -m app.scrape parse-dependencies
|
||||
- name: Generate the repositories index
|
||||
run: |
|
||||
python -m app.index index-repos
|
||||
- name: Generate the dependencies index
|
||||
run: |
|
||||
python -m app.index index-dependencies
|
||||
- name: Commit the changes
|
||||
uses: stefanzweifel/git-auto-commit-action@v4
|
||||
with:
|
||||
commit_message: "Scraped repositories from Source Graph, parsed the dependencies, and generated the indexes"
|
166
.gitignore
vendored
166
.gitignore
vendored
|
@ -1,6 +1,162 @@
|
|||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
links.txt
|
||||
unique_links.txt
|
||||
imports.txt
|
||||
reps/
|
||||
.vscode
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
.idea/
|
||||
|
||||
# Ruff cache
|
||||
.ruff_cache/
|
||||
|
|
1
.node-version
Normal file
1
.node-version
Normal file
|
@ -0,0 +1 @@
|
|||
v18.18.0
|
53
.pre-commit-config.yaml
Normal file
53
.pre-commit-config.yaml
Normal file
|
@ -0,0 +1,53 @@
|
|||
---
|
||||
default_language_version:
|
||||
python: python3.11
|
||||
default_install_hook_types: [pre-commit, commit-msg]
|
||||
default_stages: [commit]
|
||||
fail_fast: false
|
||||
minimum_pre_commit_version: 3.3.3
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: mixed-line-ending
|
||||
args: ["--fix=lf"]
|
||||
- id: check-added-large-files
|
||||
- id: check-toml
|
||||
- id: check-yaml
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.280
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [--fix, --exit-non-zero-on-fix]
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: mypy
|
||||
name: mypy
|
||||
entry: |
|
||||
python -m mypy .
|
||||
types: [python]
|
||||
language: system
|
||||
require_serial: true
|
||||
pass_filenames: false
|
||||
- repo: https://github.com/ambv/black
|
||||
rev: 23.7.0
|
||||
hooks:
|
||||
- id: black
|
||||
- repo: https://github.com/tox-dev/pyproject-fmt
|
||||
rev: "0.13.0"
|
||||
hooks:
|
||||
- id: pyproject-fmt
|
||||
args: ["--indent=4"]
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: "v3.0.3"
|
||||
hooks:
|
||||
- id: prettier
|
||||
entry: prettier --write --list-different --ignore-unknown --config frontend/.prettierrc --ignore-path frontend/.prettierignore
|
||||
files: ^frontend/
|
||||
language_version: 18.18.0
|
||||
- repo: https://github.com/jorisroovers/gitlint
|
||||
rev: "v0.19.1"
|
||||
hooks:
|
||||
- id: gitlint
|
139
Makefile
Normal file
139
Makefile
Normal file
|
@ -0,0 +1,139 @@
|
|||
# we want bash behaviour in all shell invocations
|
||||
SHELL := bash
|
||||
# Run each target in a separate shell
|
||||
.ONESHELL:
|
||||
# Fail on error inside any functions or subshells
|
||||
.SHELLFLAGS := -eu -o pipefail -c
|
||||
# Remove partially created files on error
|
||||
.DELETE_ON_ERROR:
|
||||
# Warn when an undefined variable is referenced
|
||||
MAKEFLAGS += --warn-undefined-variables
|
||||
# Disable built-in rules
|
||||
MAKEFLAGS += --no-builtin-rules
|
||||
# A catalog of requirements files
|
||||
REQUIREMENTS?=requirements
|
||||
|
||||
help: # Show this help
|
||||
@echo "Usage: make [target]"
|
||||
@echo ""
|
||||
@echo "Targets:"
|
||||
@echo " help Show this help"
|
||||
@echo " requirements-base Compile base requirements"
|
||||
@echo " requirements-test Compile test requirements"
|
||||
@echo " requirements-dev Compile dev requirements"
|
||||
@echo " requirements Compile all requirements"
|
||||
@echo " install Install the app locally"
|
||||
@echo " install-front Install frontend"
|
||||
@echo " install-test Install the app locally with test dependencies"
|
||||
@echo " install-dev Install the app locally with dev dependencies"
|
||||
@echo " install-test-dev Install the app locally with test and dev dependencies"
|
||||
@echo " init-test-dev Install the app locally with test and dev dependencies. Also install pre-commit hooks."
|
||||
@echo " reinit-test-dev Reinstall pre-commit hooks"
|
||||
@echo " lint Run linters"
|
||||
@echo " test Run tests"
|
||||
@echo " migrate Run migrations"
|
||||
@echo " revision Create a new migration"
|
||||
@echo " front Run frontend"
|
||||
@echo " scrape-repos Scrape repos"
|
||||
@echo " parse-dependencies Scrape dependencies"
|
||||
@echo " index-repos Index repos"
|
||||
@echo " index-dependencies Index dependencies"
|
||||
|
||||
requirements-base: # Compile base requirements
|
||||
python -m piptools compile \
|
||||
--output-file=requirements/base.txt \
|
||||
-v \
|
||||
pyproject.toml
|
||||
|
||||
requirements-test: requirements-base # Compile test requirements
|
||||
python -m piptools compile \
|
||||
--extra=test \
|
||||
--output-file=requirements/test.txt \
|
||||
-v \
|
||||
pyproject.toml
|
||||
|
||||
requirements-dev: requirements-base # Compile dev requirements
|
||||
python -m piptools compile \
|
||||
--extra=dev \
|
||||
--output-file=requirements/dev.txt \
|
||||
-v \
|
||||
pyproject.toml
|
||||
|
||||
requirements: requirements-base requirements-test requirements-dev # Compile all requirements
|
||||
.PHONY: requirements
|
||||
|
||||
install: # Install the app locally
|
||||
python -m pip install -r $(REQUIREMENTS)/base.txt .
|
||||
.PHONY: install
|
||||
|
||||
install-test: # Install the app locally with test dependencies
|
||||
python -m pip install \
|
||||
-r $(REQUIREMENTS)/base.txt \
|
||||
-r $(REQUIREMENTS)/test.txt \
|
||||
--editable .
|
||||
.PHONY: install-test
|
||||
|
||||
install-dev: # Install the app locally with dev dependencies
|
||||
python -m pip install \
|
||||
-r $(REQUIREMENTS)/base.txt \
|
||||
-r $(REQUIREMENTS)/dev.txt \
|
||||
--editable .
|
||||
.PHONY: install-dev
|
||||
|
||||
install-test-dev: # Install the app locally with test and dev dependencies
|
||||
python -m pip install \
|
||||
-r $(REQUIREMENTS)/base.txt \
|
||||
-r $(REQUIREMENTS)/test.txt \
|
||||
-r $(REQUIREMENTS)/dev.txt \
|
||||
--editable .
|
||||
.PHONY: install-test-dev
|
||||
|
||||
install-front: # Install frontend
|
||||
cd frontend && pnpm install
|
||||
.PHONY: install-front
|
||||
|
||||
init-test-dev: install-test-dev # Install the app locally with test and dev dependencies. Also install pre-commit hooks.
|
||||
pre-commit install
|
||||
.PHONY: init-test-dev
|
||||
|
||||
reinit-test-dev: init-test-dev # Reinstall pre-commit hooks
|
||||
pre-commit install --install-hooks --overwrite
|
||||
.PHONY: reinit-test-dev
|
||||
|
||||
lint: # Run linters
|
||||
pre-commit run --all-files
|
||||
.PHONY: lint
|
||||
|
||||
test: # Run tests
|
||||
python -m pytest -vv -s --cov=app --cov-report=xml --cov-branch app
|
||||
.PHONY: test
|
||||
|
||||
migrate: # Run migrations
|
||||
python -m alembic upgrade heads
|
||||
.PHONY: migrate
|
||||
|
||||
revision: # Create a new migration
|
||||
python -m alembic revision --autogenerate -m "$(message)"
|
||||
.PHONY: revision
|
||||
|
||||
front: install-front # Run frontend
|
||||
cd frontend && pnpm dev
|
||||
.PHONY: front
|
||||
|
||||
scrape-repos: # Scrape repos
|
||||
python -m app.scrape scrape-repos
|
||||
.PHONY: scrape-repos
|
||||
|
||||
parse-dependencies: # Scrape dependencies
|
||||
python -m app.scrape parse-dependencies
|
||||
.PHONY: parse-dependencies
|
||||
|
||||
index-repos: # Index repos
|
||||
python -m app.index index-repos
|
||||
.PHONY: index-repos
|
||||
|
||||
index-dependencies: # Index dependencies
|
||||
python -m app.index index-dependencies
|
||||
.PHONY: index-dependencies
|
||||
|
||||
.DEFAULT_GOAL := init-test-dev # Set the default goal to init-dev-test
|
591
README.md
591
README.md
|
@ -1,550 +1,57 @@
|
|||
# Awesome FastAPI Projects
|
||||
|
||||
> **Warning**
|
||||
> If you want to help me to maintain this project, please send a message on https://github.com/Kludex/awesome-fastapi-projects/issues/16.
|
||||
> I'll happily onboard you.
|
||||
View the website: https://Kludex.github.io/awesome-fastapi-projects/
|
||||
|
||||
The aim of this repository is to have an organized list of projects that use FastAPI.
|
||||
## Local Development
|
||||
|
||||
If you're looking for FastAPI content, you might want to check [Awesome FastAPI](https://github.com/mjhea0/awesome-fastapi).
|
||||
### Setup
|
||||
|
||||
## Table
|
||||
#### Python and Virtual Environment
|
||||
|
||||
| Project | Dependencies |
|
||||
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|[2020_slowdelivery_take](https://github.com/zekaio/2020_slowdelivery_take ) |aiohttp, pytest, tortoise |
|
||||
|[AI-Service](https://github.com/eHelply/AI-Service ) |kombu, setuptools, pymongo, redis, sentry_asgi, sentry_sdk, pymlconf, socketio, src |
|
||||
|[African_Wildlife_Classifier](https://github.com/peterbacalso/African_Wildlife_Classifier ) |aiohttp, fastai, torch |
|
||||
|[Auth-Service](https://github.com/eHelply/Auth-Service ) |kombu, requests, setuptools, passlib, pymongo, redis, sentry_asgi, sentry_sdk, pymlconf, jwt, socketio, src, bson |
|
||||
|[BenchmarkRoundOne](https://github.com/peterdeme/BenchmarkRoundOne ) |aiohttp |
|
||||
|[Benchmarker](https://github.com/vutran1710/Benchmarker ) |apis |
|
||||
|[Blog](https://github.com/elbruno/Blog ) |face_recognition, imutils, PIL, flask, cv2 |
|
||||
|[CCTO](https://github.com/SnarferX/CCTO ) |sets, SimpleHTTPSErver, requests, dircache, ovm_lib, jarray, pip, httplib, urllib3, SockerServer, ansible, hello, java, ovmclient |
|
||||
|[CFE_30D.PY](https://github.com/torredefarol24/CFE_30D.PY ) |formatting, flask, scrape, logger, requests, pandas, download_util, requests_html |
|
||||
|[COVID-19API](https://github.com/zeroday0619/COVID-19API ) |aiohttp, ujson, async_lru, API, scrapy, fastapi_plugins, aioredis, bs4 |
|
||||
|[COVID-QA](https://github.com/deepset-ai/COVID-QA ) |elasticapm, eval, sentence_transformers, sklearn, elasticsearch, tqdm, scrapy, requests, backend, nltk, haystack, preprocess, farm, langid, datasources, torch, tfidf_train, covid_nlp |
|
||||
|[CUCM-Phone-Info](https://github.com/jsteinberg1/CUCM-Phone-Info ) |rq, cryptography, zeep, requests, redis, lxml, lib, apscheduler, api, OpenSSL, sqlalchemy, jwt, bs4 |
|
||||
|[Chatbot](https://github.com/aramakus/Chatbot ) |nltk, chatterbot, sklearn, requests, tqdm, chatbot |
|
||||
|[ChatiChatou](https://github.com/auredentan/ChatiChatou ) |chat, aioredis |
|
||||
|[CheckersAI](https://github.com/HuiiBuh/CheckersAI ) |pytest, aiohttp, game, api, conftest, checkers |
|
||||
|[Cloud_APS1](https://github.com/ehrhardt98/Cloud_APS1 ) |pymongo, requests |
|
||||
|[CryptoViz](https://github.com/MohanVashist1/CryptoViz ) |bs4, requests, pyti, pymongo, lib, binance, dateutil, cryptocalculator, channelfinder, dotenv, flask, boto3, click, fastapi_users |
|
||||
|[DS-API](https://github.com/BW-Post-Here-06-2020/DS-API ) |dotenv, praw |
|
||||
|[Daft](https://github.com/mpalka31/DaftAcademyCourse ) |main, queries, schemas, pytest, jose, database, crud |
|
||||
|[Daims_FastAPI](https://github.com/GeorgeMiao219/Daims_FastAPI ) |auth, dns, dataset, config, url, utils, database |
|
||||
|[Data-Engineering](https://github.com/De-Dash/Data-Engineering ) |nltk, aiohttp, api |
|
||||
|[Docker-Builder](https://github.com/Javier162380/Docker-Builder ) |rq, pytest, endpoints, requests, redis, builder, docker, settings, docker_builder, models, worker, middlewares, router |
|
||||
|[Dispatch](https://github.com/Netflix/dispatch ) |SQLAlchemy-Searchable, aiofiles, alembic‚ arrow‚ bcrypt, cachetools, click, email-validator, emails, fastapi_permissions, google-api-python-client, google-auth-oauthlib, h11, httpx, jinja2, jira, joblib, numpy, oauth2client, pandas, pdpyras, psycopg2-binary, pyparsing, python-dateutil, python-jose, python-multipart, pytz, requests, rsa, schedule, sentry-asgi, sentry-sdk, sh, slackclient, spacy, sqlalchemy, sqlalchemy-filters, statsmodels, tabulate, tenacity, uvicorn |
|
||||
|[DockerImageResNet50](https://github.com/OrenLeung/DockerImageResNet50 ) |redis, PIL, locust, keras |
|
||||
|[Downotifier](https://github.com/herbzhao/Downotifier ) |twilio, bs4, requests, URLChecker, TwilioWhatsapp |
|
||||
|[ExcelTools](https://github.com/xx2life/ExcelTools ) |motor, databases |
|
||||
|[ExpenseAPI](https://github.com/MushroomMaula/ExpenseAPI ) |pytest, faker, sqlalchemy, myapp, fastapi_login, alembic, dotenv |
|
||||
|[FAAS](https://github.com/jacksonhenry3/FAAS ) |flask |
|
||||
|[Fast-API-Starter](https://github.com/khaman1/Fast-API-Starter ) |shared, urls |
|
||||
|[Fast-Project](https://github.com/alex-2201/Fast-Project ) |redis, conn, cassandra, apis, pymysql, logzero, models, utils, middlewares |
|
||||
|[FastAPI-Auth](https://github.com/Madpilot0/FastAPI-Auth ) |bcrypt, lib, routes, yaml, jwt, MySQLdb |
|
||||
|[FastAPI-CRUD](https://github.com/Mr-Manna/FastAPI-CRUD ) |databases, sqlalchemy, post, config |
|
||||
|[FastAPI-Demo](https://github.com/JPMike/FastAPI-Demo ) |requests |
|
||||
|[FastAPI-Example](https://github.com/gauravgola96/FastAPI-Example ) |botocore, aiobotocore, dotenv, s3_events, boto3, utils |
|
||||
|[FastAPI-Learning-Example](https://github.com/oinsd/FastAPI-Learning-Example ) |main, main_b, jwt, routers, sqlalchemy, database, passlib |
|
||||
|[FastAPI-React-PostgreSQL_Full-Stack](https://github.com/scionoftech/FastAPI-React-PostgreSQL_Full-Stack ) |db, setuptools, logs, passlib, utils, sqlalchemy, conf, routes, auth, jwt |
|
||||
|[FastAPITutorial](https://github.com/HeywoodKing/FastAPITutorial ) |invoke, config |
|
||||
|[FastAPI_Tortoise_template](https://github.com/GoddessEyes/FastAPI_Tortoise_template ) |celery, jwt, tortoise, template, passlib, emails |
|
||||
|[FastAPI_Vue](https://github.com/quietking0312/FastAPI_Vue ) |pymongo, apps |
|
||||
|[FastAPI_app1](https://github.com/Sai-Vamsi-Ogety/FastAPI_app1 ) |spacy |
|
||||
|[FastAPI_proto](https://github.com/bartomo/FastAPI_proto ) |controllers, urls, sqlalchemy, db, models |
|
||||
|[FastAdmin](https://github.com/CoderCharm/FastAdmin ) |passlib, loguru, sqlalchemy, myapp, jose, alembic |
|
||||
|[Fastapi](https://github.com/xyz25/Fastapi ) |router_app, sqlalchemy |
|
||||
|[FastapiLearn](https://github.com/xuqm/FastapiLearn ) |part4, part11, part7, part8, part15, part2, part10, application, part14, part12, part9, part13, part3, part16, part1, part5, part6 |
|
||||
|[Fusion-Vision](https://github.com/sdhnshu/Fusion-Vision ) |wandb, pytest, sklearn, requests, tqdm, torch, text_classification |
|
||||
|[GET_POST](https://github.com/mitsumushibayama/GET_POST ) |MySQLdb |
|
||||
|[GraphQLAPI](https://github.com/webjunkie01/GraphQLAPI ) |graphene, cryptacular, pytest, graphene_sqlalchemy, sqlalchemy |
|
||||
|[Gringotts](https://github.com/cning112/Gringotts ) |werkzeug, requests, flask_restplus, typing_extensions, apis, flask_cors, libs, flask, routers, models |
|
||||
|[HjwzwApi](https://github.com/k1dave6412/HjwzwApi ) |backend, requests, aiohttp, asyncpg, loguru, sqlalchemy, myapp, alembic, databases, bs4 |
|
||||
|[Image-Diff-back](https://github.com/filipe-guerra/Image-Diff-back ) |imutils, skimage, imageDiff, cv2 |
|
||||
|[Item-Xref](https://github.com/Roselingit/Item-Xref ) |psycopg2 |
|
||||
|[KOARCH](https://github.com/janstrohschein/KOARCH ) |classes, emotion_recognition_master, pydash, sklearn, requests, tweepy, scipy, kafka, yaml, psycopg2, emotion_predictor, avro, keras |
|
||||
|[Logging-Service](https://github.com/eHelply/Logging-Service ) |kombu, setuptools, pymongo, redis, sentry_asgi, sentry_sdk, pymlconf, socketio, src |
|
||||
|[MLServer](https://github.com/SeldonIO/MLServer ) |orjson, grpc, click, pytest, mlserver, google, setuptools |
|
||||
|[MallAPI](https://github.com/CoderCharm/MallAPI ) |alembic, loguru, sqlalchemy, extensions, setting, myapp, api, utils |
|
||||
|[MealCare](https://github.com/hack4impact-mcgill/MealCare ) |pony, passlib, config, tests, sqlalchemy, psycopg2, jwt |
|
||||
|[MetodoClasificacionTexto](https://github.com/talend-Erick/MetodoClasificacionTexto ) |gensim, nltk, spacy, src, pandas |
|
||||
|[MyChild](https://github.com/YunusovSamat/MyChild ) |passlib, asyncpg, tortoise, sqlalchemy, alembic, databases, jwt |
|
||||
|[MyNotes](https://github.com/wq758511990/MyNotes ) |serial, werkzeug, flask_sqlalchemy, flask_httpauth, sqlalchemy, wtforms, pytz, webapp, flask, itsdangerous |
|
||||
|[NIMSPARQL](https://github.com/Ningensei848/NIMSPARQL ) |elasticapm, sqlalchemy, elasticsearch, db, api |
|
||||
|[NT118](https://github.com/maivanhung33/NT118 ) |request, kombu, common, elasticsearch, geopy, requests, injector, pymongo, redis, consul, boltons, cachetools, controller, elasticsearch_dsl, sqlalchemy, service, pytz, response |
|
||||
|[News-Summarizer](https://github.com/CYBINT-IN/News-Summarizer ) |scipy, sentence_transformers, flask, sklearn, torch, requests, transformers, pyLDAvis, bs4 |
|
||||
|[Notification-Service](https://github.com/eHelply/Notification-Service ) |kombu, setuptools, pymongo, redis, sentry_asgi, sentry_sdk, pymlconf, socketio, src |
|
||||
|[Pycharm](https://github.com/godori004/Pycharm ) |urllib3, flask_restful, flask, src, pandas, google |
|
||||
|[Python-example](https://github.com/hzjsea/Python-example ) |netmiko, static_files, requests, basedb, sqlalchemy, ansible, zabbix_info |
|
||||
|[Python3Test](https://github.com/hjslovehr/Python3Test ) |requests, redis, openpyxl, sqlhelper, pymssql, md5_helper, flask, helpers, bs4 |
|
||||
|[PythonFastApi](https://github.com/kbeaugrand/PythonFastApi ) |win32service, win32event, servicemanager, win32serviceutil, yaml |
|
||||
|[RemoteVP-Npc](https://github.com/viccom/RemoteVP-Npc ) |win32serviceutil, serial, requests, apps, pythoncom, cores, helper, ping3, bases, hbmqtt, conf, Crypto, wmi |
|
||||
|[RestFramework](https://github.com/rakesh4real/RestFramework ) |django, streamapp, rest_framework, other_app, jwt, senddata, my_app, passlib |
|
||||
|[SOA](https://github.com/FisnikL/SOA ) |prometheus_client, sqlalchemy, sqlalchemy_utils, database |
|
||||
|[Semester-Stats](https://github.com/Rushyanth111/Semester-Stats ) |playhouse, faker, setuptools, peewee, mailmerge, docx |
|
||||
|[Service-Template](https://github.com/eHelply/Service-Template ) |kombu, setuptools, pymongo, redis, sentry_asgi, sentry_sdk, pymlconf, socketio, src |
|
||||
|[SpotifyAPI](https://github.com/dixneuf19/SpotifyAPI ) |spotipy, dotenv, src |
|
||||
|[StanleyIpkiss](https://github.com/JimmiBram/StanleyIpkiss ) |tests, sibase |
|
||||
|[Studium-ds](https://github.com/Lambda-School-Labs/Studium-ds ) |retrieve_definition, gauge_plot, requests, calendar_heatmap, inflect |
|
||||
|[TT_COVID19_API_site_and_emailer](https://github.com/VSpectrum/TT_COVID19_API_site_and_emailer ) |email_validator, requests, config, emailer |
|
||||
|[TaskXenter](https://github.com/CoolSpring8/TaskXenter ) |celery, sentry_sdk, loguru, databases, jwt, tortoise, passlib, httpx |
|
||||
|[Telegram-BusBot-DataManager](https://github.com/David-Lor/Telegram-BusBot-DataManager ) |pymongo, motor, busbot_data_manager |
|
||||
|[WAM_server_API_template](https://github.com/rl-institut/WAM_server_API_template ) |flask_app, celery, flask, pytest, worker, sphinx_rtd_theme |
|
||||
|[WEB](https://github.com/maivanhung33/WEB ) |request, kombu, common, elasticsearch, geopy, requests, injector, pymongo, redis, consul, boltons, cachetools, controller, elasticsearch_dsl, sqlalchemy, service, pytz, response |
|
||||
|[WFM](https://github.com/unegade/WFM ) |pika, aio_pika, BSSAPI, locust |
|
||||
|[WebApp-PhoneClassifier](https://github.com/minh-dg/WebApp-PhoneClassifier ) |fastai |
|
||||
|[WitnessMe](https://github.com/byt3bl33d3r/WitnessMe ) |xmltodict, imgcat, pytest, pyppeteer, jinja2, terminaltables, prompt_toolkit, pkg_resources, aiosqlite, yaml, witnessme |
|
||||
|[Words_record](https://github.com/LMFrank/Words_record ) |databases, pytest, pymysql, sqlalchemy, requests |
|
||||
|[Workspace](https://github.com/AYCHSPACE/Workspace ) |tqdm, jupyter_core, setuptools, tornado, crontab, pip, psutil, IPython, git, notebook |
|
||||
|[YoutubeBackup](https://github.com/FratStar/YoutubeBackup ) |google_auth_oauthlib, zeep, googleapiclient, spyne, google |
|
||||
|[Zulu](https://github.com/OSS-team-zulu/Zulu ) |requests, passlib, pymongo, geojson, tests, zulu, dynaconf, jwt, bson, pytest |
|
||||
|[ai-dungeon](https://github.com/AntonNeld/ai-dungeon ) |errors, test_utils, typing_extensions, pytest, models, api, dungeon |
|
||||
|[aioprometheus](https://github.com/claws/aioprometheus ) |setuptools, aiohttp, alabaster, quart, psutil, asynctest, sphinx, aioprometheus, quantile, prometheus_metrics_proto |
|
||||
|[aita](https://github.com/logan-connolly/aita ) |pytest, requests, tqdm, aiohttp, asyncpg, aita, loguru, orm, typer, sqlalchemy, databases, praw |
|
||||
|[alda-online](https://github.com/JesseChua94/alda-online ) |models, alda |
|
||||
|[alg_interface_fastapi_project](https://github.com/wuzaipei/alg_interface_fastapi_project ) |system_file, pymysql, sqlalchemy, data_model |
|
||||
|[alice-yamaha-skill](https://github.com/toshka/alice-yamaha-skill ) |exceptions, auth, yaml, capabilities, routers, rxv, config, httpx |
|
||||
|[allay-ds](https://github.com/Lambda-School-Labs/allay-ds ) |wandb, spacy, sklearn, requests, scrapy, process_data, fastapi_app, tensorflow, numpy, indeed, en_core_web_sm, dotenv |
|
||||
|[allure_reporter](https://github.com/penguinlav/allure_reporter ) |jinja2, requests, allure_reporter, loguru, aiofiles, async_generator, pytest |
|
||||
|[andrewhou-zonar](https://github.com/amhou/andrewhou-zonar ) |main, requests, util |
|
||||
|[apex-tracker](https://github.com/Timzan/apex-tracker ) |routers, requests, config |
|
||||
|[api](https://github.com/wuzaipei/alg_interface_fastapi_project ) |pyazo_api, alembic, dotenv, jwt, sqlalchemy, passlib |
|
||||
|[api-ocr](https://github.com/dzakyputra/api-ocr ) |tesserocr, PIL, cv2, modules |
|
||||
|[api-poc](https://github.com/igorfarias30/fastapi-poc ) |sklearn |
|
||||
|[api.authentication](https://github.com/jrhuerta/api.authentication ) |api_authentication |
|
||||
|[api_works](https://github.com/omrylcn/api_works ) |PIL, requests |
|
||||
|[apiestas](https://github.com/franloza/apiestas ) |crawling, scrapy, fuzzywuzzy, loguru, slugify, api, js2xml, dateparser, typer, twisted, pytz, motor |
|
||||
|[apitoolbox](https://github.com/zuarbase/apitoolbox ) |pytest, setuptools, passlib, sqlalchemy_filters, tests, sqlalchemy, ordered_uuid, tzlocal, pytz, jwt, apitoolbox, itsdangerous |
|
||||
|[arcas](https://github.com/maialogic/arcas ) |graphene, snapshottest |
|
||||
|[asgi-server-timing-middleware](https://github.com/sm-Fifteen/asgi-server-timing-middleware ) |yappi, asgi_server_timing, setuptools |
|
||||
|[asu-schedule](https://github.com/skhortiuk/asu-schedule ) |aiohttp, src, validators, bs4 |
|
||||
|[async_django_session](https://github.com/imbolc/async_django_session ) |setuptools, aiohttp, asyncpg, async_django_session, cfg, django, databases |
|
||||
|[authal-poc](https://github.com/michal-nakoneczny/authal-poc ) |authal, asynctest, pytest, bson, requests, tests, httpx |
|
||||
|[auto_populate](https://github.com/NealWhitlock/auto_populate ) |psycopg2, getter |
|
||||
|[autoloadtest](https://github.com/hkiang01/autoloadtest ) |locust, hypothesis |
|
||||
|[automagic_hashtag](https://github.com/MarianMalvin/automagic_hashtag ) |utils, google |
|
||||
|[b0mb3r](https://github.com/crinny/b0mb3r ) |setuptools, sentry_sdk, loguru, pkg_resources, b0mb3r, phonenumbers, click, httpx |
|
||||
|[b2blue-challenge](https://github.com/lfvilella/b2blue-challenge ) |pydantic_sqlalchemy, sqlalchemy |
|
||||
|[b3ta](https://github.com/nm17/b3ta ) |setuptools, sentry_sdk, loguru, pkg_resources, service, b0mb3r, phonenumbers, click, httpx |
|
||||
|[backend](https://github.com/ElusiveSpirit/fbd-backend ) |asynctest, transliterate |
|
||||
|[backend-skeleton](https://github.com/dmontagu/backend-skeleton ) |pytest, requests, typing_extensions, sqlalchemy_utils, tests, sqlalchemy, myapp, alembic, tenacity |
|
||||
|[balikobot_tmp_2](https://github.com/Horac-Bouthon/balikobot_tmp_2 ) |logger_wrapper, databases, schemas, sqlalchemy, conf, routers, sql_factory |
|
||||
|[base-fastapi-postgresql](https://github.com/alldevic/base-fastapi-postgresql ) |alembic, arq, sqlalchemy, sqlalchemy_utils, gino |
|
||||
|[basic-python-postgis-docker](https://github.com/stephenhillier/basic-python-postgis-docker ) |alembic, tenacity, sqlalchemy, myapp |
|
||||
|[bc_rates](https://github.com/MRobalinho/bc_rates ) |bc |
|
||||
|[belajar-fastapi](https://github.com/aansubarkah/belajar-fastapi ) |yfinance, sqlalchemy, models, config, tutorial, database |
|
||||
|[bench-rest-api-frameworks](https://github.com/py4mac/bench-rest-api-frameworks ) |sanic, locust, aiohttp, tornado, api, main, core |
|
||||
|[biomedical-computing-project-backend](https://github.com/SinaKhalili/biomedical-computing-project-backend ) |extra, xlrd, CoronaAnalysis |
|
||||
|[blog](https://github.com/deagle-z/blog ) |blog, loguru, jwt, sqlalchemy |
|
||||
|[blog-posts](https://github.com/tiangolo/blog-posts ) |pytest, sqlalchemy, jose, passlib |
|
||||
|[bluesky](https://github.com/paulorodriguesxv/bluesky ) |pytest, passlib, aiohttp, decouple, sqlalchemy_utils, sqlalchemy, myapp, alembic, jwt |
|
||||
|[boilerplate](https://github.com/pmcarlos/fast-api-boilerplate ) |utils, config |
|
||||
|[book-graphics](https://github.com/kamartem/book-graphics ) |alembic, sentry_sdk, asyncpg, loguru, sqlalchemy, gino |
|
||||
|[bplustogether](https://github.com/wborbajr/bplustogether ) |dynaconf, databases, loguru, sqlalchemy, bpapi |
|
||||
|[bracket_install](https://github.com/mobjack/bracket_install ) |six, flask, requests, colorama |
|
||||
|[bread-app](https://github.com/aidan-mcbride/bread-app ) |pyArango, pytest, jwt, routers, api, tests, passlib |
|
||||
|[bridgeapp](https://github.com/jasujm/bridgeapp ) |click, orjson, bridgeapp, click_log, pytest, zmq |
|
||||
|[budget-blocks-ds](https://github.com/Lambda-School-Labs/budget-blocks-ds ) |DB, geopy, transactionhist, mainhelp, census, main, psycopg2, dotenv, routers |
|
||||
|[bullshit-website](https://github.com/kaylynn234/bullshit-website ) |corporate_bullshit |
|
||||
|[burriking](https://github.com/txemac/burriking ) |infrastructure, dependency_injector, werkzeug, requests, pymongo, freezegun, tests, config, main, user_interface, application, database, flask, bson, pytest, flasgger, domain |
|
||||
|[bw3_attempt](https://github.com/worldwidekatie/bw3_attempt ) |dotenv, sklearn, requests, joblib |
|
||||
|[c4p2n](https://github.com/piterpy-meetup/c4p2n ) |c4p2n, fastapi_security_typeform, notion, ppm_telegram_bot_client |
|
||||
|[cah-v2](https://github.com/Mdellit110/cah-v2 ) |graphene, schema, graphene_sqlalchemy, sqlalchemy, models, config, schema_blackcards, database, schema_whitecards |
|
||||
|[calculator](https://github.com/ch0c01ate/calculator ) |pymongo, calculator, requests, dbsetup |
|
||||
|[canvote](https://github.com/alvintangz/canvote ) |jinja2, requests, passlib, sqlalchemy_pagination, humps, sqlalchemy, myapp, pytz, alembic, secure, jwt |
|
||||
|[cashflow](https://github.com/VSpectrum/cashflow ) |pytest, config |
|
||||
|[ccrbackend](https://github.com/danieldominguete/ccrbackend ) |six, dotenv, dynamorm, mangum, src, jose, marshmallow |
|
||||
|[challenge](https://github.com/Creditas/challenge ) |iris_classifier, sklearn |
|
||||
|[chapushillo](https://github.com/maxipavlovic/chapushillo ) |pusher, dotenv, pusher_client |
|
||||
|[checkmarx](https://github.com/zetkin/checkmarx ) |scanner, checkmarx, pytest, pyzbar, setuptools |
|
||||
|[chinook_fastapi](https://github.com/sandervandorsten/chinook_fastapi ) |dotenv, setuptools, chinook_fastapi |
|
||||
|[cloudrun-fastapi](https://github.com/anthcor/cloudrun-fastapi ) |pytest, actions, passlib, sqlalchemy_utils, config, tests, main, sqlalchemy, myapp, database, google, alembic, schemas, jwt, routers, gunicorn_config, models |
|
||||
|[cnapp-fastapi](https://github.com/cnapp/cnapp-fastapi ) |sanic, daiquiri, jinja2, prometheus_client, cnapps, starlette_prometheus |
|
||||
|[code_example_fastapi](https://github.com/d-tamirlan/code_example_fastapi ) |tortoise, src, envparse, fastapi_users |
|
||||
|[codewars-watchdog](https://github.com/uriyyo/codewars-watchdog ) |git |
|
||||
|[coding-challenges](https://github.com/fjemi/coding-challenges ) |password_generator, tqdm, jsbeautifier, dateutil, matplotlib, flask_api, word_square_check, flask |
|
||||
|[concurrency](https://github.com/Jorricks/concurrency ) |PIL, setuptools, redis, dataclasses_json, src, asgiref, click |
|
||||
|[contact-form](https://github.com/k4ssyi/contact-form ) |dotenv, controllers, urls, settings, utils, request_body |
|
||||
|[cookiecutter-python](https://github.com/ValentinCalomme/cookiecutter-python ) |typer |
|
||||
|[cookiecutter-simcore-py-fastapi](https://github.com/pcrespov/cookiecutter-simcore-py-fastapi ) |cryptography, pytest, servicelib, setuptools, simcore_service_catalog, aiohttp, pkg_resources, attr, ptvsd, aiopg, sqlalchemy, tenacity, yarl, httpx |
|
||||
|[coronavirus-tg-api](https://github.com/egbakou/coronavirus-tg-api ) |aiohttp, invoke, asyncmock, dateparser, async_asgi_testclient, async_generator, dotenv, pytest, bs4 |
|
||||
|[coursera-ibm-ai-workflow-submission](https://github.com/eightBEC/coursera-ibm-ai-workflow-submission ) |loguru, sklearn, pytest, joblib |
|
||||
|[coursework](https://github.com/algorithm-ssau/coursework ) |dotenv, databases, motor |
|
||||
|[covid-19-Chile](https://github.com/RentadroneCL/covid-19-Chile ) |orator, dotenv, routes, pendulum, covid_19_chile, config, database |
|
||||
|[covid-19-dashboard-api](https://github.com/zeshuaro/covid-19-dashboard-api ) |sortedcontainers, dotenv, jwt, pycountry, requests, google, passlib |
|
||||
|[covid19-id](https://github.com/anzharip/covid19-id ) |selenium, requests |
|
||||
|[covidfaq](https://github.com/dialoguemd/covidfaq ) |pytest, spacy, sklearn, tqdm, requests, covidfaq, selenium, bert_reranker, structlog, spacy_langdetect, yaml, html2text, boto3, torch, transformers, bs4 |
|
||||
|[covidsage](https://github.com/chancey-gardener/covidsage ) |spacy, data_loader, requests, backends, nlu_client, numpy, matplotlib, plotter, rasa, rasa_sdk, pymysql, discourse |
|
||||
|[cpython3-fastapi](https://github.com/phongln/cpython3-fastapi ) |blog, admin |
|
||||
|[custom-actions-app-python](https://github.com/Frameio/custom-actions-app-python ) |pytest, requests, custom_actions |
|
||||
|[daftacademy-python-levelup](https://github.com/holdenkold/daftacademy-python-levelup ) |main, pytest |
|
||||
|[daiei-backend](https://github.com/a-nakajima-at-shokuryu/daiei-backend ) |fastapi_server, dotenv, models, utils, dateutil, settings |
|
||||
|[daizu-online-judge-backend](https://github.com/SoyBeansLab/daizu-online-judge-backend ) |psycopg2, infrastructure, marshmallow_dataclass, interface, marshmallow, domain |
|
||||
|[dask-remote](https://github.com/octoenergy/dask-remote ) |distributed, typing_extensions, pytest, requests, dask_remote, setuptools |
|
||||
|[datarepo](https://github.com/PUG-PB-Traducao/datarepo ) |bcrypt, dotenv, fastapi_sqlalchemy, sqlalchemy |
|
||||
|[debitcard](https://github.com/xpnewmedia/debitcard ) |debitcard |
|
||||
|[debts](https://github.com/afonasev/debts ) |pytest, locust, factory, passlib, sentry_asgi, sentry_sdk, contextvars_executor, server, context_logging, sqlalchemy, alembic, jwt |
|
||||
|[decentralized-news-publishing](https://github.com/teddymarkov/decentralized-news-publishing ) |ariadne |
|
||||
|[demo-dms](https://github.com/gucharbon/demo-dms ) |urllib3, typer, pytest, minio, backend |
|
||||
|[deploy](https://github.com/pcmalves/codeploy-teste ) |main |
|
||||
|[devops-assignment](https://github.com/fokinpv/devops-assignment ) |asynctest, pytest, service, pulp |
|
||||
|[devs-conn-api](https://github.com/txemac/devs-conn-api ) |github, pytest, sqlalchemy, sqlalchemy_utils, data, twitter, tests |
|
||||
|[dfstore](https://github.com/Exhor/dfstore ) |requests, src, tests, feather |
|
||||
|[dig-bioindex](https://github.com/broadinstitute/dig-bioindex ) |requests, colorama, orjson, lib, enlighten, sqlalchemy, dotenv, boto3, click |
|
||||
|[django-fastapi-example](https://github.com/jordaneremieff/django-fastapi-example ) |django, api |
|
||||
|[docker-fastapi-demo](https://github.com/pylixm/docker-fastapi-demo ) |alembic, loguru, databases, core, sqlalchemy, service, myapp, api |
|
||||
|[docker-workshop](https://github.com/blairdrummond/docker-workshop ) |scipy, PIL, dash, flask, inference, sklearn, requests, numpy, joblib, flask_caching |
|
||||
|[downloads_statistics](https://github.com/mmidu/downloads_statistics ) |redis, main, seeds, controllers, DownloadSeeder, requests, models, config, utils, DownloadsController |
|
||||
|[dreamcatcher](https://github.com/sbathgate/dreamcatcher ) |pytest, passlib, sqlalchemy, myapp, jose, alembic, celery, tenacity, raven, emails |
|
||||
|[duke](https://github.com/jamescurtin/duke ) |duke, PIL, pytest |
|
||||
|[eXchangeAPI-PY](https://github.com/wborbajr/eXchangeAPI-PY ) |pymongo, dynaconf, exchangeapi, motor |
|
||||
|[educator_api](https://github.com/wycliffogembo87/educator_api ) |pony, requests, passlib, loguru, api, settings, schemas, core, models, util |
|
||||
|[effortless_fast_api](https://github.com/davideasaf/effortless_fast_api ) |requests, passlib, invoke, config, utils, main, sqlalchemy, schemas, jwt, models, services, pytest |
|
||||
|[embl_python_software_engineer_test](https://github.com/yusra-haider/embl_python_software_engineer_test ) |sqlalchemy, requests |
|
||||
|[eml_analyzer](https://github.com/ninoseki/eml_analyzer ) |async_timeout, pytest, eml_parser, compressed_rtf, arrow, compoundfiles, olefile, loguru, fastapi_utils, tests, dateparser, asynctest, magic, aiospamc, ioc_finder, respx, httpx |
|
||||
|[entroprise-api](https://github.com/peterdeepsix/entroprise-api ) |simpletransformers, service, models, api |
|
||||
|[eu-taxation-customs-api](https://github.com/aditya-08/eu-taxation-customs-api ) |main, zeep, requests |
|
||||
|[events-api](https://github.com/txemac/events-api ) |pytz, xmltodict, pytest, sqlalchemy, sqlalchemy_utils, apscheduler, data, requests, tests |
|
||||
|[example-fastapi-sqlachemy-pytest](https://github.com/timhughes/example-fastapi-sqlachemy-pytest ) |example, pytest, sqlalchemy |
|
||||
|[excerpt-formatter](https://github.com/cmckillop/excerpt-formatter ) |text_formatter, slack_manager, requests, models |
|
||||
|[fake-news-game](https://github.com/jsandovalc/fake-news-game ) |databases |
|
||||
|[fantasy-fencing](https://github.com/Innoviox/fantasy-fencing ) |colorlog, selenium, click, scrape, requests, logs, bs4 |
|
||||
|[fapi](https://github.com/glebofff/fapi ) |api, sqlalchemy, myapp, alembic, fastapi_sqlalchemy, core, models, click |
|
||||
|[fast](https://github.com/foobar8675/fastai-bite ) |routers, user |
|
||||
|[fast-api-asyncdb](https://github.com/tsadimas/fast-api-asyncdb ) |dotenv, databases, sqlalchemy, settings, fastapi_users |
|
||||
|[fast-api-boilerplate](https://github.com/pmcarlos/fast-api-boilerplate ) |alembic, dotenv, fastapi_sqlalchemy, core, sqlalchemy, myapp |
|
||||
|[fast-api-rest](https://github.com/javier-lopez/fast-api-rest ) |config, bjoern |
|
||||
|[fast-api-sql-template](https://github.com/Jonatha-Varjao/fast-api-sql-template ) |pytest, googletrans, requests, passlib, sqlalchemy_utils, sqlalchemy, alembic, jwt, emails |
|
||||
|[fast-elm](https://github.com/yuzebin/fast-elm ) |bcrypt, fmmodel, requests, fmsecurity, passlib, fmdb, slugify, fmcrud, fmapi, databases, motor, jwt, fmtoken, bson |
|
||||
|[fastAPI-be](https://github.com/BonneC/fastAPI-be ) |alembic, dotenv, sqlalchemy, models, api, config, myapp, crud |
|
||||
|[fastApi](https://github.com/fionasummer/fastApiSelf ) |my_super_project, graphene, projetStage, sqlalchemy |
|
||||
|[fastApiSelf](https://github.com/fionasummer/fastApiSelf ) |pytest, rr, sqlalchemy, websocket, fackApi, app1 |
|
||||
|[fastApiSimple](https://github.com/marirs/fastApiSimple ) |pymongo, geoip2, motor, server |
|
||||
|[fastAppAppointment](https://github.com/EdvinaNakos/fastAppAppointment ) |main, controller, databases, sqlalchemy, db, service |
|
||||
|[fast_api](https://github.com/davideasaf/effortless_fast_api ) |app_utils, bcrypt, schemas, jwt, sqlalchemy, models, database, crud |
|
||||
|[fast_api_camelcase_example](https://github.com/ahmednafies/fast_api_camelcase_example ) |humps, models |
|
||||
|[fast_api_self_study](https://github.com/cuongld2/fast_api_self_study ) |bcrypt, jwt, sqlalchemy, sql_app |
|
||||
|[fastaaps](https://github.com/sergio-chumacero/fastaaps ) |pymongo, celery, motor |
|
||||
|[fastai-bite](https://github.com/foobar8675/fastai-bite ) |PIL, graphene, my_utils, fastai2, cv2, httpx |
|
||||
|[fastapi-GraphQL-full-stack-docker-github-actions-graphene-test-client-template](https://github.com/gaylonalfano/fastapi-GraphQL-full-stack-docker-github-actions-graphene-test-client-template )|graphene, pytest, sqlalchemy, graphql, snapshottest |
|
||||
|[fastapi-GraphQL-full-stack-docker-travisci-starlette-test-client-template](https://github.com/gaylonalfano/fastapi-GraphQL-full-stack-docker-travisci-starlette-test-client-template ) |graphene, pytest |
|
||||
|[fastapi-admin](https://github.com/long2ice/fastapi-admin ) |colorama, passlib, fastapi_admin, prompt_toolkit, tortoise, module, xlsxwriter, jwt |
|
||||
|[fastapi-auth](https://github.com/dmontagu/fastapi-auth ) |argon2, bcrypt, pytest, requests, typing_extensions, fastapi_auth, tests, sqlalchemy, jwt |
|
||||
|[fastapi-backend](https://github.com/dev-courses/fastapi-backend ) |bcrypt, pytest, passlib, asyncpg, loguru, slugify, tests, asgi_lifespan, sqlalchemy, docker, alembic, psycopg2, databases, jwt, httpx |
|
||||
|[fastapi-boilerplate](https://github.com/teamhide/fastapi-boilerplate ) |alembic, jwt, sqlalchemy, core, myapp |
|
||||
|[fastapi-celery](https://github.com/GregaVrbancic/fastapi-celery ) |worker, celery |
|
||||
|[fastapi-celery-redis-rabbitmq](https://github.com/karthikasasanka/fastapi-celery-redis-rabbitmq ) |redis, celery, shopping |
|
||||
|[fastapi-cool](https://github.com/genchsusu/fastapi-cool ) |kombu, aiosmtplib, tortoise, yaml, settings, celery, jwt, ldap3 |
|
||||
|[fastapi-crud](https://github.com/sophiabrandt/fastapi-crud ) |databases, pytest, sqlalchemy, db |
|
||||
|[fastapi-crud-sync](https://github.com/testdrivenio/fastapi-crud-sync ) |pytest, sqlalchemy |
|
||||
|[fastapi-demo](https://github.com/sonhal/fastapi-demo ) |rethinkdb, fastapi_demo |
|
||||
|[fastapi-discovery](https://github.com/DenisOH/fastapi-discovery ) |sqlalchemy, fastapi_discovery |
|
||||
|[fastapi-docs-cn](https://github.com/apachecn/fastapi-docs-cn ) |graphene, passlib, peewee, couchbase, sqlalchemy, flask, databases, jwt |
|
||||
|[fastapi-esf-demo](https://github.com/hightfly/fastapi-esf-demo ) |databases, sqlalchemy, sqlalchemy_utils |
|
||||
|[fastapi-etag](https://github.com/steinitzu/fastapi-etag ) |pytest, requests, fastapi_etag |
|
||||
|[fastapi-exploring](https://github.com/manjurulhoque/fastapi-exploring ) |alembic, dotenv, fastapi_sqlalchemy, sqlalchemy, myapp |
|
||||
|[fastapi-for-firebase](https://github.com/attakei/fastapi-for-firebase ) |fastapi_for_firebase, pytest |
|
||||
|[fastapi-fullstack](https://github.com/lfvilella/fastapi-fullstack ) |pytest, sqlalchemy, validate_docbr |
|
||||
|[fastapi-healthcheck](https://github.com/jeffsiver/fastapi-healthcheck ) |healthcheck, setuptools |
|
||||
|[fastapi-helloworld](https://github.com/yashwanthl/fastapi-helloworld ) |nltk, sklearn, modules |
|
||||
|[fastapi-hero](https://github.com/Hedde/fastapi-heroku-test ) |dotenv, motor, core, db, bson, models, api |
|
||||
|[fastapi-hex-boilerplate](https://github.com/GArmane/fastapi-hex-boilerplate ) |factory, toolz, tests, sqlalchemy, myapp, alembic, dotenv, databases, pytest |
|
||||
|[fastapi-jsonrpc](https://github.com/smagafurov/fastapi-jsonrpc ) |fastapi_jsonrpc, aiojobs, pytest, setuptools |
|
||||
|[fastapi-jwt](https://github.com/carminati-marco/fastapi-jwt ) |ms_auth, auth, jwt, passlib |
|
||||
|[fastapi-layered-architecture](https://github.com/teamhide/fastapi-layered-architecture ) |alembic, pythondi, jwt, sqlalchemy, core, myapp |
|
||||
|[fastapi-login](https://github.com/parsd/fastapi-login ) |pytest, requests, setuptools, fastapi_login, assertpy |
|
||||
|[fastapi-mako](https://github.com/LouisYZK/fastapi-mako ) |mako, setuptools |
|
||||
|[fastapi-mangum-example](https://github.com/chgangaraju/fastapi-mangum-example ) |mangum |
|
||||
|[fastapi-microblog](https://github.com/vkhnychenko/fastapi-microblog ) |alembic, core, sqlalchemy, microblog, myapp, user |
|
||||
|[fastapi-ml-scaffolding](https://github.com/jmeisele/fastapi-ml-scaffolding ) |loguru, pytest, fastapi_scaffolding, joblib |
|
||||
|[fastapi-ml-skeleton](https://github.com/eightBEC/fastapi-ml-skeleton ) |loguru, pytest, fastapi_skeleton, joblib |
|
||||
|[fastapi-mount](https://github.com/Midnighter/fastapi-mount ) |mount_demo |
|
||||
|[fastapi-nuxt-blog](https://github.com/tokusumi/fastapi-nuxt-blog ) |PIL, passlib, sqlalchemy_utils, sqlalchemy, myapp, sendgrid, pytz, alembic, jwt, boto3, pytest |
|
||||
|[fastapi-permissions](https://github.com/holgi/fastapi-permissions ) |pytest, jwt, fastapi_permissions, passlib |
|
||||
|[fastapi-playground](https://github.com/FlorianBorn/fastapi-playground ) |PIL, fastai |
|
||||
|[fastapi-plugins](https://github.com/madkote/fastapi-plugins ) |fastapi_plugins, setuptools, aioredis, tenacity, aiojobs, pytest |
|
||||
|[fastapi-poc](https://github.com/igorfarias30/fastapi-poc ) |main, data, models, todo |
|
||||
|[fastapi-postgres-aws-lambda](https://github.com/KurtKline/fastapi-postgres-aws-lambda ) |mangum, sqlalchemy |
|
||||
|[fastapi-realworld-example-app-mysql](https://github.com/xiaozl/fastapi-realworld-example-app-mysql ) |bcrypt, passlib, asyncpg, loguru, aiomysql, tests, asgi_lifespan, docker, psycopg2, databases, jwt, pytest, httpx |
|
||||
|[fastapi-route](https://github.com/franodos/fastapi-route ) |response, setuptools |
|
||||
|[fastapi-satella-metrics](https://github.com/piotrmaslanka/fastapi-satella-metrics ) |fastapi_satella_metrics, requests, setuptools, satella |
|
||||
|[fastapi-scaffold](https://github.com/ibegyourpardon/fastapi-scaffold ) |xuan |
|
||||
|[fastapi-simple-cachecontrol](https://github.com/attakei/fastapi-simple-cachecontrol ) |fastapi_simple_cachecontrol, pytest |
|
||||
|[fastapi-simple-template](https://github.com/MushroomMaula/fastapi-simple-template ) |alembic, dotenv, pytest, faker, sqlalchemy, myapp, fastapi_login |
|
||||
|[fastapi-snippets](https://github.com/hhatto/fastapi-snippets ) |caches, fastapi_plugins, aioredis |
|
||||
|[fastapi-sqlalchemy](https://github.com/zuarbase/fastapi-sqlalchemy-example ) |pytest, setuptools, passlib, sqlalchemy_filters, tests, sqlalchemy, ordered_uuid, tzlocal, pytz, fastapi_sqlalchemy, jwt, itsdangerous |
|
||||
|[fastapi-sqlalchemy-example](https://github.com/zuarbase/fastapi-sqlalchemy-example ) |alembic, itsdangerous, fastapi_sqlalchemy, pytest, fastapi_sqlalchemy_example, sqlalchemy, myapp, click, setuptools |
|
||||
|[fastapi-starter-kit](https://github.com/Shinichi-Nakagawa/fastapi-starter-kit ) |book, pytest, sqlalchemy, db |
|
||||
|[fastapi-tdd-docker](https://github.com/pmcarlos/fastapi-tdd-docker ) |pytest, tortoise |
|
||||
|[fastapi-template](https://github.com/jefmud/fastapi-templates ) |pytest, setuptools, passlib, sentry_sdk, sqlalchemy_utils, gino, sqlalchemy, jose, alembic, fastapi_template, migrations |
|
||||
|[fastapi-test](https://github.com/jrwalk/fastapi-test ) |pytest, pandas |
|
||||
|[fastapi-tools](https://github.com/so1n/fastapi-tools ) |fastapi_tools, prometheus_client, httpx |
|
||||
|[fastapi-tortoise](https://github.com/prostomarkeloff/fastapi-tortoise ) |loguru, pytest, tortoise, requests, tests |
|
||||
|[fastapi-users](https://github.com/frankie567/fastapi-users ) |passlib, tortoise, tests, asynctest, asgi_lifespan, sqlalchemy, httpx_oauth, makefun, databases, motor, jwt, fastapi_users, pytest, httpx |
|
||||
|[fastapi-utils](https://github.com/dmontagu/fastapi-utils ) |pytest, fastapi_utils, tests, sqlalchemy |
|
||||
|[fastapi-uvicorn-gunicorn-nginx-supervisor-boilerplate](https://github.com/sumitsk20/fastapi-uvicorn-gunicorn-nginx-supervisor-boilerplate ) |orjson, motor, core, server, bson, apps, utils |
|
||||
|[fastapi-versioning](https://github.com/DeanWay/fastapi-versioning ) |example, typing_extensions, fastapi_versioning, setuptools |
|
||||
|[fastapi-vue-test](https://github.com/abreumatheus/fastapi-vue-test ) |PIL, sqlalchemy, myapp, pytz, alembic, boto3 |
|
||||
|[fastapi-websockets](https://github.com/natanael-silvamt/fastapi-websockets ) |api |
|
||||
|[fastapi-whisper-rest-api](https://github.com/BolajiOlajide/fastapi-whisper-rest-api ) |api |
|
||||
|[fastapi_admin](https://github.com/Chise1/fastapi_admin ) |apps, setuptools, passlib, fastapi_admin, sqlalchemy, settings, databases, pymysql, jwt |
|
||||
|[fastapi_catchup](https://github.com/Kamihara/fastapi_catchup ) |intro |
|
||||
|[fastapi_douyin](https://github.com/bigdataboy2020/fastapi_douyin ) |routers, requests, settings, exceptions, spiders |
|
||||
|[fastapi_gino](https://github.com/pengyejun/fastapi_gino ) |gino_starlette, pytest, gino_fastapi_demo, sqlalchemy, myapp, aioredis, alembic, poetry, urllib2 |
|
||||
|[fastapi_microblog](https://github.com/mrworksome/fastapi_microblog ) |alembic, databases, core, sqlalchemy, microblog, myapp, user, passlib, fastapi_users |
|
||||
|[fastapi_mock](https://github.com/superxuu/fastapi_mock ) |apis, yaml, common |
|
||||
|[fastapi_model](https://github.com/Nathanlauga/fastapi_model ) |loguru, joblib |
|
||||
|[fastapi_playground](https://github.com/Rurson/fastapi_playground ) |pony, db |
|
||||
|[fastapi_preset](https://github.com/cln-m4rie/fastapi_preset ) |setuptools |
|
||||
|[fastapi_router](https://github.com/christopherzimmerman/fastapi_router ) |fastapi_router, trimport, setuptools |
|
||||
|[fastapi_serviceutils](https://github.com/skallfass/fastapi_serviceutils_template ) |pytest, requests, setuptools, toolz, loguru, fastapi_serviceutils, yaml, cookiecutter, sqlalchemy, databases |
|
||||
|[fastapi_serviceutils_template](https://github.com/skallfass/fastapi_serviceutils_template ) |fastapi_serviceutils |
|
||||
|[fastapi_stu](https://github.com/Thousandhack/fastapi_stu ) |aliyunsdkcore, jwt, sqlalchemy, passlib |
|
||||
|[fastapi_template](https://github.com/ytxfate/fastapi_template ) |pymongo, redis, jwt, minio, project |
|
||||
|[fastapi_todo_hex](https://github.com/josemlp91/fastapi_todo_hex ) |factory, toolz, pytest_factoryboy, tests, sqlalchemy, myapp, alembic, dotenv, databases, pytest, todolist |
|
||||
|[fastbroker](https://github.com/cetanu/fastbroker ) |pkg_resources, ujson, fastbroker, jsonschema, setuptools |
|
||||
|[fastrf](https://github.com/iancleary/fastrf ) |asgi_lifespan, pytest, fastrf, httpx |
|
||||
|[fbd-backend](https://github.com/ElusiveSpirit/fbd-backend ) |redis, pytest_mock, celery, loguru, ujson, pytest, tests |
|
||||
|[finance-api](https://github.com/rennerocha/finance-api ) |finance_api, dynaconf, pytest, requests |
|
||||
|[first_fastapi](https://github.com/vittorduartte/first_fastapi ) |routes, ext, schemas, sqlalchemy, rules, models |
|
||||
|[firstderm_demo](https://github.com/polyrand/firstderm_demo ) |requests |
|
||||
|[foreign-subs](https://github.com/joe-eklund/foreign-subs ) |fsubs, setuptools, pymongo, typer, jwt, bson |
|
||||
|[frontapp](https://github.com/Blasterai/frontapp ) |decorator, frontapp, requests, envparse, attr |
|
||||
|[fullasync](https://github.com/lipengsh/fullasync ) |celery, tasks, core, api |
|
||||
|[fullstack-fastapi-elasticsearch](https://github.com/cleobulo/fullstack-fastapi-elasticsearch ) |core, elasticsearch, db, models, api |
|
||||
|[fuzzy-octo-funicular](https://github.com/mattkatz/fuzzy-octo-funicular ) |main |
|
||||
|[gaia-fastapi-demo](https://github.com/muslax/gaia-fastapi-demo ) |email_validator, passlib, pymongo, sentry_sdk, motor, jwt, bson, emails |
|
||||
|[gaki-server](https://github.com/shitangdama/gaki-server ) |alembic, jwt, sqlalchemy, myapp, passlib |
|
||||
|[galicea-odoo-addons-ecosystem](https://github.com/galicea/galicea-odoo-addons-ecosystem ) |cryptography, werkzeug, jwcrypto, odoo, git |
|
||||
|[game](https://github.com/jsandovalc/fake-news-game ) |basegame |
|
||||
|[gateway-microservice](https://github.com/anforaProject/gateway-microservice ) |aiohttp, v1 |
|
||||
|[gerenciador-tarefas](https://github.com/matheuspsouza/gerenciador-tarefas ) |gerenciador_tarefas |
|
||||
|[gfw-tile-cache](https://github.com/wri/gfw-tile-cache ) |gino_starlette, mercantile, pytest, pendulum, async_lru, requests, shapely, cachetools, sqlalchemy, aenum |
|
||||
|[gifter_api](https://github.com/zhanymkanov/gifter_api ) |pytest, jwt, sqlalchemy, tests, passlib |
|
||||
|[gigscovery_backend](https://github.com/martijnboers/gigscovery_backend ) |functions, sklearn, bandsintown, spotipy |
|
||||
|[gino-admin](https://github.com/xnuinside/gino-admin ) |sanic, pytest, click, jinja2, db, requests, passlib, expiring_dict, gino_admin, asyncpg, dsnparse, sqlalchemy_utils, gino, urllib3, aiofiles, yaml, sqlalchemy, sanic_jwt, sanic_jinja2 |
|
||||
|[gino-starlette](https://github.com/python-gino/gino-starlette ) |requests, gino_fastapi_demo, asyncpg, gino, urllib3, importlib_metadata, sqlalchemy, alembic, pytest, click |
|
||||
|[girandole](https://github.com/bartkl/girandole ) |beets, plugin, girandole, yaml |
|
||||
|[girias.json](https://github.com/kvnol/girias.json ) |tinydb |
|
||||
|[gitea-dispatcher](https://github.com/Trim21/gitea-dispatcher ) |httpx |
|
||||
|[gke-cicd-sandbox](https://github.com/mana-ysh/gke-cicd-sandbox ) |exampleapp, pytest |
|
||||
|[glittr-fastapi](https://github.com/kellischeuble/glittr-fastapi ) |glittr, sqlalchemy |
|
||||
|[graph-server](https://github.com/ZettaAI/graph-server ) |pychunkedgraph, numpy |
|
||||
|[graphql-python-server](https://github.com/apoveda25/graphql-python-server ) |env, gql, dotenv, schemas, ariadne, models, database, arango |
|
||||
|[graphql-tutorial](https://github.com/jcremona/graphql-tutorial ) |schema, graphene |
|
||||
|[grocery-stores-home-delivery](https://github.com/dz-experts/grocery-stores-home-delivery ) |db, app_factory, setuptools, crud, api, typer, sqlalchemy, myapp, alembic, constans, schemas, core, models |
|
||||
|[guid_tracker](https://github.com/nwcell/guid_tracker ) |alembic, databases, aiocache, guid, pytest, sqlalchemy, sqlalchemy_utils, myapp |
|
||||
|[hackaton-berlin-legal-tech-2020](https://github.com/jjanczur/hackaton-berlin-legal-tech-2020 ) |pytest, sklearn, joblib, nltk, loguru, cleaner, config, fastapi_skeleton, bs4 |
|
||||
|[hello-world-fastapi](https://github.com/benhid/hello-world-fastapi ) |fastapp, setuptools, rich |
|
||||
|[henry](https://github.com/jacksonhenry3/FAAS ) |infrastructure, interface, sqlalchemy |
|
||||
|[heroku-google-translate-api](https://github.com/fawazahmed0/heroku-google-translate-api ) |googletrans |
|
||||
|[hh_parser](https://github.com/Kuzyashin/hh_parser ) |passlib, aiohttp, tortoise, template, celery, jwt, emails |
|
||||
|[home_recruiters](https://github.com/Mohamed-Kaizen/home_recruiters ) |users, pendulum, passlib, jobs, tortoise, typer, usernames, jwt, core, confusable_homoglyphs, username |
|
||||
|[http-battle](https://github.com/MihaiAnei/http-battle ) |flask_restful, flask, python_flask |
|
||||
|[hvilkenfisk](https://github.com/David-IL/hvilkenfisk ) |imutils, fish_utils, sklearn, h5py, cv2, keras, img_preprocess, model_utils |
|
||||
|[iam_working](https://github.com/carlosporta/iam_working ) |iam_working |
|
||||
|[iem-web-services](https://github.com/akrherz/iem-web-services ) |setuptools, pyiem, shapely, metpy, pytz, pygrib, pyproj, pandas, geopandas, pytest |
|
||||
|[ihan-productizer-example](https://github.com/digitalliving/ihan-productizer-example ) |aiohttp, ujson, settings_local, settings |
|
||||
|[image_capabilities_backend](https://github.com/alejgo06/image_capabilities_backend ) |PIL, cv2 |
|
||||
|[image_classifier_backend](https://github.com/alejgo06/image_classifier_backend ) |PIL, cv2 |
|
||||
|[image_detector_backend](https://github.com/alejgo06/image_detector_backend ) |PIL, torch, utils, torchvision, pycocotools, models, cv2 |
|
||||
|[img-filter-api](https://github.com/RaRhAeu/img-filter-api ) |aiohttp, cv2, api, requests, pytest |
|
||||
|[ingaia_app](https://github.com/higaooliveira/ingaia_app ) |pytest, sqlalchemy, resources, requests, config, spotipy, domain |
|
||||
|[innovativeproject-photographers-portfolio](https://github.com/nokia-wroclaw/innovativeproject-photographers-portfolio ) |fastapi_mail, jwt, sqlalchemy, src, database, passlib |
|
||||
|[inpainting_backen](https://github.com/kaikai03/inpainting_backen ) |tinydb |
|
||||
|[introduction-to-async-await](https://github.com/ci42/introduction-to-async-await ) |unsync, unsyncable, colorama |
|
||||
|[iter8-analytics](https://github.com/iter8-tools/iter8-analytics ) |analytics_namespace, requests, flask_restplus, setuptools, experiment_namespace, yaml, jsonschema, iter8_analytics, django, flask, requests_mock |
|
||||
|[jjs](https://github.com/jjs-dev/jjs ) |bcrypt, pytest, api_models, pymongo, db_models, routes, auth, db_connect |
|
||||
|[jobsearch_statistics](https://github.com/JobtechSwe/jobsearch_statistics ) |certifi, searchstatistics, elasticsearch |
|
||||
|[json-fastapi](https://github.com/stocky37/json-fastapi ) |json_fastapi, tinydb, slugify, link_header, util |
|
||||
|[juice-box](https://github.com/dannytannertantrum/juice-box ) |tests |
|
||||
|[jupyter_fastapi](https://github.com/Zsailer/jupyter_fastapi ) |jupyter_server, tornado, hypothesis, pytest, jupyter_fastapi, hypothesis_jsonschema, setuptools |
|
||||
|[k8s-traefik-example](https://github.com/Hedde/k8s-traefik-example ) |flask |
|
||||
|[kartu-beckend](https://github.com/AntonKristiono/kartu-beckend ) |PIL, config, yaml, autocrop, routes, motor, bson, cv2 |
|
||||
|[koalachat](https://github.com/therumbler/koalachat ) |koalachat, aiofiles |
|
||||
|[koalastream](https://github.com/therumbler/koalastream ) |aiofiles, koalastream |
|
||||
|[kritika](https://github.com/Egnod/kritika ) |alembic, sitri, slugify, sqlalchemy, myapp, kritika |
|
||||
|[kubeflow-containers-desktop](https://github.com/StatCan/kubeflow-containers-desktop ) |tqdm, jupyter_core, setuptools, tornado, crontab, pip, psutil, IPython, git, notebook |
|
||||
|[kubernetes-experiments](https://github.com/richard-to/kubernetes-experiments ) |redis, alembic, sqlalchemy, myapp, passlib |
|
||||
|[kuma](https://github.com/deepalikumar/sync_async_compare ) |bcrypt, docstring_parser, passlib, orjson, loguru, sqlalchemy, myapp, jupyter_client, alembic, databases, jwt, nbformat |
|
||||
|[kvmail](https://github.com/la9ran9e/kvmail ) |tarantool, dotenv, settings |
|
||||
|[lab_monitor](https://github.com/nutanix-japan/lab_monitor ) |pymongo, requests, schedule, client |
|
||||
|[lagom](https://github.com/meadsteve/lagom ) |sybil, pytest, lagom, typing_extensions, tests, django, flask |
|
||||
|[lang-python](https://github.com/tim-barnes/lang-python ) |submod2, worker_a, etcd3, rabbit, worker_b, optmod1, psutil, mongoengine, pika, test_pb2, grpc, google, optmod2, submod1, celery, flask, models, pytest |
|
||||
|[league-manager](https://github.com/Project-SRC/league-manager ) |jwt, src, environs, passlib |
|
||||
|[learn-kubernetes](https://github.com/quan-vu/learn-kubernetes ) |flask, flask_restful |
|
||||
|[lgbsttracker_api](https://github.com/py4mac/lgbsttracker_api ) |lgbsttracker, lgbsttracker_api |
|
||||
|[lightgbm-project-demo](https://github.com/raywu60kg/lightgbm-project-demo ) |psycopg2, sklearn, src, tests, ray |
|
||||
|[linkalong-pre-inteview-task](https://github.com/pydevd/linkalong-pre-inteview-task ) |decouple, linkalong |
|
||||
|[love-alarm](https://github.com/dl-eric/love-alarm ) |pymongo, redis, nexmo, jwt, boto3, bson, passlib |
|
||||
|[maale-map-bot](https://github.com/kudanai/maale-map-bot ) |pyngrok, dotenv, discord, bots, viberbot, models, settings, fuzzywuzzy, telegram |
|
||||
|[manageme-api](https://github.com/managemeapp/manageme-api ) |mangum, today_app, setuptools |
|
||||
|[mangum-fastapi-aws-test](https://github.com/dan-danciu/mangum-fastapi-aws-test ) |mangum |
|
||||
|[mars-rover](https://github.com/rSkogeby/mars-rover ) |models, views |
|
||||
|[memefly-ds](https://github.com/Lambda-School-Labs/memefly-ds ) |wandb, memefly, requests, pymongo, decouple, api, tensorflow, dotenv, boto3, click |
|
||||
|[metabot](https://github.com/vasyukvv42/metabot ) |metabot, requests, fastapi_metabot, setuptools, aiohttp, pymongo, help, typing_extensions, feedback, aioredis, vacations, slackers, motor, mockaioredis, bson, slack, pytest, httpx |
|
||||
|[micro-data-lake](https://github.com/abxda/micro-data-lake ) |sklearn, newspaper, altair, airflow, sqlalchemy, IPython, minio, setproctitle |
|
||||
|[microservices](https://github.com/hugosteixeira/microservices ) |requests |
|
||||
|[mironov_blog_pwa](https://github.com/yuriymironov96/mironov_blog_pwa ) |alembic, dotenv, sqlalchemy, myapp, apps |
|
||||
|[ml-project-template](https://github.com/raywu60kg/ml-project-template ) |psycopg2, src, tests, ray |
|
||||
|[ml-workspace](https://github.com/DennisRasey/ml-workspace ) |jupyter_core, tqdm, setuptools, tornado, crontab, pip, psutil, IPython, git, notebook |
|
||||
|[modelo-fast-api](https://github.com/knienkotter/modelo-fast-api ) |pytest, gerenciador_tarefas |
|
||||
|[moneyTransfer](https://github.com/williamsyb/moneyTransfer ) |faker, db, app_factory, passlib, apis, config, utils, main, sqlalchemy, auth, schemas, jwt, pytest |
|
||||
|[mongodb_admin](https://github.com/ArtemZaitsev1994/mongodb_admin ) |trafaret_config, envparse, pymongo, base, motor, jwt, bson |
|
||||
|[mri_image_reconstruction](https://github.com/Pradhy729/mri_image_reconstruction ) |utils, config |
|
||||
|[msys2-web](https://github.com/msys2/msys2-web ) |pytest, pgpdump, jinja2, fastapi_etag, respx, httpx |
|
||||
|[musixfy_service](https://github.com/divyam234/musixfy_service ) |requests, Cryptodome |
|
||||
|[mv-backend](https://github.com/Sreerajta/mv-backend ) |cassandra, requests, passlib, redis, fnc, global_config, sqlalchemy, jwt, greenstalk |
|
||||
|[mvp-metric](https://github.com/DjaPy/mvp-metric ) |databases, sqlalchemy, dateutil |
|
||||
|[mvs_eland_api](https://github.com/rl-institut/mvs_eland_api ) |celery, pytest, worker, mvs_eland_tool, sphinx_rtd_theme |
|
||||
|[my-notes-app](https://github.com/jgabriel1/my-notes-app ) |databases, pytest, jwt, sqlalchemy, passlib |
|
||||
|[mychef](https://github.com/logan-connolly/mychef ) |spacy, requests, scrapy, tqdm, aiohttp, asyncpg, loguru, orm, reddit, scraper, sqlalchemy, databases, praw, pytest, jsonlines, bs4 |
|
||||
|[nasa_neo](https://github.com/cmmarti/nasa_neo ) |repo, requests |
|
||||
|[nereid](https://github.com/Geosyntec/nereid ) |nereid, pytest, redis, typing_extensions, numpy, matplotlib, scipy, yaml, pint, celery, pandas |
|
||||
|[ners](https://github.com/cedricconol/ners ) |utils, config |
|
||||
|[netpalm](https://github.com/tbotnz/netpalm ) |xmltodict, rq, netmiko, pytest, jinja2, backend, netpalm_pinned_worker, requests, jinja2schema, redis, lxml, tests, jsonschema, ncclient, routers, napalm |
|
||||
|[next-word-sentence-pred-api](https://github.com/rakesh4real/next-word-sentence-pred-api ) |tree, sqlalchemy, models, database |
|
||||
|[nile-api](https://github.com/mattgeiger/nile-api ) |alembic, bcrypt, databases, jwt, sqlalchemy, api |
|
||||
|[nlp_api](https://github.com/rdenadai/nlp_api ) |nltk, decouple, spacy, utils |
|
||||
|[nmdc-server](https://github.com/microbiomedata/nmdc-server ) |pytest, faker, factory, requests, setuptools, pkg_resources, sqlalchemy, alembic, nmdc_server |
|
||||
|[nomine](https://github.com/gzzo/nomine ) |graphql_sqlalchemy, ariadne, sqlalchemy, nomine, graphql |
|
||||
|[nurse-schedule](https://github.com/csabex94/nurse-schedule ) |spital_routes, mongoengine, nurse_routes, jwt, models, utils, passlib |
|
||||
|[oasis](https://github.com/instedd/oasis ) |users, bcrypt, pytest, stories, requests, mysql, flask_cors, sqlalchemy, myapp, sigfig, database, jenkspy, alembic, flask, auth, jwt, router, emails |
|
||||
|[ocfapi](https://github.com/nikhiljha/ocfapi ) |ocflib |
|
||||
|[oreMeet](https://github.com/mfortini/oreMeet ) |ics, worker |
|
||||
|[owi_Result-Collector](https://github.com/JensGe/owi_Result-Collector ) |sqlalchemy, tests |
|
||||
|[page-visitor-counter](https://github.com/trapflag/page-visitor-counter ) |redis |
|
||||
|[paste.bin](https://github.com/magiskboy/paste.bin ) |pymongo, redis, gridfs, bson |
|
||||
| |PIL, PyPDF4 |
|
||||
|[perfume-ai](https://github.com/gurutaka/perfume-ai ) |PIL, torchvision, predict, cv2, torch |
|
||||
|[persistent-identifier-service](https://github.com/OpertusMundi/persistent-identifier-service ) |ompid, yaml, sqlalchemy |
|
||||
|[personal-learning](https://github.com/hoangthienan95/personal-learning ) |pyimagesearch, sklearn, calculator, cv2, keras |
|
||||
|[pi-camera-hardware-server](https://github.com/NSnietol/pi-camera-hardware-server ) |loguru, picamera, requests, src |
|
||||
|[piccolo_api](https://github.com/piccolo-orm/piccolo_api ) |setuptools, asyncpg, test_session, piccolo, livereload, jwt, piccolo_api, asgiref |
|
||||
|[plarin_task](https://github.com/jjoskey/plarin_task ) |pymongo, main, employees, pytest, faker, bson, settings |
|
||||
|[plonkowski.b-gmail.com](https://github.com/Raroog/plonkowski.b-gmail.com ) |main |
|
||||
|[plotly-graphene](https://github.com/mdpham/plotly-graphene ) |pymongo, mutation, graphene, schema, minio, loompy, bson, query, minio_client |
|
||||
|[pneumoniadetection](https://github.com/aalikadic/pneumoniadetection ) |kaggle, PIL, classifier, keras |
|
||||
|[pol](https://github.com/RedHatInsights/policies-notifications ) |pytest, requests, aioresponses, etc, orjson, aiohttp, redis, sentry_sdk, tests, sqlalchemy, myapp, aioredis, pytz, alembic, databases, aiologger |
|
||||
|[policies-notifications](https://github.com/RedHatInsights/policies-notifications ) |pytest, jinja2, aiokafka, aiohttp, asyncpg, prometheus_client, apscheduler, gino, tests, sqlalchemy, myapp, starlette_prometheus, alembic |
|
||||
|[poll-app](https://github.com/jgabriel1/poll-app ) |pymongo, dotenv, selenium, pytest, flask_testing, tests, config |
|
||||
|[ppm-telegram-bot](https://github.com/piterpy-meetup/ppm-telegram-bot ) |pip, aiogram, fastapi_security_telegram_webhook, ppm_telegram_bot |
|
||||
|[ppr](https://github.com/bcgov/ppr ) |pytest, endpoints, requests, setuptools, sentry_sdk, freezegun, config, main, repository, sqlalchemy, myapp, datedelta, pytz, alembic, dotenv, schemas, models |
|
||||
|[practical-python](https://github.com/sudharsan2020/practical-python ) |redis, celery_with_fastapi, celery |
|
||||
|[programs](https://github.com/deta/programs ) |deta, jinja2, colors, svgs, passlib |
|
||||
|[projects](https://github.com/hungd25/projects ) |sklearn, lrsgd, utils, numpy, matplotlib, nose, models_partc, src, phonenumbers |
|
||||
|[property-prediction](https://github.com/solanyn/property-prediction ) |botocore, psycopg2, pytest, catboost, sklearn, hello_world, boto3, requests |
|
||||
|[proxmox-slack-bot](https://github.com/PlenusPyramis/proxmox-slack-bot ) |slack_model, requests, slack |
|
||||
|[proxy-checker](https://github.com/infaticaio/proxy-checker ) |aiohttp, main, checker, aiosocks |
|
||||
|[py-cidemia-security](https://github.com/cidemia/py-cidemia-security ) |werkzeug, requests, setuptools, passlib, yaml, cidemiasecurity, jwt |
|
||||
|[pyBets](https://github.com/GabrielCappelli/pyBets ) |error_messages, db_models, views, sqlalchemy, application, database, app_logging, models, services, pytest |
|
||||
|[pyapi](https://github.com/srghosh29/pyapi ) |apis |
|
||||
|[pycon2020-FastAPI](https://github.com/gloriasky/pycon2020-FastAPI ) |schemas, sqlalchemy, models, api, database |
|
||||
|[pyctuator](https://github.com/SolarEdgeTech/pyctuator ) |werkzeug, multidict, requests, aiohttp, redis, tests, psutil, pyctuator, sqlalchemy, flask, pytest |
|
||||
|[pydantic-ddd-exploration](https://github.com/allgreed/pydantic-ddd-exploration ) |sqlalchemy, src |
|
||||
|[pyml](https://github.com/dockerian/pyml ) |PIL, pytest, setuptools, gevent, h5py, tests, numpy, dateutil, matplotlib, scipy, mock, connexion, yaml, google, jsonpickle, flask, ml, asgiref |
|
||||
|[pypis](https://github.com/jurelou/pypis ) |packaging, setuptools, asyncpg, loguru, pkg_resources, sqlalchemy, stdlib_list, dynaconf, pypis, httpx |
|
||||
|[python](https://github.com/jijinggang/test_python ) |requests, requests_html, unidecode, lxml, openpyxl, send_mail, download_util, formatting, settings, templates, flask |
|
||||
|[python-api-template](https://github.com/mana-ysh/python-api-template ) |pytest |
|
||||
|[python-asyncio-crud](https://github.com/aleimu/python-asyncio-crud ) |sanic, six, uvloop, muggle_ocr, redis, flask_sqlalchemy, aiomysql, sqlalchemy, aioredis, flask |
|
||||
|[python-crash-course](https://github.com/cyberinsane/python-crash-course ) |tinydb, schema, pandas, requests |
|
||||
|[python-deployment-samples](https://github.com/edisga/python-deployment-samples ) |aiohttp, webtest, django, pyramidsite, flask, pyramid, flask_cors, my_app, setuptools, other_app |
|
||||
|[python-fastapi-hex-todo](https://github.com/GArmane/python-fastapi-hex-todo ) |pytest, faker, factory, passlib, toolz, asyncpg, pytest_factoryboy, tests, sqlalchemy, myapp, alembic, dotenv, databases, jwt, todolist |
|
||||
|[python-graphql](https://github.com/tsungchih/python-graphql ) |graphene, aiohttp, loguru, ujson, psutil, graphql |
|
||||
|[python-ml-service-template](https://github.com/paramoshin/python-ml-service-template ) |yaml, joblib, starlette_prometheus |
|
||||
|[python-playground](https://github.com/tgmti/python-playground ) |flask_restful, flask, sqlalchemy |
|
||||
|[python-poetry-docker-example](https://github.com/michael0liver/python-poetry-docker-example ) |pytest |
|
||||
|[python-private-service-layout](https://github.com/U-Company/python-private-service-layout ) |fire, loguru, prometheus_client, pytest, vault_client, info, clients, tests, setuptools |
|
||||
|[python_benchmark](https://github.com/T4D3K/python_benchmark ) |fastapi_frm, locust, sqlalchemy, tortoise, falcon, gino, falcon_frm |
|
||||
|[python_common_tools](https://github.com/CheungChan/python_common_tools ) |requests, setuptools, redis, python_common_tools, paramiko, stackprinter, DBUtils, pymysql |
|
||||
|[python_fastapi](https://github.com/playwhyyza/python_fastapi ) |jwt, passlib |
|
||||
|[qa-api](https://github.com/venuraja79/qa-api ) |elasticapm, controller, haystack, config |
|
||||
|[qhub-home](https://github.com/Quansight/qhub-home ) |toml, flit, setuptools |
|
||||
|[qr-generator](https://github.com/gosha20777/qr-generator ) |qrcode |
|
||||
|[rakm](https://github.com/emanuelaguna/rakm ) |pytest, requests, aiohttp, asyncache, invoke, asyncmock, tests, dateutil, cachetools, async_asgi_testclient, async_generator, dotenv |
|
||||
|[random_projects](https://github.com/thepartisan101/random_projects ) |flask_restful, graphene, extraction, selenium, flask, flask_graphql, schema, sqlalchemy, requests |
|
||||
|[recommender_fastapi](https://github.com/gjorgjinac/recommender_fastapi ) |repository, pybreaker, helper, sqlalchemy, default_ad_listener, requests, web, defaults |
|
||||
|[recommender_server](https://github.com/gjorgjinac/recommender_server ) |repository, pybreaker, helper, sqlalchemy, default_ad_listener, requests, web, defaults |
|
||||
|[ref-final-project-backend](https://github.com/Reyvel/ref-final-project-backend ) |faust, kafka, msgpack, settings, utils |
|
||||
|[repost-fastapi](https://github.com/pckv/repost-fastapi ) |dotenv, jwt, sqlalchemy, repost, passlib |
|
||||
|[responsive-layout-part](https://github.com/lmacchiavelli/responsive-layout-part ) |PIL |
|
||||
|[rest_api_docker_mongo_aws](https://github.com/RodrigoMachado9/rest_api_docker_mongo_aws ) |pymongo, six, bottle_resource, beartype, bottle, spacy, passlib |
|
||||
|[restbot](https://github.com/cwerner/restbot ) |restbot, loguru, pytest, joblib |
|
||||
|[reternal-backend](https://github.com/d3vzer0/reternal-backend ) |pymongo, redis, flask_restful, flask_jwt_extended, sigma, workers, mongoengine, yaml, environment, database, pytz, flask_socketio, celery, flask, jwt, bson |
|
||||
|[reverse-geocoder](https://github.com/claws/reverse-geocoder ) |databases, sqlalchemy, geoalchemy2 |
|
||||
|[rjgtoys-xc](https://github.com/bobgautier/rjgtoys-xc ) |pytest, jinja2, requests, rjgtoys, apierrors, sphinx, sphinx_autodoc_typehints |
|
||||
|[rn-fastapi-app](https://github.com/cloudeyes/rn-fastapi-app ) |sample, psycopg2, fastapi_login, MySQLdb, sqlalchemy, fastalchemy, passlib |
|
||||
|[runn](https://github.com/alex-2201/runn ) |requests, passlib, tests, utils, sqlalchemy, jwt, logzero, pytest, emails |
|
||||
|[sauti-fastapi](https://github.com/taycurran/sauti-fastapi ) |dotenv, pstgres, sqlalchemy |
|
||||
|[sauti-monitor-ds-api](https://github.com/taycurran/sauti-monitor-ds-api ) |dotenv, sqlalchemy |
|
||||
|[scaling-spoon](https://github.com/bloomingmath/scaling-spoon ) |email_validator, pony, bcrypt, pytest, extensions, passlib, pymongo, selenium, blinker, main, async_asgi_testclient, dotenv, motor, schemas, routers, bson, models |
|
||||
|[sensehat-fastapi](https://github.com/cmlccie/sensehat-fastapi ) |sense_hat, senseapi |
|
||||
|[server-open-alpr](https://github.com/NSnietol/server-open-alpr ) |openalpr, loguru, src |
|
||||
|[serverless-fastapi-cdk](https://github.com/jaehyeon-kim/serverless-fastapi-cdk ) |mangum, resources, src, aws_cdk |
|
||||
|[show-tell-api](https://github.com/team-eyespace/show-tell-api ) |nltk, PIL, TensorBoardCaption, NIC, evaluate, flask, jwt, preprocessing, keras, utils |
|
||||
|[signal-cli-rest-api](https://github.com/SebastianLuebke/signal-cli-rest-api ) |pyqrcode, requests, signal_cli_rest_api |
|
||||
|[simple-kp](https://github.com/ranking-agent/simple-kp ) |aiosqlite, pytest, simple_kp, reasoner_pydantic, data, setuptools |
|
||||
|[simple-messenger](https://github.com/IvanDubrowin/simple-messenger ) |server |
|
||||
|[simple-report-data-table-vuetify](https://github.com/shizidushu/simple-report-data-table-vuetify ) |bcrypt, dotenv, mongoengine, jwt, pypandoc, bson, casbin, passlib |
|
||||
|[simpleapp-aws-fargate](https://github.com/kbaafi/simpleapp-aws-fargate ) |controllers, databases, sqlalchemy, boto3, data, config, services, settings, requests |
|
||||
|[siso-library](https://github.com/nfowl/siso-library ) |databases, sqlalchemy |
|
||||
|[sklearn-docker-api](https://github.com/crocopie/sklearn-docker-api ) |sklearn, service, joblib |
|
||||
|[slowapi](https://github.com/laurentS/slowapi ) |limits, redis, slowapi, tests, hiro, mock |
|
||||
|[smart-social-distancing](https://github.com/neuralet/smart-social-distancing ) |openvino, tflite_runtime, invoke, ui, tools, wget, scipy, libs |
|
||||
|[sms_gateway](https://github.com/arxell/sms_gateway ) |passlib, aiohttp, sentry_sdk, aiopg, sqlalchemy, myapp, grpc, google, alembic, psycopg2, jwt, click, grpclib |
|
||||
|[social-insights](https://github.com/dsc-umass/social-insights ) |sklearn, requests, joblib, nltk, tweepy, numpy, firebase_admin, twitter_credentials, scipy, spellchecker, get_tweets, google, flask, keras, xgboost, suggest |
|
||||
|[sociallyhigh](https://github.com/NikhilSharmay/sociallyhigh ) |pkg_resources, sociallyhigh |
|
||||
|[sparky](https://github.com/perfecto25/sparky ) |sparky, config |
|
||||
|[speechRecognition_api](https://github.com/X-CCS/speechRecognition_api ) |librosa, sklearn, aukit, aip, Django_jianlong, videototextapp, videoaddsubtitleapp, myapp, IPython, my_app, other_app, djcelery, django, celery, video2audio_noiseReduction, keras |
|
||||
|[start-fastapi](https://github.com/utmhikari/start-fastapi ) |model, middleware, controller, service, application, dotenv |
|
||||
|[startapp](https://github.com/marlin-dev/startapp ) |extensions, marshmallow, setuptools, passlib, flask_env, flask_jwt_extended, sentry_sdk, flask_sqlalchemy, sqlalchemy_utils, gino, sqlalchemy, flask_marshmallow, takeaway, settings, flask_migrate, starlette_prometheus, flask, app_init, core|
|
||||
|[statsfy](https://github.com/skmatz/statsfy ) |spotifier |
|
||||
|[stock-tracker](https://github.com/jgabriel1/stock-tracker ) |pymongo, aiohttp, dotenv, pytest, jwt, server, requests, passlib |
|
||||
|[stores](https://github.com/dz-experts/grocery-stores-home-delivery ) |stores, pytest, api, util |
|
||||
|[storyboard_renderer](https://github.com/Weltii/storyboard_renderer ) |render_job, layouts, bridge, statics, layout_bridge, utils |
|
||||
|[summaries](https://github.com/bradstimpson/summaries ) |nltk, newspaper, pytest, tortoise |
|
||||
|[summarize-api](https://github.com/gary23w/summarize-api ) |nltk, newspaper, pytest, tortoise |
|
||||
|[surfacescan](https://github.com/vbogretsov/surfacescan ) |networkx, pytest, faker, atomiclong, locust, surfacescan, pkg_resources, tests |
|
||||
|[sweetHome](https://github.com/zkity/sweetHome ) |serial, lib |
|
||||
|[sync_async_compare](https://github.com/deepalikumar/sync_async_compare ) |blog, faker, marshmallow, flask_sqlalchemy, sqlalchemy_utils, flask_apispec, commands, sqlalchemy, myapp, resources, settings, flask_migrate, alembic, dotenv, flask, databases, pytest |
|
||||
|[takeAction-Backend](https://github.com/AndyKChen/takeAction-Backend ) |ml_controller, googlesearch, sklearn, getURL, bs4, keras, utils, numpy |
|
||||
|[tariffengineapi](https://github.com/SofieneEnnaoui/tariffengineapi ) |memory_profiler, redis, numba, tariffs_modules, tests, api_fastapi, memory |
|
||||
|[task_manager](https://github.com/VladOsiichuk/task_manager ) |passlib, gino, sqlalchemy, myapp, alembic, jwt |
|
||||
|[taskriptor-web](https://github.com/nasuka/taskriptor-web ) |alembic, sqlalchemy, myapp |
|
||||
|[tdd-fastapi-template](https://github.com/markusntz/tdd-fastapi-template ) |fastapi_tdd_docker, pytest, tortoise |
|
||||
|[teached](https://github.com/Mohamed-Kaizen/teached ) |pytest, pendulum, requests, passlib, loguru, tortoise, teached, typer, importlib_metadata, nox, usernames, dropbox, jwt, confusable_homoglyphs, username |
|
||||
|[techsoulai_backend](https://github.com/zhanymkanov/techsoulai_backend ) |pytest, jamspell, jwt, sqlalchemy, pymystem3, passlib |
|
||||
|[temperature-web](https://github.com/sj175/temperature-web ) |board, dotenv, adafruit_dht, boto3, requests |
|
||||
|[test_fastapi](https://github.com/leblancfg/test_fastapi ) |PIL, magic, fastapi_versioning, autocrop, cv2, requests, google |
|
||||
|[test_shop](https://github.com/Ayaks7/test_shop ) |requests, passlib, peewee, user_orders, my_app, other_app, django, yoyo, catalog, auth, jwt, pytest, peewee_async, tastypie |
|
||||
|[testfastapi](https://github.com/18438655078/testfastapi ) |tortoise, boto3 |
|
||||
|[todo-list-fastapi](https://github.com/mcauto/todo-list-fastapi ) |aiofiles, pytest, jwt, sqlalchemy, src, passlib |
|
||||
|[todoapp](https://github.com/prashunchitkr/todoapp ) |alembic, jwt, sqlalchemy, myapp, passlib |
|
||||
|[trainee_mi](https://github.com/goncharov-roman/trainee_mi ) |pymongo |
|
||||
|[transactions_api](https://github.com/txemac/transactions_api ) |pytest, sqlalchemy, sqlalchemy_utils, database |
|
||||
|[transformers](https://github.com/raphtlw/transformers ) |aitextgen |
|
||||
|[translator](https://github.com/eightytwo/translator ) |translator, schemas, api, httpx |
|
||||
|[try-fast-api](https://github.com/kayshcache/try-fast-api ) |dotenv |
|
||||
|[users-service](https://github.com/falled10/users-service ) |werkzeug, pytest, oauth2client, sqlalchemy_utils, api, config, main, repository, humps, sqlalchemy, myapp, alembic, jwt, routers, authlib, services |
|
||||
|[uvicorn-gunicorn-fastapi](https://github.com/dshadow/uvicorn-gunicorn-fastapi-docker ) |test_utils, docker, requests, pytest |
|
||||
|[vollmacht](https://github.com/ogeller/vollmacht ) |fitz |
|
||||
|[volunteer-database](https://github.com/crowdsourcemedical/volunteer-database ) |alembic, pytest, jwt, sqlalchemy, myapp, passlib |
|
||||
|[wamedex2](https://github.com/gwf-uwaterloo/wamedex2 ) |pytest, pyserini, hnswlib, pkg_resources, helper, freezegun, dateparser |
|
||||
|[wazo-router-calld](https://github.com/wazo-platform/wazo-router-calld ) |wazo_router_calld, consul, click, sqlalchemy, setuptools |
|
||||
|[web-api](https://github.com/aroio/web-api ) |exceptions, yaml, auth, jwt, routers, models, data |
|
||||
|[web-avatarify](https://github.com/charlielito/web-avatarify ) |PIL, afy, skimage, moviepy, requests, imageio, tqdm, animate, sync_batchnorm, ffmpeg, torch, matplotlib, scipy, face_alignment, yaml, python_path, cv2, modules |
|
||||
|[web-frameworks-benchmark](https://github.com/kesha1225/web-frameworks-benchmark ) |muffin, sanic, pyramid, falcon, requests, bottle, aiohttp, bobo, quart, hug, japronto, request_bench, my_app, other_app, django, flask, cherrypy, kumquat, freezy |
|
||||
|[web_speedtest](https://github.com/Kulikovpavel/web_speedtest ) |flask |
|
||||
|[weblink-downloader](https://github.com/cortexin/weblink-downloader ) |passlib, asyncpg, tests, link_downloader, databases, pytest, httpx |
|
||||
|[xo_back](https://github.com/octomen/xo_back ) |pytest, api |
|
||||
|[xpublish](https://github.com/xarray-contrib/xpublish ) |xarray, pytest, cachey, xpublish, setuptools, pkg_resources, h5py, netCDF4, numcodecs, dask, zarr |
|
||||
|[xraysink](https://github.com/garyd203/xraysink ) |aiohttp, jsonpickle, xraysink, async_asgi_testclient, aws_xray_sdk, requests, pytest |
|
||||
|[yappfm-backend](https://github.com/yappfm/yappfm-backend ) |alembic, sqlalchemy, myapp, yappfm, gino |
|
||||
|[yatsm](https://github.com/danielmartins/yatsm ) |dramatiq, walrus, pytest, pendulum, passlib, apscheduler, typer, assertpy, pytz, jwt, yatsm |
|
||||
|[yumemi_intern_API](https://github.com/tkrk1209/yumemi_intern_API ) |aiohttp, sqlalchemy, myapp, google, alembic |
|
||||
|[zo](https://github.com/wazo-platform/wazo-router-calld ) |PIL, requests, ssdb, setuptools, loguru, urllib3, zo, munch |
|
||||
The instructions below assume you have [pyenv](https://github.com/pyenv/pyenv) installed.
|
||||
If you don't, use any other method to create a virtual environment
|
||||
and install Python 3.11.4.
|
||||
|
||||
## Contributing
|
||||
- Install Python 3.11.4
|
||||
|
||||
Pull requests are welcome.
|
||||
```shell
|
||||
pyenv install 3.11.4
|
||||
```
|
||||
|
||||
- Create a virtual environment
|
||||
|
||||
```shell
|
||||
pyenv virtualenv 3.11.4 awesome-fastapi-projects
|
||||
```
|
||||
|
||||
- Activate the virtual environment
|
||||
|
||||
```shell
|
||||
pyenv local awesome-fastapi-projects
|
||||
```
|
||||
|
||||
#### Install dependencies and pre-commit hooks
|
||||
|
||||
There is a `Makefile` with some useful commands to help you get started.
|
||||
For available commands, run `make help`. To install dependencies and pre-commit hooks, run:
|
||||
|
||||
```shell
|
||||
make
|
||||
```
|
||||
|
||||
#### Frontend
|
||||
|
||||
The frontend is built with [React](https://reactjs.org/) and [Next.js](https://nextjs.org/).
|
||||
It is being statically built and served on GitHub Pages: https://Kludex.github.io/awesome-fastapi-projects/
|
||||
|
||||
To run the frontend locally, you need to install [Node.js](https://nodejs.org/en/) and [pnpm](https://pnpm.io/).
|
||||
The node version is specified in the `.node-version` file.
|
||||
To easily manage the node version, you can use [fnm](https://github.com/Schniz/fnm).
|
||||
Then, run the following commands:
|
||||
|
||||
```shell
|
||||
make front
|
||||
```
|
||||
|
||||
This will install the dependencies and start the development server.
|
||||
The frontend will be available at http://localhost:3000.
|
||||
|
|
107
alembic.ini
Normal file
107
alembic.ini
Normal file
|
@ -0,0 +1,107 @@
|
|||
# A generic, single database configuration.
|
||||
[alembic]
|
||||
# path to migration scripts
|
||||
script_location = migrations
|
||||
|
||||
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
|
||||
# Uncomment the line below if you want the files to be prepended with date and time
|
||||
# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
|
||||
|
||||
# sys.path path, will be prepended to sys.path if present.
|
||||
# defaults to the current working directory.
|
||||
prepend_sys_path = .
|
||||
|
||||
# timezone to use when rendering the date within the migration file
|
||||
# as well as the filename.
|
||||
# If specified, requires the python-dateutil library that can be
|
||||
# installed by adding `alembic[tz]` to the pip requirements
|
||||
# string value is passed to dateutil.tz.gettz()
|
||||
# leave blank for localtime
|
||||
# timezone =
|
||||
|
||||
# max length of characters to apply to the
|
||||
# "slug" field
|
||||
# truncate_slug_length = 40
|
||||
|
||||
# set to 'true' to run the environment during
|
||||
# the 'revision' command, regardless of autogenerate
|
||||
# revision_environment = false
|
||||
|
||||
# set to 'true' to allow .pyc and .pyo files without
|
||||
# a source .py file to be detected as revisions in the
|
||||
# versions/ directory
|
||||
# sourceless = false
|
||||
|
||||
# version location specification; This defaults
|
||||
# to migrations/versions. When using multiple version
|
||||
# directories, initial revisions must be specified with --version-path.
|
||||
# The path separator used here should be the separator specified by "version_path_separator" below.
|
||||
# version_locations = %(here)s/bar:%(here)s/bat:migrations/versions
|
||||
|
||||
# version path separator; As mentioned above, this is the character used to split
|
||||
# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
|
||||
# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
|
||||
# Valid values for version_path_separator are:
|
||||
#
|
||||
# version_path_separator = :
|
||||
# version_path_separator = ;
|
||||
# version_path_separator = space
|
||||
version_path_separator = os # Use os.pathsep. Default configuration used for new projects.
|
||||
|
||||
# set to 'true' to search source files recursively
|
||||
# in each "version_locations" directory
|
||||
# new in Alembic version 1.10
|
||||
# recursive_version_locations = false
|
||||
|
||||
# the output encoding used when revision files
|
||||
# are written from script.py.mako
|
||||
# output_encoding = utf-8
|
||||
|
||||
sqlalchemy.url = sqlite+aiosqlite:///db.sqlite3
|
||||
|
||||
|
||||
[post_write_hooks]
|
||||
# post_write_hooks defines scripts or Python functions that are run
|
||||
# on newly generated revision scripts. See the documentation for further
|
||||
# detail and examples
|
||||
|
||||
# format using "black" - use the console_scripts runner, against the "black" entrypoint
|
||||
# hooks = black
|
||||
# black.type = console_scripts
|
||||
# black.entrypoint = black
|
||||
# black.options = -l 79 REVISION_SCRIPT_FILENAME
|
||||
|
||||
# Logging configuration
|
||||
[loggers]
|
||||
keys = root,sqlalchemy,alembic
|
||||
|
||||
[handlers]
|
||||
keys = console
|
||||
|
||||
[formatters]
|
||||
keys = generic
|
||||
|
||||
[logger_root]
|
||||
level = WARN
|
||||
handlers = console
|
||||
qualname =
|
||||
|
||||
[logger_sqlalchemy]
|
||||
level = WARN
|
||||
handlers =
|
||||
qualname = sqlalchemy.engine
|
||||
|
||||
[logger_alembic]
|
||||
level = INFO
|
||||
handlers =
|
||||
qualname = alembic
|
||||
|
||||
[handler_console]
|
||||
class = StreamHandler
|
||||
args = (sys.stderr,)
|
||||
level = NOTSET
|
||||
formatter = generic
|
||||
|
||||
[formatter_generic]
|
||||
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||
datefmt = %H:%M:%S
|
1
app/__init__.py
Normal file
1
app/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
"""A web application for indexing the projects using FastAPI."""
|
105
app/conftest.py
Normal file
105
app/conftest.py
Normal file
|
@ -0,0 +1,105 @@
|
|||
"""The application-level conftest."""
|
||||
import asyncio
|
||||
import contextlib
|
||||
from collections.abc import AsyncGenerator, Generator
|
||||
from typing import Literal
|
||||
|
||||
import pytest
|
||||
import stamina
|
||||
from dirty_equals import IsList
|
||||
from pytest_mock import MockerFixture
|
||||
from sqlalchemy.ext.asyncio import AsyncConnection, AsyncSession
|
||||
|
||||
from app.database import Dependency, Repo
|
||||
from app.factories import DependencyCreateDataFactory
|
||||
from app.source_graph.factories import SourceGraphRepoDataFactory
|
||||
from app.source_graph.models import SourceGraphRepoData
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, scope="session")
|
||||
def anyio_backend() -> Literal["asyncio"]:
|
||||
"""Use asyncio as the async backend."""
|
||||
return "asyncio"
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, scope="session")
|
||||
def _deactivate_retries() -> None:
|
||||
"""Deactivate stamina retries."""
|
||||
stamina.set_active(False)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _test_db(mocker: MockerFixture) -> None:
|
||||
"""Use the in-memory database for tests."""
|
||||
mocker.patch("app.database.DB_PATH", "")
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def event_loop(
|
||||
request: pytest.FixtureRequest,
|
||||
) -> Generator[asyncio.AbstractEventLoop, None, None]:
|
||||
"""
|
||||
Create an instance of the default event loop for a session.
|
||||
|
||||
An event loop is destroyed at the end of the test session.
|
||||
https://docs.pytest.org/en/6.2.x/fixture.html#fixture-scopes
|
||||
"""
|
||||
with contextlib.closing(loop := asyncio.get_event_loop_policy().get_event_loop()):
|
||||
yield loop
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
async def test_db_connection() -> AsyncGenerator[AsyncConnection, None]:
|
||||
"""Use the in-memory database for tests."""
|
||||
from app.database import Base, engine
|
||||
|
||||
try:
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(Base.metadata.drop_all)
|
||||
await conn.run_sync(Base.metadata.create_all)
|
||||
yield conn
|
||||
finally:
|
||||
# for AsyncEngine created in function scope, close and
|
||||
# clean-up pooled connections
|
||||
await engine.dispose()
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
async def test_db_session(
|
||||
test_db_connection: AsyncConnection,
|
||||
) -> AsyncGenerator[AsyncSession, None]:
|
||||
"""Use the in-memory database for tests."""
|
||||
from app.uow import async_session_uow
|
||||
|
||||
async with async_session_uow() as session:
|
||||
yield session
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
async def some_repos(
|
||||
test_db_session: AsyncSession,
|
||||
source_graph_repo_data_factory: SourceGraphRepoDataFactory,
|
||||
dependency_create_data_factory: DependencyCreateDataFactory,
|
||||
) -> list[Repo]:
|
||||
"""Create some repos."""
|
||||
source_graph_repos_data: list[
|
||||
SourceGraphRepoData
|
||||
] = source_graph_repo_data_factory.batch(10)
|
||||
assert source_graph_repos_data == IsList(length=10)
|
||||
repos = [
|
||||
Repo(
|
||||
url=str(source_graph_repo_data.repo_url),
|
||||
description=source_graph_repo_data.description,
|
||||
stars=source_graph_repo_data.stars,
|
||||
source_graph_repo_id=source_graph_repo_data.repo_id,
|
||||
dependencies=[
|
||||
Dependency(**dependency_create_data.model_dump())
|
||||
for dependency_create_data in dependency_create_data_factory.batch(5)
|
||||
],
|
||||
)
|
||||
for source_graph_repo_data in source_graph_repos_data
|
||||
]
|
||||
test_db_session.add_all(repos)
|
||||
await test_db_session.flush()
|
||||
await asyncio.gather(*[test_db_session.refresh(repo) for repo in repos])
|
||||
return repos
|
107
app/database.py
Normal file
107
app/database.py
Normal file
|
@ -0,0 +1,107 @@
|
|||
"""
|
||||
Database models and session.
|
||||
|
||||
The database is a SQLite database, and is stored in the root
|
||||
of the project as `db.sqlite3`.
|
||||
|
||||
The database is managed using Alembic, and migrations
|
||||
are stored in the `migrations/` directory.
|
||||
|
||||
The module defines the following models:
|
||||
|
||||
- `Repo`: A repository that is being tracked.
|
||||
- `Dependency`: A dependency of a repository.
|
||||
- `RepoDependency`: A relationship between a repository and a dependency.
|
||||
|
||||
The database is accessed asynchronously using SQLAlchemy's async API.
|
||||
"""
|
||||
from collections.abc import AsyncGenerator
|
||||
from pathlib import PurePath
|
||||
from typing import Final
|
||||
|
||||
from sqlalchemy import BigInteger, ForeignKey, MetaData, String, Text, UniqueConstraint
|
||||
from sqlalchemy.ext.asyncio import (
|
||||
AsyncAttrs,
|
||||
AsyncEngine,
|
||||
AsyncSession,
|
||||
async_sessionmaker,
|
||||
create_async_engine,
|
||||
)
|
||||
from sqlalchemy.orm import (
|
||||
Mapped,
|
||||
declarative_base,
|
||||
mapped_column,
|
||||
relationship,
|
||||
)
|
||||
|
||||
DB_PATH: Final[PurePath] = PurePath(__file__).parent.parent / "db.sqlite3"
|
||||
|
||||
SQLALCHEMY_DATABASE_URL: Final[str] = f"sqlite+aiosqlite:///{DB_PATH}"
|
||||
|
||||
engine: Final[AsyncEngine] = create_async_engine(SQLALCHEMY_DATABASE_URL)
|
||||
|
||||
async_session_maker: Final[async_sessionmaker[AsyncSession]] = async_sessionmaker(
|
||||
engine, expire_on_commit=False, autoflush=False, autocommit=False
|
||||
)
|
||||
|
||||
metadata = MetaData(
|
||||
naming_convention={
|
||||
"ix": "ix_%(table_name)s_%(column_0_N_name)s ",
|
||||
"uq": "uq_%(table_name)s_%(column_0_N_name)s ",
|
||||
"ck": "ck_%(table_name)s_%(constraint_name)s ",
|
||||
"fk": "fk_%(table_name)s_%(column_0_N_name)s_%(referred_table_name)s",
|
||||
"pk": "pk_%(table_name)s",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
|
||||
"""Get an async session."""
|
||||
async with async_session_maker() as session:
|
||||
yield session
|
||||
|
||||
|
||||
Base = declarative_base(metadata=metadata, cls=AsyncAttrs)
|
||||
|
||||
|
||||
class Repo(Base):
|
||||
"""A repository that is being tracked."""
|
||||
|
||||
__tablename__ = "repo"
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
url: Mapped[str] = mapped_column(nullable=False, unique=True)
|
||||
description: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
stars: Mapped[int] = mapped_column(BigInteger, nullable=False)
|
||||
source_graph_repo_id: Mapped[int | None] = mapped_column(
|
||||
BigInteger, nullable=True, unique=True
|
||||
)
|
||||
dependencies: Mapped[list["Dependency"]] = relationship(
|
||||
"Dependency", secondary="repo_dependency", back_populates="repos"
|
||||
)
|
||||
last_checked_revision: Mapped[str | None] = mapped_column(
|
||||
String(255), nullable=True
|
||||
)
|
||||
__table_args__ = (UniqueConstraint("url", "source_graph_repo_id"),)
|
||||
|
||||
|
||||
class Dependency(Base):
|
||||
"""A dependency of a repository."""
|
||||
|
||||
__tablename__ = "dependency"
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
name: Mapped[str] = mapped_column(String(255), nullable=False, unique=True)
|
||||
repos: Mapped[list["Repo"]] = relationship(
|
||||
"Repo", secondary="repo_dependency", back_populates="dependencies"
|
||||
)
|
||||
|
||||
|
||||
class RepoDependency(Base):
|
||||
"""A relationship between a repository and a dependency."""
|
||||
|
||||
__tablename__ = "repo_dependency"
|
||||
repo_id: Mapped[int] = mapped_column(
|
||||
ForeignKey(Repo.id, ondelete="CASCADE"), primary_key=True
|
||||
)
|
||||
dependency_id: Mapped[int] = mapped_column(
|
||||
ForeignKey(Dependency.id, ondelete="CASCADE"), primary_key=True
|
||||
)
|
149
app/dependencies.py
Normal file
149
app/dependencies.py
Normal file
|
@ -0,0 +1,149 @@
|
|||
"""Dependencies parsing."""
|
||||
import asyncio
|
||||
import subprocess
|
||||
from collections.abc import Sequence
|
||||
|
||||
import aiofiles.tempfile
|
||||
import stamina
|
||||
from loguru import logger
|
||||
|
||||
from app.database import Repo
|
||||
from app.models import DependencyCreateData
|
||||
from app.types import RevisionHash
|
||||
|
||||
|
||||
async def run_command(*cmd: str, cwd: str | None = None) -> str:
|
||||
"""
|
||||
Run the given command in a subprocess and return the stdout as plain text.
|
||||
|
||||
:param cmd: The command to run.
|
||||
:param cwd: The working directory to run the command in.
|
||||
:return: The stdout result
|
||||
"""
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
*cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
cwd=cwd,
|
||||
)
|
||||
|
||||
stdout, stderr = await process.communicate()
|
||||
|
||||
if process.returncode != 0:
|
||||
raise RuntimeError(
|
||||
f"Command '{cmd}' failed with exit code '{process.returncode}':\n"
|
||||
f"[stdout]: '{stdout.decode()}'\n"
|
||||
f"[stderr]: '{stderr.decode()}'"
|
||||
)
|
||||
|
||||
return stdout.decode()
|
||||
|
||||
|
||||
async def acquire_dependencies_data_for_repository(
|
||||
repo: Repo,
|
||||
) -> tuple[RevisionHash, list[DependencyCreateData]]:
|
||||
"""
|
||||
Acquire dependencies for the given repository.
|
||||
|
||||
The function will use the "third-party-imports" tool to
|
||||
parse the third-party dependencies of the repository.
|
||||
|
||||
Since this tool has been written in Rust and is basically
|
||||
a CLI tool, the parsing will happen is a subprocess.
|
||||
|
||||
:param repo: A repository for which to return the dependencies.
|
||||
:return: The dependencies data required to create the dependencies in the DB.
|
||||
"""
|
||||
logger.info(
|
||||
"Acquiring the dependencies data for the repo with id {repo_id}.",
|
||||
repo_id=repo.id,
|
||||
enqueue=True,
|
||||
)
|
||||
async with aiofiles.tempfile.TemporaryDirectory() as directory:
|
||||
# Clone the repository
|
||||
logger.info(
|
||||
"Cloning the repo with id {repo_id} into the directory {directory}.",
|
||||
repo_id=repo.id,
|
||||
directory=directory,
|
||||
enqueue=True,
|
||||
)
|
||||
await run_command(
|
||||
"git",
|
||||
"clone",
|
||||
"--depth",
|
||||
"1",
|
||||
repo.url,
|
||||
directory,
|
||||
)
|
||||
|
||||
# Get the latest commit hash
|
||||
logger.info(
|
||||
"Getting the latest commit hash for the repo with id {repo_id}.",
|
||||
repo_id=repo.id,
|
||||
enqueue=True,
|
||||
)
|
||||
revision: str = await run_command(
|
||||
"git",
|
||||
"rev-parse",
|
||||
"HEAD",
|
||||
cwd=directory,
|
||||
)
|
||||
|
||||
if repo.last_checked_revision == revision:
|
||||
# Assume there are no new dependencies to return
|
||||
# since all the repo dependencies have already
|
||||
# been parsed.
|
||||
logger.info(
|
||||
"The repo with id {repo_id} has already been updated.",
|
||||
repo_id=repo.id,
|
||||
enqueue=True,
|
||||
)
|
||||
return RevisionHash(revision), []
|
||||
|
||||
# Parse the dependencies
|
||||
async for attempt in stamina.retry_context(on=RuntimeError, attempts=3):
|
||||
with attempt:
|
||||
logger.info(
|
||||
"Parsing the dependencies for the repo with id {repo_id}.",
|
||||
repo_id=repo.id,
|
||||
enqueue=True,
|
||||
)
|
||||
dependencies: str = await run_command(
|
||||
"third-party-imports",
|
||||
directory,
|
||||
)
|
||||
if dependencies:
|
||||
logger.info(
|
||||
"Successfully parsed the dependencies for the repo with id {repo_id}.",
|
||||
repo_id=repo.id,
|
||||
enqueue=True,
|
||||
)
|
||||
# Split the dependencies by new line
|
||||
dependencies_list: Sequence[str] = dependencies.split("\n")
|
||||
# Drop the first two lines (the info lines)
|
||||
dependencies_list = (
|
||||
dependencies_list[2:] if len(dependencies_list) > 2 else []
|
||||
)
|
||||
logger.info(
|
||||
"Found {count} dependencies for the repo with id {repo_id}.",
|
||||
count=len(dependencies_list),
|
||||
repo_id=repo.id,
|
||||
enqueue=True,
|
||||
)
|
||||
else:
|
||||
logger.info(
|
||||
"No dependencies found for the repo with id {repo_id}.",
|
||||
repo_id=repo.id,
|
||||
enqueue=True,
|
||||
)
|
||||
dependencies_list = []
|
||||
return (
|
||||
RevisionHash(revision),
|
||||
[
|
||||
DependencyCreateData(
|
||||
name=dependency.strip(),
|
||||
)
|
||||
for dependency in dependencies_list
|
||||
if dependency.strip()
|
||||
],
|
||||
)
|
12
app/factories.py
Normal file
12
app/factories.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
"""Factories for creating models for testing."""
|
||||
from polyfactory.factories.pydantic_factory import ModelFactory
|
||||
from polyfactory.pytest_plugin import register_fixture
|
||||
|
||||
from app.models import DependencyCreateData
|
||||
|
||||
|
||||
@register_fixture
|
||||
class DependencyCreateDataFactory(ModelFactory[DependencyCreateData]):
|
||||
"""Factory for creating DependencyCreateData."""
|
||||
|
||||
__model__ = DependencyCreateData
|
104
app/index.py
Normal file
104
app/index.py
Normal file
|
@ -0,0 +1,104 @@
|
|||
"""
|
||||
Create repos and dependencies indexes.
|
||||
|
||||
This script creates can create two indexes:
|
||||
|
||||
- ``repos_index.json``: Contains all the repositories and their dependencies.
|
||||
- ``dependencies_index.json``: Contains all the dependencies and the
|
||||
repositories that depend on them.
|
||||
|
||||
The indexes are used by the frontend to display the data and perform searches.
|
||||
"""
|
||||
import asyncio
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Final
|
||||
|
||||
import aiofiles
|
||||
import sqlalchemy.orm
|
||||
import typer
|
||||
|
||||
from app.database import Dependency, Repo
|
||||
from app.models import DependencyDetail, RepoDetail
|
||||
from app.uow import async_session_uow
|
||||
|
||||
#: The path to the repos index file.
|
||||
REPOS_INDEX_PATH: Final[Path] = Path(__file__).parent.parent / "repos_index.json"
|
||||
#: The path to the dependencies index file.
|
||||
DEPENDENCIES_INDEX_PATH: Final[Path] = (
|
||||
Path(__file__).parent.parent / "dependencies_index.json"
|
||||
)
|
||||
|
||||
app = typer.Typer()
|
||||
|
||||
|
||||
async def create_repos_index() -> None:
|
||||
"""
|
||||
Create repos_index.json file from database.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
async with async_session_uow() as session, aiofiles.open(
|
||||
REPOS_INDEX_PATH, "w"
|
||||
) as index_file:
|
||||
await index_file.write(
|
||||
json.dumps(
|
||||
{
|
||||
"repos": [
|
||||
RepoDetail.model_validate(repo).model_dump()
|
||||
async for repo in (
|
||||
await session.stream_scalars(
|
||||
sqlalchemy.select(Repo)
|
||||
.order_by(Repo.id)
|
||||
.options(sqlalchemy.orm.selectinload(Repo.dependencies))
|
||||
)
|
||||
)
|
||||
],
|
||||
},
|
||||
indent=4,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def create_dependencies_index() -> None:
|
||||
"""
|
||||
Create dependencies_index.json file from database.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
async with async_session_uow() as session, aiofiles.open(
|
||||
DEPENDENCIES_INDEX_PATH, "w"
|
||||
) as index_file:
|
||||
dependencies = [
|
||||
DependencyDetail.model_validate(dependency).model_dump()
|
||||
async for dependency in (
|
||||
await session.stream_scalars(
|
||||
sqlalchemy.select(Dependency).order_by(Dependency.id)
|
||||
)
|
||||
)
|
||||
if dependency.name
|
||||
]
|
||||
await index_file.write(
|
||||
json.dumps(
|
||||
{
|
||||
"dependencies": dependencies,
|
||||
},
|
||||
indent=4,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@app.command()
|
||||
def index_repos() -> None:
|
||||
"""Create ``repos_index.json``."""
|
||||
asyncio.run(create_repos_index())
|
||||
|
||||
|
||||
@app.command()
|
||||
def index_dependencies() -> None:
|
||||
"""Create ``dependencies_index.json``."""
|
||||
asyncio.run(create_dependencies_index())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app()
|
38
app/models.py
Normal file
38
app/models.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
"""Module contains the models for the application."""
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, NonNegativeInt
|
||||
|
||||
from app.types import DependencyId, RepoId, RevisionHash
|
||||
|
||||
|
||||
class DependencyCreateData(BaseModel):
|
||||
"""A dependency of a repository."""
|
||||
|
||||
name: str
|
||||
|
||||
|
||||
class DependencyDetail(BaseModel):
|
||||
"""A dependency of a repository."""
|
||||
|
||||
model_config = ConfigDict(
|
||||
from_attributes=True,
|
||||
)
|
||||
|
||||
id: DependencyId
|
||||
name: str
|
||||
|
||||
|
||||
class RepoDetail(BaseModel):
|
||||
"""A repository that is being tracked."""
|
||||
|
||||
model_config = ConfigDict(
|
||||
from_attributes=True,
|
||||
)
|
||||
|
||||
id: RepoId
|
||||
url: str
|
||||
description: str
|
||||
stars: NonNegativeInt
|
||||
source_graph_repo_id: int
|
||||
dependencies: list[DependencyDetail]
|
||||
last_checked_revision: RevisionHash | None
|
243
app/scrape.py
Normal file
243
app/scrape.py
Normal file
|
@ -0,0 +1,243 @@
|
|||
"""The logic for scraping the source graph data processing it."""
|
||||
import asyncio
|
||||
|
||||
import sqlalchemy.dialects.sqlite
|
||||
import typer
|
||||
from loguru import logger
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.database import Dependency, Repo, RepoDependency
|
||||
from app.dependencies import acquire_dependencies_data_for_repository
|
||||
from app.source_graph.client import AsyncSourceGraphSSEClient
|
||||
from app.source_graph.mapper import create_or_update_repos_from_source_graph_repos_data
|
||||
from app.types import RepoId
|
||||
from app.uow import async_session_uow
|
||||
|
||||
|
||||
async def _create_dependencies_for_repo(session: AsyncSession, repo: Repo) -> None:
|
||||
"""
|
||||
Create dependencies for a repo.
|
||||
|
||||
For each parsed dependency, creates a new record in the database, if such a
|
||||
dependency does not exist.
|
||||
Then, assigns the dependencies to the given repo.
|
||||
|
||||
:param session: An asynchronous session object
|
||||
:param repo: A repo for which to create and assign the dependencies
|
||||
"""
|
||||
# Acquire the dependencies data for the repo
|
||||
logger.info(
|
||||
"Acquiring the dependencies data for the repo with id {repo_id}.",
|
||||
repo_id=repo.id,
|
||||
enqueue=True,
|
||||
)
|
||||
try:
|
||||
(
|
||||
revision,
|
||||
dependencies_create_data,
|
||||
) = await acquire_dependencies_data_for_repository(repo)
|
||||
except RuntimeError:
|
||||
# If the parsing fails,
|
||||
# just skip creating the dependencies
|
||||
logger.error(
|
||||
"Failed to acquire the dependencies data for the repo with id {repo_id}.",
|
||||
repo_id=repo.id,
|
||||
enqueue=True,
|
||||
)
|
||||
return
|
||||
if repo.last_checked_revision == revision:
|
||||
# If the repo has already been updated,
|
||||
# just skip creating the dependencies
|
||||
logger.info(
|
||||
"The repo with id {repo_id} has fresh dependencies.",
|
||||
repo_id=repo.id,
|
||||
enqueue=True,
|
||||
)
|
||||
return
|
||||
if not dependencies_create_data:
|
||||
# If there are no dependencies,
|
||||
# just skip creating the dependencies
|
||||
logger.info(
|
||||
"The repo with id {repo_id} has no dependencies.",
|
||||
repo_id=repo.id,
|
||||
enqueue=True,
|
||||
)
|
||||
return
|
||||
# Update the repo with the revision hash
|
||||
logger.info(
|
||||
"Updating the repo with id {repo_id} with the revision hash {revision}.",
|
||||
repo_id=repo.id,
|
||||
revision=revision,
|
||||
enqueue=True,
|
||||
)
|
||||
update_repo_statement = (
|
||||
sqlalchemy.update(Repo)
|
||||
.where(Repo.id == repo.id)
|
||||
.values(last_checked_revision=revision)
|
||||
)
|
||||
await session.execute(update_repo_statement)
|
||||
# Create dependencies - on conflict do nothing.
|
||||
# This is to avoid creating duplicate dependencies.
|
||||
logger.info(
|
||||
"Creating the dependencies for the repo with id {repo_id}.",
|
||||
repo_id=repo.id,
|
||||
enqueue=True,
|
||||
)
|
||||
insert_dependencies_statement = sqlalchemy.dialects.sqlite.insert(
|
||||
Dependency
|
||||
).on_conflict_do_nothing(index_elements=[Dependency.name])
|
||||
await session.execute(
|
||||
insert_dependencies_statement.returning(Dependency),
|
||||
[
|
||||
{
|
||||
"name": dependency_data.name,
|
||||
}
|
||||
for dependency_data in dependencies_create_data
|
||||
],
|
||||
)
|
||||
# Re-fetch the dependencies from the database
|
||||
dependencies = (
|
||||
await session.scalars(
|
||||
sqlalchemy.select(Dependency).where(
|
||||
Dependency.name.in_(
|
||||
[
|
||||
dependency_data.name
|
||||
for dependency_data in dependencies_create_data
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
).all()
|
||||
# Add the dependencies to the repo
|
||||
insert_repo_dependencies_statement = sqlalchemy.dialects.sqlite.insert(
|
||||
RepoDependency
|
||||
).on_conflict_do_nothing([RepoDependency.repo_id, RepoDependency.dependency_id])
|
||||
await session.execute(
|
||||
insert_repo_dependencies_statement,
|
||||
[
|
||||
{
|
||||
"repo_id": repo.id,
|
||||
"dependency_id": dependency.id,
|
||||
}
|
||||
for dependency in dependencies
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
async def scrape_source_graph_repos() -> None:
|
||||
"""
|
||||
Iterate over the source graph repos and create or update them in the database.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
async with AsyncSourceGraphSSEClient() as sg_client:
|
||||
async with async_session_uow() as session:
|
||||
async with asyncio.TaskGroup() as tg:
|
||||
logger.info(
|
||||
"Creating or updating repos from source graph repos data.",
|
||||
enqueue=True,
|
||||
)
|
||||
async for sg_repos_data in sg_client.aiter_fastapi_repos():
|
||||
logger.info(
|
||||
"Received {count} repos.",
|
||||
count=len(sg_repos_data),
|
||||
enqueue=True,
|
||||
)
|
||||
tg.create_task(
|
||||
create_or_update_repos_from_source_graph_repos_data(
|
||||
session=session,
|
||||
source_graph_repos_data=sg_repos_data,
|
||||
)
|
||||
)
|
||||
await session.commit()
|
||||
|
||||
|
||||
async def parse_dependencies_for_repo(
|
||||
semaphore: asyncio.Semaphore, repo_id: RepoId
|
||||
) -> None:
|
||||
"""
|
||||
Parse the dependencies for a given repo and create them in the database.
|
||||
|
||||
:param semaphore: A semaphore to limit the number of concurrent requests
|
||||
:param repo_id: The id of the repo for which to parse the dependencies
|
||||
:return: None
|
||||
"""
|
||||
async with async_session_uow() as session, semaphore:
|
||||
# Fetch the repo from the database
|
||||
logger.info(
|
||||
"Fetching the repo with id {repo_id}.", repo_id=repo_id, enqueue=True
|
||||
)
|
||||
repo = (
|
||||
await session.scalars(sqlalchemy.select(Repo).where(Repo.id == repo_id))
|
||||
).one()
|
||||
# Create the dependencies for the repo
|
||||
logger.info(
|
||||
"Creating the dependencies for the repo with id {repo_id}.",
|
||||
repo_id=repo_id,
|
||||
enqueue=True,
|
||||
)
|
||||
await _create_dependencies_for_repo(session=session, repo=repo)
|
||||
await session.commit()
|
||||
|
||||
|
||||
async def parse_dependencies_for_repos() -> None:
|
||||
"""
|
||||
Parse the dependencies for all the repos in the database.
|
||||
|
||||
:return: None.
|
||||
"""
|
||||
logger.info("Fetching the repos from the database.", enqueue=True)
|
||||
async with async_session_uow() as session:
|
||||
repo_ids = (
|
||||
await session.scalars(
|
||||
sqlalchemy.select(Repo.id).order_by(
|
||||
Repo.last_checked_revision.is_(None).desc()
|
||||
)
|
||||
)
|
||||
).all()
|
||||
logger.info("Fetched {count} repos.", count=len(repo_ids), enqueue=True)
|
||||
logger.info("Parsing the dependencies for the repos.", enqueue=True)
|
||||
semaphore = asyncio.Semaphore(10)
|
||||
async with asyncio.TaskGroup() as tg:
|
||||
for repo_id in repo_ids:
|
||||
logger.info(
|
||||
"Parsing the dependencies for repo {repo_id}.",
|
||||
repo_id=repo_id,
|
||||
enqueue=True,
|
||||
)
|
||||
tg.create_task(
|
||||
parse_dependencies_for_repo(
|
||||
semaphore=semaphore, repo_id=RepoId(repo_id)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
app = typer.Typer()
|
||||
|
||||
|
||||
@app.command()
|
||||
def scrape_repos() -> None:
|
||||
"""
|
||||
Scrape the FastAPI-related repositories utilizing the source graph API.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
logger.info("Scraping the source graph repos.", enqueue=True)
|
||||
asyncio.run(scrape_source_graph_repos())
|
||||
|
||||
|
||||
@app.command()
|
||||
def parse_dependencies() -> None:
|
||||
"""
|
||||
Parse the dependencies for all the repos in the database.
|
||||
|
||||
:return: None.
|
||||
"""
|
||||
logger.info(
|
||||
"Parsing the dependencies for all the repos in the database.", enqueue=True
|
||||
)
|
||||
asyncio.run(parse_dependencies_for_repos())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app()
|
1
app/source_graph/__init__.py
Normal file
1
app/source_graph/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
"""Scraping module for the application."""
|
123
app/source_graph/client.py
Normal file
123
app/source_graph/client.py
Normal file
|
@ -0,0 +1,123 @@
|
|||
"""The client for the SourceGraph API."""
|
||||
import asyncio
|
||||
from collections.abc import AsyncGenerator, Mapping, MutableMapping
|
||||
from contextlib import asynccontextmanager
|
||||
from datetime import timedelta
|
||||
from types import TracebackType
|
||||
from typing import Any, Final, Self
|
||||
from urllib.parse import quote
|
||||
|
||||
import httpx
|
||||
import stamina
|
||||
from httpx_sse import EventSource, ServerSentEvent, aconnect_sse
|
||||
from loguru import logger
|
||||
|
||||
from app.source_graph.models import SourceGraphRepoData, SourceGraphRepoDataListAdapter
|
||||
|
||||
#: The URL of the SourceGraph SSE API.
|
||||
SOURCE_GRAPH_STREAM_API_URL: Final[str] = "https://sourcegraph.com/.api/search/stream"
|
||||
|
||||
|
||||
#: The query parameters for the SourceGraph SSE API.
|
||||
FASTAPI_REPOS_QUERY_PARAMS: Final[Mapping[str, str]] = {
|
||||
"q": quote(
|
||||
" ".join(
|
||||
[
|
||||
"repo:has.content(from fastapi import FastApi)",
|
||||
"type:repo",
|
||||
"visibility:public",
|
||||
"archived:no",
|
||||
"fork:no",
|
||||
]
|
||||
)
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class AsyncSourceGraphSSEClient:
|
||||
"""
|
||||
A client for the SourceGraph SSE API.
|
||||
|
||||
To learn more about the underlying API, see the ``SourceGraph SSE API``
|
||||
https://docs.sourcegraph.com/api/stream_api#sourcegraph-stream-api
|
||||
"""
|
||||
|
||||
def __init__(self: Self) -> None:
|
||||
"""Initialize the client."""
|
||||
self._last_event_id: str | None = None
|
||||
self._reconnection_delay: float = 0.0
|
||||
self._aclient: httpx.AsyncClient = httpx.AsyncClient()
|
||||
|
||||
async def __aenter__(self: Self) -> Self:
|
||||
"""Enter the async context manager."""
|
||||
await self._aclient.__aenter__()
|
||||
return self
|
||||
|
||||
async def __aexit__(
|
||||
self: Self,
|
||||
exc_type: type[BaseException] | None = None,
|
||||
exc_val: BaseException | None = None,
|
||||
exc_tb: TracebackType | None = None,
|
||||
) -> None:
|
||||
"""Exit the async context manager."""
|
||||
return await self._aclient.__aexit__(exc_type, exc_val, exc_tb)
|
||||
|
||||
@asynccontextmanager
|
||||
async def _aconnect_sse(
|
||||
self: Self, **kwargs: MutableMapping[str, Any]
|
||||
) -> AsyncGenerator[EventSource, None]:
|
||||
"""Connect to the SourceGraph SSE API."""
|
||||
headers = kwargs.pop("headers", {})
|
||||
if self._last_event_id is not None:
|
||||
headers["Last-Event-ID"] = self._last_event_id
|
||||
async with aconnect_sse(
|
||||
client=self._aclient,
|
||||
url=str(SOURCE_GRAPH_STREAM_API_URL),
|
||||
method="GET",
|
||||
headers=headers,
|
||||
**kwargs,
|
||||
) as event_source:
|
||||
yield event_source
|
||||
|
||||
async def _aiter_sse(
|
||||
self: Self, **kwargs: MutableMapping[str, Any]
|
||||
) -> AsyncGenerator[ServerSentEvent, None]:
|
||||
"""Iterate over the SourceGraph SSE API."""
|
||||
async with self._aconnect_sse(**kwargs) as event_source:
|
||||
async for event in event_source.aiter_sse():
|
||||
yield event
|
||||
|
||||
async def _aiter_sse_with_retries(
|
||||
self: Self, **kwargs: MutableMapping[str, Any]
|
||||
) -> AsyncGenerator[ServerSentEvent, None]:
|
||||
"""Iterate over the SourceGraph SSE API with retries."""
|
||||
async for attempt in stamina.retry_context(
|
||||
on=(httpx.ReadError, httpx.ReadTimeout)
|
||||
):
|
||||
with attempt:
|
||||
await asyncio.sleep(self._reconnection_delay)
|
||||
async for event in self._aiter_sse(**kwargs):
|
||||
self._last_event_id = event.id
|
||||
if event.retry is not None:
|
||||
logger.error(
|
||||
"Received a retry event from the SourceGraph SSE API. "
|
||||
"Schedule a reconnection in {retry} milliseconds.",
|
||||
retry=event.retry,
|
||||
enqueue=True,
|
||||
)
|
||||
self._reconnection_delay = timedelta(
|
||||
milliseconds=event.retry
|
||||
).total_seconds()
|
||||
else:
|
||||
self._reconnection_delay = 0.0
|
||||
yield event
|
||||
|
||||
async def aiter_fastapi_repos(
|
||||
self: Self,
|
||||
) -> AsyncGenerator[list[SourceGraphRepoData], None]:
|
||||
"""Iterate over the SourceGraph SSE API with retries."""
|
||||
async for event in self._aiter_sse_with_retries(
|
||||
params=dict(FASTAPI_REPOS_QUERY_PARAMS)
|
||||
):
|
||||
if event.event == "matches":
|
||||
yield SourceGraphRepoDataListAdapter.validate_python(event.json())
|
12
app/source_graph/factories.py
Normal file
12
app/source_graph/factories.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
"""Factories for creating test data."""
|
||||
from polyfactory.factories.pydantic_factory import ModelFactory
|
||||
from polyfactory.pytest_plugin import register_fixture
|
||||
|
||||
from app.source_graph.models import SourceGraphRepoData
|
||||
|
||||
|
||||
@register_fixture
|
||||
class SourceGraphRepoDataFactory(ModelFactory[SourceGraphRepoData]):
|
||||
"""Factory for creating RepoCreateData."""
|
||||
|
||||
__model__ = SourceGraphRepoData
|
46
app/source_graph/mapper.py
Normal file
46
app/source_graph/mapper.py
Normal file
|
@ -0,0 +1,46 @@
|
|||
"""Mapper for source graph models to the database objects."""
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy.dialects.sqlite
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app import database
|
||||
from app.source_graph.models import SourceGraphRepoData
|
||||
|
||||
|
||||
async def create_or_update_repos_from_source_graph_repos_data(
|
||||
session: AsyncSession, source_graph_repos_data: Sequence[SourceGraphRepoData]
|
||||
) -> Sequence[database.Repo]:
|
||||
"""
|
||||
Create repos from source graph repos data.
|
||||
|
||||
If any repos already exist, update them.
|
||||
|
||||
:param session: The database session.
|
||||
:param source_graph_repos_data: The source graph repos data.
|
||||
"""
|
||||
insert_statement = sqlalchemy.dialects.sqlite.insert(database.Repo)
|
||||
update_statement = insert_statement.on_conflict_do_update(
|
||||
index_elements=[database.Repo.source_graph_repo_id],
|
||||
set_={
|
||||
"url": insert_statement.excluded.url,
|
||||
"description": insert_statement.excluded.description,
|
||||
"stars": insert_statement.excluded.stars,
|
||||
"source_graph_repo_id": insert_statement.excluded.source_graph_repo_id,
|
||||
},
|
||||
)
|
||||
|
||||
return (
|
||||
await session.scalars(
|
||||
update_statement.returning(database.Repo),
|
||||
[
|
||||
{
|
||||
"url": str(repo_data.repo_url),
|
||||
"description": repo_data.description,
|
||||
"stars": repo_data.stars,
|
||||
"source_graph_repo_id": repo_data.repo_id,
|
||||
}
|
||||
for repo_data in source_graph_repos_data
|
||||
],
|
||||
)
|
||||
).all()
|
39
app/source_graph/models.py
Normal file
39
app/source_graph/models.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
"""The models for the Source Graph data."""
|
||||
import datetime
|
||||
from typing import Literal, NewType, Self
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
Field,
|
||||
HttpUrl,
|
||||
NonNegativeInt,
|
||||
TypeAdapter,
|
||||
computed_field,
|
||||
)
|
||||
|
||||
#: The ID of a repository from the SourceGraph API.
|
||||
SourceGraphRepoId = NewType("SourceGraphRepoId", int)
|
||||
|
||||
|
||||
class SourceGraphRepoData(BaseModel):
|
||||
"""The data of a repository."""
|
||||
|
||||
type: Literal["repo"]
|
||||
repo_id: SourceGraphRepoId = Field(..., alias="repositoryID")
|
||||
repo_handle: str = Field(..., alias="repository")
|
||||
stars: NonNegativeInt = Field(..., alias="repoStars")
|
||||
last_fetched_at: datetime.datetime = Field(..., alias="repoLastFetched")
|
||||
description: str = Field(default="")
|
||||
|
||||
@computed_field # type: ignore[misc]
|
||||
@property
|
||||
def repo_url(self: Self) -> HttpUrl:
|
||||
"""The URL of the repository."""
|
||||
return TypeAdapter(HttpUrl).validate_python(f"https://{self.repo_handle}")
|
||||
|
||||
|
||||
#: The type adapter for the SourceGraphRepoData.
|
||||
SourceGraphRepoDataAdapter = TypeAdapter(SourceGraphRepoData)
|
||||
|
||||
#: The type adapter for the SourceGraphRepoData list.
|
||||
SourceGraphRepoDataListAdapter = TypeAdapter(list[SourceGraphRepoData])
|
1
app/source_graph/tests/__init__.py
Normal file
1
app/source_graph/tests/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
"""Test the scraping of the SourceGraph API."""
|
120
app/source_graph/tests/test_client.py
Normal file
120
app/source_graph/tests/test_client.py
Normal file
|
@ -0,0 +1,120 @@
|
|||
"""Test the client module for the source graph."""
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
from dirty_equals import HasLen, IsDatetime, IsInstance, IsPositiveInt
|
||||
from pydantic import Json, TypeAdapter
|
||||
|
||||
from app.source_graph.models import SourceGraphRepoData
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def source_graph_matched_repos_data() -> Json[Any]:
|
||||
"""Return the sample data of the matched repositories."""
|
||||
return [
|
||||
{
|
||||
"type": "repo",
|
||||
"repositoryID": 55636527,
|
||||
"repository": "github.com/tiangolo/sqlmodel",
|
||||
"repoStars": 10277,
|
||||
"repoLastFetched": "2023-07-31T18:47:22.875731Z",
|
||||
"description": (
|
||||
"SQL databases in Python, designed "
|
||||
"for simplicity, compatibility, "
|
||||
"and robustness."
|
||||
),
|
||||
"metadata": {
|
||||
"fastapi": "null",
|
||||
"json": "null",
|
||||
"json-schema": "null",
|
||||
"pydantic": "null",
|
||||
"python": "null",
|
||||
"sql": "null",
|
||||
"sqlalchemy": "null",
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "repo",
|
||||
"repositoryID": 59434622,
|
||||
"repository": "github.com/reflex-dev/reflex",
|
||||
"repoStars": 10061,
|
||||
"repoLastFetched": "2023-07-31T08:58:42.692906Z",
|
||||
"description": "(Previously Pynecone) 🕸 Web apps in pure Python 🐍",
|
||||
},
|
||||
{
|
||||
"type": "repo",
|
||||
"repositoryID": 42982149,
|
||||
"repository": "github.com/PaddlePaddle/PaddleNLP",
|
||||
"repoStars": 9804,
|
||||
"repoLastFetched": "2023-07-31T16:48:08.839209Z",
|
||||
"description": (
|
||||
"👑 Easy-to-use and powerful NLP library with 🤗 "
|
||||
"Awesome model zoo, supporting wide-range of NLP tasks "
|
||||
"from research to industrial applications, including"
|
||||
" 🗂Text Classification, 🔍 Neural Search, ❓ Question "
|
||||
"Answering, ℹ️ Information Extraction, "
|
||||
"📄 Document Intelligence, 💌 Sentiment Analysis etc."
|
||||
),
|
||||
"metadata": {
|
||||
"bert": "null",
|
||||
"embedding": "null",
|
||||
"ernie": "null",
|
||||
"information-extraction": "null",
|
||||
"neural-search": "null",
|
||||
"nlp": "null",
|
||||
"paddlenlp": "null",
|
||||
"pretrained-models": "null",
|
||||
"question-answering": "null",
|
||||
"search-engine": "null",
|
||||
"semantic-analysis": "null",
|
||||
"sentiment-analysis": "null",
|
||||
"seq2seq": "null",
|
||||
"transformer": "null",
|
||||
"transformers": "null",
|
||||
"uie": "null",
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "repo",
|
||||
"repositoryID": 36246068,
|
||||
"repository": "github.com/realpython/materials",
|
||||
"repoStars": 4359,
|
||||
"repoLastFetched": "2023-07-31T05:15:16.993896Z",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def test_source_graph_repo_data(source_graph_matched_repos_data: Json[Any]) -> None:
|
||||
"""Test the SourceGraphRepoData deserialization."""
|
||||
assert source_graph_matched_repos_data == HasLen(4)
|
||||
_SourceGraphRepoDataListValidator = TypeAdapter(list[SourceGraphRepoData])
|
||||
repos_parsed = _SourceGraphRepoDataListValidator.validate_python(
|
||||
source_graph_matched_repos_data
|
||||
)
|
||||
assert repos_parsed == HasLen(4)
|
||||
assert all(repo == IsInstance[SourceGraphRepoData] for repo in repos_parsed)
|
||||
assert all(
|
||||
repo.repo_id == repo_data["repositoryID"]
|
||||
for repo, repo_data in zip(
|
||||
repos_parsed, source_graph_matched_repos_data, strict=True
|
||||
)
|
||||
)
|
||||
assert all(
|
||||
repo.repo_handle == repo_data["repository"]
|
||||
for repo, repo_data in zip(
|
||||
repos_parsed, source_graph_matched_repos_data, strict=True
|
||||
)
|
||||
)
|
||||
assert all(
|
||||
repo.stars == IsPositiveInt and repo.stars == repo_data["repoStars"]
|
||||
for repo, repo_data in zip(
|
||||
repos_parsed, source_graph_matched_repos_data, strict=True
|
||||
)
|
||||
)
|
||||
assert all(
|
||||
str(repo.repo_url) == f"https://{repo_data['repository']}"
|
||||
for repo, repo_data in zip(
|
||||
repos_parsed, source_graph_matched_repos_data, strict=True
|
||||
)
|
||||
)
|
||||
assert all(repo.last_fetched_at == IsDatetime for repo in repos_parsed)
|
65
app/source_graph/tests/test_mapper.py
Normal file
65
app/source_graph/tests/test_mapper.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
"""The tests for the source graph mapper to the database objects."""
|
||||
|
||||
import pytest
|
||||
import sqlalchemy
|
||||
from dirty_equals import IsInstance, IsList
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app import database
|
||||
from app.source_graph.factories import SourceGraphRepoDataFactory
|
||||
from app.source_graph.mapper import create_or_update_repos_from_source_graph_repos_data
|
||||
from app.source_graph.models import SourceGraphRepoData
|
||||
|
||||
pytestmark = pytest.mark.anyio
|
||||
|
||||
|
||||
async def test_create_or_update_repos_from_source_graph_repos_data(
|
||||
test_db_session: AsyncSession,
|
||||
source_graph_repo_data_factory: SourceGraphRepoDataFactory,
|
||||
) -> None:
|
||||
"""Test creating repos from source graph repos data."""
|
||||
source_graph_repo_data: list[
|
||||
SourceGraphRepoData
|
||||
] = source_graph_repo_data_factory.batch(5)
|
||||
repos = await create_or_update_repos_from_source_graph_repos_data(
|
||||
test_db_session, source_graph_repo_data
|
||||
)
|
||||
assert repos == IsList(length=5)
|
||||
assert all(repo == IsInstance[database.Repo] for repo in repos)
|
||||
assert all(repo.id is not None for repo in repos)
|
||||
|
||||
|
||||
async def test_create_or_update_repos_from_source_graph_repos_data_update(
|
||||
some_repos: list[database.Repo],
|
||||
test_db_session: AsyncSession,
|
||||
source_graph_repo_data_factory: SourceGraphRepoDataFactory,
|
||||
) -> None:
|
||||
"""Test updating repos from source graph repos data."""
|
||||
assert (
|
||||
await test_db_session.execute(
|
||||
sqlalchemy.select(sqlalchemy.func.count(database.Repo.id))
|
||||
)
|
||||
).scalar() == len(some_repos)
|
||||
source_graph_repos_data: list[
|
||||
SourceGraphRepoData
|
||||
] = source_graph_repo_data_factory.batch(len(some_repos))
|
||||
source_graph_repos_data = [
|
||||
SourceGraphRepoData(
|
||||
**(
|
||||
repo_data.model_dump(by_alias=True)
|
||||
| {"repositoryID": repo.source_graph_repo_id}
|
||||
)
|
||||
)
|
||||
for repo, repo_data in zip(some_repos, source_graph_repos_data, strict=True)
|
||||
]
|
||||
repos = await create_or_update_repos_from_source_graph_repos_data(
|
||||
test_db_session, source_graph_repos_data
|
||||
)
|
||||
assert repos == IsList(length=len(some_repos))
|
||||
assert all(repo == IsInstance[database.Repo] for repo in repos)
|
||||
assert all(repo.id is not None for repo in repos)
|
||||
assert (
|
||||
await test_db_session.execute(
|
||||
sqlalchemy.select(sqlalchemy.func.count(database.Repo.id))
|
||||
)
|
||||
).scalar() == len(some_repos)
|
1
app/tests/__init__.py
Normal file
1
app/tests/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
"""The application tests."""
|
103
app/tests/test_database.py
Normal file
103
app/tests/test_database.py
Normal file
|
@ -0,0 +1,103 @@
|
|||
"""Test the operations on the database models."""
|
||||
import pytest
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy.orm
|
||||
from dirty_equals import IsList
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app import database
|
||||
from app.factories import DependencyCreateDataFactory
|
||||
from app.models import DependencyCreateData
|
||||
from app.source_graph.factories import SourceGraphRepoDataFactory
|
||||
from app.source_graph.models import SourceGraphRepoData
|
||||
|
||||
pytestmark = pytest.mark.anyio
|
||||
|
||||
|
||||
def _assert_repo_properties(
|
||||
repo: database.Repo, source_graph_repo_data: SourceGraphRepoData
|
||||
) -> bool:
|
||||
"""Assert that the repo has the expected properties."""
|
||||
assert repo.id is not None
|
||||
assert repo.url == str(source_graph_repo_data.repo_url)
|
||||
assert repo.description == source_graph_repo_data.description
|
||||
assert repo.stars == source_graph_repo_data.stars
|
||||
assert repo.source_graph_repo_id == source_graph_repo_data.repo_id
|
||||
return True
|
||||
|
||||
|
||||
async def test_create_repo_no_dependencies(
|
||||
test_db_session: AsyncSession,
|
||||
source_graph_repo_data_factory: SourceGraphRepoDataFactory,
|
||||
) -> None:
|
||||
"""Test creating a repo."""
|
||||
source_graph_repo_data: SourceGraphRepoData = source_graph_repo_data_factory.build()
|
||||
repo = database.Repo(
|
||||
url=str(source_graph_repo_data.repo_url),
|
||||
description=source_graph_repo_data.description,
|
||||
stars=source_graph_repo_data.stars,
|
||||
source_graph_repo_id=source_graph_repo_data.repo_id,
|
||||
)
|
||||
test_db_session.add(repo)
|
||||
await test_db_session.flush()
|
||||
await test_db_session.refresh(repo)
|
||||
_assert_repo_properties(repo, source_graph_repo_data)
|
||||
assert (await repo.awaitable_attrs.dependencies) == IsList(length=0)
|
||||
|
||||
|
||||
async def test_create_repo_with_dependencies(
|
||||
test_db_session: AsyncSession,
|
||||
source_graph_repo_data_factory: SourceGraphRepoDataFactory,
|
||||
dependency_create_data_factory: DependencyCreateDataFactory,
|
||||
) -> None:
|
||||
"""Test creating a repo with dependencies."""
|
||||
source_graph_repo_data: SourceGraphRepoData = source_graph_repo_data_factory.build()
|
||||
dependencies_create_data: list[
|
||||
DependencyCreateData
|
||||
] = dependency_create_data_factory.batch(5)
|
||||
repo = database.Repo(
|
||||
url=str(source_graph_repo_data.repo_url),
|
||||
description=source_graph_repo_data.description,
|
||||
stars=source_graph_repo_data.stars,
|
||||
source_graph_repo_id=source_graph_repo_data.repo_id,
|
||||
dependencies=[
|
||||
database.Dependency(**dependency_create_data.model_dump())
|
||||
for dependency_create_data in dependencies_create_data
|
||||
],
|
||||
)
|
||||
test_db_session.add(repo)
|
||||
await test_db_session.flush()
|
||||
await test_db_session.refresh(repo)
|
||||
_assert_repo_properties(repo, source_graph_repo_data)
|
||||
repo_dependencies = await repo.awaitable_attrs.dependencies
|
||||
assert repo_dependencies == IsList(length=5)
|
||||
assert all(
|
||||
repo_dependency.name == dependency.name
|
||||
for repo_dependency, dependency in zip(
|
||||
repo_dependencies, dependencies_create_data, strict=True
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def test_list_repositories(
|
||||
test_db_session: AsyncSession,
|
||||
some_repos: list[database.Repo],
|
||||
) -> None:
|
||||
"""Test listing repositories."""
|
||||
repos_from_db_result = await test_db_session.execute(
|
||||
sa.select(database.Repo).options(
|
||||
sqlalchemy.orm.joinedload(database.Repo.dependencies)
|
||||
)
|
||||
)
|
||||
repos_from_db = repos_from_db_result.scalars().unique().all()
|
||||
assert repos_from_db == IsList(length=10)
|
||||
assert all(
|
||||
repo.id == repo_data.id
|
||||
and all(
|
||||
repo_dependency.name == dependency.name
|
||||
for repo_dependency, dependency in zip(
|
||||
repo.dependencies, repo_data.dependencies, strict=True
|
||||
)
|
||||
)
|
||||
for repo, repo_data in zip(repos_from_db, some_repos, strict=True)
|
||||
)
|
6
app/types.py
Normal file
6
app/types.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
"""Type definitions for the application."""
|
||||
from typing import NewType
|
||||
|
||||
RepoId = NewType("RepoId", int)
|
||||
DependencyId = NewType("DependencyId", int)
|
||||
RevisionHash = NewType("RevisionHash", str)
|
27
app/uow.py
Normal file
27
app/uow.py
Normal file
|
@ -0,0 +1,27 @@
|
|||
"""
|
||||
The Unit of Work pattern implementation.
|
||||
|
||||
To learn more about the UoW, see:
|
||||
https://www.cosmicpython.com/book/chapter_06_uow.html
|
||||
"""
|
||||
from collections.abc import AsyncGenerator
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.database import async_session_maker
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def async_session_uow() -> AsyncGenerator[AsyncSession, None]:
|
||||
"""
|
||||
Provide a transactional scope around a series of operations.
|
||||
|
||||
:return: a UoW instance
|
||||
"""
|
||||
async with async_session_maker() as session:
|
||||
async with session.begin():
|
||||
try:
|
||||
yield session
|
||||
finally:
|
||||
await session.rollback()
|
BIN
db.sqlite3
Normal file
BIN
db.sqlite3
Normal file
Binary file not shown.
19180
dependencies_index.json
Normal file
19180
dependencies_index.json
Normal file
File diff suppressed because it is too large
Load Diff
533
dependency.md
533
dependency.md
|
@ -1,533 +0,0 @@
|
|||
| Project | Dependencies |
|
||||
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|[2020_slowdelivery_take](https://github.com/zekaio/2020_slowdelivery_take ) |aiohttp, pytest, tortoise |
|
||||
|[AI-Service](https://github.com/eHelply/AI-Service ) |kombu, setuptools, pymongo, redis, sentry_asgi, sentry_sdk, pymlconf, socketio, src |
|
||||
|[African_Wildlife_Classifier](https://github.com/peterbacalso/African_Wildlife_Classifier ) |aiohttp, fastai, torch |
|
||||
|[Auth-Service](https://github.com/eHelply/Auth-Service ) |kombu, requests, setuptools, passlib, pymongo, redis, sentry_asgi, sentry_sdk, pymlconf, jwt, socketio, src, bson |
|
||||
|[BenchmarkRoundOne](https://github.com/peterdeme/BenchmarkRoundOne ) |aiohttp |
|
||||
|[Benchmarker](https://github.com/vutran1710/Benchmarker ) |apis |
|
||||
|[Blog](https://github.com/elbruno/Blog ) |face_recognition, imutils, PIL, flask, cv2 |
|
||||
|[CCTO](https://github.com/SnarferX/CCTO ) |sets, SimpleHTTPSErver, requests, dircache, ovm_lib, jarray, pip, httplib, urllib3, SockerServer, ansible, hello, java, ovmclient |
|
||||
|[CFE_30D.PY](https://github.com/torredefarol24/CFE_30D.PY ) |formatting, flask, scrape, logger, requests, pandas, download_util, requests_html |
|
||||
|[COVID-19API](https://github.com/zeroday0619/COVID-19API ) |aiohttp, ujson, async_lru, API, scrapy, fastapi_plugins, aioredis, bs4 |
|
||||
|[COVID-QA](https://github.com/deepset-ai/COVID-QA ) |elasticapm, eval, sentence_transformers, sklearn, elasticsearch, tqdm, scrapy, requests, backend, nltk, haystack, preprocess, farm, langid, datasources, torch, tfidf_train, covid_nlp |
|
||||
|[CUCM-Phone-Info](https://github.com/jsteinberg1/CUCM-Phone-Info ) |rq, cryptography, zeep, requests, redis, lxml, lib, apscheduler, api, OpenSSL, sqlalchemy, jwt, bs4 |
|
||||
|[Chatbot](https://github.com/aramakus/Chatbot ) |nltk, chatterbot, sklearn, requests, tqdm, chatbot |
|
||||
|[ChatiChatou](https://github.com/auredentan/ChatiChatou ) |chat, aioredis |
|
||||
|[CheckersAI](https://github.com/HuiiBuh/CheckersAI ) |pytest, aiohttp, game, api, conftest, checkers |
|
||||
|[Cloud_APS1](https://github.com/ehrhardt98/Cloud_APS1 ) |pymongo, requests |
|
||||
|[CryptoViz](https://github.com/MohanVashist1/CryptoViz ) |bs4, requests, pyti, pymongo, lib, binance, dateutil, cryptocalculator, channelfinder, dotenv, flask, boto3, click, fastapi_users |
|
||||
|[DS-API](https://github.com/BW-Post-Here-06-2020/DS-API ) |dotenv, praw |
|
||||
|[Daft](https://github.com/mpalka31/DaftAcademyCourse ) |main, queries, schemas, pytest, jose, database, crud |
|
||||
|[Daims_FastAPI](https://github.com/GeorgeMiao219/Daims_FastAPI ) |auth, dns, dataset, config, url, utils, database |
|
||||
|[Data-Engineering](https://github.com/De-Dash/Data-Engineering ) |nltk, aiohttp, api |
|
||||
|[Docker-Builder](https://github.com/Javier162380/Docker-Builder ) |rq, pytest, endpoints, requests, redis, builder, docker, settings, docker_builder, models, worker, middlewares, router |
|
||||
|[DockerImageResNet50](https://github.com/OrenLeung/DockerImageResNet50 ) |redis, PIL, locust, keras |
|
||||
|[Downotifier](https://github.com/herbzhao/Downotifier ) |twilio, bs4, requests, URLChecker, TwilioWhatsapp |
|
||||
|[ExcelTools](https://github.com/xx2life/ExcelTools ) |motor, databases |
|
||||
|[ExpenseAPI](https://github.com/MushroomMaula/ExpenseAPI ) |pytest, faker, sqlalchemy, myapp, fastapi_login, alembic, dotenv |
|
||||
|[FAAS](https://github.com/jacksonhenry3/FAAS ) |flask |
|
||||
|[Fast-API-Starter](https://github.com/khaman1/Fast-API-Starter ) |shared, urls |
|
||||
|[Fast-Project](https://github.com/alex-2201/Fast-Project ) |redis, conn, cassandra, apis, pymysql, logzero, models, utils, middlewares |
|
||||
|[FastAPI-Auth](https://github.com/Madpilot0/FastAPI-Auth ) |bcrypt, lib, routes, yaml, jwt, MySQLdb |
|
||||
|[FastAPI-CRUD](https://github.com/Mr-Manna/FastAPI-CRUD ) |databases, sqlalchemy, post, config |
|
||||
|[FastAPI-Demo](https://github.com/JPMike/FastAPI-Demo ) |requests |
|
||||
|[FastAPI-Example](https://github.com/gauravgola96/FastAPI-Example ) |botocore, aiobotocore, dotenv, s3_events, boto3, utils |
|
||||
|[FastAPI-Learning-Example](https://github.com/oinsd/FastAPI-Learning-Example ) |main, main_b, jwt, routers, sqlalchemy, database, passlib |
|
||||
|[FastAPI-React-PostgreSQL_Full-Stack](https://github.com/scionoftech/FastAPI-React-PostgreSQL_Full-Stack ) |db, setuptools, logs, passlib, utils, sqlalchemy, conf, routes, auth, jwt |
|
||||
|[FastAPITutorial](https://github.com/HeywoodKing/FastAPITutorial ) |invoke, config |
|
||||
|[FastAPI_Tortoise_template](https://github.com/GoddessEyes/FastAPI_Tortoise_template ) |celery, jwt, tortoise, template, passlib, emails |
|
||||
|[FastAPI_Vue](https://github.com/quietking0312/FastAPI_Vue ) |pymongo, apps |
|
||||
|[FastAPI_app1](https://github.com/Sai-Vamsi-Ogety/FastAPI_app1 ) |spacy |
|
||||
|[FastAPI_proto](https://github.com/bartomo/FastAPI_proto ) |controllers, urls, sqlalchemy, db, models |
|
||||
|[FastAdmin](https://github.com/CoderCharm/FastAdmin ) |passlib, loguru, sqlalchemy, myapp, jose, alembic |
|
||||
|[Fastapi](https://github.com/xyz25/Fastapi ) |router_app, sqlalchemy |
|
||||
|[FastapiLearn](https://github.com/xuqm/FastapiLearn ) |part4, part11, part7, part8, part15, part2, part10, application, part14, part12, part9, part13, part3, part16, part1, part5, part6 |
|
||||
|[Fusion-Vision](https://github.com/sdhnshu/Fusion-Vision ) |wandb, pytest, sklearn, requests, tqdm, torch, text_classification |
|
||||
|[GET_POST](https://github.com/mitsumushibayama/GET_POST ) |MySQLdb |
|
||||
|[GraphQLAPI](https://github.com/webjunkie01/GraphQLAPI ) |graphene, cryptacular, pytest, graphene_sqlalchemy, sqlalchemy |
|
||||
|[Gringotts](https://github.com/cning112/Gringotts ) |werkzeug, requests, flask_restplus, typing_extensions, apis, flask_cors, libs, flask, routers, models |
|
||||
|[HjwzwApi](https://github.com/k1dave6412/HjwzwApi ) |backend, requests, aiohttp, asyncpg, loguru, sqlalchemy, myapp, alembic, databases, bs4 |
|
||||
|[Image-Diff-back](https://github.com/filipe-guerra/Image-Diff-back ) |imutils, skimage, imageDiff, cv2 |
|
||||
|[Item-Xref](https://github.com/Roselingit/Item-Xref ) |psycopg2 |
|
||||
|[KOARCH](https://github.com/janstrohschein/KOARCH ) |classes, emotion_recognition_master, pydash, sklearn, requests, tweepy, scipy, kafka, yaml, psycopg2, emotion_predictor, avro, keras |
|
||||
|[Logging-Service](https://github.com/eHelply/Logging-Service ) |kombu, setuptools, pymongo, redis, sentry_asgi, sentry_sdk, pymlconf, socketio, src |
|
||||
|[MLServer](https://github.com/SeldonIO/MLServer ) |orjson, grpc, click, pytest, mlserver, google, setuptools |
|
||||
|[MallAPI](https://github.com/CoderCharm/MallAPI ) |alembic, loguru, sqlalchemy, extensions, setting, myapp, api, utils |
|
||||
|[MealCare](https://github.com/hack4impact-mcgill/MealCare ) |pony, passlib, config, tests, sqlalchemy, psycopg2, jwt |
|
||||
|[MetodoClasificacionTexto](https://github.com/talend-Erick/MetodoClasificacionTexto ) |gensim, nltk, spacy, src, pandas |
|
||||
|[MyChild](https://github.com/YunusovSamat/MyChild ) |passlib, asyncpg, tortoise, sqlalchemy, alembic, databases, jwt |
|
||||
|[MyNotes](https://github.com/wq758511990/MyNotes ) |serial, werkzeug, flask_sqlalchemy, flask_httpauth, sqlalchemy, wtforms, pytz, webapp, flask, itsdangerous |
|
||||
|[NIMSPARQL](https://github.com/Ningensei848/NIMSPARQL ) |elasticapm, sqlalchemy, elasticsearch, db, api |
|
||||
|[NT118](https://github.com/maivanhung33/NT118 ) |request, kombu, common, elasticsearch, geopy, requests, injector, pymongo, redis, consul, boltons, cachetools, controller, elasticsearch_dsl, sqlalchemy, service, pytz, response |
|
||||
|[News-Summarizer](https://github.com/CYBINT-IN/News-Summarizer ) |scipy, sentence_transformers, flask, sklearn, torch, requests, transformers, pyLDAvis, bs4 |
|
||||
|[Notification-Service](https://github.com/eHelply/Notification-Service ) |kombu, setuptools, pymongo, redis, sentry_asgi, sentry_sdk, pymlconf, socketio, src |
|
||||
|[Pycharm](https://github.com/godori004/Pycharm ) |urllib3, flask_restful, flask, src, pandas, google |
|
||||
|[Python-example](https://github.com/hzjsea/Python-example ) |netmiko, static_files, requests, basedb, sqlalchemy, ansible, zabbix_info |
|
||||
|[Python3Test](https://github.com/hjslovehr/Python3Test ) |requests, redis, openpyxl, sqlhelper, pymssql, md5_helper, flask, helpers, bs4 |
|
||||
|[PythonFastApi](https://github.com/kbeaugrand/PythonFastApi ) |win32service, win32event, servicemanager, win32serviceutil, yaml |
|
||||
|[RemoteVP-Npc](https://github.com/viccom/RemoteVP-Npc ) |win32serviceutil, serial, requests, apps, pythoncom, cores, helper, ping3, bases, hbmqtt, conf, Crypto, wmi |
|
||||
|[RestFramework](https://github.com/rakesh4real/RestFramework ) |django, streamapp, rest_framework, other_app, jwt, senddata, my_app, passlib |
|
||||
|[SOA](https://github.com/FisnikL/SOA ) |prometheus_client, sqlalchemy, sqlalchemy_utils, database |
|
||||
|[Semester-Stats](https://github.com/Rushyanth111/Semester-Stats ) |playhouse, faker, setuptools, peewee, mailmerge, docx |
|
||||
|[Service-Template](https://github.com/eHelply/Service-Template ) |kombu, setuptools, pymongo, redis, sentry_asgi, sentry_sdk, pymlconf, socketio, src |
|
||||
|[SpotifyAPI](https://github.com/dixneuf19/SpotifyAPI ) |spotipy, dotenv, src |
|
||||
|[StanleyIpkiss](https://github.com/JimmiBram/StanleyIpkiss ) |tests, sibase |
|
||||
|[Studium-ds](https://github.com/Lambda-School-Labs/Studium-ds ) |retrieve_definition, gauge_plot, requests, calendar_heatmap, inflect |
|
||||
|[TT_COVID19_API_site_and_emailer](https://github.com/VSpectrum/TT_COVID19_API_site_and_emailer ) |email_validator, requests, config, emailer |
|
||||
|[TaskXenter](https://github.com/CoolSpring8/TaskXenter ) |celery, sentry_sdk, loguru, databases, jwt, tortoise, passlib, httpx |
|
||||
|[Telegram-BusBot-DataManager](https://github.com/David-Lor/Telegram-BusBot-DataManager ) |pymongo, motor, busbot_data_manager |
|
||||
|[WAM_server_API_template](https://github.com/rl-institut/WAM_server_API_template ) |flask_app, celery, flask, pytest, worker, sphinx_rtd_theme |
|
||||
|[WEB](https://github.com/maivanhung33/WEB ) |request, kombu, common, elasticsearch, geopy, requests, injector, pymongo, redis, consul, boltons, cachetools, controller, elasticsearch_dsl, sqlalchemy, service, pytz, response |
|
||||
|[WFM](https://github.com/unegade/WFM ) |pika, aio_pika, BSSAPI, locust |
|
||||
|[WebApp-PhoneClassifier](https://github.com/minh-dg/WebApp-PhoneClassifier ) |fastai |
|
||||
|[WitnessMe](https://github.com/byt3bl33d3r/WitnessMe ) |xmltodict, imgcat, pytest, pyppeteer, jinja2, terminaltables, prompt_toolkit, pkg_resources, aiosqlite, yaml, witnessme |
|
||||
|[Words_record](https://github.com/LMFrank/Words_record ) |databases, pytest, pymysql, sqlalchemy, requests |
|
||||
|[Workspace](https://github.com/AYCHSPACE/Workspace ) |tqdm, jupyter_core, setuptools, tornado, crontab, pip, psutil, IPython, git, notebook |
|
||||
|[YoutubeBackup](https://github.com/FratStar/YoutubeBackup ) |google_auth_oauthlib, zeep, googleapiclient, spyne, google |
|
||||
|[Zulu](https://github.com/OSS-team-zulu/Zulu ) |requests, passlib, pymongo, geojson, tests, zulu, dynaconf, jwt, bson, pytest |
|
||||
|[ai-dungeon](https://github.com/AntonNeld/ai-dungeon ) |errors, test_utils, typing_extensions, pytest, models, api, dungeon |
|
||||
|[aioprometheus](https://github.com/claws/aioprometheus ) |setuptools, aiohttp, alabaster, quart, psutil, asynctest, sphinx, aioprometheus, quantile, prometheus_metrics_proto |
|
||||
|[aita](https://github.com/logan-connolly/aita ) |pytest, requests, tqdm, aiohttp, asyncpg, aita, loguru, orm, typer, sqlalchemy, databases, praw |
|
||||
|[alda-online](https://github.com/JesseChua94/alda-online ) |models, alda |
|
||||
|[alg_interface_fastapi_project](https://github.com/wuzaipei/alg_interface_fastapi_project ) |system_file, pymysql, sqlalchemy, data_model |
|
||||
|[alice-yamaha-skill](https://github.com/toshka/alice-yamaha-skill ) |exceptions, auth, yaml, capabilities, routers, rxv, config, httpx |
|
||||
|[allay-ds](https://github.com/Lambda-School-Labs/allay-ds ) |wandb, spacy, sklearn, requests, scrapy, process_data, fastapi_app, tensorflow, numpy, indeed, en_core_web_sm, dotenv |
|
||||
|[allure_reporter](https://github.com/penguinlav/allure_reporter ) |jinja2, requests, allure_reporter, loguru, aiofiles, async_generator, pytest |
|
||||
|[andrewhou-zonar](https://github.com/amhou/andrewhou-zonar ) |main, requests, util |
|
||||
|[apex-tracker](https://github.com/Timzan/apex-tracker ) |routers, requests, config |
|
||||
|[api](https://github.com/wuzaipei/alg_interface_fastapi_project ) |pyazo_api, alembic, dotenv, jwt, sqlalchemy, passlib |
|
||||
|[api-ocr](https://github.com/dzakyputra/api-ocr ) |tesserocr, PIL, cv2, modules |
|
||||
|[api-poc](https://github.com/igorfarias30/fastapi-poc ) |sklearn |
|
||||
|[api.authentication](https://github.com/jrhuerta/api.authentication ) |api_authentication |
|
||||
|[api_works](https://github.com/omrylcn/api_works ) |PIL, requests |
|
||||
|[apiestas](https://github.com/franloza/apiestas ) |crawling, scrapy, fuzzywuzzy, loguru, slugify, api, js2xml, dateparser, typer, twisted, pytz, motor |
|
||||
|[apitoolbox](https://github.com/zuarbase/apitoolbox ) |pytest, setuptools, passlib, sqlalchemy_filters, tests, sqlalchemy, ordered_uuid, tzlocal, pytz, jwt, apitoolbox, itsdangerous |
|
||||
|[arcas](https://github.com/maialogic/arcas ) |graphene, snapshottest |
|
||||
|[asgi-server-timing-middleware](https://github.com/sm-Fifteen/asgi-server-timing-middleware ) |yappi, asgi_server_timing, setuptools |
|
||||
|[asu-schedule](https://github.com/skhortiuk/asu-schedule ) |aiohttp, src, validators, bs4 |
|
||||
|[async_django_session](https://github.com/imbolc/async_django_session ) |setuptools, aiohttp, asyncpg, async_django_session, cfg, django, databases |
|
||||
|[authal-poc](https://github.com/michal-nakoneczny/authal-poc ) |authal, asynctest, pytest, bson, requests, tests, httpx |
|
||||
|[auto_populate](https://github.com/NealWhitlock/auto_populate ) |psycopg2, getter |
|
||||
|[autoloadtest](https://github.com/hkiang01/autoloadtest ) |locust, hypothesis |
|
||||
|[automagic_hashtag](https://github.com/MarianMalvin/automagic_hashtag ) |utils, google |
|
||||
|[b0mb3r](https://github.com/crinny/b0mb3r ) |setuptools, sentry_sdk, loguru, pkg_resources, b0mb3r, phonenumbers, click, httpx |
|
||||
|[b2blue-challenge](https://github.com/lfvilella/b2blue-challenge ) |pydantic_sqlalchemy, sqlalchemy |
|
||||
|[b3ta](https://github.com/nm17/b3ta ) |setuptools, sentry_sdk, loguru, pkg_resources, service, b0mb3r, phonenumbers, click, httpx |
|
||||
|[backend](https://github.com/ElusiveSpirit/fbd-backend ) |asynctest, transliterate |
|
||||
|[backend-skeleton](https://github.com/dmontagu/backend-skeleton ) |pytest, requests, typing_extensions, sqlalchemy_utils, tests, sqlalchemy, myapp, alembic, tenacity |
|
||||
|[balikobot_tmp_2](https://github.com/Horac-Bouthon/balikobot_tmp_2 ) |logger_wrapper, databases, schemas, sqlalchemy, conf, routers, sql_factory |
|
||||
|[base-fastapi-postgresql](https://github.com/alldevic/base-fastapi-postgresql ) |alembic, arq, sqlalchemy, sqlalchemy_utils, gino |
|
||||
|[basic-python-postgis-docker](https://github.com/stephenhillier/basic-python-postgis-docker ) |alembic, tenacity, sqlalchemy, myapp |
|
||||
|[bc_rates](https://github.com/MRobalinho/bc_rates ) |bc |
|
||||
|[belajar-fastapi](https://github.com/aansubarkah/belajar-fastapi ) |yfinance, sqlalchemy, models, config, tutorial, database |
|
||||
|[bench-rest-api-frameworks](https://github.com/py4mac/bench-rest-api-frameworks ) |sanic, locust, aiohttp, tornado, api, main, core |
|
||||
|[biomedical-computing-project-backend](https://github.com/SinaKhalili/biomedical-computing-project-backend ) |extra, xlrd, CoronaAnalysis |
|
||||
|[blog](https://github.com/deagle-z/blog ) |blog, loguru, jwt, sqlalchemy |
|
||||
|[blog-posts](https://github.com/tiangolo/blog-posts ) |pytest, sqlalchemy, jose, passlib |
|
||||
|[bluesky](https://github.com/paulorodriguesxv/bluesky ) |pytest, passlib, aiohttp, decouple, sqlalchemy_utils, sqlalchemy, myapp, alembic, jwt |
|
||||
|[boilerplate](https://github.com/pmcarlos/fast-api-boilerplate ) |utils, config |
|
||||
|[book-graphics](https://github.com/kamartem/book-graphics ) |alembic, sentry_sdk, asyncpg, loguru, sqlalchemy, gino |
|
||||
|[bplustogether](https://github.com/wborbajr/bplustogether ) |dynaconf, databases, loguru, sqlalchemy, bpapi |
|
||||
|[bracket_install](https://github.com/mobjack/bracket_install ) |six, flask, requests, colorama |
|
||||
|[bread-app](https://github.com/aidan-mcbride/bread-app ) |pyArango, pytest, jwt, routers, api, tests, passlib |
|
||||
|[bridgeapp](https://github.com/jasujm/bridgeapp ) |click, orjson, bridgeapp, click_log, pytest, zmq |
|
||||
|[budget-blocks-ds](https://github.com/Lambda-School-Labs/budget-blocks-ds ) |DB, geopy, transactionhist, mainhelp, census, main, psycopg2, dotenv, routers |
|
||||
|[bullshit-website](https://github.com/kaylynn234/bullshit-website ) |corporate_bullshit |
|
||||
|[burriking](https://github.com/txemac/burriking ) |infrastructure, dependency_injector, werkzeug, requests, pymongo, freezegun, tests, config, main, user_interface, application, database, flask, bson, pytest, flasgger, domain |
|
||||
|[bw3_attempt](https://github.com/worldwidekatie/bw3_attempt ) |dotenv, sklearn, requests, joblib |
|
||||
|[c4p2n](https://github.com/piterpy-meetup/c4p2n ) |c4p2n, fastapi_security_typeform, notion, ppm_telegram_bot_client |
|
||||
|[cah-v2](https://github.com/Mdellit110/cah-v2 ) |graphene, schema, graphene_sqlalchemy, sqlalchemy, models, config, schema_blackcards, database, schema_whitecards |
|
||||
|[calculator](https://github.com/ch0c01ate/calculator ) |pymongo, calculator, requests, dbsetup |
|
||||
|[canvote](https://github.com/alvintangz/canvote ) |jinja2, requests, passlib, sqlalchemy_pagination, humps, sqlalchemy, myapp, pytz, alembic, secure, jwt |
|
||||
|[cashflow](https://github.com/VSpectrum/cashflow ) |pytest, config |
|
||||
|[ccrbackend](https://github.com/danieldominguete/ccrbackend ) |six, dotenv, dynamorm, mangum, src, jose, marshmallow |
|
||||
|[challenge](https://github.com/Creditas/challenge ) |iris_classifier, sklearn |
|
||||
|[chapushillo](https://github.com/maxipavlovic/chapushillo ) |pusher, dotenv, pusher_client |
|
||||
|[checkmarx](https://github.com/zetkin/checkmarx ) |scanner, checkmarx, pytest, pyzbar, setuptools |
|
||||
|[chinook_fastapi](https://github.com/sandervandorsten/chinook_fastapi ) |dotenv, setuptools, chinook_fastapi |
|
||||
|[cloudrun-fastapi](https://github.com/anthcor/cloudrun-fastapi ) |pytest, actions, passlib, sqlalchemy_utils, config, tests, main, sqlalchemy, myapp, database, google, alembic, schemas, jwt, routers, gunicorn_config, models |
|
||||
|[cnapp-fastapi](https://github.com/cnapp/cnapp-fastapi ) |sanic, daiquiri, jinja2, prometheus_client, cnapps, starlette_prometheus |
|
||||
|[code_example_fastapi](https://github.com/d-tamirlan/code_example_fastapi ) |tortoise, src, envparse, fastapi_users |
|
||||
|[codewars-watchdog](https://github.com/uriyyo/codewars-watchdog ) |git |
|
||||
|[coding-challenges](https://github.com/fjemi/coding-challenges ) |password_generator, tqdm, jsbeautifier, dateutil, matplotlib, flask_api, word_square_check, flask |
|
||||
|[concurrency](https://github.com/Jorricks/concurrency ) |PIL, setuptools, redis, dataclasses_json, src, asgiref, click |
|
||||
|[contact-form](https://github.com/k4ssyi/contact-form ) |dotenv, controllers, urls, settings, utils, request_body |
|
||||
|[cookiecutter-python](https://github.com/ValentinCalomme/cookiecutter-python ) |typer |
|
||||
|[cookiecutter-simcore-py-fastapi](https://github.com/pcrespov/cookiecutter-simcore-py-fastapi ) |cryptography, pytest, servicelib, setuptools, simcore_service_catalog, aiohttp, pkg_resources, attr, ptvsd, aiopg, sqlalchemy, tenacity, yarl, httpx |
|
||||
|[coronavirus-tg-api](https://github.com/egbakou/coronavirus-tg-api ) |aiohttp, invoke, asyncmock, dateparser, async_asgi_testclient, async_generator, dotenv, pytest, bs4 |
|
||||
|[coursera-ibm-ai-workflow-submission](https://github.com/eightBEC/coursera-ibm-ai-workflow-submission ) |loguru, sklearn, pytest, joblib |
|
||||
|[coursework](https://github.com/algorithm-ssau/coursework ) |dotenv, databases, motor |
|
||||
|[covid-19-Chile](https://github.com/RentadroneCL/covid-19-Chile ) |orator, dotenv, routes, pendulum, covid_19_chile, config, database |
|
||||
|[covid-19-dashboard-api](https://github.com/zeshuaro/covid-19-dashboard-api ) |sortedcontainers, dotenv, jwt, pycountry, requests, google, passlib |
|
||||
|[covid19-id](https://github.com/anzharip/covid19-id ) |selenium, requests |
|
||||
|[covidfaq](https://github.com/dialoguemd/covidfaq ) |pytest, spacy, sklearn, tqdm, requests, covidfaq, selenium, bert_reranker, structlog, spacy_langdetect, yaml, html2text, boto3, torch, transformers, bs4 |
|
||||
|[covidsage](https://github.com/chancey-gardener/covidsage ) |spacy, data_loader, requests, backends, nlu_client, numpy, matplotlib, plotter, rasa, rasa_sdk, pymysql, discourse |
|
||||
|[cpython3-fastapi](https://github.com/phongln/cpython3-fastapi ) |blog, admin |
|
||||
|[custom-actions-app-python](https://github.com/Frameio/custom-actions-app-python ) |pytest, requests, custom_actions |
|
||||
|[daftacademy-python-levelup](https://github.com/holdenkold/daftacademy-python-levelup ) |main, pytest |
|
||||
|[daiei-backend](https://github.com/a-nakajima-at-shokuryu/daiei-backend ) |fastapi_server, dotenv, models, utils, dateutil, settings |
|
||||
|[daizu-online-judge-backend](https://github.com/SoyBeansLab/daizu-online-judge-backend ) |psycopg2, infrastructure, marshmallow_dataclass, interface, marshmallow, domain |
|
||||
|[dask-remote](https://github.com/octoenergy/dask-remote ) |distributed, typing_extensions, pytest, requests, dask_remote, setuptools |
|
||||
|[datarepo](https://github.com/PUG-PB-Traducao/datarepo ) |bcrypt, dotenv, fastapi_sqlalchemy, sqlalchemy |
|
||||
|[debitcard](https://github.com/xpnewmedia/debitcard ) |debitcard |
|
||||
|[debts](https://github.com/afonasev/debts ) |pytest, locust, factory, passlib, sentry_asgi, sentry_sdk, contextvars_executor, server, context_logging, sqlalchemy, alembic, jwt |
|
||||
|[decentralized-news-publishing](https://github.com/teddymarkov/decentralized-news-publishing ) |ariadne |
|
||||
|[demo-dms](https://github.com/gucharbon/demo-dms ) |urllib3, typer, pytest, minio, backend |
|
||||
|[deploy](https://github.com/pcmalves/codeploy-teste ) |main |
|
||||
|[devops-assignment](https://github.com/fokinpv/devops-assignment ) |asynctest, pytest, service, pulp |
|
||||
|[devs-conn-api](https://github.com/txemac/devs-conn-api ) |github, pytest, sqlalchemy, sqlalchemy_utils, data, twitter, tests |
|
||||
|[dfstore](https://github.com/Exhor/dfstore ) |requests, src, tests, feather |
|
||||
|[dig-bioindex](https://github.com/broadinstitute/dig-bioindex ) |requests, colorama, orjson, lib, enlighten, sqlalchemy, dotenv, boto3, click |
|
||||
|[django-fastapi-example](https://github.com/jordaneremieff/django-fastapi-example ) |django, api |
|
||||
|[docker-fastapi-demo](https://github.com/pylixm/docker-fastapi-demo ) |alembic, loguru, databases, core, sqlalchemy, service, myapp, api |
|
||||
|[docker-workshop](https://github.com/blairdrummond/docker-workshop ) |scipy, PIL, dash, flask, inference, sklearn, requests, numpy, joblib, flask_caching |
|
||||
|[downloads_statistics](https://github.com/mmidu/downloads_statistics ) |redis, main, seeds, controllers, DownloadSeeder, requests, models, config, utils, DownloadsController |
|
||||
|[dreamcatcher](https://github.com/sbathgate/dreamcatcher ) |pytest, passlib, sqlalchemy, myapp, jose, alembic, celery, tenacity, raven, emails |
|
||||
|[duke](https://github.com/jamescurtin/duke ) |duke, PIL, pytest |
|
||||
|[eXchangeAPI-PY](https://github.com/wborbajr/eXchangeAPI-PY ) |pymongo, dynaconf, exchangeapi, motor |
|
||||
|[educator_api](https://github.com/wycliffogembo87/educator_api ) |pony, requests, passlib, loguru, api, settings, schemas, core, models, util |
|
||||
|[effortless_fast_api](https://github.com/davideasaf/effortless_fast_api ) |requests, passlib, invoke, config, utils, main, sqlalchemy, schemas, jwt, models, services, pytest |
|
||||
|[embl_python_software_engineer_test](https://github.com/yusra-haider/embl_python_software_engineer_test ) |sqlalchemy, requests |
|
||||
|[eml_analyzer](https://github.com/ninoseki/eml_analyzer ) |async_timeout, pytest, eml_parser, compressed_rtf, arrow, compoundfiles, olefile, loguru, fastapi_utils, tests, dateparser, asynctest, magic, aiospamc, ioc_finder, respx, httpx |
|
||||
|[entroprise-api](https://github.com/peterdeepsix/entroprise-api ) |simpletransformers, service, models, api |
|
||||
|[eu-taxation-customs-api](https://github.com/aditya-08/eu-taxation-customs-api ) |main, zeep, requests |
|
||||
|[events-api](https://github.com/txemac/events-api ) |pytz, xmltodict, pytest, sqlalchemy, sqlalchemy_utils, apscheduler, data, requests, tests |
|
||||
|[example-fastapi-sqlachemy-pytest](https://github.com/timhughes/example-fastapi-sqlachemy-pytest ) |example, pytest, sqlalchemy |
|
||||
|[excerpt-formatter](https://github.com/cmckillop/excerpt-formatter ) |text_formatter, slack_manager, requests, models |
|
||||
|[fake-news-game](https://github.com/jsandovalc/fake-news-game ) |databases |
|
||||
|[fantasy-fencing](https://github.com/Innoviox/fantasy-fencing ) |colorlog, selenium, click, scrape, requests, logs, bs4 |
|
||||
|[fapi](https://github.com/glebofff/fapi ) |api, sqlalchemy, myapp, alembic, fastapi_sqlalchemy, core, models, click |
|
||||
|[fast](https://github.com/foobar8675/fastai-bite ) |routers, user |
|
||||
|[fast-api-asyncdb](https://github.com/tsadimas/fast-api-asyncdb ) |dotenv, databases, sqlalchemy, settings, fastapi_users |
|
||||
|[fast-api-boilerplate](https://github.com/pmcarlos/fast-api-boilerplate ) |alembic, dotenv, fastapi_sqlalchemy, core, sqlalchemy, myapp |
|
||||
|[fast-api-rest](https://github.com/javier-lopez/fast-api-rest ) |config, bjoern |
|
||||
|[fast-api-sql-template](https://github.com/Jonatha-Varjao/fast-api-sql-template ) |pytest, googletrans, requests, passlib, sqlalchemy_utils, sqlalchemy, alembic, jwt, emails |
|
||||
|[fast-elm](https://github.com/yuzebin/fast-elm ) |bcrypt, fmmodel, requests, fmsecurity, passlib, fmdb, slugify, fmcrud, fmapi, databases, motor, jwt, fmtoken, bson |
|
||||
|[fastAPI-be](https://github.com/BonneC/fastAPI-be ) |alembic, dotenv, sqlalchemy, models, api, config, myapp, crud |
|
||||
|[fastApi](https://github.com/fionasummer/fastApiSelf ) |my_super_project, graphene, projetStage, sqlalchemy |
|
||||
|[fastApiSelf](https://github.com/fionasummer/fastApiSelf ) |pytest, rr, sqlalchemy, websocket, fackApi, app1 |
|
||||
|[fastApiSimple](https://github.com/marirs/fastApiSimple ) |pymongo, geoip2, motor, server |
|
||||
|[fastAppAppointment](https://github.com/EdvinaNakos/fastAppAppointment ) |main, controller, databases, sqlalchemy, db, service |
|
||||
|[fast_api](https://github.com/davideasaf/effortless_fast_api ) |app_utils, bcrypt, schemas, jwt, sqlalchemy, models, database, crud |
|
||||
|[fast_api_camelcase_example](https://github.com/ahmednafies/fast_api_camelcase_example ) |humps, models |
|
||||
|[fast_api_self_study](https://github.com/cuongld2/fast_api_self_study ) |bcrypt, jwt, sqlalchemy, sql_app |
|
||||
|[fastaaps](https://github.com/sergio-chumacero/fastaaps ) |pymongo, celery, motor |
|
||||
|[fastai-bite](https://github.com/foobar8675/fastai-bite ) |PIL, graphene, my_utils, fastai2, cv2, httpx |
|
||||
|[fastapi-GraphQL-full-stack-docker-github-actions-graphene-test-client-template](https://github.com/gaylonalfano/fastapi-GraphQL-full-stack-docker-github-actions-graphene-test-client-template )|graphene, pytest, sqlalchemy, graphql, snapshottest |
|
||||
|[fastapi-GraphQL-full-stack-docker-travisci-starlette-test-client-template](https://github.com/gaylonalfano/fastapi-GraphQL-full-stack-docker-travisci-starlette-test-client-template ) |graphene, pytest |
|
||||
|[fastapi-admin](https://github.com/long2ice/fastapi-admin ) |colorama, passlib, fastapi_admin, prompt_toolkit, tortoise, module, xlsxwriter, jwt |
|
||||
|[fastapi-auth](https://github.com/dmontagu/fastapi-auth ) |argon2, bcrypt, pytest, requests, typing_extensions, fastapi_auth, tests, sqlalchemy, jwt |
|
||||
|[fastapi-backend](https://github.com/dev-courses/fastapi-backend ) |bcrypt, pytest, passlib, asyncpg, loguru, slugify, tests, asgi_lifespan, sqlalchemy, docker, alembic, psycopg2, databases, jwt, httpx |
|
||||
|[fastapi-boilerplate](https://github.com/teamhide/fastapi-boilerplate ) |alembic, jwt, sqlalchemy, core, myapp |
|
||||
|[fastapi-celery](https://github.com/GregaVrbancic/fastapi-celery ) |worker, celery |
|
||||
|[fastapi-celery-redis-rabbitmq](https://github.com/karthikasasanka/fastapi-celery-redis-rabbitmq ) |redis, celery, shopping |
|
||||
|[fastapi-cool](https://github.com/genchsusu/fastapi-cool ) |kombu, aiosmtplib, tortoise, yaml, settings, celery, jwt, ldap3 |
|
||||
|[fastapi-crud](https://github.com/sophiabrandt/fastapi-crud ) |databases, pytest, sqlalchemy, db |
|
||||
|[fastapi-crud-sync](https://github.com/testdrivenio/fastapi-crud-sync ) |pytest, sqlalchemy |
|
||||
|[fastapi-demo](https://github.com/sonhal/fastapi-demo ) |rethinkdb, fastapi_demo |
|
||||
|[fastapi-discovery](https://github.com/DenisOH/fastapi-discovery ) |sqlalchemy, fastapi_discovery |
|
||||
|[fastapi-docs-cn](https://github.com/apachecn/fastapi-docs-cn ) |graphene, passlib, peewee, couchbase, sqlalchemy, flask, databases, jwt |
|
||||
|[fastapi-esf-demo](https://github.com/hightfly/fastapi-esf-demo ) |databases, sqlalchemy, sqlalchemy_utils |
|
||||
|[fastapi-etag](https://github.com/steinitzu/fastapi-etag ) |pytest, requests, fastapi_etag |
|
||||
|[fastapi-exploring](https://github.com/manjurulhoque/fastapi-exploring ) |alembic, dotenv, fastapi_sqlalchemy, sqlalchemy, myapp |
|
||||
|[fastapi-for-firebase](https://github.com/attakei/fastapi-for-firebase ) |fastapi_for_firebase, pytest |
|
||||
|[fastapi-fullstack](https://github.com/lfvilella/fastapi-fullstack ) |pytest, sqlalchemy, validate_docbr |
|
||||
|[fastapi-healthcheck](https://github.com/jeffsiver/fastapi-healthcheck ) |healthcheck, setuptools |
|
||||
|[fastapi-helloworld](https://github.com/yashwanthl/fastapi-helloworld ) |nltk, sklearn, modules |
|
||||
|[fastapi-hero](https://github.com/Hedde/fastapi-heroku-test ) |dotenv, motor, core, db, bson, models, api |
|
||||
|[fastapi-hex-boilerplate](https://github.com/GArmane/fastapi-hex-boilerplate ) |factory, toolz, tests, sqlalchemy, myapp, alembic, dotenv, databases, pytest |
|
||||
|[fastapi-jsonrpc](https://github.com/smagafurov/fastapi-jsonrpc ) |fastapi_jsonrpc, aiojobs, pytest, setuptools |
|
||||
|[fastapi-jwt](https://github.com/carminati-marco/fastapi-jwt ) |ms_auth, auth, jwt, passlib |
|
||||
|[fastapi-layered-architecture](https://github.com/teamhide/fastapi-layered-architecture ) |alembic, pythondi, jwt, sqlalchemy, core, myapp |
|
||||
|[fastapi-login](https://github.com/parsd/fastapi-login ) |pytest, requests, setuptools, fastapi_login, assertpy |
|
||||
|[fastapi-mako](https://github.com/LouisYZK/fastapi-mako ) |mako, setuptools |
|
||||
|[fastapi-mangum-example](https://github.com/chgangaraju/fastapi-mangum-example ) |mangum |
|
||||
|[fastapi-microblog](https://github.com/vkhnychenko/fastapi-microblog ) |alembic, core, sqlalchemy, microblog, myapp, user |
|
||||
|[fastapi-ml-scaffolding](https://github.com/jmeisele/fastapi-ml-scaffolding ) |loguru, pytest, fastapi_scaffolding, joblib |
|
||||
|[fastapi-ml-skeleton](https://github.com/eightBEC/fastapi-ml-skeleton ) |loguru, pytest, fastapi_skeleton, joblib |
|
||||
|[fastapi-mount](https://github.com/Midnighter/fastapi-mount ) |mount_demo |
|
||||
|[fastapi-nuxt-blog](https://github.com/tokusumi/fastapi-nuxt-blog ) |PIL, passlib, sqlalchemy_utils, sqlalchemy, myapp, sendgrid, pytz, alembic, jwt, boto3, pytest |
|
||||
|[fastapi-permissions](https://github.com/holgi/fastapi-permissions ) |pytest, jwt, fastapi_permissions, passlib |
|
||||
|[fastapi-playground](https://github.com/FlorianBorn/fastapi-playground ) |PIL, fastai |
|
||||
|[fastapi-plugins](https://github.com/madkote/fastapi-plugins ) |fastapi_plugins, setuptools, aioredis, tenacity, aiojobs, pytest |
|
||||
|[fastapi-poc](https://github.com/igorfarias30/fastapi-poc ) |main, data, models, todo |
|
||||
|[fastapi-postgres-aws-lambda](https://github.com/KurtKline/fastapi-postgres-aws-lambda ) |mangum, sqlalchemy |
|
||||
|[fastapi-realworld-example-app-mysql](https://github.com/xiaozl/fastapi-realworld-example-app-mysql ) |bcrypt, passlib, asyncpg, loguru, aiomysql, tests, asgi_lifespan, docker, psycopg2, databases, jwt, pytest, httpx |
|
||||
|[fastapi-route](https://github.com/franodos/fastapi-route ) |response, setuptools |
|
||||
|[fastapi-satella-metrics](https://github.com/piotrmaslanka/fastapi-satella-metrics ) |fastapi_satella_metrics, requests, setuptools, satella |
|
||||
|[fastapi-scaffold](https://github.com/ibegyourpardon/fastapi-scaffold ) |xuan |
|
||||
|[fastapi-simple-cachecontrol](https://github.com/attakei/fastapi-simple-cachecontrol ) |fastapi_simple_cachecontrol, pytest |
|
||||
|[fastapi-simple-template](https://github.com/MushroomMaula/fastapi-simple-template ) |alembic, dotenv, pytest, faker, sqlalchemy, myapp, fastapi_login |
|
||||
|[fastapi-snippets](https://github.com/hhatto/fastapi-snippets ) |caches, fastapi_plugins, aioredis |
|
||||
|[fastapi-sqlalchemy](https://github.com/zuarbase/fastapi-sqlalchemy-example ) |pytest, setuptools, passlib, sqlalchemy_filters, tests, sqlalchemy, ordered_uuid, tzlocal, pytz, fastapi_sqlalchemy, jwt, itsdangerous |
|
||||
|[fastapi-sqlalchemy-example](https://github.com/zuarbase/fastapi-sqlalchemy-example ) |alembic, itsdangerous, fastapi_sqlalchemy, pytest, fastapi_sqlalchemy_example, sqlalchemy, myapp, click, setuptools |
|
||||
|[fastapi-starter-kit](https://github.com/Shinichi-Nakagawa/fastapi-starter-kit ) |book, pytest, sqlalchemy, db |
|
||||
|[fastapi-tdd-docker](https://github.com/pmcarlos/fastapi-tdd-docker ) |pytest, tortoise |
|
||||
|[fastapi-template](https://github.com/jefmud/fastapi-templates ) |pytest, setuptools, passlib, sentry_sdk, sqlalchemy_utils, gino, sqlalchemy, jose, alembic, fastapi_template, migrations |
|
||||
|[fastapi-test](https://github.com/jrwalk/fastapi-test ) |pytest, pandas |
|
||||
|[fastapi-tools](https://github.com/so1n/fastapi-tools ) |fastapi_tools, prometheus_client, httpx |
|
||||
|[fastapi-tortoise](https://github.com/prostomarkeloff/fastapi-tortoise ) |loguru, pytest, tortoise, requests, tests |
|
||||
|[fastapi-users](https://github.com/frankie567/fastapi-users ) |passlib, tortoise, tests, asynctest, asgi_lifespan, sqlalchemy, httpx_oauth, makefun, databases, motor, jwt, fastapi_users, pytest, httpx |
|
||||
|[fastapi-utils](https://github.com/dmontagu/fastapi-utils ) |pytest, fastapi_utils, tests, sqlalchemy |
|
||||
|[fastapi-uvicorn-gunicorn-nginx-supervisor-boilerplate](https://github.com/sumitsk20/fastapi-uvicorn-gunicorn-nginx-supervisor-boilerplate ) |orjson, motor, core, server, bson, apps, utils |
|
||||
|[fastapi-versioning](https://github.com/DeanWay/fastapi-versioning ) |example, typing_extensions, fastapi_versioning, setuptools |
|
||||
|[fastapi-vue-test](https://github.com/abreumatheus/fastapi-vue-test ) |PIL, sqlalchemy, myapp, pytz, alembic, boto3 |
|
||||
|[fastapi-websockets](https://github.com/natanael-silvamt/fastapi-websockets ) |api |
|
||||
|[fastapi-whisper-rest-api](https://github.com/BolajiOlajide/fastapi-whisper-rest-api ) |api |
|
||||
|[fastapi_admin](https://github.com/Chise1/fastapi_admin ) |apps, setuptools, passlib, fastapi_admin, sqlalchemy, settings, databases, pymysql, jwt |
|
||||
|[fastapi_catchup](https://github.com/Kamihara/fastapi_catchup ) |intro |
|
||||
|[fastapi_douyin](https://github.com/bigdataboy2020/fastapi_douyin ) |routers, requests, settings, exceptions, spiders |
|
||||
|[fastapi_gino](https://github.com/pengyejun/fastapi_gino ) |gino_starlette, pytest, gino_fastapi_demo, sqlalchemy, myapp, aioredis, alembic, poetry, urllib2 |
|
||||
|[fastapi_microblog](https://github.com/mrworksome/fastapi_microblog ) |alembic, databases, core, sqlalchemy, microblog, myapp, user, passlib, fastapi_users |
|
||||
|[fastapi_mock](https://github.com/superxuu/fastapi_mock ) |apis, yaml, common |
|
||||
|[fastapi_model](https://github.com/Nathanlauga/fastapi_model ) |loguru, joblib |
|
||||
|[fastapi_playground](https://github.com/Rurson/fastapi_playground ) |pony, db |
|
||||
|[fastapi_preset](https://github.com/cln-m4rie/fastapi_preset ) |setuptools |
|
||||
|[fastapi_router](https://github.com/christopherzimmerman/fastapi_router ) |fastapi_router, trimport, setuptools |
|
||||
|[fastapi_serviceutils](https://github.com/skallfass/fastapi_serviceutils_template ) |pytest, requests, setuptools, toolz, loguru, fastapi_serviceutils, yaml, cookiecutter, sqlalchemy, databases |
|
||||
|[fastapi_serviceutils_template](https://github.com/skallfass/fastapi_serviceutils_template ) |fastapi_serviceutils |
|
||||
|[fastapi_stu](https://github.com/Thousandhack/fastapi_stu ) |aliyunsdkcore, jwt, sqlalchemy, passlib |
|
||||
|[fastapi_template](https://github.com/ytxfate/fastapi_template ) |pymongo, redis, jwt, minio, project |
|
||||
|[fastapi_todo_hex](https://github.com/josemlp91/fastapi_todo_hex ) |factory, toolz, pytest_factoryboy, tests, sqlalchemy, myapp, alembic, dotenv, databases, pytest, todolist |
|
||||
|[fastbroker](https://github.com/cetanu/fastbroker ) |pkg_resources, ujson, fastbroker, jsonschema, setuptools |
|
||||
|[fastrf](https://github.com/iancleary/fastrf ) |asgi_lifespan, pytest, fastrf, httpx |
|
||||
|[fbd-backend](https://github.com/ElusiveSpirit/fbd-backend ) |redis, pytest_mock, celery, loguru, ujson, pytest, tests |
|
||||
|[finance-api](https://github.com/rennerocha/finance-api ) |finance_api, dynaconf, pytest, requests |
|
||||
|[first_fastapi](https://github.com/vittorduartte/first_fastapi ) |routes, ext, schemas, sqlalchemy, rules, models |
|
||||
|[firstderm_demo](https://github.com/polyrand/firstderm_demo ) |requests |
|
||||
|[foreign-subs](https://github.com/joe-eklund/foreign-subs ) |fsubs, setuptools, pymongo, typer, jwt, bson |
|
||||
|[frontapp](https://github.com/Blasterai/frontapp ) |decorator, frontapp, requests, envparse, attr |
|
||||
|[fullasync](https://github.com/lipengsh/fullasync ) |celery, tasks, core, api |
|
||||
|[fullstack-fastapi-elasticsearch](https://github.com/cleobulo/fullstack-fastapi-elasticsearch ) |core, elasticsearch, db, models, api |
|
||||
|[fuzzy-octo-funicular](https://github.com/mattkatz/fuzzy-octo-funicular ) |main |
|
||||
|[gaia-fastapi-demo](https://github.com/muslax/gaia-fastapi-demo ) |email_validator, passlib, pymongo, sentry_sdk, motor, jwt, bson, emails |
|
||||
|[gaki-server](https://github.com/shitangdama/gaki-server ) |alembic, jwt, sqlalchemy, myapp, passlib |
|
||||
|[galicea-odoo-addons-ecosystem](https://github.com/galicea/galicea-odoo-addons-ecosystem ) |cryptography, werkzeug, jwcrypto, odoo, git |
|
||||
|[game](https://github.com/jsandovalc/fake-news-game ) |basegame |
|
||||
|[gateway-microservice](https://github.com/anforaProject/gateway-microservice ) |aiohttp, v1 |
|
||||
|[gerenciador-tarefas](https://github.com/matheuspsouza/gerenciador-tarefas ) |gerenciador_tarefas |
|
||||
|[gfw-tile-cache](https://github.com/wri/gfw-tile-cache ) |gino_starlette, mercantile, pytest, pendulum, async_lru, requests, shapely, cachetools, sqlalchemy, aenum |
|
||||
|[gifter_api](https://github.com/zhanymkanov/gifter_api ) |pytest, jwt, sqlalchemy, tests, passlib |
|
||||
|[gigscovery_backend](https://github.com/martijnboers/gigscovery_backend ) |functions, sklearn, bandsintown, spotipy |
|
||||
|[gino-admin](https://github.com/xnuinside/gino-admin ) |sanic, pytest, click, jinja2, db, requests, passlib, expiring_dict, gino_admin, asyncpg, dsnparse, sqlalchemy_utils, gino, urllib3, aiofiles, yaml, sqlalchemy, sanic_jwt, sanic_jinja2 |
|
||||
|[gino-starlette](https://github.com/python-gino/gino-starlette ) |requests, gino_fastapi_demo, asyncpg, gino, urllib3, importlib_metadata, sqlalchemy, alembic, pytest, click |
|
||||
|[girandole](https://github.com/bartkl/girandole ) |beets, plugin, girandole, yaml |
|
||||
|[girias.json](https://github.com/kvnol/girias.json ) |tinydb |
|
||||
|[gitea-dispatcher](https://github.com/Trim21/gitea-dispatcher ) |httpx |
|
||||
|[gke-cicd-sandbox](https://github.com/mana-ysh/gke-cicd-sandbox ) |exampleapp, pytest |
|
||||
|[glittr-fastapi](https://github.com/kellischeuble/glittr-fastapi ) |glittr, sqlalchemy |
|
||||
|[graph-server](https://github.com/ZettaAI/graph-server ) |pychunkedgraph, numpy |
|
||||
|[graphql-python-server](https://github.com/apoveda25/graphql-python-server ) |env, gql, dotenv, schemas, ariadne, models, database, arango |
|
||||
|[graphql-tutorial](https://github.com/jcremona/graphql-tutorial ) |schema, graphene |
|
||||
|[grocery-stores-home-delivery](https://github.com/dz-experts/grocery-stores-home-delivery ) |db, app_factory, setuptools, crud, api, typer, sqlalchemy, myapp, alembic, constans, schemas, core, models |
|
||||
|[guid_tracker](https://github.com/nwcell/guid_tracker ) |alembic, databases, aiocache, guid, pytest, sqlalchemy, sqlalchemy_utils, myapp |
|
||||
|[hackaton-berlin-legal-tech-2020](https://github.com/jjanczur/hackaton-berlin-legal-tech-2020 ) |pytest, sklearn, joblib, nltk, loguru, cleaner, config, fastapi_skeleton, bs4 |
|
||||
|[hello-world-fastapi](https://github.com/benhid/hello-world-fastapi ) |fastapp, setuptools, rich |
|
||||
|[henry](https://github.com/jacksonhenry3/FAAS ) |infrastructure, interface, sqlalchemy |
|
||||
|[heroku-google-translate-api](https://github.com/fawazahmed0/heroku-google-translate-api ) |googletrans |
|
||||
|[hh_parser](https://github.com/Kuzyashin/hh_parser ) |passlib, aiohttp, tortoise, template, celery, jwt, emails |
|
||||
|[home_recruiters](https://github.com/Mohamed-Kaizen/home_recruiters ) |users, pendulum, passlib, jobs, tortoise, typer, usernames, jwt, core, confusable_homoglyphs, username |
|
||||
|[http-battle](https://github.com/MihaiAnei/http-battle ) |flask_restful, flask, python_flask |
|
||||
|[hvilkenfisk](https://github.com/David-IL/hvilkenfisk ) |imutils, fish_utils, sklearn, h5py, cv2, keras, img_preprocess, model_utils |
|
||||
|[iam_working](https://github.com/carlosporta/iam_working ) |iam_working |
|
||||
|[iem-web-services](https://github.com/akrherz/iem-web-services ) |setuptools, pyiem, shapely, metpy, pytz, pygrib, pyproj, pandas, geopandas, pytest |
|
||||
|[ihan-productizer-example](https://github.com/digitalliving/ihan-productizer-example ) |aiohttp, ujson, settings_local, settings |
|
||||
|[image_capabilities_backend](https://github.com/alejgo06/image_capabilities_backend ) |PIL, cv2 |
|
||||
|[image_classifier_backend](https://github.com/alejgo06/image_classifier_backend ) |PIL, cv2 |
|
||||
|[image_detector_backend](https://github.com/alejgo06/image_detector_backend ) |PIL, torch, utils, torchvision, pycocotools, models, cv2 |
|
||||
|[img-filter-api](https://github.com/RaRhAeu/img-filter-api ) |aiohttp, cv2, api, requests, pytest |
|
||||
|[ingaia_app](https://github.com/higaooliveira/ingaia_app ) |pytest, sqlalchemy, resources, requests, config, spotipy, domain |
|
||||
|[innovativeproject-photographers-portfolio](https://github.com/nokia-wroclaw/innovativeproject-photographers-portfolio ) |fastapi_mail, jwt, sqlalchemy, src, database, passlib |
|
||||
|[inpainting_backen](https://github.com/kaikai03/inpainting_backen ) |tinydb |
|
||||
|[introduction-to-async-await](https://github.com/ci42/introduction-to-async-await ) |unsync, unsyncable, colorama |
|
||||
|[iter8-analytics](https://github.com/iter8-tools/iter8-analytics ) |analytics_namespace, requests, flask_restplus, setuptools, experiment_namespace, yaml, jsonschema, iter8_analytics, django, flask, requests_mock |
|
||||
|[jjs](https://github.com/jjs-dev/jjs ) |bcrypt, pytest, api_models, pymongo, db_models, routes, auth, db_connect |
|
||||
|[jobsearch_statistics](https://github.com/JobtechSwe/jobsearch_statistics ) |certifi, searchstatistics, elasticsearch |
|
||||
|[json-fastapi](https://github.com/stocky37/json-fastapi ) |json_fastapi, tinydb, slugify, link_header, util |
|
||||
|[juice-box](https://github.com/dannytannertantrum/juice-box ) |tests |
|
||||
|[jupyter_fastapi](https://github.com/Zsailer/jupyter_fastapi ) |jupyter_server, tornado, hypothesis, pytest, jupyter_fastapi, hypothesis_jsonschema, setuptools |
|
||||
|[k8s-traefik-example](https://github.com/Hedde/k8s-traefik-example ) |flask |
|
||||
|[kartu-beckend](https://github.com/AntonKristiono/kartu-beckend ) |PIL, config, yaml, autocrop, routes, motor, bson, cv2 |
|
||||
|[koalachat](https://github.com/therumbler/koalachat ) |koalachat, aiofiles |
|
||||
|[koalastream](https://github.com/therumbler/koalastream ) |aiofiles, koalastream |
|
||||
|[kritika](https://github.com/Egnod/kritika ) |alembic, sitri, slugify, sqlalchemy, myapp, kritika |
|
||||
|[kubeflow-containers-desktop](https://github.com/StatCan/kubeflow-containers-desktop ) |tqdm, jupyter_core, setuptools, tornado, crontab, pip, psutil, IPython, git, notebook |
|
||||
|[kubernetes-experiments](https://github.com/richard-to/kubernetes-experiments ) |redis, alembic, sqlalchemy, myapp, passlib |
|
||||
|[kuma](https://github.com/deepalikumar/sync_async_compare ) |bcrypt, docstring_parser, passlib, orjson, loguru, sqlalchemy, myapp, jupyter_client, alembic, databases, jwt, nbformat |
|
||||
|[kvmail](https://github.com/la9ran9e/kvmail ) |tarantool, dotenv, settings |
|
||||
|[lab_monitor](https://github.com/nutanix-japan/lab_monitor ) |pymongo, requests, schedule, client |
|
||||
|[lagom](https://github.com/meadsteve/lagom ) |sybil, pytest, lagom, typing_extensions, tests, django, flask |
|
||||
|[lang-python](https://github.com/tim-barnes/lang-python ) |submod2, worker_a, etcd3, rabbit, worker_b, optmod1, psutil, mongoengine, pika, test_pb2, grpc, google, optmod2, submod1, celery, flask, models, pytest |
|
||||
|[league-manager](https://github.com/Project-SRC/league-manager ) |jwt, src, environs, passlib |
|
||||
|[learn-kubernetes](https://github.com/quan-vu/learn-kubernetes ) |flask, flask_restful |
|
||||
|[lgbsttracker_api](https://github.com/py4mac/lgbsttracker_api ) |lgbsttracker, lgbsttracker_api |
|
||||
|[lightgbm-project-demo](https://github.com/raywu60kg/lightgbm-project-demo ) |psycopg2, sklearn, src, tests, ray |
|
||||
|[linkalong-pre-inteview-task](https://github.com/pydevd/linkalong-pre-inteview-task ) |decouple, linkalong |
|
||||
|[love-alarm](https://github.com/dl-eric/love-alarm ) |pymongo, redis, nexmo, jwt, boto3, bson, passlib |
|
||||
|[maale-map-bot](https://github.com/kudanai/maale-map-bot ) |pyngrok, dotenv, discord, bots, viberbot, models, settings, fuzzywuzzy, telegram |
|
||||
|[manageme-api](https://github.com/managemeapp/manageme-api ) |mangum, today_app, setuptools |
|
||||
|[mangum-fastapi-aws-test](https://github.com/dan-danciu/mangum-fastapi-aws-test ) |mangum |
|
||||
|[mars-rover](https://github.com/rSkogeby/mars-rover ) |models, views |
|
||||
|[memefly-ds](https://github.com/Lambda-School-Labs/memefly-ds ) |wandb, memefly, requests, pymongo, decouple, api, tensorflow, dotenv, boto3, click |
|
||||
|[metabot](https://github.com/vasyukvv42/metabot ) |metabot, requests, fastapi_metabot, setuptools, aiohttp, pymongo, help, typing_extensions, feedback, aioredis, vacations, slackers, motor, mockaioredis, bson, slack, pytest, httpx |
|
||||
|[micro-data-lake](https://github.com/abxda/micro-data-lake ) |sklearn, newspaper, altair, airflow, sqlalchemy, IPython, minio, setproctitle |
|
||||
|[microservices](https://github.com/hugosteixeira/microservices ) |requests |
|
||||
|[mironov_blog_pwa](https://github.com/yuriymironov96/mironov_blog_pwa ) |alembic, dotenv, sqlalchemy, myapp, apps |
|
||||
|[ml-project-template](https://github.com/raywu60kg/ml-project-template ) |psycopg2, src, tests, ray |
|
||||
|[ml-workspace](https://github.com/DennisRasey/ml-workspace ) |jupyter_core, tqdm, setuptools, tornado, crontab, pip, psutil, IPython, git, notebook |
|
||||
|[modelo-fast-api](https://github.com/knienkotter/modelo-fast-api ) |pytest, gerenciador_tarefas |
|
||||
|[moneyTransfer](https://github.com/williamsyb/moneyTransfer ) |faker, db, app_factory, passlib, apis, config, utils, main, sqlalchemy, auth, schemas, jwt, pytest |
|
||||
|[mongodb_admin](https://github.com/ArtemZaitsev1994/mongodb_admin ) |trafaret_config, envparse, pymongo, base, motor, jwt, bson |
|
||||
|[mri_image_reconstruction](https://github.com/Pradhy729/mri_image_reconstruction ) |utils, config |
|
||||
|[msys2-web](https://github.com/msys2/msys2-web ) |pytest, pgpdump, jinja2, fastapi_etag, respx, httpx |
|
||||
|[musixfy_service](https://github.com/divyam234/musixfy_service ) |requests, Cryptodome |
|
||||
|[mv-backend](https://github.com/Sreerajta/mv-backend ) |cassandra, requests, passlib, redis, fnc, global_config, sqlalchemy, jwt, greenstalk |
|
||||
|[mvp-metric](https://github.com/DjaPy/mvp-metric ) |databases, sqlalchemy, dateutil |
|
||||
|[mvs_eland_api](https://github.com/rl-institut/mvs_eland_api ) |celery, pytest, worker, mvs_eland_tool, sphinx_rtd_theme |
|
||||
|[my-notes-app](https://github.com/jgabriel1/my-notes-app ) |databases, pytest, jwt, sqlalchemy, passlib |
|
||||
|[mychef](https://github.com/logan-connolly/mychef ) |spacy, requests, scrapy, tqdm, aiohttp, asyncpg, loguru, orm, reddit, scraper, sqlalchemy, databases, praw, pytest, jsonlines, bs4 |
|
||||
|[nasa_neo](https://github.com/cmmarti/nasa_neo ) |repo, requests |
|
||||
|[nereid](https://github.com/Geosyntec/nereid ) |nereid, pytest, redis, typing_extensions, numpy, matplotlib, scipy, yaml, pint, celery, pandas |
|
||||
|[ners](https://github.com/cedricconol/ners ) |utils, config |
|
||||
|[netpalm](https://github.com/tbotnz/netpalm ) |xmltodict, rq, netmiko, pytest, jinja2, backend, netpalm_pinned_worker, requests, jinja2schema, redis, lxml, tests, jsonschema, ncclient, routers, napalm |
|
||||
|[next-word-sentence-pred-api](https://github.com/rakesh4real/next-word-sentence-pred-api ) |tree, sqlalchemy, models, database |
|
||||
|[nile-api](https://github.com/mattgeiger/nile-api ) |alembic, bcrypt, databases, jwt, sqlalchemy, api |
|
||||
|[nlp_api](https://github.com/rdenadai/nlp_api ) |nltk, decouple, spacy, utils |
|
||||
|[nmdc-server](https://github.com/microbiomedata/nmdc-server ) |pytest, faker, factory, requests, setuptools, pkg_resources, sqlalchemy, alembic, nmdc_server |
|
||||
|[nomine](https://github.com/gzzo/nomine ) |graphql_sqlalchemy, ariadne, sqlalchemy, nomine, graphql |
|
||||
|[nurse-schedule](https://github.com/csabex94/nurse-schedule ) |spital_routes, mongoengine, nurse_routes, jwt, models, utils, passlib |
|
||||
|[oasis](https://github.com/instedd/oasis ) |users, bcrypt, pytest, stories, requests, mysql, flask_cors, sqlalchemy, myapp, sigfig, database, jenkspy, alembic, flask, auth, jwt, router, emails |
|
||||
|[ocfapi](https://github.com/nikhiljha/ocfapi ) |ocflib |
|
||||
|[oreMeet](https://github.com/mfortini/oreMeet ) |ics, worker |
|
||||
|[owi_Result-Collector](https://github.com/JensGe/owi_Result-Collector ) |sqlalchemy, tests |
|
||||
|[page-visitor-counter](https://github.com/trapflag/page-visitor-counter ) |redis |
|
||||
|[paste.bin](https://github.com/magiskboy/paste.bin ) |pymongo, redis, gridfs, bson |
|
||||
| |PIL, PyPDF4 |
|
||||
|[perfume-ai](https://github.com/gurutaka/perfume-ai ) |PIL, torchvision, predict, cv2, torch |
|
||||
|[persistent-identifier-service](https://github.com/OpertusMundi/persistent-identifier-service ) |ompid, yaml, sqlalchemy |
|
||||
|[personal-learning](https://github.com/hoangthienan95/personal-learning ) |pyimagesearch, sklearn, calculator, cv2, keras |
|
||||
|[pi-camera-hardware-server](https://github.com/NSnietol/pi-camera-hardware-server ) |loguru, picamera, requests, src |
|
||||
|[piccolo_api](https://github.com/piccolo-orm/piccolo_api ) |setuptools, asyncpg, test_session, piccolo, livereload, jwt, piccolo_api, asgiref |
|
||||
|[plarin_task](https://github.com/jjoskey/plarin_task ) |pymongo, main, employees, pytest, faker, bson, settings |
|
||||
|[plonkowski.b-gmail.com](https://github.com/Raroog/plonkowski.b-gmail.com ) |main |
|
||||
|[plotly-graphene](https://github.com/mdpham/plotly-graphene ) |pymongo, mutation, graphene, schema, minio, loompy, bson, query, minio_client |
|
||||
|[pneumoniadetection](https://github.com/aalikadic/pneumoniadetection ) |kaggle, PIL, classifier, keras |
|
||||
|[pol](https://github.com/RedHatInsights/policies-notifications ) |pytest, requests, aioresponses, etc, orjson, aiohttp, redis, sentry_sdk, tests, sqlalchemy, myapp, aioredis, pytz, alembic, databases, aiologger |
|
||||
|[policies-notifications](https://github.com/RedHatInsights/policies-notifications ) |pytest, jinja2, aiokafka, aiohttp, asyncpg, prometheus_client, apscheduler, gino, tests, sqlalchemy, myapp, starlette_prometheus, alembic |
|
||||
|[poll-app](https://github.com/jgabriel1/poll-app ) |pymongo, dotenv, selenium, pytest, flask_testing, tests, config |
|
||||
|[ppm-telegram-bot](https://github.com/piterpy-meetup/ppm-telegram-bot ) |pip, aiogram, fastapi_security_telegram_webhook, ppm_telegram_bot |
|
||||
|[ppr](https://github.com/bcgov/ppr ) |pytest, endpoints, requests, setuptools, sentry_sdk, freezegun, config, main, repository, sqlalchemy, myapp, datedelta, pytz, alembic, dotenv, schemas, models |
|
||||
|[practical-python](https://github.com/sudharsan2020/practical-python ) |redis, celery_with_fastapi, celery |
|
||||
|[programs](https://github.com/deta/programs ) |deta, jinja2, colors, svgs, passlib |
|
||||
|[projects](https://github.com/hungd25/projects ) |sklearn, lrsgd, utils, numpy, matplotlib, nose, models_partc, src, phonenumbers |
|
||||
|[property-prediction](https://github.com/solanyn/property-prediction ) |botocore, psycopg2, pytest, catboost, sklearn, hello_world, boto3, requests |
|
||||
|[proxmox-slack-bot](https://github.com/PlenusPyramis/proxmox-slack-bot ) |slack_model, requests, slack |
|
||||
|[proxy-checker](https://github.com/infaticaio/proxy-checker ) |aiohttp, main, checker, aiosocks |
|
||||
|[py-cidemia-security](https://github.com/cidemia/py-cidemia-security ) |werkzeug, requests, setuptools, passlib, yaml, cidemiasecurity, jwt |
|
||||
|[pyBets](https://github.com/GabrielCappelli/pyBets ) |error_messages, db_models, views, sqlalchemy, application, database, app_logging, models, services, pytest |
|
||||
|[pyapi](https://github.com/srghosh29/pyapi ) |apis |
|
||||
|[pycon2020-FastAPI](https://github.com/gloriasky/pycon2020-FastAPI ) |schemas, sqlalchemy, models, api, database |
|
||||
|[pyctuator](https://github.com/SolarEdgeTech/pyctuator ) |werkzeug, multidict, requests, aiohttp, redis, tests, psutil, pyctuator, sqlalchemy, flask, pytest |
|
||||
|[pydantic-ddd-exploration](https://github.com/allgreed/pydantic-ddd-exploration ) |sqlalchemy, src |
|
||||
|[pyml](https://github.com/dockerian/pyml ) |PIL, pytest, setuptools, gevent, h5py, tests, numpy, dateutil, matplotlib, scipy, mock, connexion, yaml, google, jsonpickle, flask, ml, asgiref |
|
||||
|[pypis](https://github.com/jurelou/pypis ) |packaging, setuptools, asyncpg, loguru, pkg_resources, sqlalchemy, stdlib_list, dynaconf, pypis, httpx |
|
||||
|[python](https://github.com/jijinggang/test_python ) |requests, requests_html, unidecode, lxml, openpyxl, send_mail, download_util, formatting, settings, templates, flask |
|
||||
|[python-api-template](https://github.com/mana-ysh/python-api-template ) |pytest |
|
||||
|[python-asyncio-crud](https://github.com/aleimu/python-asyncio-crud ) |sanic, six, uvloop, muggle_ocr, redis, flask_sqlalchemy, aiomysql, sqlalchemy, aioredis, flask |
|
||||
|[python-crash-course](https://github.com/cyberinsane/python-crash-course ) |tinydb, schema, pandas, requests |
|
||||
|[python-deployment-samples](https://github.com/edisga/python-deployment-samples ) |aiohttp, webtest, django, pyramidsite, flask, pyramid, flask_cors, my_app, setuptools, other_app |
|
||||
|[python-fastapi-hex-todo](https://github.com/GArmane/python-fastapi-hex-todo ) |pytest, faker, factory, passlib, toolz, asyncpg, pytest_factoryboy, tests, sqlalchemy, myapp, alembic, dotenv, databases, jwt, todolist |
|
||||
|[python-graphql](https://github.com/tsungchih/python-graphql ) |graphene, aiohttp, loguru, ujson, psutil, graphql |
|
||||
|[python-ml-service-template](https://github.com/paramoshin/python-ml-service-template ) |yaml, joblib, starlette_prometheus |
|
||||
|[python-playground](https://github.com/tgmti/python-playground ) |flask_restful, flask, sqlalchemy |
|
||||
|[python-poetry-docker-example](https://github.com/michael0liver/python-poetry-docker-example ) |pytest |
|
||||
|[python-private-service-layout](https://github.com/U-Company/python-private-service-layout ) |fire, loguru, prometheus_client, pytest, vault_client, info, clients, tests, setuptools |
|
||||
|[python_benchmark](https://github.com/T4D3K/python_benchmark ) |fastapi_frm, locust, sqlalchemy, tortoise, falcon, gino, falcon_frm |
|
||||
|[python_common_tools](https://github.com/CheungChan/python_common_tools ) |requests, setuptools, redis, python_common_tools, paramiko, stackprinter, DBUtils, pymysql |
|
||||
|[python_fastapi](https://github.com/playwhyyza/python_fastapi ) |jwt, passlib |
|
||||
|[qa-api](https://github.com/venuraja79/qa-api ) |elasticapm, controller, haystack, config |
|
||||
|[qhub-home](https://github.com/Quansight/qhub-home ) |toml, flit, setuptools |
|
||||
|[qr-generator](https://github.com/gosha20777/qr-generator ) |qrcode |
|
||||
|[rakm](https://github.com/emanuelaguna/rakm ) |pytest, requests, aiohttp, asyncache, invoke, asyncmock, tests, dateutil, cachetools, async_asgi_testclient, async_generator, dotenv |
|
||||
|[random_projects](https://github.com/thepartisan101/random_projects ) |flask_restful, graphene, extraction, selenium, flask, flask_graphql, schema, sqlalchemy, requests |
|
||||
|[recommender_fastapi](https://github.com/gjorgjinac/recommender_fastapi ) |repository, pybreaker, helper, sqlalchemy, default_ad_listener, requests, web, defaults |
|
||||
|[recommender_server](https://github.com/gjorgjinac/recommender_server ) |repository, pybreaker, helper, sqlalchemy, default_ad_listener, requests, web, defaults |
|
||||
|[ref-final-project-backend](https://github.com/Reyvel/ref-final-project-backend ) |faust, kafka, msgpack, settings, utils |
|
||||
|[repost-fastapi](https://github.com/pckv/repost-fastapi ) |dotenv, jwt, sqlalchemy, repost, passlib |
|
||||
|[responsive-layout-part](https://github.com/lmacchiavelli/responsive-layout-part ) |PIL |
|
||||
|[rest_api_docker_mongo_aws](https://github.com/RodrigoMachado9/rest_api_docker_mongo_aws ) |pymongo, six, bottle_resource, beartype, bottle, spacy, passlib |
|
||||
|[restbot](https://github.com/cwerner/restbot ) |restbot, loguru, pytest, joblib |
|
||||
|[reternal-backend](https://github.com/d3vzer0/reternal-backend ) |pymongo, redis, flask_restful, flask_jwt_extended, sigma, workers, mongoengine, yaml, environment, database, pytz, flask_socketio, celery, flask, jwt, bson |
|
||||
|[reverse-geocoder](https://github.com/claws/reverse-geocoder ) |databases, sqlalchemy, geoalchemy2 |
|
||||
|[rjgtoys-xc](https://github.com/bobgautier/rjgtoys-xc ) |pytest, jinja2, requests, rjgtoys, apierrors, sphinx, sphinx_autodoc_typehints |
|
||||
|[rn-fastapi-app](https://github.com/cloudeyes/rn-fastapi-app ) |sample, psycopg2, fastapi_login, MySQLdb, sqlalchemy, fastalchemy, passlib |
|
||||
|[runn](https://github.com/alex-2201/runn ) |requests, passlib, tests, utils, sqlalchemy, jwt, logzero, pytest, emails |
|
||||
|[sauti-fastapi](https://github.com/taycurran/sauti-fastapi ) |dotenv, pstgres, sqlalchemy |
|
||||
|[sauti-monitor-ds-api](https://github.com/taycurran/sauti-monitor-ds-api ) |dotenv, sqlalchemy |
|
||||
|[scaling-spoon](https://github.com/bloomingmath/scaling-spoon ) |email_validator, pony, bcrypt, pytest, extensions, passlib, pymongo, selenium, blinker, main, async_asgi_testclient, dotenv, motor, schemas, routers, bson, models |
|
||||
|[sensehat-fastapi](https://github.com/cmlccie/sensehat-fastapi ) |sense_hat, senseapi |
|
||||
|[server-open-alpr](https://github.com/NSnietol/server-open-alpr ) |openalpr, loguru, src |
|
||||
|[serverless-fastapi-cdk](https://github.com/jaehyeon-kim/serverless-fastapi-cdk ) |mangum, resources, src, aws_cdk |
|
||||
|[show-tell-api](https://github.com/team-eyespace/show-tell-api ) |nltk, PIL, TensorBoardCaption, NIC, evaluate, flask, jwt, preprocessing, keras, utils |
|
||||
|[signal-cli-rest-api](https://github.com/SebastianLuebke/signal-cli-rest-api ) |pyqrcode, requests, signal_cli_rest_api |
|
||||
|[simple-kp](https://github.com/ranking-agent/simple-kp ) |aiosqlite, pytest, simple_kp, reasoner_pydantic, data, setuptools |
|
||||
|[simple-messenger](https://github.com/IvanDubrowin/simple-messenger ) |server |
|
||||
|[simple-report-data-table-vuetify](https://github.com/shizidushu/simple-report-data-table-vuetify ) |bcrypt, dotenv, mongoengine, jwt, pypandoc, bson, casbin, passlib |
|
||||
|[simpleapp-aws-fargate](https://github.com/kbaafi/simpleapp-aws-fargate ) |controllers, databases, sqlalchemy, boto3, data, config, services, settings, requests |
|
||||
|[siso-library](https://github.com/nfowl/siso-library ) |databases, sqlalchemy |
|
||||
|[sklearn-docker-api](https://github.com/crocopie/sklearn-docker-api ) |sklearn, service, joblib |
|
||||
|[slowapi](https://github.com/laurentS/slowapi ) |limits, redis, slowapi, tests, hiro, mock |
|
||||
|[smart-social-distancing](https://github.com/neuralet/smart-social-distancing ) |openvino, tflite_runtime, invoke, ui, tools, wget, scipy, libs |
|
||||
|[sms_gateway](https://github.com/arxell/sms_gateway ) |passlib, aiohttp, sentry_sdk, aiopg, sqlalchemy, myapp, grpc, google, alembic, psycopg2, jwt, click, grpclib |
|
||||
|[social-insights](https://github.com/dsc-umass/social-insights ) |sklearn, requests, joblib, nltk, tweepy, numpy, firebase_admin, twitter_credentials, scipy, spellchecker, get_tweets, google, flask, keras, xgboost, suggest |
|
||||
|[sociallyhigh](https://github.com/NikhilSharmay/sociallyhigh ) |pkg_resources, sociallyhigh |
|
||||
|[sparky](https://github.com/perfecto25/sparky ) |sparky, config |
|
||||
|[speechRecognition_api](https://github.com/X-CCS/speechRecognition_api ) |librosa, sklearn, aukit, aip, Django_jianlong, videototextapp, videoaddsubtitleapp, myapp, IPython, my_app, other_app, djcelery, django, celery, video2audio_noiseReduction, keras |
|
||||
|[start-fastapi](https://github.com/utmhikari/start-fastapi ) |model, middleware, controller, service, application, dotenv |
|
||||
|[startapp](https://github.com/marlin-dev/startapp ) |extensions, marshmallow, setuptools, passlib, flask_env, flask_jwt_extended, sentry_sdk, flask_sqlalchemy, sqlalchemy_utils, gino, sqlalchemy, flask_marshmallow, takeaway, settings, flask_migrate, starlette_prometheus, flask, app_init, core|
|
||||
|[statsfy](https://github.com/skmatz/statsfy ) |spotifier |
|
||||
|[stock-tracker](https://github.com/jgabriel1/stock-tracker ) |pymongo, aiohttp, dotenv, pytest, jwt, server, requests, passlib |
|
||||
|[stores](https://github.com/dz-experts/grocery-stores-home-delivery ) |stores, pytest, api, util |
|
||||
|[storyboard_renderer](https://github.com/Weltii/storyboard_renderer ) |render_job, layouts, bridge, statics, layout_bridge, utils |
|
||||
|[summaries](https://github.com/bradstimpson/summaries ) |nltk, newspaper, pytest, tortoise |
|
||||
|[summarize-api](https://github.com/gary23w/summarize-api ) |nltk, newspaper, pytest, tortoise |
|
||||
|[surfacescan](https://github.com/vbogretsov/surfacescan ) |networkx, pytest, faker, atomiclong, locust, surfacescan, pkg_resources, tests |
|
||||
|[sweetHome](https://github.com/zkity/sweetHome ) |serial, lib |
|
||||
|[sync_async_compare](https://github.com/deepalikumar/sync_async_compare ) |blog, faker, marshmallow, flask_sqlalchemy, sqlalchemy_utils, flask_apispec, commands, sqlalchemy, myapp, resources, settings, flask_migrate, alembic, dotenv, flask, databases, pytest |
|
||||
|[takeAction-Backend](https://github.com/AndyKChen/takeAction-Backend ) |ml_controller, googlesearch, sklearn, getURL, bs4, keras, utils, numpy |
|
||||
|[tariffengineapi](https://github.com/SofieneEnnaoui/tariffengineapi ) |memory_profiler, redis, numba, tariffs_modules, tests, api_fastapi, memory |
|
||||
|[task_manager](https://github.com/VladOsiichuk/task_manager ) |passlib, gino, sqlalchemy, myapp, alembic, jwt |
|
||||
|[taskriptor-web](https://github.com/nasuka/taskriptor-web ) |alembic, sqlalchemy, myapp |
|
||||
|[tdd-fastapi-template](https://github.com/markusntz/tdd-fastapi-template ) |fastapi_tdd_docker, pytest, tortoise |
|
||||
|[teached](https://github.com/Mohamed-Kaizen/teached ) |pytest, pendulum, requests, passlib, loguru, tortoise, teached, typer, importlib_metadata, nox, usernames, dropbox, jwt, confusable_homoglyphs, username |
|
||||
|[techsoulai_backend](https://github.com/zhanymkanov/techsoulai_backend ) |pytest, jamspell, jwt, sqlalchemy, pymystem3, passlib |
|
||||
|[temperature-web](https://github.com/sj175/temperature-web ) |board, dotenv, adafruit_dht, boto3, requests |
|
||||
|[test_fastapi](https://github.com/leblancfg/test_fastapi ) |PIL, magic, fastapi_versioning, autocrop, cv2, requests, google |
|
||||
|[test_shop](https://github.com/Ayaks7/test_shop ) |requests, passlib, peewee, user_orders, my_app, other_app, django, yoyo, catalog, auth, jwt, pytest, peewee_async, tastypie |
|
||||
|[testfastapi](https://github.com/18438655078/testfastapi ) |tortoise, boto3 |
|
||||
|[todo-list-fastapi](https://github.com/mcauto/todo-list-fastapi ) |aiofiles, pytest, jwt, sqlalchemy, src, passlib |
|
||||
|[todoapp](https://github.com/prashunchitkr/todoapp ) |alembic, jwt, sqlalchemy, myapp, passlib |
|
||||
|[trainee_mi](https://github.com/goncharov-roman/trainee_mi ) |pymongo |
|
||||
|[transactions_api](https://github.com/txemac/transactions_api ) |pytest, sqlalchemy, sqlalchemy_utils, database |
|
||||
|[transformers](https://github.com/raphtlw/transformers ) |aitextgen |
|
||||
|[translator](https://github.com/eightytwo/translator ) |translator, schemas, api, httpx |
|
||||
|[try-fast-api](https://github.com/kayshcache/try-fast-api ) |dotenv |
|
||||
|[users-service](https://github.com/falled10/users-service ) |werkzeug, pytest, oauth2client, sqlalchemy_utils, api, config, main, repository, humps, sqlalchemy, myapp, alembic, jwt, routers, authlib, services |
|
||||
|[uvicorn-gunicorn-fastapi](https://github.com/dshadow/uvicorn-gunicorn-fastapi-docker ) |test_utils, docker, requests, pytest |
|
||||
|[vollmacht](https://github.com/ogeller/vollmacht ) |fitz |
|
||||
|[volunteer-database](https://github.com/crowdsourcemedical/volunteer-database ) |alembic, pytest, jwt, sqlalchemy, myapp, passlib |
|
||||
|[wamedex2](https://github.com/gwf-uwaterloo/wamedex2 ) |pytest, pyserini, hnswlib, pkg_resources, helper, freezegun, dateparser |
|
||||
|[wazo-router-calld](https://github.com/wazo-platform/wazo-router-calld ) |wazo_router_calld, consul, click, sqlalchemy, setuptools |
|
||||
|[web-api](https://github.com/aroio/web-api ) |exceptions, yaml, auth, jwt, routers, models, data |
|
||||
|[web-avatarify](https://github.com/charlielito/web-avatarify ) |PIL, afy, skimage, moviepy, requests, imageio, tqdm, animate, sync_batchnorm, ffmpeg, torch, matplotlib, scipy, face_alignment, yaml, python_path, cv2, modules |
|
||||
|[web-frameworks-benchmark](https://github.com/kesha1225/web-frameworks-benchmark ) |muffin, sanic, pyramid, falcon, requests, bottle, aiohttp, bobo, quart, hug, japronto, request_bench, my_app, other_app, django, flask, cherrypy, kumquat, freezy |
|
||||
|[web_speedtest](https://github.com/Kulikovpavel/web_speedtest ) |flask |
|
||||
|[weblink-downloader](https://github.com/cortexin/weblink-downloader ) |passlib, asyncpg, tests, link_downloader, databases, pytest, httpx |
|
||||
|[xo_back](https://github.com/octomen/xo_back ) |pytest, api |
|
||||
|[xpublish](https://github.com/xarray-contrib/xpublish ) |xarray, pytest, cachey, xpublish, setuptools, pkg_resources, h5py, netCDF4, numcodecs, dask, zarr |
|
||||
|[xraysink](https://github.com/garyd203/xraysink ) |aiohttp, jsonpickle, xraysink, async_asgi_testclient, aws_xray_sdk, requests, pytest |
|
||||
|[yappfm-backend](https://github.com/yappfm/yappfm-backend ) |alembic, sqlalchemy, myapp, yappfm, gino |
|
||||
|[yatsm](https://github.com/danielmartins/yatsm ) |dramatiq, walrus, pytest, pendulum, passlib, apscheduler, typer, assertpy, pytz, jwt, yatsm |
|
||||
|[yumemi_intern_API](https://github.com/tkrk1209/yumemi_intern_API ) |aiohttp, sqlalchemy, myapp, google, alembic |
|
||||
|[zo](https://github.com/wazo-platform/wazo-router-calld ) |PIL, requests, ssdb, setuptools, loguru, urllib3, zo, munch |
|
1
frontend/.env
Normal file
1
frontend/.env
Normal file
|
@ -0,0 +1 @@
|
|||
NEXT_PUBLIC_PROJECT_REPO_URL="https://github.com/Kludex/awesome-fastapi-projects"
|
3
frontend/.eslintrc.json
Normal file
3
frontend/.eslintrc.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"extends": ["next/core-web-vitals", "prettier"]
|
||||
}
|
35
frontend/.gitignore
vendored
Normal file
35
frontend/.gitignore
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
3
frontend/.prettierignore
Normal file
3
frontend/.prettierignore
Normal file
|
@ -0,0 +1,3 @@
|
|||
out
|
||||
.next
|
||||
next-env.d.ts
|
19
frontend/.prettierrc
Normal file
19
frontend/.prettierrc
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"arrowParens": "always",
|
||||
"bracketSameLine": false,
|
||||
"bracketSpacing": true,
|
||||
"semi": true,
|
||||
"singleQuote": false,
|
||||
"jsxSingleQuote": false,
|
||||
"quoteProps": "as-needed",
|
||||
"trailingComma": "all",
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"vueIndentScriptAndStyle": false,
|
||||
"proseWrap": "preserve",
|
||||
"insertPragma": false,
|
||||
"printWidth": 80,
|
||||
"requirePragma": false,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"embeddedLanguageFormatting": "auto"
|
||||
}
|
34
frontend/README.md
Normal file
34
frontend/README.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
16
frontend/components.json
Normal file
16
frontend/components.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "default",
|
||||
"rsc": true,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.ts",
|
||||
"css": "src/app/globals.css",
|
||||
"baseColor": "slate",
|
||||
"cssVariables": true
|
||||
},
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils"
|
||||
}
|
||||
}
|
22
frontend/next.config.js
Normal file
22
frontend/next.config.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
|
||||
const pkg = require("./package.json");
|
||||
|
||||
// starts a command line process to get the git hash
|
||||
const commitHash = require("child_process")
|
||||
.execSync('git log --pretty=format:"%h" -n1')
|
||||
.toString()
|
||||
.trim();
|
||||
|
||||
const nextConfig = {
|
||||
output: "export",
|
||||
basePath:
|
||||
// TODO: Change to load from env variable
|
||||
process.env.NODE_ENV === "production" ? "/awesome-fastapi-projects" : "",
|
||||
env: {
|
||||
commitHash,
|
||||
frontendAppVersion: pkg.version,
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
47
frontend/package.json
Normal file
47
frontend/package.json
Normal file
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"name": "frontend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"prettier:lint": "prettier --check .",
|
||||
"prettier:format": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^3.3.1",
|
||||
"@orama/orama": "^1.2.3",
|
||||
"@radix-ui/react-avatar": "^1.0.3",
|
||||
"@radix-ui/react-dialog": "^1.0.4",
|
||||
"@radix-ui/react-label": "^2.0.2",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@tanstack/react-table": "^8.9.3",
|
||||
"@tanstack/react-virtual": "3.0.0-alpha.0",
|
||||
"@types/node": "20.5.1",
|
||||
"@types/react": "18.2.20",
|
||||
"@types/react-dom": "18.2.7",
|
||||
"autoprefixer": "10.4.15",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"cmdk": "^0.2.0",
|
||||
"eslint": "8.47.0",
|
||||
"eslint-config-next": "13.4.18",
|
||||
"lucide-react": "^0.269.0",
|
||||
"next": "13.4.18",
|
||||
"postcss": "8.4.28",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hook-form": "^7.46.1",
|
||||
"tailwind-merge": "^1.14.0",
|
||||
"tailwindcss": "3.3.3",
|
||||
"tailwindcss-animate": "^1.0.6",
|
||||
"typescript": "5.1.6",
|
||||
"zod": "^3.21.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"prettier": "^3.0.3"
|
||||
}
|
||||
}
|
4475
frontend/pnpm-lock.yaml
Normal file
4475
frontend/pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load Diff
6
frontend/postcss.config.js
Normal file
6
frontend/postcss.config.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
1
frontend/public/next.svg
Normal file
1
frontend/public/next.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
After Width: | Height: | Size: 1.3 KiB |
1
frontend/public/vercel.svg
Normal file
1
frontend/public/vercel.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>
|
After Width: | Height: | Size: 630 B |
94
frontend/src/app/columns.tsx
Normal file
94
frontend/src/app/columns.tsx
Normal file
|
@ -0,0 +1,94 @@
|
|||
"use client";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Repo } from "@/lib/schemas";
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import { ArrowUpDown, MoreHorizontal } from "lucide-react";
|
||||
|
||||
export const columns: ColumnDef<Repo>[] = [
|
||||
{
|
||||
accessorKey: "url",
|
||||
header: function () {
|
||||
return (
|
||||
<div>
|
||||
Project
|
||||
<span role="img" aria-label="rocket" className="ml-1">
|
||||
🚀
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
cell: function ({ row }) {
|
||||
return (
|
||||
<Button variant="link" className="text-accent-foreground">
|
||||
<svg
|
||||
width="15"
|
||||
height="15"
|
||||
viewBox="0 0 15 15"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M7.49933 0.25C3.49635 0.25 0.25 3.49593 0.25 7.50024C0.25 10.703 2.32715 13.4206 5.2081 14.3797C5.57084 14.446 5.70302 14.2222 5.70302 14.0299C5.70302 13.8576 5.69679 13.4019 5.69323 12.797C3.67661 13.235 3.25112 11.825 3.25112 11.825C2.92132 10.9874 2.44599 10.7644 2.44599 10.7644C1.78773 10.3149 2.49584 10.3238 2.49584 10.3238C3.22353 10.375 3.60629 11.0711 3.60629 11.0711C4.25298 12.1788 5.30335 11.8588 5.71638 11.6732C5.78225 11.205 5.96962 10.8854 6.17658 10.7043C4.56675 10.5209 2.87415 9.89918 2.87415 7.12104C2.87415 6.32925 3.15677 5.68257 3.62053 5.17563C3.54576 4.99226 3.29697 4.25521 3.69174 3.25691C3.69174 3.25691 4.30015 3.06196 5.68522 3.99973C6.26337 3.83906 6.8838 3.75895 7.50022 3.75583C8.1162 3.75895 8.73619 3.83906 9.31523 3.99973C10.6994 3.06196 11.3069 3.25691 11.3069 3.25691C11.7026 4.25521 11.4538 4.99226 11.3795 5.17563C11.8441 5.68257 12.1245 6.32925 12.1245 7.12104C12.1245 9.9063 10.4292 10.5192 8.81452 10.6985C9.07444 10.9224 9.30633 11.3648 9.30633 12.0413C9.30633 13.0102 9.29742 13.7922 9.29742 14.0299C9.29742 14.2239 9.42828 14.4496 9.79591 14.3788C12.6746 13.4179 14.75 10.7025 14.75 7.50024C14.75 3.49593 11.5036 0.25 7.49933 0.25Z"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
></path>
|
||||
</svg>
|
||||
<a
|
||||
className="ml-1"
|
||||
href={row.getValue<string>("url")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Github
|
||||
</a>
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "description",
|
||||
header: function () {
|
||||
return (
|
||||
<div>
|
||||
Description
|
||||
<span role="img" aria-label="writing" className="ml-1">
|
||||
✍️
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
cell: function ({ row }) {
|
||||
return (
|
||||
<p className="max-w-md truncate hover:overflow-visible hover:whitespace-normal leading-7 [&:not(:first-child)]:mt-6">
|
||||
{row.getValue<string>("description") || "No description"}
|
||||
</p>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "stars",
|
||||
header: function ({ column }) {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
Stars
|
||||
<span role="img" aria-label="star" className="ml-1">
|
||||
⭐
|
||||
</span>
|
||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
cell: function ({ row }) {
|
||||
return (
|
||||
<Badge variant="outline">
|
||||
{row.getValue<number>("stars").toLocaleString("en-US")}
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
118
frontend/src/app/data-table.tsx
Normal file
118
frontend/src/app/data-table.tsx
Normal file
|
@ -0,0 +1,118 @@
|
|||
"use client";
|
||||
|
||||
import {
|
||||
ColumnDef,
|
||||
SortingState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table";
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useState } from "react";
|
||||
|
||||
interface DataTableProps<TData, TValue> {
|
||||
columns: ColumnDef<TData, TValue>[];
|
||||
data: TData[];
|
||||
}
|
||||
|
||||
export function DataTable<TData, TValue>({
|
||||
columns,
|
||||
data,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const [sorting, setSorting] = useState<SortingState>([]);
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
onSortingChange: setSorting,
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
state: {
|
||||
sorting,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext(),
|
||||
)}
|
||||
</TableHead>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext(),
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={columns.length}
|
||||
className="h-24 text-center"
|
||||
>
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
<div className="flex items-center justify-end space-x-2 py-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
31
frontend/src/app/dependencies-search-provider.tsx
Normal file
31
frontend/src/app/dependencies-search-provider.tsx
Normal file
|
@ -0,0 +1,31 @@
|
|||
"use client";
|
||||
import {
|
||||
createDependenciesOrama,
|
||||
prepareDependenciesOramaIndex,
|
||||
DependenciesOramaContext,
|
||||
} from "@/lib/search";
|
||||
import { PropsWithChildren } from "react";
|
||||
import { SearchProvider } from "./search-provider";
|
||||
import { DependenciesIndex } from "@/lib/schemas";
|
||||
|
||||
export function DependenciesSearchProvider({
|
||||
children,
|
||||
dependencies,
|
||||
}: PropsWithChildren<{
|
||||
dependencies: DependenciesIndex["dependencies"];
|
||||
}>) {
|
||||
const prepareOramaIndex = async () => {
|
||||
const orama = await createDependenciesOrama();
|
||||
await prepareDependenciesOramaIndex(orama, dependencies);
|
||||
return orama;
|
||||
};
|
||||
|
||||
return (
|
||||
<SearchProvider
|
||||
createIndex={prepareOramaIndex}
|
||||
OramaContext={DependenciesOramaContext}
|
||||
>
|
||||
{children}
|
||||
</SearchProvider>
|
||||
);
|
||||
}
|
BIN
frontend/src/app/favicon.ico
Normal file
BIN
frontend/src/app/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
59
frontend/src/app/globals.css
Normal file
59
frontend/src/app/globals.css
Normal file
|
@ -0,0 +1,59 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 240 10% 3.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 240 10% 3.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 240 10% 3.9%;
|
||||
--primary: 142.1 76.2% 36.3%;
|
||||
--primary-foreground: 355.7 100% 97.3%;
|
||||
--secondary: 240 4.8% 95.9%;
|
||||
--secondary-foreground: 240 5.9% 10%;
|
||||
--muted: 240 4.8% 95.9%;
|
||||
--muted-foreground: 240 3.8% 46.1%;
|
||||
--accent: 240 4.8% 95.9%;
|
||||
--accent-foreground: 240 5.9% 10%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--border: 240 5.9% 90%;
|
||||
--input: 240 5.9% 90%;
|
||||
--ring: 142.1 76.2% 36.3%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 20 14.3% 4.1%;
|
||||
--foreground: 0 0% 95%;
|
||||
--card: 24 9.8% 10%;
|
||||
--card-foreground: 0 0% 95%;
|
||||
--popover: 0 0% 9%;
|
||||
--popover-foreground: 0 0% 95%;
|
||||
--primary: 142.1 70.6% 45.3%;
|
||||
--primary-foreground: 144.9 80.4% 10%;
|
||||
--secondary: 240 3.7% 15.9%;
|
||||
--secondary-foreground: 0 0% 98%;
|
||||
--muted: 0 0% 15%;
|
||||
--muted-foreground: 240 5% 64.9%;
|
||||
--accent: 12 6.5% 15.1%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 0 85.7% 97.3%;
|
||||
--border: 240 3.7% 15.9%;
|
||||
--input: 240 3.7% 15.9%;
|
||||
--ring: 142.4 71.8% 29.2%;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
147
frontend/src/app/layout.tsx
Normal file
147
frontend/src/app/layout.tsx
Normal file
|
@ -0,0 +1,147 @@
|
|||
import { cn } from "@/lib/utils";
|
||||
import "./globals.css";
|
||||
import type { Metadata } from "next";
|
||||
import { Inter } from "next/font/google";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { Icons } from "@/components/ui/icons";
|
||||
import Link from "next/link";
|
||||
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
||||
import { FolderGit, Rocket } from "lucide-react";
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Awesome FastAPI projects",
|
||||
description: "An automatically generated list of awesome FastAPI projects",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>
|
||||
<div className="hidden flex-col md:flex">
|
||||
<div className="border-b">
|
||||
<div className="w-full flex h-16 items-center justify-center px-4">
|
||||
<nav className="container flex items-center justify-between space-x-4 lg:space-x-6 mx-6">
|
||||
<p className="text-md font-medium transition-colors">
|
||||
A list of FastAPI projects! 😎 🚀
|
||||
</p>
|
||||
<Link
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href={process.env.NEXT_PUBLIC_PROJECT_REPO_URL ?? ""}
|
||||
className={cn(
|
||||
buttonVariants({ variant: "ghost" }),
|
||||
"h-6 w-6 flex items-center justify-center p-0 rounded-full",
|
||||
)}
|
||||
>
|
||||
<Icons.gitHub className="h-6 w-6" />
|
||||
</Link>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<main className="container">
|
||||
<h1 className="scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl text-center mt-8">
|
||||
Awesome FastAPI projects{" "}
|
||||
<span role="img" aria-label="party">
|
||||
🎉
|
||||
</span>
|
||||
</h1>
|
||||
{children}
|
||||
</main>
|
||||
<div className="border-t">
|
||||
<footer className="bg-white dark:bg-gray-900">
|
||||
<div className="w-full max-w-screen-xl mx-auto p-4 md:py-8">
|
||||
<div className="sm:flex sm:items-center sm:justify-between gap-4">
|
||||
<div className="grid gap-4 md:grid-cols-1 lg:grid-cols-2">
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-sm font-medium">
|
||||
Revision
|
||||
</CardTitle>
|
||||
<FolderGit className="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-xl font-bold">
|
||||
{process.env.commitHash}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 space-x-4 pb-2">
|
||||
<CardTitle className="text-sm font-medium">
|
||||
Application Version
|
||||
</CardTitle>
|
||||
<Rocket className="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-xl font-bold">
|
||||
{process.env.frontendAppVersion}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
<div className="flex flex-col mt-4 sm:mt-0 sm:flex-row sm:items-center sm:justify-between">
|
||||
<p className="text-sm leading-7 [&:not(:first-child)]:mt-6">
|
||||
This project wouldn't be possible without{" "}
|
||||
<a
|
||||
href="https://nextjs.org/"
|
||||
className="font-bold hover:underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Next.js
|
||||
</a>
|
||||
,{" "}
|
||||
<a
|
||||
href="https://ui.shadcn.com/"
|
||||
className="font-bold hover:underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Shadcn UI
|
||||
</a>
|
||||
,{" "}
|
||||
<a
|
||||
href="https://tailwindcss.com/"
|
||||
className="font-bold hover:underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Tailwind CSS
|
||||
</a>
|
||||
,{" "}
|
||||
<a
|
||||
href="https://react.dev/"
|
||||
className="font-bold hover:underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
React
|
||||
</a>{" "}
|
||||
and{" "}
|
||||
<a
|
||||
href="https://www.oramasearch.com/"
|
||||
className="font-bold hover:underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Orama Search
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
// TODO: improve pagination - sync with the URL, add a "go to page" input
|
||||
// TODO: refactor the layout and the components
|
22
frontend/src/app/page.tsx
Normal file
22
frontend/src/app/page.tsx
Normal file
|
@ -0,0 +1,22 @@
|
|||
import {
|
||||
loadDependenciesIndexServerOnly,
|
||||
loadReposIndexServerOnly,
|
||||
} from "@/lib/indexes";
|
||||
import { ReposTable } from "./repos-table";
|
||||
import { ReposSearchProvider } from "./repos-search-provider";
|
||||
import { DependenciesSearchProvider } from "./dependencies-search-provider";
|
||||
|
||||
export default async function Home() {
|
||||
const { repos } = await loadReposIndexServerOnly();
|
||||
const { dependencies } = await loadDependenciesIndexServerOnly();
|
||||
// refactor repos and dependencies to be loaded from the context
|
||||
return (
|
||||
<section className="py-10">
|
||||
<ReposSearchProvider repos={repos}>
|
||||
<DependenciesSearchProvider dependencies={dependencies}>
|
||||
<ReposTable repos={repos} dependencies={dependencies} />
|
||||
</DependenciesSearchProvider>
|
||||
</ReposSearchProvider>
|
||||
</section>
|
||||
);
|
||||
}
|
31
frontend/src/app/repos-search-provider.tsx
Normal file
31
frontend/src/app/repos-search-provider.tsx
Normal file
|
@ -0,0 +1,31 @@
|
|||
"use client";
|
||||
import {
|
||||
createReposOrama,
|
||||
prepareReposOramaIndex,
|
||||
ReposOramaContext,
|
||||
} from "@/lib/search";
|
||||
import { PropsWithChildren } from "react";
|
||||
import { SearchProvider } from "./search-provider";
|
||||
import { RepoIndex } from "@/lib/schemas";
|
||||
|
||||
export function ReposSearchProvider({
|
||||
children,
|
||||
repos,
|
||||
}: PropsWithChildren<{
|
||||
repos: RepoIndex["repos"];
|
||||
}>) {
|
||||
const prepareOramaIndex = async () => {
|
||||
const orama = await createReposOrama();
|
||||
await prepareReposOramaIndex(orama, repos);
|
||||
return orama;
|
||||
};
|
||||
|
||||
return (
|
||||
<SearchProvider
|
||||
createIndex={prepareOramaIndex}
|
||||
OramaContext={ReposOramaContext}
|
||||
>
|
||||
{children}
|
||||
</SearchProvider>
|
||||
);
|
||||
}
|
82
frontend/src/app/repos-table.tsx
Normal file
82
frontend/src/app/repos-table.tsx
Normal file
|
@ -0,0 +1,82 @@
|
|||
"use client";
|
||||
import { Repo, Dependency } from "@/lib/schemas";
|
||||
import { search } from "@orama/orama";
|
||||
import { SearchForm } from "./search-form";
|
||||
import { columns } from "./columns";
|
||||
import { DataTable } from "./data-table";
|
||||
import { useReposOrama } from "@/lib/search";
|
||||
import { useState } from "react";
|
||||
import { useQuerySearchFormData } from "@/lib/hooks";
|
||||
import React from "react";
|
||||
|
||||
export function ReposTable({
|
||||
repos,
|
||||
dependencies,
|
||||
}: {
|
||||
repos: Repo[];
|
||||
dependencies: Dependency[];
|
||||
}) {
|
||||
const reposOrama = useReposOrama();
|
||||
const [searchedRepos, setSearchedRepos] = useState<Repo[]>(repos);
|
||||
const { searchQueryFromQueryParam, dependenciesQueryFromQueryParam } =
|
||||
useQuerySearchFormData(dependencies);
|
||||
|
||||
const onSearchSubmit = React.useCallback(
|
||||
async ({
|
||||
search: description,
|
||||
dependencies,
|
||||
}: {
|
||||
search: string;
|
||||
dependencies: Dependency[];
|
||||
}) => {
|
||||
if (!reposOrama.isIndexed || !reposOrama.orama) {
|
||||
throw new Error("Repos Orama is not initialized");
|
||||
}
|
||||
const results = await search<Repo>(reposOrama.orama, {
|
||||
term: description,
|
||||
properties: ["description"],
|
||||
limit: repos.length,
|
||||
});
|
||||
const searchedRepos = results.hits.map((hit) => hit.document as Repo);
|
||||
// Workaround because Orama doesn't support filtering by properties of objects in arrays
|
||||
const filteredRepos = searchedRepos.filter((repo) => {
|
||||
return dependencies.every((dependency) => {
|
||||
return repo.dependencies.some(
|
||||
(repoDependency) => repoDependency.id === dependency.id,
|
||||
);
|
||||
});
|
||||
});
|
||||
setSearchedRepos(filteredRepos);
|
||||
},
|
||||
[repos, reposOrama.isIndexed, reposOrama.orama],
|
||||
);
|
||||
|
||||
const _ref = React.useCallback(
|
||||
(node: HTMLDivElement | null) => {
|
||||
if (node !== null) {
|
||||
if (reposOrama.isIndexed && reposOrama.orama) {
|
||||
onSearchSubmit({
|
||||
search: searchQueryFromQueryParam(),
|
||||
dependencies: dependenciesQueryFromQueryParam(),
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
dependenciesQueryFromQueryParam,
|
||||
onSearchSubmit,
|
||||
reposOrama.isIndexed,
|
||||
reposOrama.orama,
|
||||
searchQueryFromQueryParam,
|
||||
],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="container mb-4 max-w-xl" ref={_ref}>
|
||||
<SearchForm onSubmit={onSearchSubmit} dependencies={dependencies} />
|
||||
</div>
|
||||
<DataTable columns={columns} data={searchedRepos} />
|
||||
</>
|
||||
);
|
||||
}
|
125
frontend/src/app/search-form.tsx
Normal file
125
frontend/src/app/search-form.tsx
Normal file
|
@ -0,0 +1,125 @@
|
|||
"use client";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useForm } from "react-hook-form";
|
||||
import * as z from "zod";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { MultiSelect } from "@/components/ui/multiselect";
|
||||
import { Dependency, dependencySchema } from "@/lib/schemas";
|
||||
import { useSearchParams, useRouter, usePathname } from "next/navigation";
|
||||
import React from "react";
|
||||
import { useQuerySearchFormData } from "@/lib/hooks";
|
||||
|
||||
const FormSchema = z.object({
|
||||
search: z
|
||||
.string()
|
||||
.min(0)
|
||||
.max(256, { message: "Search must be less than 256 characters" })
|
||||
.default(""),
|
||||
dependencies: z.array(dependencySchema).default(() => []),
|
||||
});
|
||||
|
||||
export interface SearchFormProps {
|
||||
onSubmit: (data: z.infer<typeof FormSchema>) => void;
|
||||
dependencies: Dependency[];
|
||||
}
|
||||
|
||||
export function SearchForm({ onSubmit, dependencies }: SearchFormProps) {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const {
|
||||
searchQueryFromQueryParam,
|
||||
searchQueryToQueryParam,
|
||||
dependenciesQueryFromQueryParam,
|
||||
dependenciesQueryToQueryParam,
|
||||
} = useQuerySearchFormData(dependencies);
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues: {
|
||||
search: searchQueryFromQueryParam(),
|
||||
dependencies: dependenciesQueryFromQueryParam(),
|
||||
},
|
||||
});
|
||||
|
||||
const createQueryString = React.useCallback(
|
||||
({
|
||||
searchQueryValue,
|
||||
dependenciesQueryValue,
|
||||
}: {
|
||||
searchQueryValue: string;
|
||||
dependenciesQueryValue: Dependency[];
|
||||
}) => {
|
||||
const params = new URLSearchParams(searchParams);
|
||||
params.set("search", searchQueryToQueryParam(searchQueryValue));
|
||||
params.set(
|
||||
"dependencies",
|
||||
dependenciesQueryToQueryParam(dependenciesQueryValue),
|
||||
);
|
||||
|
||||
return params.toString();
|
||||
},
|
||||
[dependenciesQueryToQueryParam, searchParams, searchQueryToQueryParam],
|
||||
);
|
||||
|
||||
const onSubmitWrapper = (data: z.infer<typeof FormSchema>) => {
|
||||
onSubmit(data);
|
||||
// update URL search params
|
||||
const queryString = createQueryString({
|
||||
searchQueryValue: data.search,
|
||||
dependenciesQueryValue: data.dependencies,
|
||||
});
|
||||
router.replace(`${pathname}?${queryString}`);
|
||||
};
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmitWrapper)} className="space-y-6">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="search"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Search for a repository</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="Search..." {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
The search is performed on the repository description.
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="dependencies"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Dependencies</FormLabel>
|
||||
<FormControl>
|
||||
<MultiSelect data={dependencies} {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
Filter by dependencies used in the repository.
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Button type="submit">Search</Button>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
}
|
38
frontend/src/app/search-provider.tsx
Normal file
38
frontend/src/app/search-provider.tsx
Normal file
|
@ -0,0 +1,38 @@
|
|||
"use client";
|
||||
import { IOramaContext } from "@/lib/search";
|
||||
import { Context, PropsWithChildren, useEffect, useState } from "react";
|
||||
|
||||
import { Orama, ProvidedTypes as OramaProvidedTypes } from "@orama/orama";
|
||||
|
||||
export type SearchProviderProps<
|
||||
OramaParameters extends Partial<OramaProvidedTypes> = any,
|
||||
> = PropsWithChildren<{
|
||||
OramaContext: Context<IOramaContext<OramaParameters>>;
|
||||
createIndex: () => Promise<Orama<OramaParameters>>;
|
||||
}>;
|
||||
|
||||
export function SearchProvider<
|
||||
OramaParameters extends Partial<OramaProvidedTypes>,
|
||||
>({
|
||||
children,
|
||||
OramaContext,
|
||||
createIndex,
|
||||
}: SearchProviderProps<OramaParameters>) {
|
||||
const [orama, setOrama] = useState<Orama<OramaParameters> | null>(null);
|
||||
const [isIndexed, setIsIndexed] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
async function initializeOrama() {
|
||||
setIsIndexed(false);
|
||||
await createIndex().then(setOrama);
|
||||
setIsIndexed(true);
|
||||
}
|
||||
initializeOrama();
|
||||
}, [createIndex]);
|
||||
|
||||
return (
|
||||
<OramaContext.Provider value={{ orama, isIndexed }}>
|
||||
{children}
|
||||
</OramaContext.Provider>
|
||||
);
|
||||
}
|
36
frontend/src/components/ui/badge.tsx
Normal file
36
frontend/src/components/ui/badge.tsx
Normal file
|
@ -0,0 +1,36 @@
|
|||
import * as React from "react";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
|
||||
secondary:
|
||||
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
destructive:
|
||||
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
||||
outline: "text-foreground",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export interface BadgeProps
|
||||
extends React.HTMLAttributes<HTMLDivElement>,
|
||||
VariantProps<typeof badgeVariants> {}
|
||||
|
||||
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||
return (
|
||||
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
export { Badge, badgeVariants };
|
56
frontend/src/components/ui/button.tsx
Normal file
56
frontend/src/components/ui/button.tsx
Normal file
|
@ -0,0 +1,56 @@
|
|||
import * as React from "react";
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||
outline:
|
||||
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-4 py-2",
|
||||
sm: "h-9 rounded-md px-3",
|
||||
lg: "h-11 rounded-md px-8",
|
||||
icon: "h-10 w-10",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export interface ButtonProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {
|
||||
asChild?: boolean;
|
||||
}
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : "button";
|
||||
return (
|
||||
<Comp
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
Button.displayName = "Button";
|
||||
|
||||
export { Button, buttonVariants };
|
86
frontend/src/components/ui/card.tsx
Normal file
86
frontend/src/components/ui/card.tsx
Normal file
|
@ -0,0 +1,86 @@
|
|||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Card = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"rounded-lg border bg-card text-card-foreground shadow-sm",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
Card.displayName = "Card";
|
||||
|
||||
const CardHeader = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
CardHeader.displayName = "CardHeader";
|
||||
|
||||
const CardTitle = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLHeadingElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<h3
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"text-2xl font-semibold leading-none tracking-tight",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
CardTitle.displayName = "CardTitle";
|
||||
|
||||
const CardDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<p
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
CardDescription.displayName = "CardDescription";
|
||||
|
||||
const CardContent = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
||||
));
|
||||
CardContent.displayName = "CardContent";
|
||||
|
||||
const CardFooter = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("flex items-center p-6 pt-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
CardFooter.displayName = "CardFooter";
|
||||
|
||||
export {
|
||||
Card,
|
||||
CardHeader,
|
||||
CardFooter,
|
||||
CardTitle,
|
||||
CardDescription,
|
||||
CardContent,
|
||||
};
|
155
frontend/src/components/ui/command.tsx
Normal file
155
frontend/src/components/ui/command.tsx
Normal file
|
@ -0,0 +1,155 @@
|
|||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import { DialogProps } from "@radix-ui/react-dialog";
|
||||
import { Command as CommandPrimitive } from "cmdk";
|
||||
import { Search } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Dialog, DialogContent } from "@/components/ui/dialog";
|
||||
|
||||
const Command = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive>,
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<CommandPrimitive
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
Command.displayName = CommandPrimitive.displayName;
|
||||
|
||||
interface CommandDialogProps extends DialogProps {}
|
||||
|
||||
const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
|
||||
return (
|
||||
<Dialog {...props}>
|
||||
<DialogContent className="overflow-hidden p-0 shadow-lg">
|
||||
<Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
|
||||
{children}
|
||||
</Command>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
const CommandInput = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive.Input>,
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div className="flex items-center border-b px-3" cmdk-input-wrapper="">
|
||||
<Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
<CommandPrimitive.Input
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
));
|
||||
|
||||
CommandInput.displayName = CommandPrimitive.Input.displayName;
|
||||
|
||||
const CommandList = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive.List>,
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<CommandPrimitive.List
|
||||
ref={ref}
|
||||
className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
||||
CommandList.displayName = CommandPrimitive.List.displayName;
|
||||
|
||||
const CommandEmpty = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive.Empty>,
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
|
||||
>((props, ref) => (
|
||||
<CommandPrimitive.Empty
|
||||
ref={ref}
|
||||
className="py-6 text-center text-sm"
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
||||
CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
|
||||
|
||||
const CommandGroup = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive.Group>,
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<CommandPrimitive.Group
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
||||
CommandGroup.displayName = CommandPrimitive.Group.displayName;
|
||||
|
||||
const CommandSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<CommandPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn("-mx-1 h-px bg-border", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
|
||||
|
||||
const CommandItem = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<CommandPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
||||
CommandItem.displayName = CommandPrimitive.Item.displayName;
|
||||
|
||||
const CommandShortcut = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLSpanElement>) => {
|
||||
return (
|
||||
<span
|
||||
className={cn(
|
||||
"ml-auto text-xs tracking-widest text-muted-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
CommandShortcut.displayName = "CommandShortcut";
|
||||
|
||||
export {
|
||||
Command,
|
||||
CommandDialog,
|
||||
CommandInput,
|
||||
CommandList,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandItem,
|
||||
CommandShortcut,
|
||||
CommandSeparator,
|
||||
};
|
123
frontend/src/components/ui/dialog.tsx
Normal file
123
frontend/src/components/ui/dialog.tsx
Normal file
|
@ -0,0 +1,123 @@
|
|||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
||||
import { X } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Dialog = DialogPrimitive.Root;
|
||||
|
||||
const DialogTrigger = DialogPrimitive.Trigger;
|
||||
|
||||
const DialogPortal = ({
|
||||
className,
|
||||
...props
|
||||
}: DialogPrimitive.DialogPortalProps) => (
|
||||
<DialogPrimitive.Portal className={cn(className)} {...props} />
|
||||
);
|
||||
DialogPortal.displayName = DialogPrimitive.Portal.displayName;
|
||||
|
||||
const DialogOverlay = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Overlay
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
||||
|
||||
const DialogContent = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<DialogPortal>
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
||||
<X className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
));
|
||||
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
||||
|
||||
const DialogHeader = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col space-y-1.5 text-center sm:text-left",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
DialogHeader.displayName = "DialogHeader";
|
||||
|
||||
const DialogFooter = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
DialogFooter.displayName = "DialogFooter";
|
||||
|
||||
const DialogTitle = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"text-lg font-semibold leading-none tracking-tight",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
||||
|
||||
const DialogDescription = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
||||
|
||||
export {
|
||||
Dialog,
|
||||
DialogTrigger,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogFooter,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
};
|
177
frontend/src/components/ui/form.tsx
Normal file
177
frontend/src/components/ui/form.tsx
Normal file
|
@ -0,0 +1,177 @@
|
|||
import * as React from "react";
|
||||
import * as LabelPrimitive from "@radix-ui/react-label";
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import {
|
||||
Controller,
|
||||
ControllerProps,
|
||||
FieldPath,
|
||||
FieldValues,
|
||||
FormProvider,
|
||||
useFormContext,
|
||||
} from "react-hook-form";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Label } from "@/components/ui/label";
|
||||
|
||||
const Form = FormProvider;
|
||||
|
||||
type FormFieldContextValue<
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
||||
> = {
|
||||
name: TName;
|
||||
};
|
||||
|
||||
const FormFieldContext = React.createContext<FormFieldContextValue>(
|
||||
{} as FormFieldContextValue,
|
||||
);
|
||||
|
||||
const FormField = <
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
||||
>({
|
||||
...props
|
||||
}: ControllerProps<TFieldValues, TName>) => {
|
||||
return (
|
||||
<FormFieldContext.Provider value={{ name: props.name }}>
|
||||
<Controller {...props} />
|
||||
</FormFieldContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const useFormField = () => {
|
||||
const fieldContext = React.useContext(FormFieldContext);
|
||||
const itemContext = React.useContext(FormItemContext);
|
||||
const { getFieldState, formState } = useFormContext();
|
||||
|
||||
const fieldState = getFieldState(fieldContext.name, formState);
|
||||
|
||||
if (!fieldContext) {
|
||||
throw new Error("useFormField should be used within <FormField>");
|
||||
}
|
||||
|
||||
const { id } = itemContext;
|
||||
|
||||
return {
|
||||
id,
|
||||
name: fieldContext.name,
|
||||
formItemId: `${id}-form-item`,
|
||||
formDescriptionId: `${id}-form-item-description`,
|
||||
formMessageId: `${id}-form-item-message`,
|
||||
...fieldState,
|
||||
};
|
||||
};
|
||||
|
||||
type FormItemContextValue = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
const FormItemContext = React.createContext<FormItemContextValue>(
|
||||
{} as FormItemContextValue,
|
||||
);
|
||||
|
||||
const FormItem = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const id = React.useId();
|
||||
|
||||
return (
|
||||
<FormItemContext.Provider value={{ id }}>
|
||||
<div ref={ref} className={cn("space-y-2", className)} {...props} />
|
||||
</FormItemContext.Provider>
|
||||
);
|
||||
});
|
||||
FormItem.displayName = "FormItem";
|
||||
|
||||
const FormLabel = React.forwardRef<
|
||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { error, formItemId } = useFormField();
|
||||
|
||||
return (
|
||||
<Label
|
||||
ref={ref}
|
||||
className={cn(error && "text-destructive", className)}
|
||||
htmlFor={formItemId}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
FormLabel.displayName = "FormLabel";
|
||||
|
||||
const FormControl = React.forwardRef<
|
||||
React.ElementRef<typeof Slot>,
|
||||
React.ComponentPropsWithoutRef<typeof Slot>
|
||||
>(({ ...props }, ref) => {
|
||||
const { error, formItemId, formDescriptionId, formMessageId } =
|
||||
useFormField();
|
||||
|
||||
return (
|
||||
<Slot
|
||||
ref={ref}
|
||||
id={formItemId}
|
||||
aria-describedby={
|
||||
!error
|
||||
? `${formDescriptionId}`
|
||||
: `${formDescriptionId} ${formMessageId}`
|
||||
}
|
||||
aria-invalid={!!error}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
FormControl.displayName = "FormControl";
|
||||
|
||||
const FormDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { formDescriptionId } = useFormField();
|
||||
|
||||
return (
|
||||
<p
|
||||
ref={ref}
|
||||
id={formDescriptionId}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
FormDescription.displayName = "FormDescription";
|
||||
|
||||
const FormMessage = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, children, ...props }, ref) => {
|
||||
const { error, formMessageId } = useFormField();
|
||||
const body = error ? String(error?.message) : children;
|
||||
|
||||
if (!body) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<p
|
||||
ref={ref}
|
||||
id={formMessageId}
|
||||
className={cn("text-sm font-medium text-destructive", className)}
|
||||
{...props}
|
||||
>
|
||||
{body}
|
||||
</p>
|
||||
);
|
||||
});
|
||||
FormMessage.displayName = "FormMessage";
|
||||
|
||||
export {
|
||||
useFormField,
|
||||
Form,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormMessage,
|
||||
FormField,
|
||||
};
|
97
frontend/src/components/ui/icons.tsx
Normal file
97
frontend/src/components/ui/icons.tsx
Normal file
|
@ -0,0 +1,97 @@
|
|||
type IconProps = React.HTMLAttributes<SVGElement>;
|
||||
|
||||
export const Icons = {
|
||||
logo: (props: IconProps) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" {...props}>
|
||||
<rect width="256" height="256" fill="none" />
|
||||
<line
|
||||
x1="208"
|
||||
y1="128"
|
||||
x2="128"
|
||||
y2="208"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="16"
|
||||
/>
|
||||
<line
|
||||
x1="192"
|
||||
y1="40"
|
||||
x2="40"
|
||||
y2="192"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="16"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
gitHub: (props: IconProps) => (
|
||||
<svg viewBox="0 0 438.549 438.549" {...props}>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M409.132 114.573c-19.608-33.596-46.205-60.194-79.798-79.8-33.598-19.607-70.277-29.408-110.063-29.408-39.781 0-76.472 9.804-110.063 29.408-33.596 19.605-60.192 46.204-79.8 79.8C9.803 148.168 0 184.854 0 224.63c0 47.78 13.94 90.745 41.827 128.906 27.884 38.164 63.906 64.572 108.063 79.227 5.14.954 8.945.283 11.419-1.996 2.475-2.282 3.711-5.14 3.711-8.562 0-.571-.049-5.708-.144-15.417a2549.81 2549.81 0 01-.144-25.406l-6.567 1.136c-4.187.767-9.469 1.092-15.846 1-6.374-.089-12.991-.757-19.842-1.999-6.854-1.231-13.229-4.086-19.13-8.559-5.898-4.473-10.085-10.328-12.56-17.556l-2.855-6.57c-1.903-4.374-4.899-9.233-8.992-14.559-4.093-5.331-8.232-8.945-12.419-10.848l-1.999-1.431c-1.332-.951-2.568-2.098-3.711-3.429-1.142-1.331-1.997-2.663-2.568-3.997-.572-1.335-.098-2.43 1.427-3.289 1.525-.859 4.281-1.276 8.28-1.276l5.708.853c3.807.763 8.516 3.042 14.133 6.851 5.614 3.806 10.229 8.754 13.846 14.842 4.38 7.806 9.657 13.754 15.846 17.847 6.184 4.093 12.419 6.136 18.699 6.136 6.28 0 11.704-.476 16.274-1.423 4.565-.952 8.848-2.383 12.847-4.285 1.713-12.758 6.377-22.559 13.988-29.41-10.848-1.14-20.601-2.857-29.264-5.14-8.658-2.286-17.605-5.996-26.835-11.14-9.235-5.137-16.896-11.516-22.985-19.126-6.09-7.614-11.088-17.61-14.987-29.979-3.901-12.374-5.852-26.648-5.852-42.826 0-23.035 7.52-42.637 22.557-58.817-7.044-17.318-6.379-36.732 1.997-58.24 5.52-1.715 13.706-.428 24.554 3.853 10.85 4.283 18.794 7.952 23.84 10.994 5.046 3.041 9.089 5.618 12.135 7.708 17.705-4.947 35.976-7.421 54.818-7.421s37.117 2.474 54.823 7.421l10.849-6.849c7.419-4.57 16.18-8.758 26.262-12.565 10.088-3.805 17.802-4.853 23.134-3.138 8.562 21.509 9.325 40.922 2.279 58.24 15.036 16.18 22.559 35.787 22.559 58.817 0 16.178-1.958 30.497-5.853 42.966-3.9 12.471-8.941 22.457-15.125 29.979-6.191 7.521-13.901 13.85-23.131 18.986-9.232 5.14-18.182 8.85-26.84 11.136-8.662 2.286-18.415 4.004-29.263 5.146 9.894 8.562 14.842 22.077 14.842 40.539v60.237c0 3.422 1.19 6.279 3.572 8.562 2.379 2.279 6.136 2.95 11.276 1.995 44.163-14.653 80.185-41.062 108.068-79.226 27.88-38.161 41.825-81.126 41.825-128.906-.01-39.771-9.818-76.454-29.414-110.049z"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
radix: (props: IconProps) => (
|
||||
<svg viewBox="0 0 25 25" fill="none" {...props}>
|
||||
<path
|
||||
d="M12 25C7.58173 25 4 21.4183 4 17C4 12.5817 7.58173 9 12 9V25Z"
|
||||
fill="currentcolor"
|
||||
></path>
|
||||
<path d="M12 0H4V8H12V0Z" fill="currentcolor"></path>
|
||||
<path
|
||||
d="M17 8C19.2091 8 21 6.20914 21 4C21 1.79086 19.2091 0 17 0C14.7909 0 13 1.79086 13 4C13 6.20914 14.7909 8 17 8Z"
|
||||
fill="currentcolor"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
aria: (props: IconProps) => (
|
||||
<svg role="img" viewBox="0 0 24 24" fill="currentColor" {...props}>
|
||||
<path d="M13.966 22.624l-1.69-4.281H8.122l3.892-9.144 5.662 13.425zM8.884 1.376H0v21.248zm15.116 0h-8.884L24 22.624Z" />
|
||||
</svg>
|
||||
),
|
||||
pnpm: (props: IconProps) => (
|
||||
<svg viewBox="0 0 24 24" {...props}>
|
||||
<path
|
||||
d="M0 0v7.5h7.5V0zm8.25 0v7.5h7.498V0zm8.25 0v7.5H24V0zM8.25 8.25v7.5h7.498v-7.5zm8.25 0v7.5H24v-7.5zM0 16.5V24h7.5v-7.5zm8.25 0V24h7.498v-7.5zm8.25 0V24H24v-7.5z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
react: (props: IconProps) => (
|
||||
<svg viewBox="0 0 24 24" {...props}>
|
||||
<path
|
||||
d="M14.23 12.004a2.236 2.236 0 0 1-2.235 2.236 2.236 2.236 0 0 1-2.236-2.236 2.236 2.236 0 0 1 2.235-2.236 2.236 2.236 0 0 1 2.236 2.236zm2.648-10.69c-1.346 0-3.107.96-4.888 2.622-1.78-1.653-3.542-2.602-4.887-2.602-.41 0-.783.093-1.106.278-1.375.793-1.683 3.264-.973 6.365C1.98 8.917 0 10.42 0 12.004c0 1.59 1.99 3.097 5.043 4.03-.704 3.113-.39 5.588.988 6.38.32.187.69.275 1.102.275 1.345 0 3.107-.96 4.888-2.624 1.78 1.654 3.542 2.603 4.887 2.603.41 0 .783-.09 1.106-.275 1.374-.792 1.683-3.263.973-6.365C22.02 15.096 24 13.59 24 12.004c0-1.59-1.99-3.097-5.043-4.032.704-3.11.39-5.587-.988-6.38-.318-.184-.688-.277-1.092-.278zm-.005 1.09v.006c.225 0 .406.044.558.127.666.382.955 1.835.73 3.704-.054.46-.142.945-.25 1.44-.96-.236-2.006-.417-3.107-.534-.66-.905-1.345-1.727-2.035-2.447 1.592-1.48 3.087-2.292 4.105-2.295zm-9.77.02c1.012 0 2.514.808 4.11 2.28-.686.72-1.37 1.537-2.02 2.442-1.107.117-2.154.298-3.113.538-.112-.49-.195-.964-.254-1.42-.23-1.868.054-3.32.714-3.707.19-.09.4-.127.563-.132zm4.882 3.05c.455.468.91.992 1.36 1.564-.44-.02-.89-.034-1.345-.034-.46 0-.915.01-1.36.034.44-.572.895-1.096 1.345-1.565zM12 8.1c.74 0 1.477.034 2.202.093.406.582.802 1.203 1.183 1.86.372.64.71 1.29 1.018 1.946-.308.655-.646 1.31-1.013 1.95-.38.66-.773 1.288-1.18 1.87-.728.063-1.466.098-2.21.098-.74 0-1.477-.035-2.202-.093-.406-.582-.802-1.204-1.183-1.86-.372-.64-.71-1.29-1.018-1.946.303-.657.646-1.313 1.013-1.954.38-.66.773-1.286 1.18-1.868.728-.064 1.466-.098 2.21-.098zm-3.635.254c-.24.377-.48.763-.704 1.16-.225.39-.435.782-.635 1.174-.265-.656-.49-1.31-.676-1.947.64-.15 1.315-.283 2.015-.386zm7.26 0c.695.103 1.365.23 2.006.387-.18.632-.405 1.282-.66 1.933-.2-.39-.41-.783-.64-1.174-.225-.392-.465-.774-.705-1.146zm3.063.675c.484.15.944.317 1.375.498 1.732.74 2.852 1.708 2.852 2.476-.005.768-1.125 1.74-2.857 2.475-.42.18-.88.342-1.355.493-.28-.958-.646-1.956-1.1-2.98.45-1.017.81-2.01 1.085-2.964zm-13.395.004c.278.96.645 1.957 1.1 2.98-.45 1.017-.812 2.01-1.086 2.964-.484-.15-.944-.318-1.37-.5-1.732-.737-2.852-1.706-2.852-2.474 0-.768 1.12-1.742 2.852-2.476.42-.18.88-.342 1.356-.494zm11.678 4.28c.265.657.49 1.312.676 1.948-.64.157-1.316.29-2.016.39.24-.375.48-.762.705-1.158.225-.39.435-.788.636-1.18zm-9.945.02c.2.392.41.783.64 1.175.23.39.465.772.705 1.143-.695-.102-1.365-.23-2.006-.386.18-.63.406-1.282.66-1.933zM17.92 16.32c.112.493.2.968.254 1.423.23 1.868-.054 3.32-.714 3.708-.147.09-.338.128-.563.128-1.012 0-2.514-.807-4.11-2.28.686-.72 1.37-1.536 2.02-2.44 1.107-.118 2.154-.3 3.113-.54zm-11.83.01c.96.234 2.006.415 3.107.532.66.905 1.345 1.727 2.035 2.446-1.595 1.483-3.092 2.295-4.11 2.295-.22-.005-.406-.05-.553-.132-.666-.38-.955-1.834-.73-3.703.054-.46.142-.944.25-1.438zm4.56.64c.44.02.89.034 1.345.034.46 0 .915-.01 1.36-.034-.44.572-.895 1.095-1.345 1.565-.455-.47-.91-.993-1.36-1.565z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
tailwind: (props: IconProps) => (
|
||||
<svg viewBox="0 0 24 24" {...props}>
|
||||
<path
|
||||
d="M12.001,4.8c-3.2,0-5.2,1.6-6,4.8c1.2-1.6,2.6-2.2,4.2-1.8c0.913,0.228,1.565,0.89,2.288,1.624 C13.666,10.618,15.027,12,18.001,12c3.2,0,5.2-1.6,6-4.8c-1.2,1.6-2.6,2.2-4.2,1.8c-0.913-0.228-1.565-0.89-2.288-1.624 C16.337,6.182,14.976,4.8,12.001,4.8z M6.001,12c-3.2,0-5.2,1.6-6,4.8c1.2-1.6,2.6-2.2,4.2-1.8c0.913,0.228,1.565,0.89,2.288,1.624 c1.177,1.194,2.538,2.576,5.512,2.576c3.2,0,5.2-1.6,6-4.8c-1.2,1.6-2.6,2.2-4.2,1.8c-0.913-0.228-1.565-0.89-2.288-1.624 C10.337,13.382,8.976,12,6.001,12z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
spinner: (props: IconProps) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
{...props}
|
||||
>
|
||||
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
|
||||
</svg>
|
||||
),
|
||||
};
|
25
frontend/src/components/ui/input.tsx
Normal file
25
frontend/src/components/ui/input.tsx
Normal file
|
@ -0,0 +1,25 @@
|
|||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export interface InputProps
|
||||
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
||||
|
||||
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
({ className, type, ...props }, ref) => {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
Input.displayName = "Input";
|
||||
|
||||
export { Input };
|
26
frontend/src/components/ui/label.tsx
Normal file
26
frontend/src/components/ui/label.tsx
Normal file
|
@ -0,0 +1,26 @@
|
|||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as LabelPrimitive from "@radix-ui/react-label";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const labelVariants = cva(
|
||||
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
||||
);
|
||||
|
||||
const Label = React.forwardRef<
|
||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
||||
VariantProps<typeof labelVariants>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<LabelPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(labelVariants(), className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
Label.displayName = LabelPrimitive.Root.displayName;
|
||||
|
||||
export { Label };
|
194
frontend/src/components/ui/multiselect.tsx
Normal file
194
frontend/src/components/ui/multiselect.tsx
Normal file
|
@ -0,0 +1,194 @@
|
|||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import { X } from "lucide-react";
|
||||
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Command, CommandGroup, CommandItem } from "@/components/ui/command";
|
||||
import { Command as CommandPrimitive } from "cmdk";
|
||||
import { useVirtual } from "@tanstack/react-virtual";
|
||||
import { useDependenciesOrama } from "@/lib/search";
|
||||
import { search } from "@orama/orama";
|
||||
import { Dependency } from "@/lib/schemas";
|
||||
|
||||
export function MultiSelect<DataType extends { id: string; name: string }>({
|
||||
data,
|
||||
onChange,
|
||||
value,
|
||||
}: {
|
||||
data: DataType[];
|
||||
onChange: (data: DataType[]) => void;
|
||||
value: DataType[];
|
||||
}) {
|
||||
const inputRef = React.useRef<HTMLInputElement>(null);
|
||||
const scrollableContainerRef = React.useRef(null);
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [selected, setSelected] = React.useState<DataType[]>(value);
|
||||
const [selectables, setSelectables] = React.useState<DataType[]>(
|
||||
data.filter((dataPoint) => !value.some((el) => el.id === dataPoint.id)),
|
||||
);
|
||||
const [inputValue, setInputValue] = React.useState("");
|
||||
const dependenciesOrama = useDependenciesOrama();
|
||||
|
||||
const onChangeInputValue = React.useCallback(
|
||||
async (dependencyName: string) => {
|
||||
if (!dependenciesOrama.isIndexed || !dependenciesOrama.orama) {
|
||||
throw new Error("Dependencies Orama is not initialized");
|
||||
}
|
||||
const results = await search<Dependency>(dependenciesOrama.orama, {
|
||||
term: dependencyName,
|
||||
properties: ["name"],
|
||||
limit: data.length,
|
||||
});
|
||||
setSelectables(
|
||||
results.hits
|
||||
.map((hit) => hit.document as DataType)
|
||||
.filter(
|
||||
(dataPoint) => !selected.some((el) => el.id === dataPoint.id),
|
||||
),
|
||||
);
|
||||
setInputValue(dependencyName);
|
||||
},
|
||||
[data, dependenciesOrama.isIndexed, dependenciesOrama.orama, selected],
|
||||
);
|
||||
|
||||
const rowVirtualizer = useVirtual({
|
||||
size: selectables.length,
|
||||
parentRef: scrollableContainerRef,
|
||||
overscan: 10,
|
||||
});
|
||||
|
||||
const handleUnselect = React.useCallback(
|
||||
(dataPoint: DataType) => {
|
||||
if (selected.length === 1) {
|
||||
setSelected([]);
|
||||
setSelectables(data);
|
||||
onChange([]);
|
||||
inputRef.current?.focus();
|
||||
return;
|
||||
} else {
|
||||
setSelected((prev) => prev.filter((el) => el.id !== dataPoint.id));
|
||||
setSelectables((prev) =>
|
||||
!prev.some((el) => el.id === dataPoint.id)
|
||||
? [dataPoint, ...prev]
|
||||
: prev,
|
||||
);
|
||||
onChange(selected.filter((el) => el.id !== dataPoint.id));
|
||||
}
|
||||
},
|
||||
[selected, data, onChange],
|
||||
);
|
||||
|
||||
const handleSelect = React.useCallback(
|
||||
(dataPoint: DataType) => {
|
||||
setInputValue("");
|
||||
setSelected((prev) => [...prev, dataPoint]);
|
||||
setSelectables((prev) => prev.filter((el) => el.id !== dataPoint.id));
|
||||
onChange([...selected, dataPoint]);
|
||||
},
|
||||
[onChange, selected],
|
||||
);
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
const input = inputRef.current;
|
||||
if (input) {
|
||||
if (e.key === "Delete" || e.key === "Backspace") {
|
||||
if (input.value === "") {
|
||||
if (selected.length > 0) {
|
||||
handleUnselect(selected[selected.length - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// This is not a default behaviour of the <input /> field
|
||||
if (e.key === "Escape") {
|
||||
input.blur();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Command
|
||||
onKeyDown={handleKeyDown}
|
||||
className="overflow-visible bg-transparent"
|
||||
>
|
||||
<div className="group border border-input px-3 py-2 text-sm ring-offset-background rounded-md focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2">
|
||||
<div className="flex gap-1 flex-wrap">
|
||||
{selected.map((dataPoint) => {
|
||||
return (
|
||||
<Badge key={dataPoint.id} variant="secondary">
|
||||
{dataPoint.name}
|
||||
<button
|
||||
className="ml-1 ring-offset-background rounded-full outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
handleUnselect(dataPoint);
|
||||
}
|
||||
}}
|
||||
onMouseDown={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
onClick={() => handleUnselect(dataPoint)}
|
||||
>
|
||||
<X className="h-3 w-3 text-muted-foreground hover:text-foreground" />
|
||||
</button>
|
||||
</Badge>
|
||||
);
|
||||
})}
|
||||
{/* Avoid having the "Search" Icon */}
|
||||
<CommandPrimitive.Input
|
||||
ref={inputRef}
|
||||
value={inputValue}
|
||||
onValueChange={onChangeInputValue}
|
||||
onBlur={() => setOpen(false)}
|
||||
onFocus={() => setOpen(true)}
|
||||
placeholder="Select..."
|
||||
className="ml-2 bg-transparent outline-none placeholder:text-muted-foreground flex-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative mt-2">
|
||||
{open && selectables.length > 0 ? (
|
||||
<div className="absolute w-full z-10 top-0 rounded-md border bg-popover text-popover-foreground shadow-md outline-none animate-in">
|
||||
<CommandGroup
|
||||
ref={scrollableContainerRef}
|
||||
className="h-96 overflow-auto"
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
height: `${rowVirtualizer.totalSize}px`,
|
||||
width: "100%",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
{rowVirtualizer.virtualItems.map((virtualRow) => (
|
||||
<CommandItem
|
||||
key={virtualRow.index}
|
||||
onMouseDown={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
onSelect={() => {
|
||||
handleSelect(selectables[virtualRow.index]);
|
||||
}}
|
||||
className="cursor-pointer"
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: "100%",
|
||||
height: `${virtualRow.size}px`,
|
||||
transform: `translateY(${virtualRow.start}px)`,
|
||||
}}
|
||||
>
|
||||
{selectables[virtualRow.index].name}
|
||||
</CommandItem>
|
||||
))}
|
||||
</div>
|
||||
</CommandGroup>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</Command>
|
||||
);
|
||||
}
|
114
frontend/src/components/ui/table.tsx
Normal file
114
frontend/src/components/ui/table.tsx
Normal file
|
@ -0,0 +1,114 @@
|
|||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Table = React.forwardRef<
|
||||
HTMLTableElement,
|
||||
React.HTMLAttributes<HTMLTableElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div className="w-full overflow-auto">
|
||||
<table
|
||||
ref={ref}
|
||||
className={cn("w-full caption-bottom text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
));
|
||||
Table.displayName = "Table";
|
||||
|
||||
const TableHeader = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
|
||||
));
|
||||
TableHeader.displayName = "TableHeader";
|
||||
|
||||
const TableBody = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tbody
|
||||
ref={ref}
|
||||
className={cn("[&_tr:last-child]:border-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableBody.displayName = "TableBody";
|
||||
|
||||
const TableFooter = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tfoot
|
||||
ref={ref}
|
||||
className={cn("bg-primary font-medium text-primary-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableFooter.displayName = "TableFooter";
|
||||
|
||||
const TableRow = React.forwardRef<
|
||||
HTMLTableRowElement,
|
||||
React.HTMLAttributes<HTMLTableRowElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tr
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableRow.displayName = "TableRow";
|
||||
|
||||
const TableHead = React.forwardRef<
|
||||
HTMLTableCellElement,
|
||||
React.ThHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<th
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableHead.displayName = "TableHead";
|
||||
|
||||
const TableCell = React.forwardRef<
|
||||
HTMLTableCellElement,
|
||||
React.TdHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<td
|
||||
ref={ref}
|
||||
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableCell.displayName = "TableCell";
|
||||
|
||||
const TableCaption = React.forwardRef<
|
||||
HTMLTableCaptionElement,
|
||||
React.HTMLAttributes<HTMLTableCaptionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<caption
|
||||
ref={ref}
|
||||
className={cn("mt-4 text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableCaption.displayName = "TableCaption";
|
||||
|
||||
export {
|
||||
Table,
|
||||
TableHeader,
|
||||
TableBody,
|
||||
TableFooter,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableCell,
|
||||
TableCaption,
|
||||
};
|
77
frontend/src/lib/hooks.ts
Normal file
77
frontend/src/lib/hooks.ts
Normal file
|
@ -0,0 +1,77 @@
|
|||
import { usePathname, useSearchParams, useRouter } from "next/navigation";
|
||||
import React from "react";
|
||||
import { Dependency } from "./schemas";
|
||||
|
||||
export const useQuerySearchFormData = (dependencies: Dependency[]) => {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const searchQueryToQueryParam = React.useCallback(
|
||||
(searchQueryValue: string) => {
|
||||
if (!searchQueryValue) {
|
||||
return "";
|
||||
}
|
||||
return encodeURIComponent(searchQueryValue);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const searchQueryFromQueryParam = React.useCallback(() => {
|
||||
if (!searchParams.has("search")) {
|
||||
return "";
|
||||
}
|
||||
return decodeURIComponent(searchParams.get("search") ?? "");
|
||||
}, [searchParams]);
|
||||
|
||||
const dependenciesQueryToQueryParam = React.useCallback(
|
||||
(dependenciesQueryValue: Dependency[]) => {
|
||||
if (!dependenciesQueryValue) {
|
||||
return "";
|
||||
}
|
||||
// join dependencies names with `~`
|
||||
let encodedArray = dependenciesQueryValue.map(
|
||||
(dependency) => dependency.name,
|
||||
);
|
||||
// join the names with `~`
|
||||
let encodedArrayValue = encodedArray.join("~");
|
||||
// URL encode the string
|
||||
return encodeURIComponent(encodedArrayValue);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const dependenciesQueryFromQueryParam = React.useCallback(() => {
|
||||
if (!searchParams.has("dependencies")) {
|
||||
return [];
|
||||
}
|
||||
// URL decode the string
|
||||
let decodedArray = decodeURIComponent(
|
||||
searchParams.get("dependencies") ?? "",
|
||||
);
|
||||
// split dependencies names with `~`
|
||||
const dependenciesNames = decodedArray.split("~");
|
||||
// deduplicate dependencies names
|
||||
const uniqueDependenciesNames = new Set(dependenciesNames).values();
|
||||
// filter out empty strings
|
||||
const filteredDependenciesNames = Array.from(
|
||||
uniqueDependenciesNames,
|
||||
).filter((dependencyName) => dependencyName !== "");
|
||||
// return dependencies objects
|
||||
return (
|
||||
filteredDependenciesNames
|
||||
.map((dependencyName) =>
|
||||
dependencies.find((dependency) => dependency.name === dependencyName),
|
||||
)
|
||||
// filter out undefined values
|
||||
.filter(Boolean) as Dependency[]
|
||||
);
|
||||
}, [searchParams, dependencies]);
|
||||
|
||||
return {
|
||||
searchQueryToQueryParam,
|
||||
searchQueryFromQueryParam,
|
||||
dependenciesQueryToQueryParam,
|
||||
dependenciesQueryFromQueryParam,
|
||||
};
|
||||
};
|
69
frontend/src/lib/indexes.ts
Normal file
69
frontend/src/lib/indexes.ts
Normal file
|
@ -0,0 +1,69 @@
|
|||
import { cache } from "react";
|
||||
import "server-only";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import { dependenciesIndexSchema, reposIndexSchema } from "./schemas";
|
||||
import { ZodError } from "zod";
|
||||
|
||||
// TODO: docstrings
|
||||
|
||||
export const REPOS_INDEX_FILE_PATH = path.normalize(
|
||||
path.join(__dirname, "..", "..", "..", "..", "repos_index.json"),
|
||||
);
|
||||
|
||||
export const DEPENDENCIES_INDEX_FILE_PATH = path.normalize(
|
||||
path.join(__dirname, "..", "..", "..", "..", "dependencies_index.json"),
|
||||
);
|
||||
|
||||
export const preload = () => {
|
||||
void loadReposIndexServerOnly();
|
||||
void loadDependenciesIndexServerOnly();
|
||||
};
|
||||
|
||||
// TODO: tests
|
||||
|
||||
export const loadReposIndexServerOnly = cache(async () => {
|
||||
try {
|
||||
const indexData = JSON.parse(
|
||||
await fs.promises.readFile(REPOS_INDEX_FILE_PATH, "utf-8"),
|
||||
(key, value) => {
|
||||
if (key === "id" || key === "source_graph_repo_id") {
|
||||
return String(value);
|
||||
}
|
||||
return value;
|
||||
},
|
||||
);
|
||||
return await reposIndexSchema.parseAsync(indexData);
|
||||
} catch (err) {
|
||||
if (err instanceof ZodError) {
|
||||
throw new Error(
|
||||
`Failed to parse the repos index: ${JSON.stringify(err.format())}`,
|
||||
);
|
||||
}
|
||||
throw new Error(`Failed to load the repos index: ${err}`);
|
||||
}
|
||||
});
|
||||
|
||||
export const loadDependenciesIndexServerOnly = cache(async () => {
|
||||
try {
|
||||
const indexData = JSON.parse(
|
||||
await fs.promises.readFile(DEPENDENCIES_INDEX_FILE_PATH, "utf-8"),
|
||||
(key, value) => {
|
||||
if (key === "id") {
|
||||
return String(value);
|
||||
}
|
||||
return value;
|
||||
},
|
||||
);
|
||||
return await dependenciesIndexSchema.parseAsync(indexData);
|
||||
} catch (err) {
|
||||
if (err instanceof ZodError) {
|
||||
throw new Error(
|
||||
`Failed to parse the dependencies index: ${JSON.stringify(
|
||||
err.format(),
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
throw new Error(`Failed to load the dependencies index: ${err}`);
|
||||
}
|
||||
});
|
67
frontend/src/lib/query-params.ts
Normal file
67
frontend/src/lib/query-params.ts
Normal file
|
@ -0,0 +1,67 @@
|
|||
import { AppRouterInstance } from "next/dist/shared/lib/app-router-context";
|
||||
|
||||
export interface QueryParamsManagerInterface {
|
||||
requestQueryUpdate: (key: string, value: string) => void;
|
||||
requestQueryDelete: (key: string) => void;
|
||||
requestQueryClear: () => void;
|
||||
commit: () => void;
|
||||
}
|
||||
|
||||
export class QueryParamsManager implements QueryParamsManagerInterface {
|
||||
constructor(
|
||||
private readonly router: AppRouterInstance,
|
||||
private readonly pathname: string,
|
||||
private readonly initialParams: URLSearchParams,
|
||||
private readonly _queue: Array<
|
||||
[key: string, value: string | null] | ["clear"]
|
||||
> = [],
|
||||
private readonly _internalState: Map<string, string> = new Map(),
|
||||
) {
|
||||
this.initialParams.forEach((value, key) => {
|
||||
this._internalState.set(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
requestQueryUpdate(key: string, value: string) {
|
||||
this._queue.push([key, value]);
|
||||
}
|
||||
|
||||
requestQueryDelete(key: string) {
|
||||
this._queue.push([key, null]);
|
||||
}
|
||||
|
||||
requestQueryClear() {
|
||||
this._queue.push(["clear"]);
|
||||
}
|
||||
|
||||
private _processQueue() {
|
||||
for (const request of this._queue) {
|
||||
if (request.length === 1) {
|
||||
const [command] = request;
|
||||
if (command === "clear") {
|
||||
this._internalState.clear();
|
||||
}
|
||||
} else {
|
||||
if (request.length === 2) {
|
||||
const [key, value] = request;
|
||||
if (value === null) {
|
||||
this._internalState.delete(key);
|
||||
} else {
|
||||
this._internalState.set(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this._queue.length = 0;
|
||||
}
|
||||
|
||||
commit() {
|
||||
this._processQueue();
|
||||
const params = new URLSearchParams();
|
||||
this._internalState.forEach((value, key) => {
|
||||
params.set(key, value);
|
||||
});
|
||||
const newUrl = `${this.pathname}?${params.toString()}`;
|
||||
this.router.replace(newUrl);
|
||||
}
|
||||
}
|
29
frontend/src/lib/schemas.ts
Normal file
29
frontend/src/lib/schemas.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { z } from "zod";
|
||||
|
||||
export const dependencySchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
export const repoSchema = z.object({
|
||||
id: z.string(),
|
||||
url: z.string(),
|
||||
description: z.string(),
|
||||
stars: z.number().min(0),
|
||||
source_graph_repo_id: z.string(),
|
||||
dependencies: z.array(dependencySchema),
|
||||
last_checked_revision: z.nullable(z.string()),
|
||||
});
|
||||
|
||||
export const reposIndexSchema = z.object({
|
||||
repos: z.array(repoSchema),
|
||||
});
|
||||
|
||||
export const dependenciesIndexSchema = z.object({
|
||||
dependencies: z.array(dependencySchema),
|
||||
});
|
||||
|
||||
export type Dependency = z.infer<typeof dependencySchema>;
|
||||
export type Repo = z.infer<typeof repoSchema>;
|
||||
export type RepoIndex = z.infer<typeof reposIndexSchema>;
|
||||
export type DependenciesIndex = z.infer<typeof dependenciesIndexSchema>;
|
88
frontend/src/lib/search.ts
Normal file
88
frontend/src/lib/search.ts
Normal file
|
@ -0,0 +1,88 @@
|
|||
import {
|
||||
Orama,
|
||||
ProvidedTypes as OramaProvidedTypes,
|
||||
create,
|
||||
insertMultiple,
|
||||
} from "@orama/orama";
|
||||
import { DependenciesIndex, RepoIndex } from "./schemas";
|
||||
import { Context, createContext, useContext } from "react";
|
||||
|
||||
export interface IOramaContext<
|
||||
OramaParameters extends Partial<OramaProvidedTypes> = any,
|
||||
> {
|
||||
isIndexed: boolean;
|
||||
orama: Orama<OramaParameters> | null;
|
||||
}
|
||||
|
||||
export function createOramaContext<
|
||||
OramaParameters extends Partial<OramaProvidedTypes>,
|
||||
>(): Context<IOramaContext<OramaParameters>> {
|
||||
return createContext<IOramaContext<OramaParameters>>({
|
||||
isIndexed: false,
|
||||
orama: null,
|
||||
});
|
||||
}
|
||||
|
||||
export interface ReposOramaParameters extends Partial<OramaProvidedTypes> {
|
||||
Index: { description: string };
|
||||
}
|
||||
|
||||
export type ReposOrama = Orama<ReposOramaParameters>;
|
||||
|
||||
export async function createReposOrama(): Promise<ReposOrama> {
|
||||
const orama = await create({
|
||||
schema: {
|
||||
description: "string",
|
||||
},
|
||||
id: "repos-index",
|
||||
});
|
||||
return orama;
|
||||
}
|
||||
|
||||
export async function prepareReposOramaIndex(
|
||||
orama: ReposOrama,
|
||||
data: RepoIndex["repos"],
|
||||
): Promise<ReposOrama> {
|
||||
await insertMultiple(orama, data, 100);
|
||||
return orama;
|
||||
}
|
||||
|
||||
export const ReposOramaContext = createOramaContext<ReposOramaParameters>();
|
||||
ReposOramaContext.displayName = "ReposOramaContext";
|
||||
|
||||
export const useReposOrama = () => {
|
||||
return useContext(ReposOramaContext);
|
||||
};
|
||||
|
||||
export interface DependenciesOramaParameters
|
||||
extends Partial<OramaProvidedTypes> {
|
||||
Index: { name: string };
|
||||
}
|
||||
|
||||
export type DependenciesOrama = Orama<DependenciesOramaParameters>;
|
||||
|
||||
export async function createDependenciesOrama(): Promise<DependenciesOrama> {
|
||||
const orama = await create({
|
||||
schema: {
|
||||
name: "string",
|
||||
},
|
||||
id: "dependencies-index",
|
||||
});
|
||||
return orama;
|
||||
}
|
||||
|
||||
export async function prepareDependenciesOramaIndex(
|
||||
orama: DependenciesOrama,
|
||||
data: DependenciesIndex["dependencies"],
|
||||
): Promise<DependenciesOrama> {
|
||||
await insertMultiple(orama, data, 100);
|
||||
return orama;
|
||||
}
|
||||
|
||||
export const DependenciesOramaContext =
|
||||
createOramaContext<DependenciesOramaParameters>();
|
||||
DependenciesOramaContext.displayName = "DependenciesOramaContext";
|
||||
|
||||
export const useDependenciesOrama = () => {
|
||||
return useContext(DependenciesOramaContext);
|
||||
};
|
10
frontend/src/lib/utils.ts
Normal file
10
frontend/src/lib/utils.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { type ClassValue, clsx } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
export function wait(ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
75
frontend/tailwind.config.ts
Normal file
75
frontend/tailwind.config.ts
Normal file
|
@ -0,0 +1,75 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
darkMode: ["class"],
|
||||
content: [
|
||||
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
],
|
||||
theme: {
|
||||
container: {
|
||||
center: true,
|
||||
padding: "2rem",
|
||||
screens: {
|
||||
"2xl": "1400px",
|
||||
},
|
||||
},
|
||||
extend: {
|
||||
colors: {
|
||||
border: "hsl(var(--border))",
|
||||
input: "hsl(var(--input))",
|
||||
ring: "hsl(var(--ring))",
|
||||
background: "hsl(var(--background))",
|
||||
foreground: "hsl(var(--foreground))",
|
||||
primary: {
|
||||
DEFAULT: "hsl(var(--primary))",
|
||||
foreground: "hsl(var(--primary-foreground))",
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: "hsl(var(--secondary))",
|
||||
foreground: "hsl(var(--secondary-foreground))",
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: "hsl(var(--destructive))",
|
||||
foreground: "hsl(var(--destructive-foreground))",
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: "hsl(var(--muted))",
|
||||
foreground: "hsl(var(--muted-foreground))",
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: "hsl(var(--accent))",
|
||||
foreground: "hsl(var(--accent-foreground))",
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: "hsl(var(--popover))",
|
||||
foreground: "hsl(var(--popover-foreground))",
|
||||
},
|
||||
card: {
|
||||
DEFAULT: "hsl(var(--card))",
|
||||
foreground: "hsl(var(--card-foreground))",
|
||||
},
|
||||
},
|
||||
borderRadius: {
|
||||
lg: "var(--radius)",
|
||||
md: "calc(var(--radius) - 2px)",
|
||||
sm: "calc(var(--radius) - 4px)",
|
||||
},
|
||||
keyframes: {
|
||||
"accordion-down": {
|
||||
from: { height: 0 },
|
||||
to: { height: "var(--radix-accordion-content-height)" },
|
||||
},
|
||||
"accordion-up": {
|
||||
from: { height: "var(--radix-accordion-content-height)" },
|
||||
to: { height: 0 },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
"accordion-down": "accordion-down 0.2s ease-out",
|
||||
"accordion-up": "accordion-up 0.2s ease-out",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [require("tailwindcss-animate")],
|
||||
};
|
27
frontend/tsconfig.json
Normal file
27
frontend/tsconfig.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
from typing import List
|
||||
import json
|
||||
from pytablewriter import MarkdownTableWriter
|
||||
from stdlib_list import stdlib_list
|
||||
|
||||
NATIVE = ["fastapi", "starlette", "pydantic", "typing", "uvicorn", "app"]
|
||||
|
||||
|
||||
def filter_list(dependencies: List[str]) -> List[str]:
|
||||
return [
|
||||
dependency
|
||||
for dependency in dependencies
|
||||
if not (
|
||||
dependency in NATIVE
|
||||
or dependency in stdlib_list("3.8")
|
||||
or dependency.startswith("_")
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def format_with_link(project: str) -> str:
|
||||
links = open("unique_links.txt", "r")
|
||||
for link in links.readlines():
|
||||
if project in link:
|
||||
return f"[{project}]({link})"
|
||||
|
||||
with open("results.json") as json_file:
|
||||
data = json.load(json_file)
|
||||
writer = MarkdownTableWriter()
|
||||
writer.headers = ["Project", "Dependencies"]
|
||||
writer.value_matrix = [
|
||||
[format_with_link(project), ", ".join(filter_list(dependencies))]
|
||||
for project, dependencies in data.items()
|
||||
if (
|
||||
len(filter_list(dependencies)) > 0
|
||||
and len(filter_list(dependencies)) < 20
|
||||
and project != ""
|
||||
)
|
||||
]
|
||||
writer.write_table()
|
1
migrations/README
Normal file
1
migrations/README
Normal file
|
@ -0,0 +1 @@
|
|||
Generic single-database configuration with an async dbapi.
|
0
migrations/__init__.py
Normal file
0
migrations/__init__.py
Normal file
90
migrations/env.py
Normal file
90
migrations/env.py
Normal file
|
@ -0,0 +1,90 @@
|
|||
import asyncio
|
||||
from logging.config import fileConfig
|
||||
|
||||
from alembic import context
|
||||
from sqlalchemy import pool
|
||||
from sqlalchemy.engine import Connection
|
||||
from sqlalchemy.ext.asyncio import async_engine_from_config
|
||||
|
||||
from app.database import Base
|
||||
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
if config.config_file_name is not None:
|
||||
fileConfig(config.config_file_name)
|
||||
|
||||
# add your model's MetaData object here
|
||||
# for 'autogenerate' support
|
||||
# from myapp import mymodel
|
||||
# target_metadata = mymodel.Base.metadata
|
||||
target_metadata = Base.metadata
|
||||
|
||||
# other values from the config, defined by the needs of env.py,
|
||||
# can be acquired:
|
||||
# my_important_option = config.get_main_option("my_important_option")
|
||||
# ... etc.
|
||||
|
||||
|
||||
def run_migrations_offline() -> None:
|
||||
"""Run migrations in 'offline' mode.
|
||||
|
||||
This configures the context with just a URL
|
||||
and not an Engine, though an Engine is acceptable
|
||||
here as well. By skipping the Engine creation
|
||||
we don't even need a DBAPI to be available.
|
||||
|
||||
Calls to context.execute() here emit the given string to the
|
||||
script output.
|
||||
|
||||
"""
|
||||
url = config.get_main_option("sqlalchemy.url")
|
||||
context.configure(
|
||||
url=url,
|
||||
target_metadata=target_metadata,
|
||||
literal_binds=True,
|
||||
dialect_opts={"paramstyle": "named"},
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
def do_run_migrations(connection: Connection) -> None:
|
||||
context.configure(connection=connection, target_metadata=target_metadata)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
async def run_async_migrations() -> None:
|
||||
"""In this scenario we need to create an Engine
|
||||
and associate a connection with the context.
|
||||
|
||||
"""
|
||||
|
||||
connectable = async_engine_from_config(
|
||||
config.get_section(config.config_ini_section, {}),
|
||||
prefix="sqlalchemy.",
|
||||
poolclass=pool.NullPool,
|
||||
)
|
||||
|
||||
async with connectable.connect() as connection:
|
||||
await connection.run_sync(do_run_migrations)
|
||||
|
||||
await connectable.dispose()
|
||||
|
||||
|
||||
def run_migrations_online() -> None:
|
||||
"""Run migrations in 'online' mode."""
|
||||
|
||||
asyncio.run(run_async_migrations())
|
||||
|
||||
|
||||
if context.is_offline_mode():
|
||||
run_migrations_offline()
|
||||
else:
|
||||
run_migrations_online()
|
24
migrations/script.py.mako
Normal file
24
migrations/script.py.mako
Normal file
|
@ -0,0 +1,24 @@
|
|||
"""${message}
|
||||
|
||||
Revision ID: ${up_revision}
|
||||
Revises: ${down_revision | comma,n}
|
||||
Create Date: ${create_date}
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
${imports if imports else ""}
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = ${repr(up_revision)}
|
||||
down_revision = ${repr(down_revision)}
|
||||
branch_labels = ${repr(branch_labels)}
|
||||
depends_on = ${repr(depends_on)}
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
${upgrades if upgrades else "pass"}
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
${downgrades if downgrades else "pass"}
|
73
migrations/versions/90eb9d1f9267_set_up_the_database.py
Normal file
73
migrations/versions/90eb9d1f9267_set_up_the_database.py
Normal file
|
@ -0,0 +1,73 @@
|
|||
"""Set up the database
|
||||
|
||||
Revision ID: 90eb9d1f9267
|
||||
Revises:
|
||||
Create Date: 2023-08-15 14:13:30.562069
|
||||
|
||||
"""
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "90eb9d1f9267"
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table(
|
||||
"dependency",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("name", sa.String(length=255), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id", name=op.f("pk_dependency")),
|
||||
sa.UniqueConstraint("name", name=op.f("uq_dependency_name ")),
|
||||
)
|
||||
op.create_table(
|
||||
"repo",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("url", sa.String(), nullable=False),
|
||||
sa.Column("description", sa.Text(), nullable=False),
|
||||
sa.Column("stars", sa.BigInteger(), nullable=False),
|
||||
sa.Column("source_graph_repo_id", sa.BigInteger(), nullable=True),
|
||||
sa.PrimaryKeyConstraint("id", name=op.f("pk_repo")),
|
||||
sa.UniqueConstraint(
|
||||
"source_graph_repo_id", name=op.f("uq_repo_source_graph_repo_id ")
|
||||
),
|
||||
sa.UniqueConstraint(
|
||||
"url",
|
||||
"source_graph_repo_id",
|
||||
name=op.f("uq_repo_url_source_graph_repo_id "),
|
||||
),
|
||||
sa.UniqueConstraint("url", name=op.f("uq_repo_url ")),
|
||||
)
|
||||
op.create_table(
|
||||
"repo_dependency",
|
||||
sa.Column("repo_id", sa.Integer(), nullable=False),
|
||||
sa.Column("dependency_id", sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(
|
||||
["dependency_id"],
|
||||
["dependency.id"],
|
||||
name=op.f("fk_repo_dependency_dependency_id_dependency"),
|
||||
ondelete="CASCADE",
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["repo_id"],
|
||||
["repo.id"],
|
||||
name=op.f("fk_repo_dependency_repo_id_repo"),
|
||||
ondelete="CASCADE",
|
||||
),
|
||||
sa.PrimaryKeyConstraint(
|
||||
"repo_id", "dependency_id", name=op.f("pk_repo_dependency")
|
||||
),
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table("repo_dependency")
|
||||
op.drop_table("repo")
|
||||
op.drop_table("dependency")
|
||||
# ### end Alembic commands ###
|
0
migrations/versions/__init__.py
Normal file
0
migrations/versions/__init__.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
"""Add a last_checked_revision column
|
||||
|
||||
Revision ID: ac7c35039d70
|
||||
Revises: 90eb9d1f9267
|
||||
Create Date: 2023-08-16 22:35:25.314490
|
||||
|
||||
"""
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "ac7c35039d70"
|
||||
down_revision = "90eb9d1f9267"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column(
|
||||
"repo", sa.Column("last_checked_revision", sa.String(length=255), nullable=True)
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column("repo", "last_checked_revision")
|
||||
# ### end Alembic commands ###
|
138
pyproject.toml
Normal file
138
pyproject.toml
Normal file
|
@ -0,0 +1,138 @@
|
|||
[project]
|
||||
name = "awesome-fastapi-projects"
|
||||
version = "0.1.0"
|
||||
description = "A web app built to index awesome projects built with FastAPI."
|
||||
authors = [
|
||||
{name = "Vladyslav Fedoriuk", email="vlad.fedoriuk2000@gmail.com"}
|
||||
]
|
||||
requires-python = ">=3.11"
|
||||
classifiers = [
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
]
|
||||
dependencies = [
|
||||
"aiofiles",
|
||||
"aiosqlite",
|
||||
"alembic",
|
||||
"anyio",
|
||||
"httpx",
|
||||
"httpx-sse",
|
||||
"loguru",
|
||||
"pydantic",
|
||||
"sqlalchemy[asyncio,mypy]",
|
||||
"stamina",
|
||||
"third-party-imports",
|
||||
"typer[all]",
|
||||
]
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"black",
|
||||
"ipython",
|
||||
"mypy",
|
||||
"pip-tools",
|
||||
"pre-commit",
|
||||
"pyproject-fmt",
|
||||
"ruff",
|
||||
"types-aiofiles",
|
||||
]
|
||||
test = [
|
||||
"dirty-equals",
|
||||
"polyfactory",
|
||||
"pytest",
|
||||
"pytest-anyio",
|
||||
"pytest-cov",
|
||||
"pytest-mock",
|
||||
]
|
||||
|
||||
[tool.setuptools]
|
||||
py-modules = ["app"]
|
||||
|
||||
[tool.black]
|
||||
line-length = 88
|
||||
target_version = ['py311']
|
||||
include = '\.pyi?$'
|
||||
force-exclude = '''
|
||||
(
|
||||
/(
|
||||
\.bzr
|
||||
| \.direnv
|
||||
| \.eggs
|
||||
| \.git
|
||||
| \.git-rewrite
|
||||
| \.hg
|
||||
| \.mypy_cache
|
||||
| \.ruff_cache
|
||||
| \.tox
|
||||
| \.nox
|
||||
| \.pants.d
|
||||
| \.pytype
|
||||
| \.venv
|
||||
| \.svn
|
||||
| \.ipynb_checkpoints
|
||||
| \.egg-info
|
||||
| __pypackages__
|
||||
| _build
|
||||
| buck-out
|
||||
| build
|
||||
| dist
|
||||
| node_modules
|
||||
| venv
|
||||
)/
|
||||
)
|
||||
''' # To comply with ruff config
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 88
|
||||
target-version = 'py311'
|
||||
required-version = "0.0.280"
|
||||
extend-select = [
|
||||
"D", # pydocstyle
|
||||
"W", # pycodestyle
|
||||
"C90", # mccabe
|
||||
"I", # isort
|
||||
"UP", # pyupgrade
|
||||
"ASYNC", # flake8-async
|
||||
"S", # flake8-bandit
|
||||
"B", # flake8-bugbear
|
||||
"ANN", # flake8-annotations
|
||||
"S", # flake8-bandit
|
||||
"C4", # flake8-comprehensions
|
||||
"T10", # flake8-debugger
|
||||
"INP", # flake8-no-pep420
|
||||
"PT", # flake8-pytest-style
|
||||
"TID", # flake8-tidy-imports
|
||||
"PTH", # flake8-use-pathlib
|
||||
"ERA", # eradicate
|
||||
"Q", # flake8-quotes
|
||||
]
|
||||
[tool.ruff.per-file-ignores]
|
||||
# Ignore missing docstrings in migrations and alembic files
|
||||
"**/migrations/*.py" = ["D"]
|
||||
"**/migrations/env.py" = ["ERA001"]
|
||||
"**/tests/*.py" = ["S101"]
|
||||
"**/conftest.py" = ["S101"]
|
||||
|
||||
[tool.ruff.pydocstyle]
|
||||
convention = "numpy"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = "tests"
|
||||
|
||||
[tool.coverage.run]
|
||||
branch = true
|
||||
omit = [
|
||||
"tests/*",
|
||||
]
|
||||
|
||||
[tool.mypy]
|
||||
plugins = [
|
||||
"pydantic.mypy",
|
||||
"sqlalchemy.ext.mypy.plugin",
|
||||
]
|
||||
strict = true
|
||||
exclude = [
|
||||
"tests",
|
||||
"migrations",
|
||||
"conftest.py",
|
||||
"factories.py",
|
||||
]
|
81834
repos_index.json
Normal file
81834
repos_index.json
Normal file
File diff suppressed because it is too large
Load Diff
89
requirements/base.txt
Normal file
89
requirements/base.txt
Normal file
|
@ -0,0 +1,89 @@
|
|||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.11
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --output-file=requirements/base.txt pyproject.toml
|
||||
#
|
||||
aiofiles==23.1.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
aiosqlite==0.19.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
alembic==1.11.1
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
annotated-types==0.5.0
|
||||
# via pydantic
|
||||
anyio==3.7.1
|
||||
# via
|
||||
# awesome-fastapi-projects (pyproject.toml)
|
||||
# httpcore
|
||||
certifi==2023.7.22
|
||||
# via
|
||||
# httpcore
|
||||
# httpx
|
||||
click==8.1.6
|
||||
# via typer
|
||||
colorama==0.4.6
|
||||
# via typer
|
||||
greenlet==2.0.2
|
||||
# via sqlalchemy
|
||||
h11==0.14.0
|
||||
# via httpcore
|
||||
httpcore==0.18.0
|
||||
# via httpx
|
||||
httpx==0.25.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
httpx-sse==0.3.1
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
idna==3.4
|
||||
# via
|
||||
# anyio
|
||||
# httpx
|
||||
loguru==0.7.2
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
mako==1.2.4
|
||||
# via alembic
|
||||
markdown-it-py==3.0.0
|
||||
# via rich
|
||||
markupsafe==2.1.3
|
||||
# via mako
|
||||
mdurl==0.1.2
|
||||
# via markdown-it-py
|
||||
mypy==1.4.1
|
||||
# via sqlalchemy
|
||||
mypy-extensions==1.0.0
|
||||
# via mypy
|
||||
pydantic==2.1.1
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
pydantic-core==2.4.0
|
||||
# via pydantic
|
||||
pygments==2.15.1
|
||||
# via rich
|
||||
rich==13.5.2
|
||||
# via typer
|
||||
shellingham==1.5.0.post1
|
||||
# via typer
|
||||
sniffio==1.3.0
|
||||
# via
|
||||
# anyio
|
||||
# httpcore
|
||||
# httpx
|
||||
sqlalchemy[asyncio,mypy]==2.0.19
|
||||
# via
|
||||
# alembic
|
||||
# awesome-fastapi-projects (pyproject.toml)
|
||||
stamina==23.1.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
tenacity==8.2.2
|
||||
# via stamina
|
||||
third-party-imports==0.0.7
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
typer[all]==0.9.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
typing-extensions==4.7.1
|
||||
# via
|
||||
# alembic
|
||||
# mypy
|
||||
# pydantic
|
||||
# pydantic-core
|
||||
# sqlalchemy
|
||||
# typer
|
185
requirements/dev.txt
Normal file
185
requirements/dev.txt
Normal file
|
@ -0,0 +1,185 @@
|
|||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.11
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --extra=dev --output-file=requirements/dev.txt pyproject.toml
|
||||
#
|
||||
aiofiles==23.1.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
aiosqlite==0.19.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
alembic==1.11.1
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
annotated-types==0.5.0
|
||||
# via pydantic
|
||||
anyio==3.7.1
|
||||
# via
|
||||
# awesome-fastapi-projects (pyproject.toml)
|
||||
# httpcore
|
||||
asttokens==2.2.1
|
||||
# via stack-data
|
||||
backcall==0.2.0
|
||||
# via ipython
|
||||
black==23.7.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
build==0.10.0
|
||||
# via pip-tools
|
||||
certifi==2023.7.22
|
||||
# via
|
||||
# httpcore
|
||||
# httpx
|
||||
cfgv==3.3.1
|
||||
# via pre-commit
|
||||
click==8.1.6
|
||||
# via
|
||||
# black
|
||||
# pip-tools
|
||||
# typer
|
||||
colorama==0.4.6
|
||||
# via typer
|
||||
decorator==5.1.1
|
||||
# via ipython
|
||||
distlib==0.3.7
|
||||
# via virtualenv
|
||||
executing==1.2.0
|
||||
# via stack-data
|
||||
filelock==3.12.2
|
||||
# via virtualenv
|
||||
greenlet==2.0.2
|
||||
# via sqlalchemy
|
||||
h11==0.14.0
|
||||
# via httpcore
|
||||
httpcore==0.18.0
|
||||
# via httpx
|
||||
httpx==0.25.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
httpx-sse==0.3.1
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
identify==2.5.26
|
||||
# via pre-commit
|
||||
idna==3.4
|
||||
# via
|
||||
# anyio
|
||||
# httpx
|
||||
ipython==8.14.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
jedi==0.19.0
|
||||
# via ipython
|
||||
loguru==0.7.2
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
mako==1.2.4
|
||||
# via alembic
|
||||
markdown-it-py==3.0.0
|
||||
# via rich
|
||||
markupsafe==2.1.3
|
||||
# via mako
|
||||
matplotlib-inline==0.1.6
|
||||
# via ipython
|
||||
mdurl==0.1.2
|
||||
# via markdown-it-py
|
||||
mypy==1.4.1
|
||||
# via
|
||||
# awesome-fastapi-projects (pyproject.toml)
|
||||
# sqlalchemy
|
||||
mypy-extensions==1.0.0
|
||||
# via
|
||||
# black
|
||||
# mypy
|
||||
natsort==8.4.0
|
||||
# via pyproject-fmt
|
||||
nodeenv==1.8.0
|
||||
# via pre-commit
|
||||
packaging==23.1
|
||||
# via
|
||||
# black
|
||||
# build
|
||||
# pyproject-fmt
|
||||
parso==0.8.3
|
||||
# via jedi
|
||||
pathspec==0.11.1
|
||||
# via black
|
||||
pexpect==4.8.0
|
||||
# via ipython
|
||||
pickleshare==0.7.5
|
||||
# via ipython
|
||||
pip-tools==7.1.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
platformdirs==3.9.1
|
||||
# via
|
||||
# black
|
||||
# virtualenv
|
||||
pre-commit==3.3.3
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
prompt-toolkit==3.0.39
|
||||
# via ipython
|
||||
ptyprocess==0.7.0
|
||||
# via pexpect
|
||||
pure-eval==0.2.2
|
||||
# via stack-data
|
||||
pydantic==2.1.1
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
pydantic-core==2.4.0
|
||||
# via pydantic
|
||||
pygments==2.15.1
|
||||
# via
|
||||
# ipython
|
||||
# rich
|
||||
pyproject-fmt==0.13.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
pyproject-hooks==1.0.0
|
||||
# via build
|
||||
pyyaml==6.0.1
|
||||
# via pre-commit
|
||||
rich==13.5.2
|
||||
# via typer
|
||||
ruff==0.0.280
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
shellingham==1.5.0.post1
|
||||
# via typer
|
||||
six==1.16.0
|
||||
# via asttokens
|
||||
sniffio==1.3.0
|
||||
# via
|
||||
# anyio
|
||||
# httpcore
|
||||
# httpx
|
||||
sqlalchemy[asyncio,mypy]==2.0.19
|
||||
# via
|
||||
# alembic
|
||||
# awesome-fastapi-projects (pyproject.toml)
|
||||
stack-data==0.6.2
|
||||
# via ipython
|
||||
stamina==23.1.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
tenacity==8.2.2
|
||||
# via stamina
|
||||
third-party-imports==0.0.7
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
tomlkit==0.12.1
|
||||
# via pyproject-fmt
|
||||
traitlets==5.9.0
|
||||
# via
|
||||
# ipython
|
||||
# matplotlib-inline
|
||||
typer[all]==0.9.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
types-aiofiles==23.1.0.5
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
typing-extensions==4.7.1
|
||||
# via
|
||||
# alembic
|
||||
# mypy
|
||||
# pydantic
|
||||
# pydantic-core
|
||||
# sqlalchemy
|
||||
# typer
|
||||
virtualenv==20.24.2
|
||||
# via pre-commit
|
||||
wcwidth==0.2.6
|
||||
# via prompt-toolkit
|
||||
wheel==0.41.0
|
||||
# via pip-tools
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
# pip
|
||||
# setuptools
|
123
requirements/test.txt
Normal file
123
requirements/test.txt
Normal file
|
@ -0,0 +1,123 @@
|
|||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.11
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --extra=test --output-file=requirements/test.txt pyproject.toml
|
||||
#
|
||||
aiofiles==23.1.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
aiosqlite==0.19.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
alembic==1.11.1
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
annotated-types==0.5.0
|
||||
# via pydantic
|
||||
anyio==3.7.1
|
||||
# via
|
||||
# awesome-fastapi-projects (pyproject.toml)
|
||||
# httpcore
|
||||
# pytest-anyio
|
||||
certifi==2023.7.22
|
||||
# via
|
||||
# httpcore
|
||||
# httpx
|
||||
click==8.1.6
|
||||
# via typer
|
||||
colorama==0.4.6
|
||||
# via typer
|
||||
coverage[toml]==7.2.7
|
||||
# via pytest-cov
|
||||
dirty-equals==0.6.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
faker==19.2.0
|
||||
# via polyfactory
|
||||
greenlet==2.0.2
|
||||
# via sqlalchemy
|
||||
h11==0.14.0
|
||||
# via httpcore
|
||||
httpcore==0.18.0
|
||||
# via httpx
|
||||
httpx==0.25.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
httpx-sse==0.3.1
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
idna==3.4
|
||||
# via
|
||||
# anyio
|
||||
# httpx
|
||||
iniconfig==2.0.0
|
||||
# via pytest
|
||||
loguru==0.7.2
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
mako==1.2.4
|
||||
# via alembic
|
||||
markdown-it-py==3.0.0
|
||||
# via rich
|
||||
markupsafe==2.1.3
|
||||
# via mako
|
||||
mdurl==0.1.2
|
||||
# via markdown-it-py
|
||||
mypy==1.4.1
|
||||
# via sqlalchemy
|
||||
mypy-extensions==1.0.0
|
||||
# via mypy
|
||||
packaging==23.1
|
||||
# via pytest
|
||||
pluggy==1.2.0
|
||||
# via pytest
|
||||
polyfactory==2.7.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
pydantic==2.1.1
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
pydantic-core==2.4.0
|
||||
# via pydantic
|
||||
pygments==2.15.1
|
||||
# via rich
|
||||
pytest==7.4.0
|
||||
# via
|
||||
# awesome-fastapi-projects (pyproject.toml)
|
||||
# pytest-anyio
|
||||
# pytest-cov
|
||||
# pytest-mock
|
||||
pytest-anyio==0.0.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
pytest-cov==4.1.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
pytest-mock==3.11.1
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
python-dateutil==2.8.2
|
||||
# via faker
|
||||
pytz==2023.3
|
||||
# via dirty-equals
|
||||
rich==13.5.2
|
||||
# via typer
|
||||
shellingham==1.5.0.post1
|
||||
# via typer
|
||||
six==1.16.0
|
||||
# via python-dateutil
|
||||
sniffio==1.3.0
|
||||
# via
|
||||
# anyio
|
||||
# httpcore
|
||||
# httpx
|
||||
sqlalchemy[asyncio,mypy]==2.0.19
|
||||
# via
|
||||
# alembic
|
||||
# awesome-fastapi-projects (pyproject.toml)
|
||||
stamina==23.1.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
tenacity==8.2.2
|
||||
# via stamina
|
||||
third-party-imports==0.0.7
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
typer[all]==0.9.0
|
||||
# via awesome-fastapi-projects (pyproject.toml)
|
||||
typing-extensions==4.7.1
|
||||
# via
|
||||
# alembic
|
||||
# mypy
|
||||
# polyfactory
|
||||
# pydantic
|
||||
# pydantic-core
|
||||
# sqlalchemy
|
||||
# typer
|
15385
results.json
15385
results.json
File diff suppressed because it is too large
Load Diff
|
@ -1,21 +0,0 @@
|
|||
import shutil
|
||||
|
||||
import git
|
||||
from git.repo.base import Repo
|
||||
from giturlparse import parse
|
||||
|
||||
# class Progress(git.remote.RemoteProgress):
|
||||
# def update(self, op_code, cur_count, max_count=None, message=''):
|
||||
# print(self._cur_line)
|
||||
|
||||
with open("unique_links.txt") as fp:
|
||||
links = fp.readlines()
|
||||
for i, link in enumerate(links, start=1):
|
||||
link = link.rstrip()
|
||||
name = parse(link).name
|
||||
print(f"File num: {i}")
|
||||
Repo.clone_from(link, name)
|
||||
try:
|
||||
shutil.move(name, "reps")
|
||||
except:
|
||||
shutil.rmtree(name)
|
|
@ -1,13 +0,0 @@
|
|||
f_in = open("links.txt", "r")
|
||||
f_out = open("unique_links.txt", "w")
|
||||
|
||||
links = set()
|
||||
|
||||
for line in f_in.readlines():
|
||||
links.add(line)
|
||||
|
||||
for link in links:
|
||||
f_out.write(link)
|
||||
|
||||
f_in.close()
|
||||
f_out.close()
|
|
@ -1,29 +0,0 @@
|
|||
import json
|
||||
import re
|
||||
from typing import Dict, Union
|
||||
|
||||
f_in = open("imports.txt", "r")
|
||||
|
||||
mp: Dict[str, Union[set, list]] = {}
|
||||
|
||||
for line in f_in.readlines():
|
||||
try:
|
||||
rep_name = line.split("/")[1]
|
||||
except IndexError:
|
||||
rep_name = ""
|
||||
mp[rep_name] = mp.get(rep_name, set())
|
||||
result = re.search(r"from (\w+)[\.\w+]*|:[ ]*import (\w*)\n", line)
|
||||
if result:
|
||||
if result.group(1):
|
||||
mp[rep_name].add(result.group(1))
|
||||
if result.group(2):
|
||||
mp[rep_name].add(result.group(2))
|
||||
|
||||
for key in mp:
|
||||
mp[key] = list(mp[key])
|
||||
|
||||
with open("results.json", "w") as f:
|
||||
json.dump(mp, f, sort_keys=True, indent=2)
|
||||
|
||||
print(len(mp))
|
||||
f_in.close()
|
|
@ -1,4 +0,0 @@
|
|||
|
||||
for file in $(find reps -maxdepth 1 -type d); do
|
||||
grep -r "import" --include \*.py $file > imports.txt
|
||||
done
|
|
@ -1,57 +0,0 @@
|
|||
import json
|
||||
import os
|
||||
import sys
|
||||
from time import sleep
|
||||
|
||||
import requests
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
username = os.getenv("GITHUB_USERNAME")
|
||||
password = os.getenv("GITHUB_PASSWORD")
|
||||
API_URL = "https://api.github.com"
|
||||
|
||||
|
||||
def get_response(page: int) -> dict:
|
||||
res = requests.get(
|
||||
f"{API_URL}/search/code",
|
||||
auth=(username, password),
|
||||
params={"q": "fastapi language:Python", "per_page": 100, "page": page},
|
||||
)
|
||||
return res
|
||||
|
||||
|
||||
def get_next_link(link_header: str) -> str:
|
||||
return getattr(
|
||||
{
|
||||
rel: link
|
||||
for (link, rel) in re.findall(r'<(http.*?)>; rel="(.*?)"', link_header)
|
||||
},
|
||||
"next",
|
||||
None,
|
||||
)
|
||||
|
||||
|
||||
filename = "links.txt"
|
||||
file1 = open(filename, "a") # append mode
|
||||
has_next = True
|
||||
page = 1
|
||||
while has_next:
|
||||
sleep(1)
|
||||
res = get_response(page)
|
||||
res_json = res.json()
|
||||
if "items" in res_json:
|
||||
for item in res_json["items"]:
|
||||
file1.write(f"{item['repository']['html_url']}\n")
|
||||
print(f"Page: {page}")
|
||||
print(res.headers)
|
||||
# print(json.dumps(res_json, indent=4, sort_keys=True))
|
||||
# print(res.headers.get('X-RateLimit-Reset', 0))
|
||||
if int(
|
||||
res.headers.get("X-RateLimit-Remaining", 0)
|
||||
) == 0 or "422" in res.headers.get("Status", "422"):
|
||||
has_next = False
|
||||
page += 1
|
||||
|
||||
file1.close()
|
|
@ -1,22 +0,0 @@
|
|||
import re
|
||||
import sys
|
||||
|
||||
filename_in = sys.argv[1]
|
||||
filename_out = sys.argv[2]
|
||||
file_in = open(filename_in, "r")
|
||||
lines = file_in.readlines()
|
||||
file_out = open(filename_out, "w")
|
||||
|
||||
imports = set()
|
||||
|
||||
for line in lines:
|
||||
match1 = re.search(r"(from *(?!\.)(.+?)(?= |\.))", line)
|
||||
match2 = re.search(r"(: *(import) (.+))", line)
|
||||
if match1 is not None:
|
||||
imports.add(match1.group(2))
|
||||
if match2 is not None:
|
||||
imports.add(match2.group(3))
|
||||
|
||||
|
||||
for imp in sorted(list(imports)):
|
||||
file_out.write(f"{imp}\n")
|
Loading…
Reference in New Issue
Block a user