From 0d3300611d3d75bbc660f4294b263566d73191bb Mon Sep 17 00:00:00 2001 From: Dyakov Roman Date: Thu, 1 Dec 2022 23:45:35 +0300 Subject: [PATCH] Python 3.11 (#74) Co-authored-by: semen603089 Co-authored-by: Grigoriev Semyon <33061489+grigoriev-semyon@users.noreply.github.com> --- .github/workflows/build_and_publish.yml | 4 ++ .github/workflows/tests.yml | 2 +- Dockerfile | 17 +++--- calendar_backend/methods/list_calendar.py | 8 +-- calendar_backend/methods/utils.py | 2 +- calendar_backend/routes/base.py | 2 +- calendar_backend/settings.py | 5 +- gunicorn_conf.py | 67 ----------------------- tests/lecturer/photos.py | 5 ++ 9 files changed, 26 insertions(+), 86 deletions(-) delete mode 100644 gunicorn_conf.py diff --git a/.github/workflows/build_and_publish.yml b/.github/workflows/build_and_publish.yml index 0d627a6b..e250f6ef 100644 --- a/.github/workflows/build_and_publish.yml +++ b/.github/workflows/build_and_publish.yml @@ -71,6 +71,7 @@ jobs: --network=web \ --env DB_DSN=${{ secrets.DB_DSN }} \ --name ${{ env.CONTAITER_NAME }}_migration \ + --workdir="/" \ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:test \ alembic upgrade head @@ -85,6 +86,7 @@ jobs: --volume com_profcomff_api_timetable_test_static:/app/static \ --env DB_DSN=${{ secrets.DB_DSN }} \ --env GOOGLE_CLIENT_SECRET='${{ secrets.GOOGLE_CLIENT_SECRET }}' \ + --env STATIC_PATH=/app/static \ --name ${{ env.CONTAITER_NAME }} \ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:test @@ -112,6 +114,7 @@ jobs: --network=web \ --env DB_DSN=${{ secrets.DB_DSN }} \ --name ${{ env.CONTAITER_NAME }}_migration \ + --workdir="/" \ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest \ alembic upgrade head @@ -128,5 +131,6 @@ jobs: --env ADMIN_SECRET='${{ secrets.ADMIN_SECRET }}' \ --env REDIRECT_URL='https://www.profcomff.com/timetable/google' \ --env GOOGLE_CLIENT_SECRET='${{ secrets.GOOGLE_CLIENT_SECRET }}' \ + --env STATIC_PATH=/app/static \ --name ${{ env.CONTAITER_NAME }} \ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 34dd202b..5233a83b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,7 +18,7 @@ jobs: docker run -d -p 5432:5432 -e POSTGRES_HOST_AUTH_METHOD=trust --name db-test postgres:15-alpine - uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: '3.11' - name: Install dependencies run: | python -m ensurepip diff --git a/Dockerfile b/Dockerfile index 16b22b9f..cff08ad0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,13 @@ -FROM python:3.10 -WORKDIR /app -RUN mkdir -p static/cache && mkdir -p static/photo/lecturer +FROM tiangolo/uvicorn-gunicorn-fastapi:python3.11 +ENV APP_NAME=calendar_backend +ENV APP_MODULE=${APP_NAME}.routes.base:app COPY ./requirements.txt /app/ -RUN pip install --no-cache-dir -r /app/requirements.txt +RUN pip install -U -r /app/requirements.txt -ADD gunicorn_conf.py alembic.ini /app/ -ADD migrations /app/migrations -ADD calendar_backend /app/calendar_backend +COPY ./static /app/static/ -VOLUME ["/app/static"] +COPY ./alembic.ini /alembic.ini +COPY ./migrations /migrations/ -CMD [ "gunicorn", "-k", "uvicorn.workers.UvicornWorker", "-c", "/app/gunicorn_conf.py", "calendar_backend.routes.base:app" ] +COPY ./${APP_NAME} /app/${APP_NAME} diff --git a/calendar_backend/methods/list_calendar.py b/calendar_backend/methods/list_calendar.py index 7f791ac7..5e41a30a 100644 --- a/calendar_backend/methods/list_calendar.py +++ b/calendar_backend/methods/list_calendar.py @@ -54,9 +54,9 @@ async def create_user_calendar_file(user_calendar: Calendar, group: str) -> str: """ logger.debug(f"Creating .ics file from iCalendar {user_calendar.name}") try: - with open(f"{settings.ICS_PATH}/{group}", "wb") as f: + with open(f"{settings.STATIC_PATH}/cache/{group}", "wb") as f: f.write(user_calendar.to_ical()) - return f"{settings.ICS_PATH}/{group}" + return f"{settings.STATIC_PATH}/cache/{group}" except OSError as e: logger.info(f"The error {e} occurred") @@ -96,9 +96,9 @@ def check_file_for_creation_date(path_file: str) -> bool: async def create_ics(group_id: int, start: datetime.date, end: datetime.date, session: Session): - if check_file_for_creation_date(f"{settings.ICS_PATH}/{group_id}") is False: + if check_file_for_creation_date(f"{settings.STATIC_PATH}/cache/{group_id}") is False: logger.debug(f"Calendar for group '{group_id}' found in cache") - return FileResponse(f"{settings.ICS_PATH}/{group_id}") + return FileResponse(f"{settings.STATIC_PATH}/cache/{group_id}") else: async with asyncio.Lock(): logger.debug("Getting user calendar...") diff --git a/calendar_backend/methods/utils.py b/calendar_backend/methods/utils.py index c1e39f31..c21b9267 100644 --- a/calendar_backend/methods/utils.py +++ b/calendar_backend/methods/utils.py @@ -84,7 +84,7 @@ async def upload_lecturer_photo(lecturer_id: int, session: Session, file: Upload lecturer = Lecturer.get(lecturer_id, session=session) random_string = ''.join(random.choice(string.ascii_letters) for _ in range(32)) ext = file.filename.split('.')[-1] - path = os.path.join(settings.PHOTO_LECTURER_PATH, f"{random_string}.{ext}") + path = os.path.join(settings.STATIC_PATH, "photo", "lecturer", f"{random_string}.{ext}") async with aiofiles.open(path, 'wb') as out_file: content = await file.read() await out_file.write(content) diff --git a/calendar_backend/routes/base.py b/calendar_backend/routes/base.py index 399b9fe0..29df87d8 100644 --- a/calendar_backend/routes/base.py +++ b/calendar_backend/routes/base.py @@ -111,7 +111,7 @@ async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) - ) app.add_middleware(LimitUploadSize, max_upload_size=3145728) # 3MB -app.mount('/static', StaticFiles(directory='static'), 'static') +app.mount('/static', StaticFiles(directory=settings.STATIC_PATH), 'static') app.include_router(gcal) app.include_router(auth_router) diff --git a/calendar_backend/settings.py b/calendar_backend/settings.py index f66bb3cb..392b4d06 100644 --- a/calendar_backend/settings.py +++ b/calendar_backend/settings.py @@ -6,14 +6,13 @@ class Settings(BaseSettings): """Application settings""" - DB_DSN: PostgresDsn + DB_DSN: PostgresDsn = 'postgresql://postgres@localhost:5432/postgres' REDIRECT_URL: AnyHttpUrl = "https://www.profcomff.com" SCOPES: list[str] = [ "https://www.googleapis.com/auth/calendar", "https://www.googleapis.com/auth/userinfo.email", ] - ICS_PATH: DirectoryPath = "static/cache" - PHOTO_LECTURER_PATH: DirectoryPath = 'static/photo/lecturer' + STATIC_PATH: DirectoryPath | None ADMIN_SECRET: dict[str, str] = {"admin": "42"} REQUIRE_REVIEW_PHOTOS: bool = True REQUIRE_REVIEW_LECTURER_COMMENT: bool = True diff --git a/gunicorn_conf.py b/gunicorn_conf.py deleted file mode 100644 index 7dd141df..00000000 --- a/gunicorn_conf.py +++ /dev/null @@ -1,67 +0,0 @@ -import json -import multiprocessing -import os - -workers_per_core_str = os.getenv("WORKERS_PER_CORE", "1") -max_workers_str = os.getenv("MAX_WORKERS") -use_max_workers = None -if max_workers_str: - use_max_workers = int(max_workers_str) -web_concurrency_str = os.getenv("WEB_CONCURRENCY", None) - -host = os.getenv("HOST", "0.0.0.0") -port = os.getenv("PORT", "80") -bind_env = os.getenv("BIND", None) -use_loglevel = os.getenv("LOG_LEVEL", "info") -if bind_env: - use_bind = bind_env -else: - use_bind = f"{host}:{port}" - -cores = multiprocessing.cpu_count() -workers_per_core = float(workers_per_core_str) -default_web_concurrency = workers_per_core * cores -if web_concurrency_str: - web_concurrency = int(web_concurrency_str) - assert web_concurrency > 0 -else: - web_concurrency = max(int(default_web_concurrency), 2) - if use_max_workers: - web_concurrency = min(web_concurrency, use_max_workers) -accesslog_var = os.getenv("ACCESS_LOG", "-") -use_accesslog = accesslog_var or None -errorlog_var = os.getenv("ERROR_LOG", "-") -use_errorlog = errorlog_var or None -graceful_timeout_str = os.getenv("GRACEFUL_TIMEOUT", "120") -timeout_str = os.getenv("TIMEOUT", "120") -keepalive_str = os.getenv("KEEP_ALIVE", "5") - -# Gunicorn config variables -loglevel = use_loglevel -workers = web_concurrency -bind = use_bind -errorlog = use_errorlog -worker_tmp_dir = "/dev/shm" -accesslog = use_accesslog -graceful_timeout = int(graceful_timeout_str) -timeout = int(timeout_str) -keepalive = int(keepalive_str) - - -# For debugging and testing -log_data = { - "loglevel": loglevel, - "workers": workers, - "bind": bind, - "graceful_timeout": graceful_timeout, - "timeout": timeout, - "keepalive": keepalive, - "errorlog": errorlog, - "accesslog": accesslog, - # Additional, non-gunicorn variables - "workers_per_core": workers_per_core, - "use_max_workers": use_max_workers, - "host": host, - "port": port, -} -print(json.dumps(log_data)) diff --git a/tests/lecturer/photos.py b/tests/lecturer/photos.py index 782ff6a5..5feecb15 100644 --- a/tests/lecturer/photos.py +++ b/tests/lecturer/photos.py @@ -1,5 +1,10 @@ from fastapi.testclient import TestClient from starlette import status +from calendar_backend.settings import get_settings + + +settings = get_settings() +settings.STATIC_PATH = './static' def test_read_all(client_auth: TestClient, photo_path: str):