Skip to content
Snippets Groups Projects
Commit 78be68d3 authored by Adrian Block's avatar Adrian Block
Browse files

Merge branch 'dev' into feat/scraper-extension

parents df3ed34b 3d658eb6
No related branches found
No related tags found
1 merge request!3Dev
Showing with 160 additions and 58 deletions
"""semester created timestamp
Revision ID: 50a8e19d9668
Revises: bed1aefe514f
Create Date: 2022-03-10 19:01:45.914837
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '50a8e19d9668'
down_revision = 'bed1aefe514f'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('semester', sa.Column('created', sa.DateTime(), server_default=sa.text('now()'), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('semester', 'created')
# ### end Alembic commands ###
......@@ -3,7 +3,6 @@ from fastapi.openapi.utils import get_openapi
from starlette.middleware.cors import CORSMiddleware
from app.config import api_settings
from app import routes
from app.routes.api_v1.api import api_router
app = FastAPI(
......@@ -41,5 +40,4 @@ def custom_openapi():
app.openapi = custom_openapi
app.include_router(routes.courses_router, deprecated=True, tags=["courses"])
app.include_router(api_router, prefix=api_settings.API_V1_STR)
......@@ -5,16 +5,16 @@ from app import schemas
class Broker:
def find_course_by_id(self, semester_name: str, course_id: str) -> schemas.Course:
def find_course_by_id(self, semester_id: int, course_id: str) -> schemas.Course:
pass
def find_course_by_name(self, semester_name: str, course_name: str) -> schemas.Course:
def find_course_by_name(self, semester_id: int, course_name: str) -> schemas.Course:
pass
def find_courses_by_name(self, semester_name: str, course_name: str) -> List[schemas.Course]:
def find_courses_by_name(self, semester_id: int, course_name: str) -> List[schemas.Course]:
pass
def get_courses(self, semester_name: str) -> List[schemas.Course]:
def get_courses(self, semester_id: int) -> List[schemas.Course]:
pass
def get_semesters(self) -> List[schemas.SemesterWithoutCourses]:
......
import datetime
from typing import List
from sqlalchemy import func, and_
from sqlalchemy.orm import Session
from app import schemas
from app.data.brokers.broker import Broker
from app.data.storage import models
from app.processing import mapper
class DatabaseBroker(Broker):
......@@ -13,22 +16,49 @@ class DatabaseBroker(Broker):
def __init__(self, session: Session):
self.session = session
def find_course_by_id(self, semester_name: str, course_id: str) -> schemas.Course:
return self.session.query(models.Course).join(models.Semester).filter(models.Semester.name == semester_name,
def find_course_by_id(self, semester_id: int, course_id: str) -> schemas.Course:
return self.session.query(models.Course).join(models.Semester).filter(models.Semester.id == semester_id,
models.Course.cid == course_id).first()
def find_course_by_name(self, semester_name: str, course_name: str) -> schemas.Course:
return self.session.query(models.Course).join(models.Semester).filter(models.Semester.name == semester_name,
def find_course_by_name(self, semester_id: int, course_name: str) -> schemas.Course:
return self.session.query(models.Course).join(models.Semester).filter(models.Semester.id == semester_id,
models.Course.name == course_name).first()
def find_courses_by_name(self, semester_name: str, course_name: str) -> List[schemas.Course]:
return self.session.query(models.Course).join(models.Semester).filter(models.Semester.name == semester_name,
def find_courses_by_name(self, semester_id: int, course_name: str) -> List[schemas.Course]:
return self.session.query(models.Course).join(models.Semester).filter(models.Semester.id == semester_id,
models.Course.name.ilike(
f"%{course_name}%")).all()
def get_courses(self, semester_name) -> List[schemas.Course]:
def get_courses(self, semester_id: int) -> List[schemas.Course]:
return self.session.query(models.Course).join(models.Semester).filter(
models.Semester.name == semester_name).all()
models.Semester.name == semester_id).all()
def get_semesters(self) -> List[schemas.SemesterWithoutCourses]:
def get_semesters(self) -> List[schemas.SemesterWithoutCoursesButId]:
return self.session.query(models.Semester).all()
def get_newest_semesters(self) -> List[schemas.SemesterWithoutCoursesButId]:
# query every semester, but only return the newest one
sq = self.session.query(models.Semester.name.label("name"), func.max(models.Semester.created).label("created")) \
.group_by(models.Semester.name).subquery()
return self.session.query(models.Semester) \
.join(sq, and_(models.Semester.name == sq.c.name, models.Semester.created == sq.c.created)).all()
def get_all_courses_of_semester(self, semester_id: int) -> List[schemas.CourseWithoutAppointments]:
return self.session.query(models.Course).join(models.Semester).filter(models.Semester.id == semester_id).all()
def create_semester(self, semester: schemas.Semester):
if semester.created is None:
semester.created = datetime.datetime.now()
db_semester = mapper.map_semester(semester)
self.session.add(db_semester)
self.session.commit()
def delete_semester(self, semester_id: int) -> bool:
db_semester = self.session.query(models.Semester).filter(models.Semester.id == semester_id).first()
if db_semester:
self.session.delete(db_semester)
self.session.commit()
return True
else:
return False
......@@ -82,4 +82,6 @@ class Semester(Base):
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(VARCHAR(32), nullable=False)
created = Column(DateTime, nullable=True, server_default=func.now())
courses = relationship("Course", back_populates="semester")
......@@ -34,4 +34,5 @@ def map_semester(semester: schemas.Semester) -> models.Semester:
return models.Semester(
name=semester.name,
courses=[map_course(course) for course in semester.courses],
created=semester.created,
)
from .courses import courses_router
......@@ -7,6 +7,7 @@ api_key_header = APIKeyHeader(name="API-Key")
def check_api_key(api_key: str = Security(api_key_header)):
if not (api_key in api_settings.API_KEYS):
if not (api_key in [str(u) for u in api_settings.API_KEYS]):
print(api_key, api_settings.API_KEYS)
raise HTTPException(status_code=401, detail="API-Key header invalid")
return api_key
from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, HTTPException
from app import schemas
from app.data.brokers import DatabaseBroker
from app.routes.api_dependencies import check_api_key
from app.routes.dependencies import get_database_broker
router = APIRouter(
dependencies=[Depends(check_api_key)]
)
@router.post('/semester', status_code=201)
def create_semester(semester: schemas.Semester, broker: DatabaseBroker = Depends(get_database_broker)):
"""
Create a new semester
"""
broker.create_semester(semester)
@router.delete('/semester')
def delete_semester(semesterId: int, broker: DatabaseBroker = Depends(get_database_broker)):
"""
Delete a semester
"""
if not broker.delete_semester(semesterId):
raise HTTPException(status_code=404, detail="Semester not found")
from typing import List
from cachetools import TTLCache, cached
from fastapi import APIRouter, Depends, Query, Path, HTTPException
from app import schemas
from app.data.brokers import Broker
from app.data.brokers import DatabaseBroker
from app.routes.dependencies import get_database_broker
router = APIRouter()
@router.get("/search", response_model=List[schemas.CourseWithoutAppointments])
def list_and_search_courses_by_name(semester: str = Query(..., example="Sommer 2022"),
def list_and_search_courses_by_name(semesterId: int = Query(..., example=1),
title: str = Query(..., example="Systemsoftware und"),
broker: Broker = Depends(get_database_broker)):
broker: DatabaseBroker = Depends(get_database_broker)):
"""
List and search courses by name.
"""
return broker.find_courses_by_name(semester, title)
return broker.find_courses_by_name(semesterId, title)
@router.get("/all", response_model=List[schemas.CourseWithoutAppointments])
@cached(cache=TTLCache(maxsize=1024, ttl=600), key=lambda semesterId, broker: semesterId)
def list_all_courses_of_semester(semesterId: int = Query(..., example=1),
broker: DatabaseBroker = Depends(get_database_broker)):
"""
List all courses of a semester.
"""
return broker.get_all_courses_of_semester(semesterId)
@router.get("/{courseId}", response_model=schemas.Course, responses={404: {"model": schemas.DetailMessage}})
def retrieve_information_of_a_course(semester: str = Query(..., example="Sommer 2022"),
def retrieve_information_of_a_course(semesterId: int = Query(..., example=1),
courseId: str = Path(..., example="L.079.05401"),
broker: Broker = Depends(get_database_broker)):
broker: DatabaseBroker = Depends(get_database_broker)):
"""
Querries course by id with all small groups and appointments.
"""
course = broker.find_course_by_id(semester, courseId)
course = broker.find_course_by_id(semesterId, courseId)
if not course:
raise HTTPException(status_code=404, detail="Course not found")
return course
......@@ -4,20 +4,28 @@ from typing import List
from fastapi import APIRouter, Depends
from app import schemas
from app.data.brokers import Broker
from app.data.brokers import DatabaseBroker
from app.routes.dependencies import get_database_broker
router = APIRouter()
@router.get("/available", response_model=List[schemas.SemesterWithoutCourses])
def see_all_available_semesters(broker: Broker = Depends(get_database_broker)):
@router.get("/available", response_model=List[schemas.SemesterWithoutCoursesButId])
def see_all_available_semesters(broker: DatabaseBroker = Depends(get_database_broker)):
"""
Returns all available semesters.
"""
return broker.get_semesters()
@router.get("/newest", response_model=List[schemas.SemesterWithoutCoursesButId])
def see_newest_semesters(broker: DatabaseBroker = Depends(get_database_broker)):
"""
Returns newest semester of all semesters with same name.
"""
return broker.get_newest_semesters()
@router.get("/revision", response_model=int)
def see_current_data_revision():
return os.environ.get("REVISION", 1)
from typing import List
from fastapi import APIRouter, Depends, HTTPException
from app import schemas
from app.data.brokers import Broker
from app.routes.dependencies import get_database_broker
courses_router = APIRouter()
@courses_router.get("/courses", response_model=List[schemas.CourseWithoutAppointments])
def list_and_search_courses(semester: str, title: str = "", broker: Broker = Depends(get_database_broker)):
return broker.find_courses_by_name(semester, title)
@courses_router.get("/course", response_model=schemas.Course)
def get_course(semester: str, course_id: str, broker: Broker = Depends(get_database_broker)):
course = broker.find_course_by_id(semester, course_id)
if course:
return course
else:
raise HTTPException(status_code=404, detail="Course not found")
@courses_router.get("/courses/all", response_model=List[schemas.Course])
def get_all_courses(semester: str, broker: Broker = Depends(get_database_broker)):
return broker.get_courses(semester)
from .course import Course, Appointment, SmallGroup, CourseList, CourseWithoutAppointments, Semester, SemesterList, \
SemesterWithoutCourses
SemesterWithoutCourses, SemesterWithoutCoursesButId
from .message import DetailMessage
......@@ -83,6 +83,7 @@ class CourseList(BaseModel):
class SemesterWithoutCourses(BaseModel):
name: str
created: Optional[datetime.datetime] = datetime.datetime.now()
class Config:
orm_mode = True
......@@ -94,6 +95,10 @@ class SemesterWithoutCourses(BaseModel):
}
class SemesterWithoutCoursesButId(SemesterWithoutCourses):
id: int
class Semester(SemesterWithoutCourses):
courses: List[Course] = []
......
......@@ -164,6 +164,14 @@ python-versions = "*"
[package.dependencies]
beautifulsoup4 = "*"
[[package]]
name = "cachetools"
version = "5.0.0"
description = "Extensible memoizing collections and decorators"
category = "main"
optional = false
python-versions = "~=3.7"
[[package]]
name = "certifi"
version = "2021.5.30"
......@@ -1184,7 +1192,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
content-hash = "4c972a3cc4006629b30a5f0bf2cd88ce7ec10a0d33c5427ddf4d2aca222ecbfd"
content-hash = "3cd8e872d35e8646dadaaa75473e4ba9f3e9e6788e2e0e14e0111f73ff04931b"
[metadata.files]
aiohttp = [
......@@ -1294,6 +1302,10 @@ bleach = [
bs4 = [
{file = "bs4-0.0.1.tar.gz", hash = "sha256:36ecea1fd7cc5c0c6e4a1ff075df26d50da647b75376626cc186e2212886dd3a"},
]
cachetools = [
{file = "cachetools-5.0.0-py3-none-any.whl", hash = "sha256:8fecd4203a38af17928be7b90689d8083603073622229ca7077b72d8e5a976e4"},
{file = "cachetools-5.0.0.tar.gz", hash = "sha256:486471dfa8799eb7ec503a8059e263db000cdda20075ce5e48903087f79d5fd6"},
]
certifi = [
{file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"},
{file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"},
......@@ -1566,6 +1578,9 @@ markupsafe = [
{file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"},
{file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"},
{file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"},
{file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"},
{file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"},
{file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"},
{file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"},
{file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"},
......@@ -1577,6 +1592,9 @@ markupsafe = [
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"},
......@@ -1588,6 +1606,9 @@ markupsafe = [
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"},
{file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"},
......@@ -1600,6 +1621,9 @@ markupsafe = [
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"},
{file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"},
{file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"},
{file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"},
{file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"},
{file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"},
{file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"},
......@@ -1612,6 +1636,9 @@ markupsafe = [
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"},
{file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"},
{file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"},
{file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"},
{file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"},
{file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
{file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
......
......@@ -18,6 +18,7 @@ python-dotenv = "^0.19.0"
SQLAlchemy = "^1.4.29"
alembic = "^1.7.5"
psycopg2 = "^2.9.3"
cachetools = "^5.0.0"
[tool.poetry.dev-dependencies]
jupyter = "^1.0.0"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment