From bfea5eeb06ecf9be76f32fc6be57ac07bcb89192 Mon Sep 17 00:00:00 2001 From: DarkCat09 Date: Sat, 18 Feb 2023 20:01:13 +0400 Subject: [PATCH] Basic app, mainly copied from tmpl-flask --- .env | 5 ++ .env_debug | 5 ++ .gitea/template | 6 ++ .gitignore | 54 ++----------- Dockerfile | 7 ++ Makefile | 20 +++++ app/__init__.py | 0 app/main.py | 19 +++++ docker-compose.yml | 19 +++++ main.py | 9 +++ pylintrc | 187 +++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 5 ++ static/css/style.css | 60 ++++++++++++++ static/js/script.js | 4 + templates/404.html | 11 +++ templates/500.html | 15 ++++ templates/admin.html | 21 +++++ templates/base.html | 18 +++++ templates/index.html | 12 +++ templates/table.html | 18 +++++ 20 files changed, 446 insertions(+), 49 deletions(-) create mode 100644 .env create mode 100644 .env_debug create mode 100644 .gitea/template create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 app/__init__.py create mode 100644 app/main.py create mode 100644 docker-compose.yml create mode 100755 main.py create mode 100644 pylintrc create mode 100644 requirements.txt create mode 100644 static/css/style.css create mode 100644 static/js/script.js create mode 100644 templates/404.html create mode 100644 templates/500.html create mode 100644 templates/admin.html create mode 100644 templates/base.html create mode 100644 templates/index.html create mode 100644 templates/table.html diff --git a/.env b/.env new file mode 100644 index 0000000..0d64913 --- /dev/null +++ b/.env @@ -0,0 +1,5 @@ +MYSQL_HOST=${REPO_NAME_SNAKE}_db +MUSQL_PORT=3306 +MYSQL_USER=${REPO_NAME_SNAKE} +MYSQL_PASSWORD= +MYSQL_DATABASE=${REPO_NAME_SNAKE} diff --git a/.env_debug b/.env_debug new file mode 100644 index 0000000..cc0e3a0 --- /dev/null +++ b/.env_debug @@ -0,0 +1,5 @@ +MYSQL_HOST=localhost +MUSQL_PORT=3306 +MYSQL_USER=darkcat09 +MYSQL_PASSWORD= +MYSQL_DATABASE=apptest diff --git a/.gitea/template b/.gitea/template new file mode 100644 index 0000000..703649b --- /dev/null +++ b/.gitea/template @@ -0,0 +1,6 @@ +app/*.py +templates/base.html +docker-compose.yml +Makefile +.env +README.md diff --git a/.gitignore b/.gitignore index 5d381cc..b178e43 100644 --- a/.gitignore +++ b/.gitignore @@ -72,56 +72,23 @@ instance/ # 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 +#.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 +# PEP 582 __pypackages__/ -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - # Environments -.env .venv env/ venv/ @@ -129,13 +96,6 @@ ENV/ env.bak/ venv.bak/ -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - # mkdocs documentation /site @@ -153,10 +113,6 @@ dmypy.json # 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/ - +# IDE +.vscode/ +.idea/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..db03f5d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3-alpine +RUN apk update && apk upgrade && apk add py-pip make +WORKDIR /app +COPY . . +RUN pip install -r requirements.txt +CMD make prod +EXPOSE 8000 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..949d4ba --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +dev: + DEBUG="true" python3 -m uvicorn main:app --reload + +prod: + python3 -m uvicorn main:app + +format: + python3 -m autopep8 -r --in-place app/ + +check: + python3 -m mypy app/ + python3 -m pylint app/ + +docker: + docker build -t ${REPO_OWNER_LOWER}/${REPO_NAME_SNAKE} . + +clean: + rm -rf app/__pycache__ + rm -rf __pycache__ + rm -rf .mypy_cache diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..9100756 --- /dev/null +++ b/app/main.py @@ -0,0 +1,19 @@ +import secrets + +from fastapi import FastAPI +from fastapi.staticfiles import StaticFiles + +from pydantic import BaseSettings + + +class Settings(BaseSettings): + secret_key: str = secrets.token_hex(32) + + +settings = Settings() +app = FastAPI() +app.mount( + '/static', + StaticFiles(directory='../static'), + name='static', +) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..c76f131 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,19 @@ +version: "3" + +services: + ${REPO_NAME_SNAKE}: + image: ${REPO_OWNER}/${REPO_NAME_SNAKE}:latest + container_name: ${REPO_NAME_SNAKE} + restart: unless-stopped + ports: + - "8080:8000" + links: + - ${REPO_NAME_SNAKE}_db + env_file: .env + ${REPO_NAME_SNAKE}_db: + image: mariadb:latest + container_name: ${REPO_NAME_SNAKE}_db + restart: unless-stopped + volumes: + - "./database:/var/lib/mysql" + env_file: .env diff --git a/main.py b/main.py new file mode 100755 index 0000000..664c498 --- /dev/null +++ b/main.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 + +import uvicorn + +from app.main import app + + +if __name__ == '__main__': + uvicorn.run(app) diff --git a/pylintrc b/pylintrc new file mode 100644 index 0000000..258b920 --- /dev/null +++ b/pylintrc @@ -0,0 +1,187 @@ +[MAIN] +analyse-fallback-blocks=no +extension-pkg-allow-list= +extension-pkg-whitelist= +fail-on= +fail-under=10 +ignore=CVS +ignore-paths= +ignore-patterns=^\.# +ignored-modules= +jobs=4 +limit-inference-results=100 +load-plugins= +persistent=yes +py-version=3.10 +recursive=no +suggestion-mode=yes +unsafe-load-any-extension=no + +[REPORTS] +evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)) +msg-template= +reports=no +score=yes + +[MESSAGES CONTROL] +confidence=HIGH, + CONTROL_FLOW, + INFERENCE, + INFERENCE_FAILURE, + UNDEFINED +disable=raw-checker-failed, + bad-inline-option, + locally-disabled, + file-ignored, + suppressed-message, + useless-suppression, + deprecated-pragma, + use-symbolic-message-instead +enable=c-extension-no-member + +[SIMILARITIES] +ignore-comments=yes +ignore-docstrings=yes +ignore-imports=yes +ignore-signatures=yes +min-similarity-lines=4 + +[MISCELLANEOUS] +notes=FIXME, + XXX, + TODO +notes-rgx= + +[DESIGN] +exclude-too-few-public-methods= +ignored-parents= +max-args=5 +max-attributes=7 +max-bool-expr=5 +max-branches=12 +max-locals=15 +max-parents=7 +max-public-methods=20 +max-returns=6 +max-statements=50 +min-public-methods=1 + +[STRING] +check-quote-consistency=no +check-str-concat-over-line-jumps=no + +[CLASSES] +check-protected-access-in-special-methods=no +defining-attr-methods=__init__, + __new__, + setUp, + __post_init__ +exclude-protected=_asdict, + _fields, + _replace, + _source, + _make +valid-classmethod-first-arg=cls +valid-metaclass-classmethod-first-arg=cls + +[FORMAT] +expected-line-ending-format= +ignore-long-lines=^\s*(# )??$ +indent-after-paren=4 +indent-string=' ' +max-line-length=100 +max-module-lines=1000 +single-line-class-stmt=no +single-line-if-stmt=no + +[IMPORTS] +allow-any-import-level= +allow-wildcard-with-all=no +deprecated-modules= +ext-import-graph= +import-graph= +int-import-graph= +known-standard-library= +known-third-party=enchant +preferred-modules= + +[VARIABLES] +additional-builtins= +allow-global-unused-variables=yes +allowed-redefined-builtins= +callbacks=cb_, + _cb +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ +ignored-argument-names=_.*|^ignored_|^unused_ +init-import=no +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io + +[LOGGING] +logging-format-style=old +logging-modules=logging + +[EXCEPTIONS] +overgeneral-exceptions=BaseException, + Exception + +[BASIC] +argument-naming-style=snake_case +attr-naming-style=snake_case +bad-names=foo, + bar, + baz, + toto, + tutu, + tata +bad-names-rgxs= +class-attribute-naming-style=any +class-const-naming-style=UPPER_CASE +class-naming-style=PascalCase +const-naming-style=any +docstring-min-length=-1 +function-naming-style=snake_case +good-names=i, + j, + k, + f, + db, + ex, + Run, + _ +good-names-rgxs= +include-naming-hint=no +inlinevar-naming-style=any +method-naming-style=snake_case +module-naming-style=snake_case +name-group= +no-docstring-rgx=^_ +property-classes=abc.abstractproperty +variable-naming-style=snake_case + +[SPELLING] +max-spelling-suggestions=4 +spelling-dict= +spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: +spelling-ignore-words= +spelling-private-dict-file= +spelling-store-unknown-words=no + +[TYPECHECK] +contextmanager-decorators=contextlib.contextmanager +generated-members= +ignore-none=yes +ignore-on-opaque-inference=yes +ignored-checks-for-mixins=no-member, + not-async-context-manager, + not-context-manager, + attribute-defined-outside-init +ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace +missing-member-hint=yes +missing-member-hint-distance=1 +missing-member-max-choices=1 +mixin-class-rgx=.*[Mm]ixin +signature-mutators= + +[REFACTORING] +max-nested-blocks=5 +never-returning-functions=sys.exit,argparse.parse_error diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..9d933d0 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +fastapi==0.92.0 +uvicorn[standard]==0.20.0 +jinja2==3.1.2 +starlette-wtf==0.4.3 +python-dotenv==0.21.1 diff --git a/static/css/style.css b/static/css/style.css new file mode 100644 index 0000000..cdaca35 --- /dev/null +++ b/static/css/style.css @@ -0,0 +1,60 @@ +body { + height: 100vh; + padding: 0; + margin: 0; + font-family: sans-serif; + + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + + --bg: #fff; + --fg: #000; + + background: var(--bg); + color: var(--fg); +} + +@media (prefers-color-scheme: dark) { + body { + --bg: #202023; + --fg: #eee; + } +} + +header { margin-top: 5px; } +footer { margin-bottom: 5px; } + +a { + color: #5b8a55; +} +a:hover { + filter: brightness(120%); +} + +form { + display: flex; + flex-direction: column; +} +form > div { + margin-top: 5px; + display: flex; + flex-direction: row; + justify-content: end; +} +form > div > label { + width: 50%; + margin-right: 10px; +} +form > div > input { + width: 50%; + border: 1px solid var(--fg); + outline: none; + background: var(--bg); + color: var(--fg); +} +form > div > input:hover, +form > div > input:focus { + filter: brightness(130%); +} diff --git a/static/js/script.js b/static/js/script.js new file mode 100644 index 0000000..65e1260 --- /dev/null +++ b/static/js/script.js @@ -0,0 +1,4 @@ +addEventListener('load', () => { + document.getElementById('js') + .innerText = new Date().toLocaleString() +}) diff --git a/templates/404.html b/templates/404.html new file mode 100644 index 0000000..3faabe7 --- /dev/null +++ b/templates/404.html @@ -0,0 +1,11 @@ +{% extends "base.html" %} + +{% block title %}404{% endblock %} + +{% block content %} +

404: Not Found

+

+ Go to the + main page +

+{% endblock %} diff --git a/templates/500.html b/templates/500.html new file mode 100644 index 0000000..2835a07 --- /dev/null +++ b/templates/500.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} + +{% block title %}500{% endblock %} + +{% block content %} +

500: ISE

+

+ An error occured while + processing your request. +

+

+ Please, try again later + or contact web site admin. +

+{% endblock %} diff --git a/templates/admin.html b/templates/admin.html new file mode 100644 index 0000000..219eb96 --- /dev/null +++ b/templates/admin.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} + +{% block title %}Admin{% endblock %} + +{% block content %} +

Add a person to DB

+
+ {{ form.hidden_tag() }} + {% for field in form %} + {% if field.name != 'csrf_token' %} +
+ {{ field.label }} + {{ field }} +
+ {% endif %} + {% endfor %} +
+ +
+
+{% endblock %} diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..aa55818 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,18 @@ + + + + + + + {% block title %}{% endblock %} | ${REPO_NAME} + + + + +
${REPO_NAME}
+
+ {% block content %}{% endblock %} +
+ + + diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..a6c7f3b --- /dev/null +++ b/templates/index.html @@ -0,0 +1,12 @@ +{% extends "base.html" %} + +{% block title %}Main page{% endblock %} + +{% block content %} +

+ This is the default main page of + + Flask app template + +

+{% endblock %} diff --git a/templates/table.html b/templates/table.html new file mode 100644 index 0000000..252b2ea --- /dev/null +++ b/templates/table.html @@ -0,0 +1,18 @@ +{% extends "base.html" %} + +{% block title %}Database{% endblock %} + +{% block content %} +

Sample database

+ + + {% for row in rows %} + + {% for cell in row %} + + {% endfor %} + + {% endfor %} + +
{{ cell }}
+{% endblock %}