Spaces:
Runtime error
Runtime error
Soutrik
commited on
Commit
•
36ed17a
1
Parent(s):
8753097
added: fastapi framework
Browse files- .gitignore +16 -0
- Dockerfile +8 -8
- app/api/chat.py +32 -0
- app/core/__pycache__/config.cpython-310.pyc +0 -0
- app/core/celery_app.py +8 -0
- app/core/config.py +1 -1
- app/crud/chat_crud.py +14 -0
- app/db/database.py +17 -0
- app/db/models.py +12 -0
- app/schemas/chat.py +12 -0
- app/tasks/chat_task.py +6 -0
- docker-compose.yaml +11 -1
- main.py +16 -0
- poetry.lock +280 -4
- pyproject.toml +4 -0
- src/__pycache__/test_infra.cpython-310.pyc +0 -0
- src/number_manipulation.py +7 -0
- src/test_infra.py +1 -1
- tests/__init__.py +0 -0
- tests/test_db_connection.py +17 -0
.gitignore
CHANGED
@@ -3,3 +3,19 @@ aws/
|
|
3 |
*.tar.gz
|
4 |
*.tar.bz2
|
5 |
.env
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
*.tar.gz
|
4 |
*.tar.bz2
|
5 |
.env
|
6 |
+
*.pyc
|
7 |
+
*.cpython-*.*
|
8 |
+
src/__pycache__/
|
9 |
+
src/*.egg-info/
|
10 |
+
src/dist/
|
11 |
+
src/build/
|
12 |
+
src/.eggs/
|
13 |
+
src/.pytest_cache/
|
14 |
+
src/.mypy_cache/
|
15 |
+
src/.tox/
|
16 |
+
src/.coverage
|
17 |
+
src/.vscode/
|
18 |
+
src/.vscode-test/
|
19 |
+
app/core/__pycache__/
|
20 |
+
src/__pycache__/test_infra.cpython-310.pyc
|
21 |
+
app/core/__pycache__/config.cpython-310.pyc
|
Dockerfile
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
# Use an official Python base image
|
2 |
FROM python:3.10.15-slim
|
3 |
|
4 |
-
# Set environment variables
|
5 |
ENV POETRY_VERSION=1.6.1 \
|
6 |
POETRY_HOME="/opt/poetry" \
|
7 |
POETRY_VIRTUALENVS_IN_PROJECT=true \
|
@@ -20,16 +20,16 @@ WORKDIR /app
|
|
20 |
# Copy Poetry files for dependency installation
|
21 |
COPY pyproject.toml poetry.lock ./
|
22 |
|
23 |
-
# Install dependencies with Poetry
|
24 |
RUN poetry install --no-root --only main
|
25 |
|
26 |
-
# Copy the source code
|
27 |
COPY src/ src/
|
28 |
-
# copy app
|
29 |
COPY app/ app/
|
|
|
30 |
|
31 |
-
#
|
32 |
-
|
33 |
|
34 |
-
#
|
35 |
-
CMD ["
|
|
|
1 |
# Use an official Python base image
|
2 |
FROM python:3.10.15-slim
|
3 |
|
4 |
+
# Set environment variables for Poetry
|
5 |
ENV POETRY_VERSION=1.6.1 \
|
6 |
POETRY_HOME="/opt/poetry" \
|
7 |
POETRY_VIRTUALENVS_IN_PROJECT=true \
|
|
|
20 |
# Copy Poetry files for dependency installation
|
21 |
COPY pyproject.toml poetry.lock ./
|
22 |
|
23 |
+
# Install dependencies with Poetry (without development dependencies)
|
24 |
RUN poetry install --no-root --only main
|
25 |
|
26 |
+
# Copy the application source code
|
27 |
COPY src/ src/
|
|
|
28 |
COPY app/ app/
|
29 |
+
COPY main.py .
|
30 |
|
31 |
+
# Expose the port for FastAPI
|
32 |
+
EXPOSE 8000
|
33 |
|
34 |
+
# Command to run the FastAPI application with uvicorn
|
35 |
+
CMD ["poetry", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
app/api/chat.py
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import APIRouter, Depends
|
2 |
+
from sqlalchemy.ext.asyncio import AsyncSession
|
3 |
+
from app.schemas.chat import ChatMessage, ChatResponse
|
4 |
+
from app.crud.chat_crud import create_chat_message
|
5 |
+
from app.tasks.chat_task import process_chat_message
|
6 |
+
from src.number_manipulation import add_random_number
|
7 |
+
from app.db.database import get_db
|
8 |
+
|
9 |
+
router = APIRouter()
|
10 |
+
|
11 |
+
|
12 |
+
@router.post("/chat", response_model=ChatResponse)
|
13 |
+
async def chat(message: ChatMessage, db: AsyncSession = Depends(get_db)):
|
14 |
+
# Process user input using add_random_number
|
15 |
+
processed_value = add_random_number(message.user_input)
|
16 |
+
|
17 |
+
# Save message to database along with the user input and processed value
|
18 |
+
message_id = await create_chat_message(
|
19 |
+
db=db,
|
20 |
+
content=message.content,
|
21 |
+
user_input=message.user_input,
|
22 |
+
processed_value=processed_value,
|
23 |
+
)
|
24 |
+
|
25 |
+
# Trigger async processing of the chat message content in the background
|
26 |
+
process_chat_message.delay(message.content)
|
27 |
+
|
28 |
+
return ChatResponse(
|
29 |
+
message_id=message_id,
|
30 |
+
status="Message received",
|
31 |
+
processed_value=processed_value,
|
32 |
+
)
|
app/core/__pycache__/config.cpython-310.pyc
CHANGED
Binary files a/app/core/__pycache__/config.cpython-310.pyc and b/app/core/__pycache__/config.cpython-310.pyc differ
|
|
app/core/celery_app.py
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from celery import Celery
|
2 |
+
from app.core.config import settings
|
3 |
+
|
4 |
+
celery_app = Celery("chat_tasks", broker=settings.redis_url, backend=settings.redis_url)
|
5 |
+
|
6 |
+
celery_app.conf.update(
|
7 |
+
task_serializer="json", result_serializer="json", accept_content=["json"]
|
8 |
+
)
|
app/core/config.py
CHANGED
@@ -24,7 +24,7 @@ class Settings(BaseSettings):
|
|
24 |
instance = cls()
|
25 |
# Set the correct database URL without the +asyncpg
|
26 |
instance.database_url = (
|
27 |
-
f"
|
28 |
f"{'localhost' if not instance.is_docker else 'postgres'}:5432/{instance.POSTGRES_DB}"
|
29 |
)
|
30 |
instance.redis_url = (
|
|
|
24 |
instance = cls()
|
25 |
# Set the correct database URL without the +asyncpg
|
26 |
instance.database_url = (
|
27 |
+
f"postgresql+asyncpg://{instance.POSTGRES_USER}:{instance.POSTGRES_PASSWORD}@"
|
28 |
f"{'localhost' if not instance.is_docker else 'postgres'}:5432/{instance.POSTGRES_DB}"
|
29 |
)
|
30 |
instance.redis_url = (
|
app/crud/chat_crud.py
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from sqlalchemy.ext.asyncio import AsyncSession
|
2 |
+
from app.db.models import ChatMessageModel
|
3 |
+
|
4 |
+
|
5 |
+
async def create_chat_message(
|
6 |
+
db: AsyncSession, content: str, user_input: int, processed_value: int
|
7 |
+
):
|
8 |
+
new_message = ChatMessageModel(
|
9 |
+
content=content, user_input=user_input, processed_value=processed_value
|
10 |
+
)
|
11 |
+
db.add(new_message)
|
12 |
+
await db.commit()
|
13 |
+
await db.refresh(new_message) # Fetches the latest state after commit
|
14 |
+
return new_message.id
|
app/db/database.py
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
|
2 |
+
from sqlalchemy.orm import sessionmaker, declarative_base
|
3 |
+
from app.core.config import settings
|
4 |
+
|
5 |
+
# Create an async engine
|
6 |
+
engine = create_async_engine(settings.database_url, echo=True)
|
7 |
+
|
8 |
+
# Async session factory
|
9 |
+
SessionLocal = sessionmaker(bind=engine, class_=AsyncSession, expire_on_commit=False)
|
10 |
+
|
11 |
+
Base = declarative_base()
|
12 |
+
|
13 |
+
|
14 |
+
# Dependency for asynchronous database session
|
15 |
+
async def get_db():
|
16 |
+
async with SessionLocal() as session:
|
17 |
+
yield session
|
app/db/models.py
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from sqlalchemy import Column, Integer, String, DateTime, func
|
2 |
+
from app.db.database import Base
|
3 |
+
|
4 |
+
|
5 |
+
class ChatMessageModel(Base):
|
6 |
+
__tablename__ = "chat_messages"
|
7 |
+
|
8 |
+
id = Column(Integer, primary_key=True, index=True)
|
9 |
+
content = Column(String, nullable=False)
|
10 |
+
user_input = Column(Integer, nullable=False)
|
11 |
+
processed_value = Column(Integer, nullable=False)
|
12 |
+
timestamp = Column(DateTime, server_default=func.now())
|
app/schemas/chat.py
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pydantic import BaseModel
|
2 |
+
|
3 |
+
|
4 |
+
class ChatMessage(BaseModel):
|
5 |
+
content: str
|
6 |
+
user_input: int
|
7 |
+
|
8 |
+
|
9 |
+
class ChatResponse(BaseModel):
|
10 |
+
message_id: int
|
11 |
+
status: str
|
12 |
+
processed_value: int
|
app/tasks/chat_task.py
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from app.core.celery_app import celery_app
|
2 |
+
|
3 |
+
|
4 |
+
@celery_app.task
|
5 |
+
def process_chat_message(content: str):
|
6 |
+
print(f"Processing message: {content}")
|
docker-compose.yaml
CHANGED
@@ -12,6 +12,8 @@ services:
|
|
12 |
- "5432:5432"
|
13 |
volumes:
|
14 |
- postgres_data:/var/lib/postgresql/data
|
|
|
|
|
15 |
healthcheck:
|
16 |
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
17 |
interval: 30s
|
@@ -25,6 +27,8 @@ services:
|
|
25 |
- "8001:8001"
|
26 |
environment:
|
27 |
DOCKER_ENV: 1
|
|
|
|
|
28 |
healthcheck:
|
29 |
test: ["CMD", "redis-cli", "ping"]
|
30 |
interval: 30s
|
@@ -40,6 +44,8 @@ services:
|
|
40 |
- 5557:5555
|
41 |
depends_on:
|
42 |
- redis
|
|
|
|
|
43 |
|
44 |
app:
|
45 |
build:
|
@@ -59,7 +65,11 @@ services:
|
|
59 |
condition: service_healthy
|
60 |
redis:
|
61 |
condition: service_healthy
|
62 |
-
|
|
|
|
|
|
|
|
|
63 |
|
64 |
volumes:
|
65 |
postgres_data:
|
|
|
12 |
- "5432:5432"
|
13 |
volumes:
|
14 |
- postgres_data:/var/lib/postgresql/data
|
15 |
+
networks:
|
16 |
+
- my_network
|
17 |
healthcheck:
|
18 |
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
19 |
interval: 30s
|
|
|
27 |
- "8001:8001"
|
28 |
environment:
|
29 |
DOCKER_ENV: 1
|
30 |
+
networks:
|
31 |
+
- my_network
|
32 |
healthcheck:
|
33 |
test: ["CMD", "redis-cli", "ping"]
|
34 |
interval: 30s
|
|
|
44 |
- 5557:5555
|
45 |
depends_on:
|
46 |
- redis
|
47 |
+
networks:
|
48 |
+
- my_network
|
49 |
|
50 |
app:
|
51 |
build:
|
|
|
65 |
condition: service_healthy
|
66 |
redis:
|
67 |
condition: service_healthy
|
68 |
+
networks:
|
69 |
+
- my_network
|
70 |
+
ports:
|
71 |
+
- "8000:8000"
|
72 |
+
command: ["poetry", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
73 |
|
74 |
volumes:
|
75 |
postgres_data:
|
main.py
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import FastAPI
|
2 |
+
from app.api import chat
|
3 |
+
from app.db.database import Base, engine # Import Base and engine for table creation
|
4 |
+
|
5 |
+
app = FastAPI()
|
6 |
+
|
7 |
+
|
8 |
+
# Create tables on startup
|
9 |
+
@app.on_event("startup")
|
10 |
+
async def startup():
|
11 |
+
async with engine.begin() as conn:
|
12 |
+
await conn.run_sync(Base.metadata.create_all)
|
13 |
+
|
14 |
+
|
15 |
+
# Include the chat route
|
16 |
+
app.include_router(chat.router)
|
poetry.lock
CHANGED
@@ -233,13 +233,13 @@ frozenlist = ">=1.1.0"
|
|
233 |
|
234 |
[[package]]
|
235 |
name = "alembic"
|
236 |
-
version = "1.
|
237 |
description = "A database migration tool for SQLAlchemy."
|
238 |
optional = false
|
239 |
python-versions = ">=3.8"
|
240 |
files = [
|
241 |
-
{file = "alembic-1.
|
242 |
-
{file = "alembic-1.
|
243 |
]
|
244 |
|
245 |
[package.dependencies]
|
@@ -250,6 +250,20 @@ typing-extensions = ">=4"
|
|
250 |
[package.extras]
|
251 |
tz = ["backports.zoneinfo"]
|
252 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
253 |
[[package]]
|
254 |
name = "annotated-types"
|
255 |
version = "0.7.0"
|
@@ -414,6 +428,73 @@ azure-core = ">=1.28.0"
|
|
414 |
isodate = ">=0.6.0"
|
415 |
typing-extensions = ">=4.6.0"
|
416 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
417 |
[[package]]
|
418 |
name = "certifi"
|
419 |
version = "2024.8.30"
|
@@ -553,6 +634,55 @@ files = [
|
|
553 |
[package.dependencies]
|
554 |
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
555 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
556 |
[[package]]
|
557 |
name = "colorama"
|
558 |
version = "0.4.6"
|
@@ -1094,6 +1224,17 @@ files = [
|
|
1094 |
[package.extras]
|
1095 |
all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"]
|
1096 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1097 |
[[package]]
|
1098 |
name = "isodate"
|
1099 |
version = "0.7.2"
|
@@ -1105,6 +1246,39 @@ files = [
|
|
1105 |
{file = "isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6"},
|
1106 |
]
|
1107 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1108 |
[[package]]
|
1109 |
name = "loguru"
|
1110 |
version = "0.7.2"
|
@@ -1484,6 +1658,35 @@ sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-d
|
|
1484 |
test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"]
|
1485 |
xml = ["lxml (>=4.9.2)"]
|
1486 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1487 |
[[package]]
|
1488 |
name = "propcache"
|
1489 |
version = "0.2.0"
|
@@ -1836,6 +2039,46 @@ azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0
|
|
1836 |
toml = ["tomli (>=2.0.1)"]
|
1837 |
yaml = ["pyyaml (>=6.0.1)"]
|
1838 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1839 |
[[package]]
|
1840 |
name = "python-dateutil"
|
1841 |
version = "2.9.0.post0"
|
@@ -2193,6 +2436,17 @@ files = [
|
|
2193 |
doc = ["reno", "sphinx"]
|
2194 |
test = ["pytest", "tornado (>=4.5)", "typeguard"]
|
2195 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2196 |
[[package]]
|
2197 |
name = "typing-extensions"
|
2198 |
version = "4.12.2"
|
@@ -2266,6 +2520,28 @@ typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""}
|
|
2266 |
[package.extras]
|
2267 |
standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"]
|
2268 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2269 |
[[package]]
|
2270 |
name = "win32-setctime"
|
2271 |
version = "1.1.0"
|
@@ -2379,4 +2655,4 @@ propcache = ">=0.2.0"
|
|
2379 |
[metadata]
|
2380 |
lock-version = "2.0"
|
2381 |
python-versions = "3.10.15"
|
2382 |
-
content-hash = "
|
|
|
233 |
|
234 |
[[package]]
|
235 |
name = "alembic"
|
236 |
+
version = "1.14.0"
|
237 |
description = "A database migration tool for SQLAlchemy."
|
238 |
optional = false
|
239 |
python-versions = ">=3.8"
|
240 |
files = [
|
241 |
+
{file = "alembic-1.14.0-py3-none-any.whl", hash = "sha256:99bd884ca390466db5e27ffccff1d179ec5c05c965cfefc0607e69f9e411cb25"},
|
242 |
+
{file = "alembic-1.14.0.tar.gz", hash = "sha256:b00892b53b3642d0b8dbedba234dbf1924b69be83a9a769d5a624b01094e304b"},
|
243 |
]
|
244 |
|
245 |
[package.dependencies]
|
|
|
250 |
[package.extras]
|
251 |
tz = ["backports.zoneinfo"]
|
252 |
|
253 |
+
[[package]]
|
254 |
+
name = "amqp"
|
255 |
+
version = "5.2.0"
|
256 |
+
description = "Low-level AMQP client for Python (fork of amqplib)."
|
257 |
+
optional = false
|
258 |
+
python-versions = ">=3.6"
|
259 |
+
files = [
|
260 |
+
{file = "amqp-5.2.0-py3-none-any.whl", hash = "sha256:827cb12fb0baa892aad844fd95258143bce4027fdac4fccddbc43330fd281637"},
|
261 |
+
{file = "amqp-5.2.0.tar.gz", hash = "sha256:a1ecff425ad063ad42a486c902807d1482311481c8ad95a72694b2975e75f7fd"},
|
262 |
+
]
|
263 |
+
|
264 |
+
[package.dependencies]
|
265 |
+
vine = ">=5.0.0,<6.0.0"
|
266 |
+
|
267 |
[[package]]
|
268 |
name = "annotated-types"
|
269 |
version = "0.7.0"
|
|
|
428 |
isodate = ">=0.6.0"
|
429 |
typing-extensions = ">=4.6.0"
|
430 |
|
431 |
+
[[package]]
|
432 |
+
name = "billiard"
|
433 |
+
version = "4.2.1"
|
434 |
+
description = "Python multiprocessing fork with improvements and bugfixes"
|
435 |
+
optional = false
|
436 |
+
python-versions = ">=3.7"
|
437 |
+
files = [
|
438 |
+
{file = "billiard-4.2.1-py3-none-any.whl", hash = "sha256:40b59a4ac8806ba2c2369ea98d876bc6108b051c227baffd928c644d15d8f3cb"},
|
439 |
+
{file = "billiard-4.2.1.tar.gz", hash = "sha256:12b641b0c539073fc8d3f5b8b7be998956665c4233c7c1fcd66a7e677c4fb36f"},
|
440 |
+
]
|
441 |
+
|
442 |
+
[[package]]
|
443 |
+
name = "celery"
|
444 |
+
version = "5.4.0"
|
445 |
+
description = "Distributed Task Queue."
|
446 |
+
optional = false
|
447 |
+
python-versions = ">=3.8"
|
448 |
+
files = [
|
449 |
+
{file = "celery-5.4.0-py3-none-any.whl", hash = "sha256:369631eb580cf8c51a82721ec538684994f8277637edde2dfc0dacd73ed97f64"},
|
450 |
+
{file = "celery-5.4.0.tar.gz", hash = "sha256:504a19140e8d3029d5acad88330c541d4c3f64c789d85f94756762d8bca7e706"},
|
451 |
+
]
|
452 |
+
|
453 |
+
[package.dependencies]
|
454 |
+
billiard = ">=4.2.0,<5.0"
|
455 |
+
click = ">=8.1.2,<9.0"
|
456 |
+
click-didyoumean = ">=0.3.0"
|
457 |
+
click-plugins = ">=1.1.1"
|
458 |
+
click-repl = ">=0.2.0"
|
459 |
+
kombu = ">=5.3.4,<6.0"
|
460 |
+
python-dateutil = ">=2.8.2"
|
461 |
+
tzdata = ">=2022.7"
|
462 |
+
vine = ">=5.1.0,<6.0"
|
463 |
+
|
464 |
+
[package.extras]
|
465 |
+
arangodb = ["pyArango (>=2.0.2)"]
|
466 |
+
auth = ["cryptography (==42.0.5)"]
|
467 |
+
azureblockblob = ["azure-storage-blob (>=12.15.0)"]
|
468 |
+
brotli = ["brotli (>=1.0.0)", "brotlipy (>=0.7.0)"]
|
469 |
+
cassandra = ["cassandra-driver (>=3.25.0,<4)"]
|
470 |
+
consul = ["python-consul2 (==0.1.5)"]
|
471 |
+
cosmosdbsql = ["pydocumentdb (==2.3.5)"]
|
472 |
+
couchbase = ["couchbase (>=3.0.0)"]
|
473 |
+
couchdb = ["pycouchdb (==1.14.2)"]
|
474 |
+
django = ["Django (>=2.2.28)"]
|
475 |
+
dynamodb = ["boto3 (>=1.26.143)"]
|
476 |
+
elasticsearch = ["elastic-transport (<=8.13.0)", "elasticsearch (<=8.13.0)"]
|
477 |
+
eventlet = ["eventlet (>=0.32.0)"]
|
478 |
+
gcs = ["google-cloud-storage (>=2.10.0)"]
|
479 |
+
gevent = ["gevent (>=1.5.0)"]
|
480 |
+
librabbitmq = ["librabbitmq (>=2.0.0)"]
|
481 |
+
memcache = ["pylibmc (==1.6.3)"]
|
482 |
+
mongodb = ["pymongo[srv] (>=4.0.2)"]
|
483 |
+
msgpack = ["msgpack (==1.0.8)"]
|
484 |
+
pymemcache = ["python-memcached (>=1.61)"]
|
485 |
+
pyro = ["pyro4 (==4.82)"]
|
486 |
+
pytest = ["pytest-celery[all] (>=1.0.0)"]
|
487 |
+
redis = ["redis (>=4.5.2,!=4.5.5,<6.0.0)"]
|
488 |
+
s3 = ["boto3 (>=1.26.143)"]
|
489 |
+
slmq = ["softlayer-messaging (>=1.0.3)"]
|
490 |
+
solar = ["ephem (==4.1.5)"]
|
491 |
+
sqlalchemy = ["sqlalchemy (>=1.4.48,<2.1)"]
|
492 |
+
sqs = ["boto3 (>=1.26.143)", "kombu[sqs] (>=5.3.4)", "pycurl (>=7.43.0.5)", "urllib3 (>=1.26.16)"]
|
493 |
+
tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"]
|
494 |
+
yaml = ["PyYAML (>=3.10)"]
|
495 |
+
zookeeper = ["kazoo (>=1.3.1)"]
|
496 |
+
zstd = ["zstandard (==0.22.0)"]
|
497 |
+
|
498 |
[[package]]
|
499 |
name = "certifi"
|
500 |
version = "2024.8.30"
|
|
|
634 |
[package.dependencies]
|
635 |
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
636 |
|
637 |
+
[[package]]
|
638 |
+
name = "click-didyoumean"
|
639 |
+
version = "0.3.1"
|
640 |
+
description = "Enables git-like *did-you-mean* feature in click"
|
641 |
+
optional = false
|
642 |
+
python-versions = ">=3.6.2"
|
643 |
+
files = [
|
644 |
+
{file = "click_didyoumean-0.3.1-py3-none-any.whl", hash = "sha256:5c4bb6007cfea5f2fd6583a2fb6701a22a41eb98957e63d0fac41c10e7c3117c"},
|
645 |
+
{file = "click_didyoumean-0.3.1.tar.gz", hash = "sha256:4f82fdff0dbe64ef8ab2279bd6aa3f6a99c3b28c05aa09cbfc07c9d7fbb5a463"},
|
646 |
+
]
|
647 |
+
|
648 |
+
[package.dependencies]
|
649 |
+
click = ">=7"
|
650 |
+
|
651 |
+
[[package]]
|
652 |
+
name = "click-plugins"
|
653 |
+
version = "1.1.1"
|
654 |
+
description = "An extension module for click to enable registering CLI commands via setuptools entry-points."
|
655 |
+
optional = false
|
656 |
+
python-versions = "*"
|
657 |
+
files = [
|
658 |
+
{file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"},
|
659 |
+
{file = "click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"},
|
660 |
+
]
|
661 |
+
|
662 |
+
[package.dependencies]
|
663 |
+
click = ">=4.0"
|
664 |
+
|
665 |
+
[package.extras]
|
666 |
+
dev = ["coveralls", "pytest (>=3.6)", "pytest-cov", "wheel"]
|
667 |
+
|
668 |
+
[[package]]
|
669 |
+
name = "click-repl"
|
670 |
+
version = "0.3.0"
|
671 |
+
description = "REPL plugin for Click"
|
672 |
+
optional = false
|
673 |
+
python-versions = ">=3.6"
|
674 |
+
files = [
|
675 |
+
{file = "click-repl-0.3.0.tar.gz", hash = "sha256:17849c23dba3d667247dc4defe1757fff98694e90fe37474f3feebb69ced26a9"},
|
676 |
+
{file = "click_repl-0.3.0-py3-none-any.whl", hash = "sha256:fb7e06deb8da8de86180a33a9da97ac316751c094c6899382da7feeeeb51b812"},
|
677 |
+
]
|
678 |
+
|
679 |
+
[package.dependencies]
|
680 |
+
click = ">=7.0"
|
681 |
+
prompt-toolkit = ">=3.0.36"
|
682 |
+
|
683 |
+
[package.extras]
|
684 |
+
testing = ["pytest (>=7.2.1)", "pytest-cov (>=4.0.0)", "tox (>=4.4.3)"]
|
685 |
+
|
686 |
[[package]]
|
687 |
name = "colorama"
|
688 |
version = "0.4.6"
|
|
|
1224 |
[package.extras]
|
1225 |
all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"]
|
1226 |
|
1227 |
+
[[package]]
|
1228 |
+
name = "iniconfig"
|
1229 |
+
version = "2.0.0"
|
1230 |
+
description = "brain-dead simple config-ini parsing"
|
1231 |
+
optional = false
|
1232 |
+
python-versions = ">=3.7"
|
1233 |
+
files = [
|
1234 |
+
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
|
1235 |
+
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
|
1236 |
+
]
|
1237 |
+
|
1238 |
[[package]]
|
1239 |
name = "isodate"
|
1240 |
version = "0.7.2"
|
|
|
1246 |
{file = "isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6"},
|
1247 |
]
|
1248 |
|
1249 |
+
[[package]]
|
1250 |
+
name = "kombu"
|
1251 |
+
version = "5.4.2"
|
1252 |
+
description = "Messaging library for Python."
|
1253 |
+
optional = false
|
1254 |
+
python-versions = ">=3.8"
|
1255 |
+
files = [
|
1256 |
+
{file = "kombu-5.4.2-py3-none-any.whl", hash = "sha256:14212f5ccf022fc0a70453bb025a1dcc32782a588c49ea866884047d66e14763"},
|
1257 |
+
{file = "kombu-5.4.2.tar.gz", hash = "sha256:eef572dd2fd9fc614b37580e3caeafdd5af46c1eff31e7fba89138cdb406f2cf"},
|
1258 |
+
]
|
1259 |
+
|
1260 |
+
[package.dependencies]
|
1261 |
+
amqp = ">=5.1.1,<6.0.0"
|
1262 |
+
tzdata = {version = "*", markers = "python_version >= \"3.9\""}
|
1263 |
+
vine = "5.1.0"
|
1264 |
+
|
1265 |
+
[package.extras]
|
1266 |
+
azureservicebus = ["azure-servicebus (>=7.10.0)"]
|
1267 |
+
azurestoragequeues = ["azure-identity (>=1.12.0)", "azure-storage-queue (>=12.6.0)"]
|
1268 |
+
confluentkafka = ["confluent-kafka (>=2.2.0)"]
|
1269 |
+
consul = ["python-consul2 (==0.1.5)"]
|
1270 |
+
librabbitmq = ["librabbitmq (>=2.0.0)"]
|
1271 |
+
mongodb = ["pymongo (>=4.1.1)"]
|
1272 |
+
msgpack = ["msgpack (==1.1.0)"]
|
1273 |
+
pyro = ["pyro4 (==4.82)"]
|
1274 |
+
qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"]
|
1275 |
+
redis = ["redis (>=4.5.2,!=4.5.5,!=5.0.2)"]
|
1276 |
+
slmq = ["softlayer-messaging (>=1.0.3)"]
|
1277 |
+
sqlalchemy = ["sqlalchemy (>=1.4.48,<2.1)"]
|
1278 |
+
sqs = ["boto3 (>=1.26.143)", "pycurl (>=7.43.0.5)", "urllib3 (>=1.26.16)"]
|
1279 |
+
yaml = ["PyYAML (>=3.10)"]
|
1280 |
+
zookeeper = ["kazoo (>=2.8.0)"]
|
1281 |
+
|
1282 |
[[package]]
|
1283 |
name = "loguru"
|
1284 |
version = "0.7.2"
|
|
|
1658 |
test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"]
|
1659 |
xml = ["lxml (>=4.9.2)"]
|
1660 |
|
1661 |
+
[[package]]
|
1662 |
+
name = "pluggy"
|
1663 |
+
version = "1.5.0"
|
1664 |
+
description = "plugin and hook calling mechanisms for python"
|
1665 |
+
optional = false
|
1666 |
+
python-versions = ">=3.8"
|
1667 |
+
files = [
|
1668 |
+
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
|
1669 |
+
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
|
1670 |
+
]
|
1671 |
+
|
1672 |
+
[package.extras]
|
1673 |
+
dev = ["pre-commit", "tox"]
|
1674 |
+
testing = ["pytest", "pytest-benchmark"]
|
1675 |
+
|
1676 |
+
[[package]]
|
1677 |
+
name = "prompt-toolkit"
|
1678 |
+
version = "3.0.48"
|
1679 |
+
description = "Library for building powerful interactive command lines in Python"
|
1680 |
+
optional = false
|
1681 |
+
python-versions = ">=3.7.0"
|
1682 |
+
files = [
|
1683 |
+
{file = "prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e"},
|
1684 |
+
{file = "prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90"},
|
1685 |
+
]
|
1686 |
+
|
1687 |
+
[package.dependencies]
|
1688 |
+
wcwidth = "*"
|
1689 |
+
|
1690 |
[[package]]
|
1691 |
name = "propcache"
|
1692 |
version = "0.2.0"
|
|
|
2039 |
toml = ["tomli (>=2.0.1)"]
|
2040 |
yaml = ["pyyaml (>=6.0.1)"]
|
2041 |
|
2042 |
+
[[package]]
|
2043 |
+
name = "pytest"
|
2044 |
+
version = "7.4.4"
|
2045 |
+
description = "pytest: simple powerful testing with Python"
|
2046 |
+
optional = false
|
2047 |
+
python-versions = ">=3.7"
|
2048 |
+
files = [
|
2049 |
+
{file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"},
|
2050 |
+
{file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"},
|
2051 |
+
]
|
2052 |
+
|
2053 |
+
[package.dependencies]
|
2054 |
+
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
2055 |
+
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
|
2056 |
+
iniconfig = "*"
|
2057 |
+
packaging = "*"
|
2058 |
+
pluggy = ">=0.12,<2.0"
|
2059 |
+
tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
|
2060 |
+
|
2061 |
+
[package.extras]
|
2062 |
+
testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
|
2063 |
+
|
2064 |
+
[[package]]
|
2065 |
+
name = "pytest-asyncio"
|
2066 |
+
version = "0.20.3"
|
2067 |
+
description = "Pytest support for asyncio"
|
2068 |
+
optional = false
|
2069 |
+
python-versions = ">=3.7"
|
2070 |
+
files = [
|
2071 |
+
{file = "pytest-asyncio-0.20.3.tar.gz", hash = "sha256:83cbf01169ce3e8eb71c6c278ccb0574d1a7a3bb8eaaf5e50e0ad342afb33b36"},
|
2072 |
+
{file = "pytest_asyncio-0.20.3-py3-none-any.whl", hash = "sha256:f129998b209d04fcc65c96fc85c11e5316738358909a8399e93be553d7656442"},
|
2073 |
+
]
|
2074 |
+
|
2075 |
+
[package.dependencies]
|
2076 |
+
pytest = ">=6.1.0"
|
2077 |
+
|
2078 |
+
[package.extras]
|
2079 |
+
docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"]
|
2080 |
+
testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"]
|
2081 |
+
|
2082 |
[[package]]
|
2083 |
name = "python-dateutil"
|
2084 |
version = "2.9.0.post0"
|
|
|
2436 |
doc = ["reno", "sphinx"]
|
2437 |
test = ["pytest", "tornado (>=4.5)", "typeguard"]
|
2438 |
|
2439 |
+
[[package]]
|
2440 |
+
name = "tomli"
|
2441 |
+
version = "2.0.2"
|
2442 |
+
description = "A lil' TOML parser"
|
2443 |
+
optional = false
|
2444 |
+
python-versions = ">=3.8"
|
2445 |
+
files = [
|
2446 |
+
{file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"},
|
2447 |
+
{file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"},
|
2448 |
+
]
|
2449 |
+
|
2450 |
[[package]]
|
2451 |
name = "typing-extensions"
|
2452 |
version = "4.12.2"
|
|
|
2520 |
[package.extras]
|
2521 |
standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"]
|
2522 |
|
2523 |
+
[[package]]
|
2524 |
+
name = "vine"
|
2525 |
+
version = "5.1.0"
|
2526 |
+
description = "Python promises."
|
2527 |
+
optional = false
|
2528 |
+
python-versions = ">=3.6"
|
2529 |
+
files = [
|
2530 |
+
{file = "vine-5.1.0-py3-none-any.whl", hash = "sha256:40fdf3c48b2cfe1c38a49e9ae2da6fda88e4794c810050a728bd7413811fb1dc"},
|
2531 |
+
{file = "vine-5.1.0.tar.gz", hash = "sha256:8b62e981d35c41049211cf62a0a1242d8c1ee9bd15bb196ce38aefd6799e61e0"},
|
2532 |
+
]
|
2533 |
+
|
2534 |
+
[[package]]
|
2535 |
+
name = "wcwidth"
|
2536 |
+
version = "0.2.13"
|
2537 |
+
description = "Measures the displayed width of unicode strings in a terminal"
|
2538 |
+
optional = false
|
2539 |
+
python-versions = "*"
|
2540 |
+
files = [
|
2541 |
+
{file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"},
|
2542 |
+
{file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"},
|
2543 |
+
]
|
2544 |
+
|
2545 |
[[package]]
|
2546 |
name = "win32-setctime"
|
2547 |
version = "1.1.0"
|
|
|
2655 |
[metadata]
|
2656 |
lock-version = "2.0"
|
2657 |
python-versions = "3.10.15"
|
2658 |
+
content-hash = "db72df885a8c6d58dfb9ab3d27f3823b4546e1cd0c08b50b2d1bf50ffcd3798c"
|
pyproject.toml
CHANGED
@@ -39,7 +39,11 @@ redisearch = "2.0.0"
|
|
39 |
pandas = "^2.2.3"
|
40 |
python-multipart = "^0.0.17"
|
41 |
python-dotenv = "^1.0.1"
|
|
|
42 |
|
|
|
|
|
|
|
43 |
|
44 |
[build-system]
|
45 |
requires = ["poetry-core"]
|
|
|
39 |
pandas = "^2.2.3"
|
40 |
python-multipart = "^0.0.17"
|
41 |
python-dotenv = "^1.0.1"
|
42 |
+
celery = "^5.4.0"
|
43 |
|
44 |
+
[tool.poetry.dev-dependencies]
|
45 |
+
pytest = "^7.2.0"
|
46 |
+
pytest-asyncio = "^0.20.3"
|
47 |
|
48 |
[build-system]
|
49 |
requires = ["poetry-core"]
|
src/__pycache__/test_infra.cpython-310.pyc
CHANGED
Binary files a/src/__pycache__/test_infra.cpython-310.pyc and b/src/__pycache__/test_infra.cpython-310.pyc differ
|
|
src/number_manipulation.py
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import random
|
2 |
+
|
3 |
+
|
4 |
+
def add_random_number(user_input: int) -> int:
|
5 |
+
random_addition = random.randint(1, 100) # Add a random number between 1 and 100
|
6 |
+
result = user_input + random_addition
|
7 |
+
return result
|
src/test_infra.py
CHANGED
@@ -26,7 +26,7 @@ async def test_redis_connection(redis_url: str):
|
|
26 |
async def main():
|
27 |
logger.info(f"Settings: {settings.dict()}")
|
28 |
|
29 |
-
await test_postgres_connection(settings.database_url)
|
30 |
await test_redis_connection(settings.redis_url)
|
31 |
|
32 |
|
|
|
26 |
async def main():
|
27 |
logger.info(f"Settings: {settings.dict()}")
|
28 |
|
29 |
+
await test_postgres_connection(settings.database_url.replace("+asyncpg", ""))
|
30 |
await test_redis_connection(settings.redis_url)
|
31 |
|
32 |
|
tests/__init__.py
ADDED
File without changes
|
tests/test_db_connection.py
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import pytest
|
2 |
+
from sqlalchemy.ext.asyncio import AsyncSession
|
3 |
+
from sqlalchemy import text
|
4 |
+
from app.db.database import get_db
|
5 |
+
|
6 |
+
|
7 |
+
@pytest.mark.asyncio
|
8 |
+
async def test_database_connection():
|
9 |
+
# Use the get_db dependency directly for testing
|
10 |
+
async for session in get_db():
|
11 |
+
assert isinstance(
|
12 |
+
session, AsyncSession
|
13 |
+
), "Session is not an instance of AsyncSession"
|
14 |
+
|
15 |
+
# Check if the session can execute a simple query
|
16 |
+
result = await session.execute(text("SELECT 1"))
|
17 |
+
assert result.scalar() == 1, "Database did not return expected result"
|