Praneeth Yerrapragada commited on
Commit
5753020
1 Parent(s): d5684b3

refactor: make postgresdb operations async

Browse files
app/api/routers/user.py CHANGED
@@ -1,13 +1,14 @@
1
- from typing import List
2
  from fastapi import APIRouter, Depends, HTTPException
3
- from sqlalchemy.orm import Session
4
 
5
- from app.engine.postgresdb import get_db
6
  from app.schema.index import UserCreate, User as UserSchema
7
  from app.model.user import User as UserModel
8
 
9
 
10
  user_router = r = APIRouter(prefix="/api/v1/users", tags=["users"])
 
11
 
12
 
13
  @r.post(
@@ -20,18 +21,15 @@ user_router = r = APIRouter(prefix="/api/v1/users", tags=["users"])
20
  500: {"description": "Internal server error"},
21
  },
22
  )
23
- async def create_user(user: UserCreate, db: Session = Depends(get_db)):
24
  try:
25
- db_user = (
26
- db.query(UserModel).filter(UserModel.email == user.email).sort_by(UserModel.updated_at, desc=True).first()
27
- )
28
  if db_user and not db_user.is_deleted:
29
  raise HTTPException(status_code=409, detail="User already exists")
30
 
31
- db_user = UserModel(**user.dict())
32
- db.add(db_user)
33
- db.commit()
34
- db.refresh(db_user)
35
  return db_user
36
  except Exception as e:
37
  raise HTTPException(status_code=500, detail=str(e))
@@ -46,8 +44,8 @@ async def create_user(user: UserCreate, db: Session = Depends(get_db)):
46
  500: {"description": "Internal server error"},
47
  },
48
  )
49
- async def get_user(user_id: int, db: Session = Depends(get_db)):
50
- user = db.query(UserModel).get(user_id)
51
  if not user:
52
  raise HTTPException(status_code=404, detail="User not found")
53
  return user
@@ -62,14 +60,11 @@ async def get_user(user_id: int, db: Session = Depends(get_db)):
62
  500: {"description": "Internal server error"},
63
  },
64
  )
65
- async def update_user(user_id: int, user_payload: UserCreate, db: Session = Depends(get_db)):
66
- user = db.query(UserModel).get(user_id)
67
  if not user:
68
  raise HTTPException(status_code=404, detail="User not found")
69
- user.name = user_payload.name
70
- user.email = user_payload.email
71
- db.commit()
72
- db.refresh(user)
73
  return user
74
 
75
 
@@ -82,11 +77,9 @@ async def update_user(user_id: int, user_payload: UserCreate, db: Session = Depe
82
  500: {"description": "Internal server error"},
83
  },
84
  )
85
- async def delete_user(user_id: int, db: Session = Depends(get_db)):
86
- user = db.query(UserModel).get(user_id)
87
  if not user:
88
  raise HTTPException(status_code=404, detail="User not found")
89
- user.is_deleted = True
90
- db.commit()
91
- db.refresh(user)
92
  return user
 
1
+ import logging
2
  from fastapi import APIRouter, Depends, HTTPException
3
+ from sqlalchemy.ext.asyncio import AsyncSession
4
 
5
+ from app.engine.postgresdb import get_db_session
6
  from app.schema.index import UserCreate, User as UserSchema
7
  from app.model.user import User as UserModel
8
 
9
 
10
  user_router = r = APIRouter(prefix="/api/v1/users", tags=["users"])
11
+ logger = logging.getLogger(__name__)
12
 
13
 
14
  @r.post(
 
21
  500: {"description": "Internal server error"},
22
  },
23
  )
24
+ async def create_user(user: UserCreate, db: AsyncSession = Depends(get_db_session)):
25
  try:
26
+ logger.info(f"Checking if user exists: {user.dict()}")
27
+ db_user = await UserModel.get(db, email=user.email)
 
28
  if db_user and not db_user.is_deleted:
29
  raise HTTPException(status_code=409, detail="User already exists")
30
 
31
+ logger.info(f"Creating user: {user.dict()}")
32
+ db_user = await UserModel.create(db, **user.dict())
 
 
33
  return db_user
34
  except Exception as e:
35
  raise HTTPException(status_code=500, detail=str(e))
 
44
  500: {"description": "Internal server error"},
45
  },
46
  )
47
+ async def get_user(user_id: int, db: AsyncSession = Depends(get_db_session)):
48
+ user = await UserModel.get(db, id=user_id)
49
  if not user:
50
  raise HTTPException(status_code=404, detail="User not found")
51
  return user
 
60
  500: {"description": "Internal server error"},
61
  },
62
  )
63
+ async def update_user(user_id: int, user_payload: UserCreate, db: AsyncSession = Depends(get_db_session)):
64
+ user = await UserModel.get(db, id=user_id)
65
  if not user:
66
  raise HTTPException(status_code=404, detail="User not found")
67
+ user = await UserModel.update(db, id=user_id, **user_payload.dict())
 
 
 
68
  return user
69
 
70
 
 
77
  500: {"description": "Internal server error"},
78
  },
79
  )
80
+ async def delete_user(user_id: int, db: AsyncSession = Depends(get_db_session)):
81
+ user = await UserModel.get(db, id=user_id)
82
  if not user:
83
  raise HTTPException(status_code=404, detail="User not found")
84
+ user = await UserModel.delete(db, id=user_id)
 
 
85
  return user
app/engine/postgresdb.py CHANGED
@@ -1,35 +1,69 @@
1
- import os
2
  import logging
3
- from sqlalchemy import create_engine
4
- from sqlalchemy.orm import sessionmaker, declarative_base, Session
5
- from fastapi import Request
6
 
7
- from dotenv import load_dotenv
 
 
 
 
 
 
 
 
8
 
9
- load_dotenv()
10
  logger = logging.getLogger(__name__)
11
 
12
- SQLALCHEMY_DATABASE_URL = os.getenv("SQLALCHEMY_DATABASE_URL")
13
- assert SQLALCHEMY_DATABASE_URL, f"SQLALCHEMY_DATABASE_URL is not set"
14
- logger.info(f"SQLALCHEMY_DATABASE_URL: {SQLALCHEMY_DATABASE_URL}")
15
 
16
- engine = create_engine(SQLALCHEMY_DATABASE_URL)
17
 
18
- SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
19
 
20
- Base = declarative_base()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
 
23
- # def get_db():
24
- # session = SessionLocal()
25
- # logger.info("Postgres Session created successfully!")
26
- # try:
27
- # yield session
28
- # finally:
29
- # session.close()
30
- # logger.info("Postgres Session closed successfully!")
31
 
32
 
33
- # Dependency
34
- def get_db(request: Request):
35
- return request.state.db
 
 
1
  import logging
2
+ import contextlib
3
+ from typing import Any, AsyncIterator
 
4
 
5
+ from sqlalchemy.ext.asyncio import (
6
+ AsyncConnection,
7
+ AsyncSession,
8
+ async_sessionmaker,
9
+ create_async_engine,
10
+ )
11
+ from sqlalchemy.orm import declarative_base
12
+
13
+ from config.index import config as env
14
 
 
15
  logger = logging.getLogger(__name__)
16
 
17
+ Base = declarative_base()
 
 
18
 
 
19
 
20
+ class PostgresDatabase:
21
 
22
+ def __init__(self, host: str, engine_kwargs: dict[str, Any] = {}):
23
+ self._engine = create_async_engine(host, **engine_kwargs)
24
+ self._sessionmaker = async_sessionmaker(autocommit=False, bind=self._engine)
25
+
26
+ async def close(self):
27
+ if self._engine is None:
28
+ raise Exception("DatabaseSessionManager is not initialized")
29
+ await self._engine.dispose()
30
+
31
+ self._engine = None
32
+ self._sessionmaker = None
33
+
34
+ @contextlib.asynccontextmanager
35
+ async def connect(self) -> AsyncIterator[AsyncConnection]:
36
+ if self._engine is None:
37
+ raise Exception("DatabaseSessionManager is not initialized")
38
+
39
+ async with self._engine.begin() as connection:
40
+ try:
41
+ yield connection
42
+ except Exception:
43
+ await connection.rollback()
44
+ raise
45
+
46
+ @contextlib.asynccontextmanager
47
+ async def session(self) -> AsyncIterator[AsyncSession]:
48
+ if self._sessionmaker is None:
49
+ raise Exception("DatabaseSessionManager is not initialized")
50
+
51
+ session = self._sessionmaker()
52
+ try:
53
+ yield session
54
+ except Exception:
55
+ await session.rollback()
56
+ raise
57
+ finally:
58
+ await session.close()
59
+
60
+ def get_engine(self):
61
+ return self._engine
62
 
63
 
64
+ postgresdb = PostgresDatabase(env.SQLALCHEMY_DATABASE_URL, {"echo": True, "future": True})
 
 
 
 
 
 
 
65
 
66
 
67
+ async def get_db_session():
68
+ async with postgresdb.session() as session:
69
+ yield session
app/model/base.py CHANGED
@@ -1,9 +1,10 @@
1
- from datetime import datetime
2
  from sqlalchemy import Column, DateTime, Integer
3
 
4
  from app.engine.postgresdb import Base
5
 
6
- class BaseModel():
 
7
  id = Column(Integer, primary_key=True, index=True)
8
- created_at = Column(DateTime, default=datetime.now())
9
- updated_at = Column(DateTime, default=datetime.now(), onupdate=datetime.now())
 
1
+ from datetime import datetime, timezone
2
  from sqlalchemy import Column, DateTime, Integer
3
 
4
  from app.engine.postgresdb import Base
5
 
6
+
7
+ class BaseModel:
8
  id = Column(Integer, primary_key=True, index=True)
9
+ created_at = Column(DateTime, default=datetime.now(timezone.utc))
10
+ updated_at = Column(DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc))
app/model/transaction.py CHANGED
@@ -1,16 +1,18 @@
1
- from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer, String
2
- from sqlalchemy.orm import relationship
 
3
 
4
  from app.model.base import BaseModel
5
  from app.engine.postgresdb import Base
6
 
 
7
  class Transaction(Base, BaseModel):
8
  __tablename__ = "transactions"
9
- transaction_date = Column(DateTime)
10
- category = Column(String)
11
- name_description = Column(String)
12
- amount = Column(Float)
13
- type = Column(String)
14
 
15
- user_id = Column(Integer, ForeignKey("users.id"))
16
- user = relationship("User", back_populates="transactions")
 
1
+ from datetime import datetime
2
+ from sqlalchemy import ForeignKey
3
+ from sqlalchemy.orm import relationship, Mapped, mapped_column
4
 
5
  from app.model.base import BaseModel
6
  from app.engine.postgresdb import Base
7
 
8
+
9
  class Transaction(Base, BaseModel):
10
  __tablename__ = "transactions"
11
+ transaction_date: Mapped[datetime]
12
+ category: Mapped[str]
13
+ name_description: Mapped[str]
14
+ amount: Mapped[float]
15
+ type: Mapped[str]
16
 
17
+ user_id = mapped_column(ForeignKey("users.id"))
18
+ user = relationship("User", back_populates="transactions")
app/model/user.py CHANGED
@@ -1,5 +1,6 @@
1
- from sqlalchemy import Column, String, Boolean
2
- from sqlalchemy.orm import relationship
 
3
 
4
  from app.model.base import BaseModel
5
  from app.engine.postgresdb import Base
@@ -8,9 +9,56 @@ from app.engine.postgresdb import Base
8
  class User(Base, BaseModel):
9
  __tablename__ = "users"
10
 
11
- name = Column(String)
12
- email = Column(String)
13
- hashed_password = Column(String)
14
- is_deleted = Column(Boolean, default=False)
15
 
16
  transactions = relationship("Transaction", back_populates="user")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sqlalchemy.orm import relationship, Mapped, mapped_column
2
+ from sqlalchemy.sql import expression as sql
3
+ from sqlalchemy.ext.asyncio import AsyncSession
4
 
5
  from app.model.base import BaseModel
6
  from app.engine.postgresdb import Base
 
9
  class User(Base, BaseModel):
10
  __tablename__ = "users"
11
 
12
+ name: Mapped[str]
13
+ email: Mapped[str]
14
+ hashed_password: Mapped[str]
15
+ is_deleted: Mapped[bool] = mapped_column(default=False)
16
 
17
  transactions = relationship("Transaction", back_populates="user")
18
+
19
+ @classmethod
20
+ async def create(cls: "type[User]", db: AsyncSession, **kwargs) -> "User":
21
+ query = sql.insert(cls).values(**kwargs).returning(cls.id)
22
+ users = await db.execute(query)
23
+ await db.commit()
24
+ return users.first()
25
+
26
+ @classmethod
27
+ async def update(cls: "type[User]", db: AsyncSession, id: int, **kwargs) -> "User":
28
+ query = (
29
+ sql.update(cls)
30
+ .where(cls.id == id)
31
+ .values(**kwargs)
32
+ .execution_options(synchronize_session="fetch")
33
+ .returning(cls.id)
34
+ )
35
+ users = await db.execute(query)
36
+ await db.commit()
37
+ return users.first()
38
+
39
+ @classmethod
40
+ async def get(cls: "type[User]", db: AsyncSession, id: int) -> "User":
41
+ query = sql.select(cls).where(cls.id == id)
42
+ users = await db.execute(query)
43
+ (user,) = users.first()
44
+ return user
45
+
46
+ @classmethod
47
+ async def get(cls: "type[User]", db: AsyncSession, email: str) -> "User":
48
+ query = sql.select(cls).where(cls.email == email)
49
+ users = await db.execute(query)
50
+ (user,) = users.first()
51
+ return user
52
+
53
+ @classmethod
54
+ async def delete(cls: "type[User]", db: AsyncSession, id: int) -> "User":
55
+ query = (
56
+ sql.update(cls)
57
+ .where(cls.id == id)
58
+ .values(is_deleted=True)
59
+ .execution_options(synchronize_session="fetch")
60
+ .returning(cls.id)
61
+ )
62
+ users = await db.execute(query)
63
+ await db.commit()
64
+ return users.first()
app/schema/index.py CHANGED
@@ -21,7 +21,7 @@ class User(BaseModel):
21
  email: str
22
  hashed_password: str
23
  is_deleted: bool = False
24
- transactions: "List[Transaction]"
25
 
26
 
27
  class Transaction(BaseModel):
 
21
  email: str
22
  hashed_password: str
23
  is_deleted: bool = False
24
+ transactions: "List[Transaction]" = []
25
 
26
 
27
  class Transaction(BaseModel):
config/index.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+
4
+ load_dotenv()
5
+
6
+
7
+ class Config:
8
+ DEBUG = os.getenv("DEBUG", False)
9
+ SQLALCHEMY_DATABASE_URI = os.getenv("SQLALCHEMY_DATABASE_URI")
10
+ SQLALCHEMY_TRACK_MODIFICATIONS = os.getenv("SQLALCHEMY_TRACK_MODIFICATIONS", False)
11
+ ENVIRONMENT = os.getenv("ENVIRONMENT", "dev")
12
+ APP_HOST = os.getenv("APP_HOST", "0.0.0.0")
13
+ APP_PORT = int(os.getenv("APP_PORT", "8000"))
14
+ PINECONE_API_KEY = os.getenv("PINECONE_API_KEY")
15
+ PINECONE_INDEX_NAME = os.getenv("PINECONE_INDEX_NAME")
16
+ PINECONE_ENVIRONMENT = os.getenv("PINECONE_ENVIRONMENT")
17
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
18
+ OPENAI_ORGANIZATION = os.getenv("OPENAI_ORGANIZATION")
19
+ MODEL = os.getenv("MODEL", "gpt-3.5-turbo")
20
+ EMBEDDING_MODEL = os.getenv("EMBEDDING_MODEL", "text-embedding-ada-002")
21
+ EMBEDDING_DIM = os.getenv("EMBEDDING_DIM", "1536")
22
+ CHUNK_SIZE = int(os.getenv("CHUNK_SIZE", "1024"))
23
+ CHUNK_OVERLAP = int(os.getenv("CHUNK_OVERLAP", "20"))
24
+ MAX_TOKENS = int(os.getenv("MAX_TOKENS", "4000"))
25
+
26
+ POSTGRES_USER = os.getenv("POSTGRES_USER")
27
+ POSTGRES_PASSWORD = os.getenv("POSTGRES_PASSWORD")
28
+ POSTGRES_DB_NAME = os.getenv("POSTGRES_DB_NAME")
29
+ POSTGRES_DB_HOST = os.getenv("POSTGRES_DB_HOST")
30
+ POSTGRES_DB_PORT = os.getenv("POSTGRES_DB_PORT")
31
+ SQLALCHEMY_DATABASE_URL = f"postgresql+asyncpg://{POSTGRES_USER}:{POSTGRES_PASSWORD}@{POSTGRES_DB_HOST}:{POSTGRES_DB_PORT}/{POSTGRES_DB_NAME}"
32
+
33
+
34
+ config = Config
main.py CHANGED
@@ -1,89 +1,76 @@
1
- from dotenv import load_dotenv
2
-
3
- load_dotenv()
4
-
5
  import logging
6
  import os
7
  import uvicorn
8
  from contextlib import asynccontextmanager
9
- from fastapi import FastAPI, Request, Response
10
  from fastapi.middleware.cors import CORSMiddleware
11
  from fastapi.responses import RedirectResponse
12
  from app.api.routers.chat import chat_router
13
  from app.api.routers.user import user_router
14
  from app.settings import init_settings
15
- from app.observability import init_observability
16
  from fastapi.staticfiles import StaticFiles
17
  from alembic.config import Config
18
  from alembic import command
19
 
20
 
21
- from app.engine.postgresdb import engine, Base, SessionLocal
 
22
 
23
  logger = logging.getLogger("uvicorn")
24
 
25
- # Create all tables in the database
26
- Base.metadata.create_all(bind=engine)
27
 
 
 
 
 
 
 
 
 
28
 
29
- def run_migrations():
30
- alembic_cfg = Config("alembic.ini")
31
- command.upgrade(alembic_cfg, "head")
 
32
 
33
 
34
  @asynccontextmanager
35
  async def lifespan(app_: FastAPI):
36
- logger.info("Starting up...")
37
- logger.info("Run 'alembic upgrade head' to apply migrations...")
38
- run_migrations()
39
  yield
40
- logger.info("Shutting down...")
 
41
 
42
 
43
- app = FastAPI(lifespan=lifespan)
 
44
 
45
 
46
- @app.middleware("http")
47
- async def db_session_middleware(request: Request, call_next):
48
- response = Response("Internal server error", status_code=500)
49
- try:
50
- request.state.db = SessionLocal()
51
- response = await call_next(request)
52
- finally:
53
- request.state.db.close()
54
- return response
 
 
55
 
 
 
 
 
56
 
57
- init_settings()
58
- init_observability()
59
 
60
- environment = os.getenv("ENVIRONMENT", "dev") # Default to 'development' if not set
61
 
62
- if environment == "dev":
63
- logger.warning("Running in development mode - allowing CORS for all origins")
64
- app.add_middleware(
65
- CORSMiddleware,
66
- allow_origins=["*"],
67
- allow_credentials=True,
68
- allow_methods=["*"],
69
- allow_headers=["*"],
70
- )
71
 
 
72
  # Redirect to documentation page when accessing base URL
73
  @app.get("/")
74
  async def redirect_to_docs():
75
  return RedirectResponse(url="/docs")
76
 
77
-
78
- if os.path.exists("data"):
79
- app.mount("/api/data", StaticFiles(directory="data"), name="static")
80
- app.include_router(chat_router, prefix="/api/chat")
81
- app.include_router(user_router)
82
-
83
-
84
- if __name__ == "__main__":
85
- app_host = os.getenv("APP_HOST", "0.0.0.0")
86
- app_port = int(os.getenv("APP_PORT", "8000"))
87
- reload = True if environment == "dev" else False
88
-
89
- uvicorn.run(app="main:app", host=app_host, port=app_port, reload=reload)
 
 
 
 
 
1
  import logging
2
  import os
3
  import uvicorn
4
  from contextlib import asynccontextmanager
5
+ from fastapi import FastAPI
6
  from fastapi.middleware.cors import CORSMiddleware
7
  from fastapi.responses import RedirectResponse
8
  from app.api.routers.chat import chat_router
9
  from app.api.routers.user import user_router
10
  from app.settings import init_settings
 
11
  from fastapi.staticfiles import StaticFiles
12
  from alembic.config import Config
13
  from alembic import command
14
 
15
 
16
+ from app.engine.postgresdb import postgresdb
17
+ from config.index import config
18
 
19
  logger = logging.getLogger("uvicorn")
20
 
 
 
21
 
22
+ def run_migrations(connection, cfg):
23
+ cfg.attributes["connection"] = connection
24
+ command.upgrade(cfg, "head")
25
+
26
+
27
+ async def run_async_migrations():
28
+ logger.info("Running migrations...")
29
+ async_engine = postgresdb.get_engine()
30
 
31
+ async with async_engine.begin() as conn:
32
+ await conn.run_sync(run_migrations, Config("alembic.ini"))
33
+
34
+ logger.info("Finished running migrations")
35
 
36
 
37
  @asynccontextmanager
38
  async def lifespan(app_: FastAPI):
 
 
 
39
  yield
40
+ if postgresdb.get_engine() is not None:
41
+ await postgresdb.close()
42
 
43
 
44
+ init_settings()
45
+ # init_observability()
46
 
47
 
48
+ def init_app():
49
+ app = FastAPI(lifespan=lifespan)
50
+ if config.ENVIRONMENT == "dev":
51
+ logger.warning("Running in development mode - allowing CORS for all origins")
52
+ app.add_middleware(
53
+ CORSMiddleware,
54
+ allow_origins=["*"],
55
+ allow_credentials=True,
56
+ allow_methods=["*"],
57
+ allow_headers=["*"],
58
+ )
59
 
60
+ if os.path.exists("data"):
61
+ app.mount("/api/data", StaticFiles(directory="data"), name="static")
62
+ app.include_router(chat_router, prefix="/api/chat")
63
+ app.include_router(user_router)
64
 
65
+ return app
 
66
 
 
67
 
68
+ app = init_app()
 
 
 
 
 
 
 
 
69
 
70
+ if __name__ == "__main__":
71
  # Redirect to documentation page when accessing base URL
72
  @app.get("/")
73
  async def redirect_to_docs():
74
  return RedirectResponse(url="/docs")
75
 
76
+ uvicorn.run(app="main:app", host=config.APP_HOST, port=config.APP_PORT, reload=(config.ENVIRONMENT == "dev"))
 
 
 
 
 
 
 
 
 
 
 
 
migration/env.py CHANGED
@@ -1,21 +1,20 @@
1
- import os
2
  from logging.config import fileConfig
3
 
4
  from app.engine.postgresdb import Base
5
- from sqlalchemy import engine_from_config
6
  from sqlalchemy import pool
 
7
 
8
- from alembic import context
9
-
10
- from dotenv import load_dotenv
11
 
12
- load_dotenv()
13
 
14
  # this is the Alembic Config object, which provides
15
  # access to the values within the .ini file in use.
16
  config = context.config
17
 
18
- config.set_main_option("sqlalchemy.url", os.getenv("SQLALCHEMY_DATABASE_URL"))
19
 
20
  # Interpret the config file for Python logging.
21
  # This line sets up loggers basically.
@@ -37,17 +36,14 @@ target_metadata = Base.metadata
37
  # ... etc.
38
 
39
 
40
- def run_migrations_offline() -> None:
41
  """Run migrations in 'offline' mode.
42
-
43
  This configures the context with just a URL
44
  and not an Engine, though an Engine is acceptable
45
  here as well. By skipping the Engine creation
46
  we don't even need a DBAPI to be available.
47
-
48
  Calls to context.execute() here emit the given string to the
49
  script output.
50
-
51
  """
52
  url = config.get_main_option("sqlalchemy.url")
53
  context.configure(
@@ -61,29 +57,33 @@ def run_migrations_offline() -> None:
61
  context.run_migrations()
62
 
63
 
64
- def run_migrations_online() -> None:
65
- """Run migrations in 'online' mode.
66
 
 
 
 
 
 
 
67
  In this scenario we need to create an Engine
68
  and associate a connection with the context.
69
-
70
  """
71
- connectable = engine_from_config(
72
- config.get_section(config.config_ini_section, {}),
 
 
73
  prefix="sqlalchemy.",
74
  poolclass=pool.NullPool,
75
  )
76
 
77
- with connectable.connect() as connection:
78
- context.configure(
79
- connection=connection, target_metadata=target_metadata, compare_type=True
80
- )
81
 
82
- with context.begin_transaction():
83
- context.run_migrations()
84
 
85
 
86
  if context.is_offline_mode():
87
  run_migrations_offline()
88
  else:
89
- run_migrations_online()
 
1
+ import asyncio
2
  from logging.config import fileConfig
3
 
4
  from app.engine.postgresdb import Base
5
+ from sqlalchemy import engine_from_config, Connection
6
  from sqlalchemy import pool
7
+ from sqlalchemy.ext.asyncio import AsyncEngine, async_engine_from_config
8
 
9
+ from config.index import config as env
 
 
10
 
11
+ from alembic import context
12
 
13
  # this is the Alembic Config object, which provides
14
  # access to the values within the .ini file in use.
15
  config = context.config
16
 
17
+ config.set_main_option("sqlalchemy.url", env.SQLALCHEMY_DATABASE_URL)
18
 
19
  # Interpret the config file for Python logging.
20
  # This line sets up loggers basically.
 
36
  # ... etc.
37
 
38
 
39
+ def run_migrations_offline():
40
  """Run migrations in 'offline' mode.
 
41
  This configures the context with just a URL
42
  and not an Engine, though an Engine is acceptable
43
  here as well. By skipping the Engine creation
44
  we don't even need a DBAPI to be available.
 
45
  Calls to context.execute() here emit the given string to the
46
  script output.
 
47
  """
48
  url = config.get_main_option("sqlalchemy.url")
49
  context.configure(
 
57
  context.run_migrations()
58
 
59
 
60
+ def do_run_migrations(connection: Connection) -> None:
61
+ context.configure(connection=connection, target_metadata=target_metadata)
62
 
63
+ with context.begin_transaction():
64
+ context.run_migrations()
65
+
66
+
67
+ async def run_migrations_online():
68
+ """Run migrations in 'online' mode.
69
  In this scenario we need to create an Engine
70
  and associate a connection with the context.
 
71
  """
72
+ configuration = config.get_section(config.config_ini_section)
73
+ configuration["sqlalchemy.url"] = env.SQLALCHEMY_DATABASE_URL
74
+ connectable = async_engine_from_config(
75
+ configuration,
76
  prefix="sqlalchemy.",
77
  poolclass=pool.NullPool,
78
  )
79
 
80
+ async with connectable.connect() as connection:
81
+ await connection.run_sync(do_run_migrations)
 
 
82
 
83
+ await connectable.dispose()
 
84
 
85
 
86
  if context.is_offline_mode():
87
  run_migrations_offline()
88
  else:
89
+ asyncio.run(run_migrations_online())
poetry.lock CHANGED
@@ -198,6 +198,87 @@ doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphin
198
  test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"]
199
  trio = ["trio (>=0.23)"]
200
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  [[package]]
202
  name = "attrs"
203
  version = "23.2.0"
@@ -2661,87 +2742,6 @@ files = [
2661
  {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"},
2662
  ]
2663
 
2664
- [[package]]
2665
- name = "psycopg2-binary"
2666
- version = "2.9.9"
2667
- description = "psycopg2 - Python-PostgreSQL Database Adapter"
2668
- optional = false
2669
- python-versions = ">=3.7"
2670
- files = [
2671
- {file = "psycopg2-binary-2.9.9.tar.gz", hash = "sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c"},
2672
- {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c2470da5418b76232f02a2fcd2229537bb2d5a7096674ce61859c3229f2eb202"},
2673
- {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c6af2a6d4b7ee9615cbb162b0738f6e1fd1f5c3eda7e5da17861eacf4c717ea7"},
2674
- {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75723c3c0fbbf34350b46a3199eb50638ab22a0228f93fb472ef4d9becc2382b"},
2675
- {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83791a65b51ad6ee6cf0845634859d69a038ea9b03d7b26e703f94c7e93dbcf9"},
2676
- {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ef4854e82c09e84cc63084a9e4ccd6d9b154f1dbdd283efb92ecd0b5e2b8c84"},
2677
- {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed1184ab8f113e8d660ce49a56390ca181f2981066acc27cf637d5c1e10ce46e"},
2678
- {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d2997c458c690ec2bc6b0b7ecbafd02b029b7b4283078d3b32a852a7ce3ddd98"},
2679
- {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245"},
2680
- {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0c009475ee389757e6e34611d75f6e4f05f0cf5ebb76c6037508318e1a1e0d7e"},
2681
- {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8dbf6d1bc73f1d04ec1734bae3b4fb0ee3cb2a493d35ede9badbeb901fb40f6f"},
2682
- {file = "psycopg2_binary-2.9.9-cp310-cp310-win32.whl", hash = "sha256:3f78fd71c4f43a13d342be74ebbc0666fe1f555b8837eb113cb7416856c79682"},
2683
- {file = "psycopg2_binary-2.9.9-cp310-cp310-win_amd64.whl", hash = "sha256:876801744b0dee379e4e3c38b76fc89f88834bb15bf92ee07d94acd06ec890a0"},
2684
- {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26"},
2685
- {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f"},
2686
- {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2"},
2687
- {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0"},
2688
- {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53"},
2689
- {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be"},
2690
- {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27"},
2691
- {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359"},
2692
- {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2"},
2693
- {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc"},
2694
- {file = "psycopg2_binary-2.9.9-cp311-cp311-win32.whl", hash = "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d"},
2695
- {file = "psycopg2_binary-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417"},
2696
- {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf"},
2697
- {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d"},
2698
- {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212"},
2699
- {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493"},
2700
- {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996"},
2701
- {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119"},
2702
- {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba"},
2703
- {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07"},
2704
- {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb"},
2705
- {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe"},
2706
- {file = "psycopg2_binary-2.9.9-cp312-cp312-win32.whl", hash = "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93"},
2707
- {file = "psycopg2_binary-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab"},
2708
- {file = "psycopg2_binary-2.9.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2293b001e319ab0d869d660a704942c9e2cce19745262a8aba2115ef41a0a42a"},
2709
- {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9"},
2710
- {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a602ea5aff39bb9fac6308e9c9d82b9a35c2bf288e184a816002c9fae930b77"},
2711
- {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8359bf4791968c5a78c56103702000105501adb557f3cf772b2c207284273984"},
2712
- {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:275ff571376626195ab95a746e6a04c7df8ea34638b99fc11160de91f2fef503"},
2713
- {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f9b5571d33660d5009a8b3c25dc1db560206e2d2f89d3df1cb32d72c0d117d52"},
2714
- {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:420f9bbf47a02616e8554e825208cb947969451978dceb77f95ad09c37791dae"},
2715
- {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4154ad09dac630a0f13f37b583eae260c6aa885d67dfbccb5b02c33f31a6d420"},
2716
- {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a148c5d507bb9b4f2030a2025c545fccb0e1ef317393eaba42e7eabd28eb6041"},
2717
- {file = "psycopg2_binary-2.9.9-cp37-cp37m-win32.whl", hash = "sha256:68fc1f1ba168724771e38bee37d940d2865cb0f562380a1fb1ffb428b75cb692"},
2718
- {file = "psycopg2_binary-2.9.9-cp37-cp37m-win_amd64.whl", hash = "sha256:281309265596e388ef483250db3640e5f414168c5a67e9c665cafce9492eda2f"},
2719
- {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:60989127da422b74a04345096c10d416c2b41bd7bf2a380eb541059e4e999980"},
2720
- {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:246b123cc54bb5361588acc54218c8c9fb73068bf227a4a531d8ed56fa3ca7d6"},
2721
- {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34eccd14566f8fe14b2b95bb13b11572f7c7d5c36da61caf414d23b91fcc5d94"},
2722
- {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18d0ef97766055fec15b5de2c06dd8e7654705ce3e5e5eed3b6651a1d2a9a152"},
2723
- {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d3f82c171b4ccd83bbaf35aa05e44e690113bd4f3b7b6cc54d2219b132f3ae55"},
2724
- {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ead20f7913a9c1e894aebe47cccf9dc834e1618b7aa96155d2091a626e59c972"},
2725
- {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ca49a8119c6cbd77375ae303b0cfd8c11f011abbbd64601167ecca18a87e7cdd"},
2726
- {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:323ba25b92454adb36fa425dc5cf6f8f19f78948cbad2e7bc6cdf7b0d7982e59"},
2727
- {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:1236ed0952fbd919c100bc839eaa4a39ebc397ed1c08a97fc45fee2a595aa1b3"},
2728
- {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:729177eaf0aefca0994ce4cffe96ad3c75e377c7b6f4efa59ebf003b6d398716"},
2729
- {file = "psycopg2_binary-2.9.9-cp38-cp38-win32.whl", hash = "sha256:804d99b24ad523a1fe18cc707bf741670332f7c7412e9d49cb5eab67e886b9b5"},
2730
- {file = "psycopg2_binary-2.9.9-cp38-cp38-win_amd64.whl", hash = "sha256:a6cdcc3ede532f4a4b96000b6362099591ab4a3e913d70bcbac2b56c872446f7"},
2731
- {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:72dffbd8b4194858d0941062a9766f8297e8868e1dd07a7b36212aaa90f49472"},
2732
- {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:30dcc86377618a4c8f3b72418df92e77be4254d8f89f14b8e8f57d6d43603c0f"},
2733
- {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31a34c508c003a4347d389a9e6fcc2307cc2150eb516462a7a17512130de109e"},
2734
- {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15208be1c50b99203fe88d15695f22a5bed95ab3f84354c494bcb1d08557df67"},
2735
- {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1873aade94b74715be2246321c8650cabf5a0d098a95bab81145ffffa4c13876"},
2736
- {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a58c98a7e9c021f357348867f537017057c2ed7f77337fd914d0bedb35dace7"},
2737
- {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4686818798f9194d03c9129a4d9a702d9e113a89cb03bffe08c6cf799e053291"},
2738
- {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ebdc36bea43063116f0486869652cb2ed7032dbc59fbcb4445c4862b5c1ecf7f"},
2739
- {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ca08decd2697fdea0aea364b370b1249d47336aec935f87b8bbfd7da5b2ee9c1"},
2740
- {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac05fb791acf5e1a3e39402641827780fe44d27e72567a000412c648a85ba860"},
2741
- {file = "psycopg2_binary-2.9.9-cp39-cp39-win32.whl", hash = "sha256:9dba73be7305b399924709b91682299794887cbbd88e38226ed9f6712eabee90"},
2742
- {file = "psycopg2_binary-2.9.9-cp39-cp39-win_amd64.whl", hash = "sha256:f7ae5d65ccfbebdfa761585228eb4d0df3a8b15cfb53bd953e713e09fbb12957"},
2743
- ]
2744
-
2745
  [[package]]
2746
  name = "pydantic"
2747
  version = "2.7.2"
@@ -4210,4 +4210,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more
4210
  [metadata]
4211
  lock-version = "2.0"
4212
  python-versions = "^3.11,<3.12"
4213
- content-hash = "527fd744fa60d7638490e59133486e40e1a5303635e9f0fd7286ce5ec97ad4d1"
 
198
  test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"]
199
  trio = ["trio (>=0.23)"]
200
 
201
+ [[package]]
202
+ name = "async-timeout"
203
+ version = "4.0.3"
204
+ description = "Timeout context manager for asyncio programs"
205
+ optional = false
206
+ python-versions = ">=3.7"
207
+ files = [
208
+ {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"},
209
+ {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"},
210
+ ]
211
+
212
+ [[package]]
213
+ name = "asyncio"
214
+ version = "3.4.3"
215
+ description = "reference implementation of PEP 3156"
216
+ optional = false
217
+ python-versions = "*"
218
+ files = [
219
+ {file = "asyncio-3.4.3-cp33-none-win32.whl", hash = "sha256:b62c9157d36187eca799c378e572c969f0da87cd5fc42ca372d92cdb06e7e1de"},
220
+ {file = "asyncio-3.4.3-cp33-none-win_amd64.whl", hash = "sha256:c46a87b48213d7464f22d9a497b9eef8c1928b68320a2fa94240f969f6fec08c"},
221
+ {file = "asyncio-3.4.3-py3-none-any.whl", hash = "sha256:c4d18b22701821de07bd6aea8b53d21449ec0ec5680645e5317062ea21817d2d"},
222
+ {file = "asyncio-3.4.3.tar.gz", hash = "sha256:83360ff8bc97980e4ff25c964c7bd3923d333d177aa4f7fb736b019f26c7cb41"},
223
+ ]
224
+
225
+ [[package]]
226
+ name = "asyncpg"
227
+ version = "0.29.0"
228
+ description = "An asyncio PostgreSQL driver"
229
+ optional = false
230
+ python-versions = ">=3.8.0"
231
+ files = [
232
+ {file = "asyncpg-0.29.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72fd0ef9f00aeed37179c62282a3d14262dbbafb74ec0ba16e1b1864d8a12169"},
233
+ {file = "asyncpg-0.29.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52e8f8f9ff6e21f9b39ca9f8e3e33a5fcdceaf5667a8c5c32bee158e313be385"},
234
+ {file = "asyncpg-0.29.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e6823a7012be8b68301342ba33b4740e5a166f6bbda0aee32bc01638491a22"},
235
+ {file = "asyncpg-0.29.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:746e80d83ad5d5464cfbf94315eb6744222ab00aa4e522b704322fb182b83610"},
236
+ {file = "asyncpg-0.29.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ff8e8109cd6a46ff852a5e6bab8b0a047d7ea42fcb7ca5ae6eaae97d8eacf397"},
237
+ {file = "asyncpg-0.29.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:97eb024685b1d7e72b1972863de527c11ff87960837919dac6e34754768098eb"},
238
+ {file = "asyncpg-0.29.0-cp310-cp310-win32.whl", hash = "sha256:5bbb7f2cafd8d1fa3e65431833de2642f4b2124be61a449fa064e1a08d27e449"},
239
+ {file = "asyncpg-0.29.0-cp310-cp310-win_amd64.whl", hash = "sha256:76c3ac6530904838a4b650b2880f8e7af938ee049e769ec2fba7cd66469d7772"},
240
+ {file = "asyncpg-0.29.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4900ee08e85af01adb207519bb4e14b1cae8fd21e0ccf80fac6aa60b6da37b4"},
241
+ {file = "asyncpg-0.29.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a65c1dcd820d5aea7c7d82a3fdcb70e096f8f70d1a8bf93eb458e49bfad036ac"},
242
+ {file = "asyncpg-0.29.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b52e46f165585fd6af4863f268566668407c76b2c72d366bb8b522fa66f1870"},
243
+ {file = "asyncpg-0.29.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc600ee8ef3dd38b8d67421359779f8ccec30b463e7aec7ed481c8346decf99f"},
244
+ {file = "asyncpg-0.29.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:039a261af4f38f949095e1e780bae84a25ffe3e370175193174eb08d3cecab23"},
245
+ {file = "asyncpg-0.29.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6feaf2d8f9138d190e5ec4390c1715c3e87b37715cd69b2c3dfca616134efd2b"},
246
+ {file = "asyncpg-0.29.0-cp311-cp311-win32.whl", hash = "sha256:1e186427c88225ef730555f5fdda6c1812daa884064bfe6bc462fd3a71c4b675"},
247
+ {file = "asyncpg-0.29.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfe73ffae35f518cfd6e4e5f5abb2618ceb5ef02a2365ce64f132601000587d3"},
248
+ {file = "asyncpg-0.29.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6011b0dc29886ab424dc042bf9eeb507670a3b40aece3439944006aafe023178"},
249
+ {file = "asyncpg-0.29.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b544ffc66b039d5ec5a7454667f855f7fec08e0dfaf5a5490dfafbb7abbd2cfb"},
250
+ {file = "asyncpg-0.29.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d84156d5fb530b06c493f9e7635aa18f518fa1d1395ef240d211cb563c4e2364"},
251
+ {file = "asyncpg-0.29.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54858bc25b49d1114178d65a88e48ad50cb2b6f3e475caa0f0c092d5f527c106"},
252
+ {file = "asyncpg-0.29.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bde17a1861cf10d5afce80a36fca736a86769ab3579532c03e45f83ba8a09c59"},
253
+ {file = "asyncpg-0.29.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:37a2ec1b9ff88d8773d3eb6d3784dc7e3fee7756a5317b67f923172a4748a175"},
254
+ {file = "asyncpg-0.29.0-cp312-cp312-win32.whl", hash = "sha256:bb1292d9fad43112a85e98ecdc2e051602bce97c199920586be83254d9dafc02"},
255
+ {file = "asyncpg-0.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:2245be8ec5047a605e0b454c894e54bf2ec787ac04b1cb7e0d3c67aa1e32f0fe"},
256
+ {file = "asyncpg-0.29.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0009a300cae37b8c525e5b449233d59cd9868fd35431abc470a3e364d2b85cb9"},
257
+ {file = "asyncpg-0.29.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cad1324dbb33f3ca0cd2074d5114354ed3be2b94d48ddfd88af75ebda7c43cc"},
258
+ {file = "asyncpg-0.29.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:012d01df61e009015944ac7543d6ee30c2dc1eb2f6b10b62a3f598beb6531548"},
259
+ {file = "asyncpg-0.29.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000c996c53c04770798053e1730d34e30cb645ad95a63265aec82da9093d88e7"},
260
+ {file = "asyncpg-0.29.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e0bfe9c4d3429706cf70d3249089de14d6a01192d617e9093a8e941fea8ee775"},
261
+ {file = "asyncpg-0.29.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:642a36eb41b6313ffa328e8a5c5c2b5bea6ee138546c9c3cf1bffaad8ee36dd9"},
262
+ {file = "asyncpg-0.29.0-cp38-cp38-win32.whl", hash = "sha256:a921372bbd0aa3a5822dd0409da61b4cd50df89ae85150149f8c119f23e8c408"},
263
+ {file = "asyncpg-0.29.0-cp38-cp38-win_amd64.whl", hash = "sha256:103aad2b92d1506700cbf51cd8bb5441e7e72e87a7b3a2ca4e32c840f051a6a3"},
264
+ {file = "asyncpg-0.29.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5340dd515d7e52f4c11ada32171d87c05570479dc01dc66d03ee3e150fb695da"},
265
+ {file = "asyncpg-0.29.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e17b52c6cf83e170d3d865571ba574577ab8e533e7361a2b8ce6157d02c665d3"},
266
+ {file = "asyncpg-0.29.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f100d23f273555f4b19b74a96840aa27b85e99ba4b1f18d4ebff0734e78dc090"},
267
+ {file = "asyncpg-0.29.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48e7c58b516057126b363cec8ca02b804644fd012ef8e6c7e23386b7d5e6ce83"},
268
+ {file = "asyncpg-0.29.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f9ea3f24eb4c49a615573724d88a48bd1b7821c890c2effe04f05382ed9e8810"},
269
+ {file = "asyncpg-0.29.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8d36c7f14a22ec9e928f15f92a48207546ffe68bc412f3be718eedccdf10dc5c"},
270
+ {file = "asyncpg-0.29.0-cp39-cp39-win32.whl", hash = "sha256:797ab8123ebaed304a1fad4d7576d5376c3a006a4100380fb9d517f0b59c1ab2"},
271
+ {file = "asyncpg-0.29.0-cp39-cp39-win_amd64.whl", hash = "sha256:cce08a178858b426ae1aa8409b5cc171def45d4293626e7aa6510696d46decd8"},
272
+ {file = "asyncpg-0.29.0.tar.gz", hash = "sha256:d1c49e1f44fffafd9a55e1a9b101590859d881d639ea2922516f5d9c512d354e"},
273
+ ]
274
+
275
+ [package.dependencies]
276
+ async-timeout = {version = ">=4.0.3", markers = "python_version < \"3.12.0\""}
277
+
278
+ [package.extras]
279
+ docs = ["Sphinx (>=5.3.0,<5.4.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"]
280
+ test = ["flake8 (>=6.1,<7.0)", "uvloop (>=0.15.3)"]
281
+
282
  [[package]]
283
  name = "attrs"
284
  version = "23.2.0"
 
2742
  {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"},
2743
  ]
2744
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2745
  [[package]]
2746
  name = "pydantic"
2747
  version = "2.7.2"
 
4210
  [metadata]
4211
  lock-version = "2.0"
4212
  python-versions = "^3.11,<3.12"
4213
+ content-hash = "ea7579f435b0c476a57fe23453277a8e90d2815e0a86efc7ff300a5ddbd434bb"
pyproject.toml CHANGED
@@ -28,11 +28,12 @@ pydantic = "^2.7.2"
28
  dateparser = "^1.2.0"
29
  pandas = "^2.2.2"
30
  path = "^16.14.0"
31
- psycopg2-binary = "^2.9.9"
32
  pytest-xdist = "^3.6.1"
33
  pytest-asyncio = "^0.23.7"
34
  httpx = "^0.27.0"
35
  alembic = "^1.13.1"
 
 
36
 
37
  [tool.poetry.dependencies.uvicorn]
38
  extras = [ "standard" ]
 
28
  dateparser = "^1.2.0"
29
  pandas = "^2.2.2"
30
  path = "^16.14.0"
 
31
  pytest-xdist = "^3.6.1"
32
  pytest-asyncio = "^0.23.7"
33
  httpx = "^0.27.0"
34
  alembic = "^1.13.1"
35
+ asyncpg = "^0.29.0"
36
+ asyncio = "^3.4.3"
37
 
38
  [tool.poetry.dependencies.uvicorn]
39
  extras = [ "standard" ]