Soutrik commited on
Commit
36ed17a
1 Parent(s): 8753097

added: fastapi framework

Browse files
.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
- # Set entrypoint to ensure Poetry's virtual environment is used
32
- ENTRYPOINT ["poetry", "run"]
33
 
34
- # Default command to run `src.test_infra`
35
- CMD ["python", "-m", "src.test_infra"]
 
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"postgres://{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 = (
 
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
- command: ["python", "-m", "src.test_infra"]
 
 
 
 
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.13.3"
237
  description = "A database migration tool for SQLAlchemy."
238
  optional = false
239
  python-versions = ">=3.8"
240
  files = [
241
- {file = "alembic-1.13.3-py3-none-any.whl", hash = "sha256:908e905976d15235fae59c9ac42c4c5b75cfcefe3d27c0fbf7ae15a37715d80e"},
242
- {file = "alembic-1.13.3.tar.gz", hash = "sha256:203503117415561e203aa14541740643a611f641517f0209fcae63e9fa09f1a2"},
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 = "836fb248ed5b369f85bac291124c740e9bf08be971700f1f6ba8757c13afd023"
 
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"