Spaces:
Sleeping
Sleeping
pat/01-categorization-transactions
#2
by palexis3 - opened
This view is limited to 50 files because it contains too many changes. See the raw diff here.
- .dockerignore +1 -3
- .env.example +0 -8
- .gitignore +1 -2
- .vscode/settings.json +0 -11
- Dockerfile +0 -9
- Dockerfile.local.postgres +0 -23
- Makefile +0 -14
- README.md +0 -30
- alembic.ini +0 -116
- app/api/routers/chat.py +10 -5
- app/api/routers/file_upload.py +0 -43
- app/api/routers/income_statement.py +0 -64
- app/api/routers/transaction.py +0 -30
- app/api/routers/user.py +0 -89
- app/categorization/__init__.py +0 -0
- app/categorization/categorizer.py +0 -147
- app/categorization/categorizer_list.py +0 -70
- app/categorization/config.py +0 -6
- app/categorization/file_processing.py +0 -128
- app/categorization/template.py +0 -56
- app/engine/postgresdb.py +0 -81
- app/model/base.py +0 -15
- app/model/income_statement.py +0 -46
- app/model/transaction.py +0 -62
- app/model/user.py +0 -110
- app/schema/base.py +0 -14
- app/schema/index.py +0 -85
- app/service/income_statement.py +0 -37
- app/service/llm.py +0 -55
- app/transactions_rag/__init__.py +0 -0
- app/transactions_rag/categorize_transactions.ipynb +0 -571
- app/transactions_rag/transactions_2024.csv +0 -29
- config/index.py +0 -34
- data/.gitkeep +0 -0
- data/tx_data/input/transactions_2024.csv +0 -28
- main.py +30 -53
- migration/README +0 -1
- migration/env.py +0 -91
- migration/script.py.mako +0 -26
- migration/versions/4e76691ab103_add_income_statement_table.py +0 -43
- migration/versions/7ea44cbc5b1f_default_timezone.py +0 -69
- migration/versions/8feaedca36f9_users_datatype_updates.py +0 -74
- migration/versions/cd515c44401d_add_users_transactions_tables.py +0 -55
- poetry.lock +91 -835
- pyproject.toml +1 -29
- requirements.txt +11 -0
- tests/conftest.py +0 -83
- tests/pytest.ini +0 -4
- tests/test_income_statement.py +0 -51
- tests/test_transactions.py +0 -25
.dockerignore
CHANGED
|
@@ -1,5 +1,3 @@
|
|
| 1 |
__pycache__
|
| 2 |
storage
|
| 3 |
-
data/*
|
| 4 |
-
venv
|
| 5 |
-
myvenv
|
|
|
|
| 1 |
__pycache__
|
| 2 |
storage
|
| 3 |
+
data/*
|
|
|
|
|
|
.env.example
CHANGED
|
@@ -43,11 +43,3 @@ APP_HOST=0.0.0.0
|
|
| 43 |
|
| 44 |
# The port to start the backend app.
|
| 45 |
APP_PORT=8000
|
| 46 |
-
|
| 47 |
-
# Postgres database configuration
|
| 48 |
-
POSTGRES_USER=postgres
|
| 49 |
-
POSTGRES_PASSWORD=postgres
|
| 50 |
-
POSTGRES_DB_NAME=postgres
|
| 51 |
-
POSTGRES_DB_HOST=localhost
|
| 52 |
-
POSTGRES_DB_PORT=5432
|
| 53 |
-
SQLALCHEMY_DATABASE_URL=postgresql+asyncpg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_DB_HOST}:${POSTGRES_DB_PORT}/${POSTGRES_DB_NAME}
|
|
|
|
| 43 |
|
| 44 |
# The port to start the backend app.
|
| 45 |
APP_PORT=8000
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.gitignore
CHANGED
|
@@ -1,5 +1,4 @@
|
|
| 1 |
__pycache__
|
| 2 |
storage
|
| 3 |
.env
|
| 4 |
-
|
| 5 |
-
myvenv
|
|
|
|
| 1 |
__pycache__
|
| 2 |
storage
|
| 3 |
.env
|
| 4 |
+
data/*
|
|
|
.vscode/settings.json
DELETED
|
@@ -1,11 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"jupyter.notebookFileRoot": "${workspaceFolder}",
|
| 3 |
-
"[python]": {
|
| 4 |
-
"editor.defaultFormatter": "ms-python.black-formatter",
|
| 5 |
-
"editor.formatOnSave": true,
|
| 6 |
-
},
|
| 7 |
-
"ms-python.black-formatter.args": [
|
| 8 |
-
"--line-length",
|
| 9 |
-
"119"
|
| 10 |
-
]
|
| 11 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Dockerfile
CHANGED
|
@@ -25,13 +25,4 @@ COPY . .
|
|
| 25 |
# Make port 8000 available to the world outside this container
|
| 26 |
EXPOSE 8000
|
| 27 |
|
| 28 |
-
ENV POSTGRES_USER codepath_project_owner
|
| 29 |
-
ENV POSTGRES_PASSWORD 03EdiworgCJz
|
| 30 |
-
ENV POSTGRES_DB_NAME codepath_project
|
| 31 |
-
ENV POSTGRES_DB_HOST ep-icy-cloud-a5m4mcgo.us-east-2.aws.neon.tech
|
| 32 |
-
ENV POSTGRES_DB_PORT 5432
|
| 33 |
-
|
| 34 |
-
# Run migrations
|
| 35 |
-
RUN alembic upgrade head
|
| 36 |
-
|
| 37 |
CMD ["python", "main.py"]
|
|
|
|
| 25 |
# Make port 8000 available to the world outside this container
|
| 26 |
EXPOSE 8000
|
| 27 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
CMD ["python", "main.py"]
|
Dockerfile.local.postgres
DELETED
|
@@ -1,23 +0,0 @@
|
|
| 1 |
-
# Pull the official PostgreSQL 16 image
|
| 2 |
-
FROM postgres:16
|
| 3 |
-
|
| 4 |
-
# Create a directory for the database files
|
| 5 |
-
RUN mkdir -p /var/lib/postgresql/data
|
| 6 |
-
|
| 7 |
-
# Change the ownership of the data directory
|
| 8 |
-
RUN chown -R postgres:postgres /var/lib/postgresql/data
|
| 9 |
-
|
| 10 |
-
# Copy the configuration files into the data directory
|
| 11 |
-
# NOTE: the [f] suffix is to ensure that the COPY command will not fail if the files don't exist
|
| 12 |
-
COPY ./pg_hba.con[f] /var/lib/postgresql/data/pg_hba.conf
|
| 13 |
-
COPY ./postgresql.con[f] /var/lib/postgresql/data/postgresql.conf
|
| 14 |
-
|
| 15 |
-
# Include environment variables for the PostgreSQL user and password
|
| 16 |
-
ENV POSTGRES_USER=${POSTGRES_USER}
|
| 17 |
-
ENV POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
| 18 |
-
|
| 19 |
-
# Expose the default PostgreSQL port
|
| 20 |
-
EXPOSE 5432
|
| 21 |
-
|
| 22 |
-
# Start the PostgreSQL server
|
| 23 |
-
CMD ["postgres", "-c", "config_file=/var/lib/postgresql/data/postgresql.conf"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Makefile
DELETED
|
@@ -1,14 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
include .env
|
| 3 |
-
|
| 4 |
-
build-postgres:
|
| 5 |
-
docker build -t codepath_project_postgres -f Dockerfile.local.postgres .
|
| 6 |
-
|
| 7 |
-
run-postgres:
|
| 8 |
-
docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=${POSTGRES_PASSWORD} -e POSTGRES_USER=${POSTGRES_USER} --name ${POSTGRES_DB_NAME} codepath_project_postgres
|
| 9 |
-
|
| 10 |
-
run-migrations:
|
| 11 |
-
alembic upgrade head
|
| 12 |
-
|
| 13 |
-
generate-migrations:
|
| 14 |
-
alembic revision --autogenerate -m "$(migration_title)"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
README.md
CHANGED
|
@@ -69,36 +69,6 @@ The API allows CORS for all origins to simplify development. You can change this
|
|
| 69 |
ENVIRONMENT=prod python main.py
|
| 70 |
```
|
| 71 |
|
| 72 |
-
## Local Postgres database setup
|
| 73 |
-
|
| 74 |
-
To setup a local postgres database, run:
|
| 75 |
-
|
| 76 |
-
1. Build the docker image:
|
| 77 |
-
|
| 78 |
-
```bash
|
| 79 |
-
make build-postgres
|
| 80 |
-
```
|
| 81 |
-
|
| 82 |
-
2. Start the docker container:
|
| 83 |
-
|
| 84 |
-
```bash
|
| 85 |
-
make run-postgres
|
| 86 |
-
```
|
| 87 |
-
|
| 88 |
-
## Running Migrations
|
| 89 |
-
|
| 90 |
-
To generate new migrations, run:
|
| 91 |
-
|
| 92 |
-
```bash
|
| 93 |
-
make generate-migrations migration_title="<name_for_migration>"
|
| 94 |
-
```
|
| 95 |
-
|
| 96 |
-
To locally verify your changes, run:
|
| 97 |
-
|
| 98 |
-
```bash
|
| 99 |
-
make run-migrations
|
| 100 |
-
```
|
| 101 |
-
|
| 102 |
## Using Docker
|
| 103 |
|
| 104 |
1. Build an image for the FastAPI app:
|
|
|
|
| 69 |
ENVIRONMENT=prod python main.py
|
| 70 |
```
|
| 71 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
## Using Docker
|
| 73 |
|
| 74 |
1. Build an image for the FastAPI app:
|
alembic.ini
DELETED
|
@@ -1,116 +0,0 @@
|
|
| 1 |
-
# A generic, single database configuration.
|
| 2 |
-
|
| 3 |
-
[alembic]
|
| 4 |
-
# path to migration scripts
|
| 5 |
-
script_location = migration
|
| 6 |
-
|
| 7 |
-
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
|
| 8 |
-
# Uncomment the line below if you want the files to be prepended with date and time
|
| 9 |
-
# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
|
| 10 |
-
# for all available tokens
|
| 11 |
-
# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
|
| 12 |
-
|
| 13 |
-
# sys.path path, will be prepended to sys.path if present.
|
| 14 |
-
# defaults to the current working directory.
|
| 15 |
-
prepend_sys_path = .
|
| 16 |
-
|
| 17 |
-
# timezone to use when rendering the date within the migration file
|
| 18 |
-
# as well as the filename.
|
| 19 |
-
# If specified, requires the python>=3.9 or backports.zoneinfo library.
|
| 20 |
-
# Any required deps can installed by adding `alembic[tz]` to the pip requirements
|
| 21 |
-
# string value is passed to ZoneInfo()
|
| 22 |
-
# leave blank for localtime
|
| 23 |
-
# timezone =
|
| 24 |
-
|
| 25 |
-
# max length of characters to apply to the
|
| 26 |
-
# "slug" field
|
| 27 |
-
# truncate_slug_length = 40
|
| 28 |
-
|
| 29 |
-
# set to 'true' to run the environment during
|
| 30 |
-
# the 'revision' command, regardless of autogenerate
|
| 31 |
-
# revision_environment = false
|
| 32 |
-
|
| 33 |
-
# set to 'true' to allow .pyc and .pyo files without
|
| 34 |
-
# a source .py file to be detected as revisions in the
|
| 35 |
-
# versions/ directory
|
| 36 |
-
# sourceless = false
|
| 37 |
-
|
| 38 |
-
# version location specification; This defaults
|
| 39 |
-
# to migration/versions. When using multiple version
|
| 40 |
-
# directories, initial revisions must be specified with --version-path.
|
| 41 |
-
# The path separator used here should be the separator specified by "version_path_separator" below.
|
| 42 |
-
# version_locations = %(here)s/bar:%(here)s/bat:migration/versions
|
| 43 |
-
|
| 44 |
-
# version path separator; As mentioned above, this is the character used to split
|
| 45 |
-
# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
|
| 46 |
-
# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
|
| 47 |
-
# Valid values for version_path_separator are:
|
| 48 |
-
#
|
| 49 |
-
# version_path_separator = :
|
| 50 |
-
# version_path_separator = ;
|
| 51 |
-
# version_path_separator = space
|
| 52 |
-
version_path_separator = os # Use os.pathsep. Default configuration used for new projects.
|
| 53 |
-
|
| 54 |
-
# set to 'true' to search source files recursively
|
| 55 |
-
# in each "version_locations" directory
|
| 56 |
-
# new in Alembic version 1.10
|
| 57 |
-
# recursive_version_locations = false
|
| 58 |
-
|
| 59 |
-
# the output encoding used when revision files
|
| 60 |
-
# are written from script.py.mako
|
| 61 |
-
# output_encoding = utf-8
|
| 62 |
-
|
| 63 |
-
sqlalchemy.url = driver://user:pass@localhost/dbname
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
[post_write_hooks]
|
| 67 |
-
# post_write_hooks defines scripts or Python functions that are run
|
| 68 |
-
# on newly generated revision scripts. See the documentation for further
|
| 69 |
-
# detail and examples
|
| 70 |
-
|
| 71 |
-
# format using "black" - use the console_scripts runner, against the "black" entrypoint
|
| 72 |
-
# hooks = black
|
| 73 |
-
# black.type = console_scripts
|
| 74 |
-
# black.entrypoint = black
|
| 75 |
-
# black.options = -l 79 REVISION_SCRIPT_FILENAME
|
| 76 |
-
|
| 77 |
-
# lint with attempts to fix using "ruff" - use the exec runner, execute a binary
|
| 78 |
-
# hooks = ruff
|
| 79 |
-
# ruff.type = exec
|
| 80 |
-
# ruff.executable = %(here)s/.venv/bin/ruff
|
| 81 |
-
# ruff.options = --fix REVISION_SCRIPT_FILENAME
|
| 82 |
-
|
| 83 |
-
# Logging configuration
|
| 84 |
-
[loggers]
|
| 85 |
-
keys = root,sqlalchemy,alembic
|
| 86 |
-
|
| 87 |
-
[handlers]
|
| 88 |
-
keys = console
|
| 89 |
-
|
| 90 |
-
[formatters]
|
| 91 |
-
keys = generic
|
| 92 |
-
|
| 93 |
-
[logger_root]
|
| 94 |
-
level = WARN
|
| 95 |
-
handlers = console
|
| 96 |
-
qualname =
|
| 97 |
-
|
| 98 |
-
[logger_sqlalchemy]
|
| 99 |
-
level = WARN
|
| 100 |
-
handlers =
|
| 101 |
-
qualname = sqlalchemy.engine
|
| 102 |
-
|
| 103 |
-
[logger_alembic]
|
| 104 |
-
level = INFO
|
| 105 |
-
handlers =
|
| 106 |
-
qualname = alembic
|
| 107 |
-
|
| 108 |
-
[handler_console]
|
| 109 |
-
class = StreamHandler
|
| 110 |
-
args = (sys.stderr,)
|
| 111 |
-
level = NOTSET
|
| 112 |
-
formatter = generic
|
| 113 |
-
|
| 114 |
-
[formatter_generic]
|
| 115 |
-
format = %(levelname)-5.5s [%(name)s] %(message)s
|
| 116 |
-
datefmt = %H:%M:%S
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/api/routers/chat.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
from pydantic import BaseModel
|
| 2 |
from typing import List, Any, Optional, Dict, Tuple
|
| 3 |
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
| 4 |
from llama_index.core.chat_engine.types import BaseChatEngine
|
|
@@ -19,8 +19,9 @@ class _Message(BaseModel):
|
|
| 19 |
|
| 20 |
class _ChatData(BaseModel):
|
| 21 |
messages: List[_Message]
|
| 22 |
-
|
| 23 |
-
|
|
|
|
| 24 |
"example": {
|
| 25 |
"messages": [
|
| 26 |
{
|
|
@@ -30,7 +31,6 @@ class _ChatData(BaseModel):
|
|
| 30 |
]
|
| 31 |
}
|
| 32 |
}
|
| 33 |
-
)
|
| 34 |
|
| 35 |
|
| 36 |
class _SourceNodes(BaseModel):
|
|
@@ -121,7 +121,12 @@ async def chat(
|
|
| 121 |
yield VercelStreamResponse.convert_data(
|
| 122 |
{
|
| 123 |
"type": "sources",
|
| 124 |
-
"data": {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
}
|
| 126 |
)
|
| 127 |
|
|
|
|
| 1 |
+
from pydantic import BaseModel
|
| 2 |
from typing import List, Any, Optional, Dict, Tuple
|
| 3 |
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
| 4 |
from llama_index.core.chat_engine.types import BaseChatEngine
|
|
|
|
| 19 |
|
| 20 |
class _ChatData(BaseModel):
|
| 21 |
messages: List[_Message]
|
| 22 |
+
|
| 23 |
+
class Config:
|
| 24 |
+
json_schema_extra = {
|
| 25 |
"example": {
|
| 26 |
"messages": [
|
| 27 |
{
|
|
|
|
| 31 |
]
|
| 32 |
}
|
| 33 |
}
|
|
|
|
| 34 |
|
| 35 |
|
| 36 |
class _SourceNodes(BaseModel):
|
|
|
|
| 121 |
yield VercelStreamResponse.convert_data(
|
| 122 |
{
|
| 123 |
"type": "sources",
|
| 124 |
+
"data": {
|
| 125 |
+
"nodes": [
|
| 126 |
+
_SourceNodes.from_source_node(node).dict()
|
| 127 |
+
for node in response.source_nodes
|
| 128 |
+
]
|
| 129 |
+
},
|
| 130 |
}
|
| 131 |
)
|
| 132 |
|
app/api/routers/file_upload.py
DELETED
|
@@ -1,43 +0,0 @@
|
|
| 1 |
-
from typing import Annotated
|
| 2 |
-
from fastapi import APIRouter, UploadFile, Depends
|
| 3 |
-
from app.categorization.file_processing import process_file, save_results
|
| 4 |
-
from app.schema.index import FileUploadCreate
|
| 5 |
-
import asyncio
|
| 6 |
-
import os
|
| 7 |
-
import csv
|
| 8 |
-
|
| 9 |
-
from app.engine.postgresdb import get_db_session
|
| 10 |
-
from sqlalchemy.ext.asyncio import AsyncSession
|
| 11 |
-
|
| 12 |
-
file_upload_router = r = APIRouter(prefix="/api/v1/file_upload", tags=["file_upload"])
|
| 13 |
-
|
| 14 |
-
@r.post(
|
| 15 |
-
"/",
|
| 16 |
-
responses={
|
| 17 |
-
200: {"description": "File successfully uploaded"},
|
| 18 |
-
400: {"description": "Bad request"},
|
| 19 |
-
500: {"description": "Internal server error"},
|
| 20 |
-
},
|
| 21 |
-
)
|
| 22 |
-
async def create_file(input_file: UploadFile, db: AsyncSession = Depends(get_db_session)):
|
| 23 |
-
try:
|
| 24 |
-
# Create directory to store all uploaded .csv files
|
| 25 |
-
file_upload_directory_path = "data/tx_data/input"
|
| 26 |
-
if not os.path.exists(file_upload_directory_path):
|
| 27 |
-
os.makedirs(file_upload_directory_path)
|
| 28 |
-
|
| 29 |
-
# Write items of .csv filte to directory
|
| 30 |
-
with open(os.path.join(file_upload_directory_path, input_file.filename)) as output_file:
|
| 31 |
-
[output_file.write(" ".join(row)+'\n') for row in csv.reader(input_file)]
|
| 32 |
-
output_file.close()
|
| 33 |
-
|
| 34 |
-
# With the newly created file and it's path, process and save it for embedding
|
| 35 |
-
processed_file = process_file(os.path.realpath(input_file.filename))
|
| 36 |
-
result = await asyncio.gather(processed_file)
|
| 37 |
-
await save_results(db, result)
|
| 38 |
-
|
| 39 |
-
except Exception:
|
| 40 |
-
return {"message": "There was an error uploading this file. Ensure you have a .csv file with the following columns:"
|
| 41 |
-
"\n transaction_date, type, category, name_description, amount"}
|
| 42 |
-
|
| 43 |
-
return {"message": f"Successfully uploaded {input_file.filename}"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/api/routers/income_statement.py
DELETED
|
@@ -1,64 +0,0 @@
|
|
| 1 |
-
from typing import List
|
| 2 |
-
from fastapi import APIRouter, Depends, HTTPException, status
|
| 3 |
-
from sqlalchemy.ext.asyncio import AsyncSession
|
| 4 |
-
from app.model.transaction import Transaction as TransactionModel
|
| 5 |
-
from app.model.income_statement import IncomeStatement as IncomeStatementModel
|
| 6 |
-
from app.schema.index import IncomeStatementCreateRequest, IncomeStatementResponse
|
| 7 |
-
from app.engine.postgresdb import get_db_session
|
| 8 |
-
from app.service.income_statement import call_llm_to_create_income_statement
|
| 9 |
-
|
| 10 |
-
income_statement_router = r = APIRouter(prefix="/api/v1/income_statement", tags=["income_statement"])
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
@r.post(
|
| 14 |
-
"/",
|
| 15 |
-
responses={
|
| 16 |
-
200: {"description": "New transaction created"},
|
| 17 |
-
400: {"description": "Bad request"},
|
| 18 |
-
500: {"description": "Internal server error"},
|
| 19 |
-
},
|
| 20 |
-
)
|
| 21 |
-
async def create_income_statement(payload: IncomeStatementCreateRequest, db: AsyncSession = Depends(get_db_session)) -> None:
|
| 22 |
-
try:
|
| 23 |
-
await call_llm_to_create_income_statement(payload, db)
|
| 24 |
-
|
| 25 |
-
except Exception as e:
|
| 26 |
-
raise HTTPException(status_code=500, detail=str(e))
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
@r.get(
|
| 30 |
-
"/user/{user_id}",
|
| 31 |
-
response_model=List[IncomeStatementResponse],
|
| 32 |
-
responses={
|
| 33 |
-
200: {"description": "New user created"},
|
| 34 |
-
400: {"description": "Bad request"},
|
| 35 |
-
204: {"description": "No content"},
|
| 36 |
-
500: {"description": "Internal server error"},
|
| 37 |
-
},
|
| 38 |
-
)
|
| 39 |
-
async def get_income_statements(
|
| 40 |
-
user_id: int, db: AsyncSession = Depends(get_db_session)
|
| 41 |
-
) -> List[IncomeStatementResponse]:
|
| 42 |
-
"""
|
| 43 |
-
Retrieve all income statements.
|
| 44 |
-
"""
|
| 45 |
-
result = await IncomeStatementModel.get_by_user(db, user_id)
|
| 46 |
-
if len(result) == 0:
|
| 47 |
-
raise HTTPException(status_code=status.HTTP_204_NO_CONTENT, detail="No income statements found for this user")
|
| 48 |
-
return result
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
@r.get(
|
| 52 |
-
"/report/{report_id}",
|
| 53 |
-
response_model=IncomeStatementResponse,
|
| 54 |
-
responses={
|
| 55 |
-
200: {"description": "Income statement found"},
|
| 56 |
-
404: {"description": "Income statement not found"},
|
| 57 |
-
500: {"description": "Internal server error"},
|
| 58 |
-
},
|
| 59 |
-
)
|
| 60 |
-
async def get_income_statement(report_id: int, db: AsyncSession = Depends(get_db_session)) -> IncomeStatementResponse:
|
| 61 |
-
income_statement = await IncomeStatementModel.get(db, id=report_id)
|
| 62 |
-
if not income_statement:
|
| 63 |
-
raise HTTPException(status_code=404, detail="Income statement not found")
|
| 64 |
-
return income_statement
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/api/routers/transaction.py
DELETED
|
@@ -1,30 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
from typing import List
|
| 3 |
-
from fastapi import APIRouter, Depends, HTTPException, status
|
| 4 |
-
from sqlalchemy.ext.asyncio import AsyncSession
|
| 5 |
-
from app.model.transaction import Transaction as TransactionModel
|
| 6 |
-
from app.schema.index import TransactionResponse
|
| 7 |
-
from app.engine.postgresdb import get_db_session
|
| 8 |
-
|
| 9 |
-
transaction_router = r = APIRouter(prefix="/api/v1/transactions", tags=["transactions"])
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
@r.get(
|
| 13 |
-
"/{user_id}",
|
| 14 |
-
response_model=List[TransactionResponse],
|
| 15 |
-
responses={
|
| 16 |
-
200: {"description": "New user created"},
|
| 17 |
-
400: {"description": "Bad request"},
|
| 18 |
-
204: {"description": "No content"},
|
| 19 |
-
500: {"description": "Internal server error"},
|
| 20 |
-
},
|
| 21 |
-
)
|
| 22 |
-
async def get_transactions(user_id: int, db: AsyncSession = Depends(get_db_session)):
|
| 23 |
-
"""
|
| 24 |
-
Retrieve all transactions.
|
| 25 |
-
"""
|
| 26 |
-
result = await TransactionModel.get_by_user(db, user_id)
|
| 27 |
-
all_rows = result.all()
|
| 28 |
-
if len(all_rows) == 0:
|
| 29 |
-
raise HTTPException(status_code=status.HTTP_204_NO_CONTENT, detail="No transactions found for this user")
|
| 30 |
-
return all_rows
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/api/routers/user.py
DELETED
|
@@ -1,89 +0,0 @@
|
|
| 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, UserResponse, UserUpdate
|
| 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(
|
| 15 |
-
"/",
|
| 16 |
-
response_model=UserResponse,
|
| 17 |
-
responses={
|
| 18 |
-
200: {"description": "New user created"},
|
| 19 |
-
400: {"description": "Bad request"},
|
| 20 |
-
409: {"description": "Conflict"},
|
| 21 |
-
500: {"description": "Internal server error"},
|
| 22 |
-
},
|
| 23 |
-
)
|
| 24 |
-
async def create_user(user: UserCreate, db: AsyncSession = Depends(get_db_session)):
|
| 25 |
-
try:
|
| 26 |
-
db_user = await UserModel.get(db, email=user.email)
|
| 27 |
-
if db_user and not db_user.is_deleted:
|
| 28 |
-
raise HTTPException(status_code=409, detail="User already exists")
|
| 29 |
-
|
| 30 |
-
await UserModel.create(db, **user.model_dump())
|
| 31 |
-
user = await UserModel.get(db, email=user.email)
|
| 32 |
-
return user
|
| 33 |
-
except Exception as e:
|
| 34 |
-
raise HTTPException(status_code=500, detail=str(e))
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
@r.get(
|
| 38 |
-
"/{email}",
|
| 39 |
-
response_model=UserResponse,
|
| 40 |
-
responses={
|
| 41 |
-
200: {"description": "User found"},
|
| 42 |
-
404: {"description": "User not found"},
|
| 43 |
-
500: {"description": "Internal server error"},
|
| 44 |
-
},
|
| 45 |
-
)
|
| 46 |
-
async def get_user(email: str, db: AsyncSession = Depends(get_db_session)):
|
| 47 |
-
user = await UserModel.get(db, email=email)
|
| 48 |
-
if not user:
|
| 49 |
-
raise HTTPException(status_code=404, detail="User not found")
|
| 50 |
-
return user
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
@r.put(
|
| 54 |
-
"/{email}",
|
| 55 |
-
response_model=UserResponse,
|
| 56 |
-
responses={
|
| 57 |
-
200: {"description": "User updated"},
|
| 58 |
-
404: {"description": "User not found"},
|
| 59 |
-
500: {"description": "Internal server error"},
|
| 60 |
-
},
|
| 61 |
-
)
|
| 62 |
-
async def update_user(email: str, user_payload: UserUpdate, db: AsyncSession = Depends(get_db_session)):
|
| 63 |
-
try:
|
| 64 |
-
user = await UserModel.get(db, email=email)
|
| 65 |
-
if not user:
|
| 66 |
-
raise HTTPException(status_code=404, detail="User not found")
|
| 67 |
-
await UserModel.update(db, id=user.id, **user_payload.model_dump())
|
| 68 |
-
user = await UserModel.get(db, email=email)
|
| 69 |
-
return user
|
| 70 |
-
except Exception as e:
|
| 71 |
-
raise HTTPException(status_code=500, detail=str(e))
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
@r.delete(
|
| 75 |
-
"/{email}",
|
| 76 |
-
response_model=UserResponse,
|
| 77 |
-
responses={
|
| 78 |
-
200: {"description": "User deleted"},
|
| 79 |
-
404: {"description": "User not found"},
|
| 80 |
-
500: {"description": "Internal server error"},
|
| 81 |
-
},
|
| 82 |
-
)
|
| 83 |
-
async def delete_user(email: str, db: AsyncSession = Depends(get_db_session)):
|
| 84 |
-
user = await UserModel.get(db, email=email)
|
| 85 |
-
if not user:
|
| 86 |
-
raise HTTPException(status_code=404, detail="User not found")
|
| 87 |
-
await UserModel.delete(db, email=email)
|
| 88 |
-
user = await UserModel.get(db, email=email)
|
| 89 |
-
return user
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/categorization/__init__.py
DELETED
|
File without changes
|
app/categorization/categorizer.py
DELETED
|
@@ -1,147 +0,0 @@
|
|
| 1 |
-
# Standard library imports
|
| 2 |
-
import re
|
| 3 |
-
import ast
|
| 4 |
-
import json
|
| 5 |
-
import logging
|
| 6 |
-
import os
|
| 7 |
-
from typing import Any, List, Tuple, Optional, Dict, Union
|
| 8 |
-
|
| 9 |
-
# Third-party library imports
|
| 10 |
-
import numpy as np
|
| 11 |
-
import pandas as pd
|
| 12 |
-
import asyncio
|
| 13 |
-
from rapidfuzz import process
|
| 14 |
-
from tenacity import retry, wait_random_exponential, stop_after_attempt
|
| 15 |
-
from pydantic import ValidationError
|
| 16 |
-
|
| 17 |
-
# Local application/library specific imports
|
| 18 |
-
from langchain_openai import ChatOpenAI
|
| 19 |
-
from langchain.chains import LLMChain
|
| 20 |
-
from langchain.output_parsers import PydanticOutputParser, OutputFixingParser
|
| 21 |
-
from langchain.prompts import PromptTemplate
|
| 22 |
-
import app.categorization.template as CATEGORY_TEMPLATE
|
| 23 |
-
from app.categorization.config import CATEGORY_REFERENCE_OUTPUT_FILE, TX_PER_LLM_RUN
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
def fuzzy_match_list_categorizer(
|
| 27 |
-
description: str,
|
| 28 |
-
descriptions: np.ndarray,
|
| 29 |
-
description_category_pairs: pd.DataFrame,
|
| 30 |
-
threshold: int = 75,
|
| 31 |
-
) -> Optional[str]:
|
| 32 |
-
"""Find the most similar transaction description and return its category.
|
| 33 |
-
|
| 34 |
-
This function uses fuzzy string matching to compare the input description
|
| 35 |
-
against a list of known descriptions. If a sufficient match is found,
|
| 36 |
-
the function returns the category associated with the matched description.
|
| 37 |
-
|
| 38 |
-
Args:
|
| 39 |
-
description (str): The transaction description to categorize.
|
| 40 |
-
descriptions (np.ndarray): Known descriptions to compare against.
|
| 41 |
-
description_category_pairs (pd.DataFrame): DataFrame mapping descriptions to categories.
|
| 42 |
-
threshold (int): Minimum similarity score to consider a match.
|
| 43 |
-
|
| 44 |
-
Returns:
|
| 45 |
-
str or None: Category of the matched description, or None if no match found.
|
| 46 |
-
"""
|
| 47 |
-
|
| 48 |
-
# Fuzzy-match this description against the reference descriptions
|
| 49 |
-
match_results = process.extractOne(
|
| 50 |
-
description, descriptions, score_cutoff=threshold)
|
| 51 |
-
|
| 52 |
-
# If a match is found, return the category of the matched description
|
| 53 |
-
if match_results:
|
| 54 |
-
return description_category_pairs.at[match_results[2], 'category']
|
| 55 |
-
|
| 56 |
-
return None
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
async def llm_list_categorizer(tx_list: pd.DataFrame) -> pd.DataFrame:
|
| 60 |
-
"""Categorize a list of transactions using a language model.
|
| 61 |
-
|
| 62 |
-
This function uses a Language Model (LLM) to categorize a list of transaction descriptions.
|
| 63 |
-
It splits the input DataFrame into chunks and processes each chunk asynchronously to improve performance.
|
| 64 |
-
|
| 65 |
-
Args:
|
| 66 |
-
tx_list (pd.DataFrame): DataFrame containing the transaction descriptions to categorize.
|
| 67 |
-
|
| 68 |
-
Returns:
|
| 69 |
-
pd.DataFrame: DataFrame mapping transaction descriptions to their inferred categories.
|
| 70 |
-
"""
|
| 71 |
-
|
| 72 |
-
# Initialize language model and prompt
|
| 73 |
-
openai_api_key = os.environ['OPENAI_API_KEY']
|
| 74 |
-
llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo-0125",
|
| 75 |
-
api_key=openai_api_key)
|
| 76 |
-
prompt = PromptTemplate.from_template(template=CATEGORY_TEMPLATE)
|
| 77 |
-
chain = LLMChain(llm=llm, prompt=prompt)
|
| 78 |
-
|
| 79 |
-
# Iterate over the DataFrame in batches of TX_PER_LLM_RUN transactions
|
| 80 |
-
tasks = [llm_sublist_categorizer(tx_list.attrs['file_name'], chain=chain, tx_descriptions="\n".join(chunk['name/description']).strip())
|
| 81 |
-
for chunk in np.array_split(tx_list, tx_list.shape[0] // TX_PER_LLM_RUN + 1)]
|
| 82 |
-
|
| 83 |
-
# Gather results and extract (valid) outputs
|
| 84 |
-
# The results variable is a list of 'results', each 'result' being the output of a single LLM run
|
| 85 |
-
results = await asyncio.gather(*tasks)
|
| 86 |
-
|
| 87 |
-
# Extract valid results (each valid result is a list of description-category pairs)
|
| 88 |
-
valid_results = [result['output'] for result in results if result['valid']]
|
| 89 |
-
|
| 90 |
-
# Flatten the list of valid results to obtain a single list of description-category pairs
|
| 91 |
-
valid_outputs = [
|
| 92 |
-
output for valid_result in valid_results for output in valid_result]
|
| 93 |
-
|
| 94 |
-
# Return a DataFrame with the valid outputs
|
| 95 |
-
return pd.DataFrame(valid_outputs, columns=['name/description', 'category'])
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6))
|
| 99 |
-
async def llm_sublist_categorizer(
|
| 100 |
-
file_name: str,
|
| 101 |
-
chain: LLMChain,
|
| 102 |
-
tx_descriptions: str,
|
| 103 |
-
) -> Dict[str, Union[bool, List[Tuple[str, str]]]]:
|
| 104 |
-
"""Categorize a batch of transactions using a language model.
|
| 105 |
-
|
| 106 |
-
This function takes a batch of transaction descriptions and passes them to a language model
|
| 107 |
-
for categorization. The function retries on failure, with an exponential backoff.
|
| 108 |
-
|
| 109 |
-
Args:
|
| 110 |
-
file_name (str): Name of the file the transaction descriptions were extracted from.
|
| 111 |
-
chain (LLMChain): Language model chain to use for categorization.
|
| 112 |
-
tx_descriptions (str): Concatenated transaction descriptions to categorize.
|
| 113 |
-
|
| 114 |
-
Returns:
|
| 115 |
-
dict: Dictionary containing a 'valid' flag and a list of categorized descriptions.
|
| 116 |
-
"""
|
| 117 |
-
|
| 118 |
-
raw_result = await chain.arun(input_data=tx_descriptions)
|
| 119 |
-
|
| 120 |
-
logger = logging.getLogger(__name__)
|
| 121 |
-
result = {'valid': True, 'output': []}
|
| 122 |
-
try:
|
| 123 |
-
# Create a pattern to match a list Description-Category pairs (List[Tuple[str, str]])
|
| 124 |
-
pattern = r"\['([^']+)', '([^']+)'\]"
|
| 125 |
-
|
| 126 |
-
# Use it to extract all the correctly formatted pairs from the raw result
|
| 127 |
-
matches = re.findall(pattern, raw_result.replace("\\'", "'"))
|
| 128 |
-
|
| 129 |
-
# Loop over the matches, and try to parse them to ensure the content is valid
|
| 130 |
-
valid_outputs = []
|
| 131 |
-
for match in matches:
|
| 132 |
-
try:
|
| 133 |
-
parsed_pair = ast.literal_eval(str(list(match)))
|
| 134 |
-
valid_outputs.append(parsed_pair)
|
| 135 |
-
except Exception as e:
|
| 136 |
-
logger.log(logging.ERROR,
|
| 137 |
-
f"Parsing Error: {e}\nMatch: {match}\n")
|
| 138 |
-
result['valid'] = False
|
| 139 |
-
|
| 140 |
-
result['output'] = valid_outputs
|
| 141 |
-
|
| 142 |
-
except Exception as e:
|
| 143 |
-
logging.log(
|
| 144 |
-
logging.ERROR, f"| File: {file_name} | Unexpected Error: {e}\nRaw Result: {raw_result}")
|
| 145 |
-
result['valid'] = False
|
| 146 |
-
|
| 147 |
-
return result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/categorization/categorizer_list.py
DELETED
|
@@ -1,70 +0,0 @@
|
|
| 1 |
-
# Standard library imports
|
| 2 |
-
import os
|
| 3 |
-
from datetime import datetime
|
| 4 |
-
|
| 5 |
-
# Third-party library imports
|
| 6 |
-
import pandas as pd
|
| 7 |
-
|
| 8 |
-
# Local application/library specific imports
|
| 9 |
-
from app.categorization.config import CATEGORY_REFERENCE_OUTPUT_FILE
|
| 10 |
-
from app.categorization.categorizer import llm_list_categorizer, fuzzy_match_list_categorizer
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
async def categorize_list(tx_list: pd.DataFrame) -> pd.DataFrame:
|
| 14 |
-
"""Asynchronously categorize a list of transactions.
|
| 15 |
-
|
| 16 |
-
This function categorizes a list of transactions using a combination of fuzzy matching
|
| 17 |
-
and a language model. It looks up new transaction descriptions in the reference file
|
| 18 |
-
(a combination of user input and previous executions) to minimize API calls.
|
| 19 |
-
Any uncategorized transactions are sent to the language model, and new description-category
|
| 20 |
-
pairs are added to the reference file.
|
| 21 |
-
|
| 22 |
-
Args:
|
| 23 |
-
tx_list (pd.DataFrame): The list of transactions to categorize.
|
| 24 |
-
|
| 25 |
-
Returns:
|
| 26 |
-
pd.DataFrame: The original DataFrame with an additional column for the category.
|
| 27 |
-
"""
|
| 28 |
-
|
| 29 |
-
if os.path.exists(CATEGORY_REFERENCE_OUTPUT_FILE):
|
| 30 |
-
# Read description-category pairs from the reference file
|
| 31 |
-
description_category_pairs = pd.read_csv(
|
| 32 |
-
CATEGORY_REFERENCE_OUTPUT_FILE, header=None, names=['name/description', 'category']
|
| 33 |
-
)
|
| 34 |
-
|
| 35 |
-
# Extract only descriptions for faster matching
|
| 36 |
-
descriptions = description_category_pairs['name/description'].values
|
| 37 |
-
|
| 38 |
-
# Use fuzzy matching to find similar descriptions and assign the category
|
| 39 |
-
tx_list['category'] = tx_list['name/description'].apply(
|
| 40 |
-
fuzzy_match_list_categorizer,
|
| 41 |
-
args=(descriptions, description_category_pairs),
|
| 42 |
-
)
|
| 43 |
-
|
| 44 |
-
# Filter out uncategorized transactions, deduplicate, and sort by description
|
| 45 |
-
uncategorized_descriptions = (
|
| 46 |
-
tx_list[tx_list['category'].isnull()]
|
| 47 |
-
.drop_duplicates(subset=['name/description'])
|
| 48 |
-
.sort_values(by=['name/description'])
|
| 49 |
-
)
|
| 50 |
-
|
| 51 |
-
# Ask the language model to categorize the remaining descriptions
|
| 52 |
-
if not uncategorized_descriptions.empty:
|
| 53 |
-
categorized_descriptions = await llm_list_categorizer(
|
| 54 |
-
uncategorized_descriptions[['name/description', 'category']]
|
| 55 |
-
)
|
| 56 |
-
|
| 57 |
-
categorized_descriptions.dropna(inplace=True)
|
| 58 |
-
|
| 59 |
-
# Update the category for uncategorized transactions based on the language model results
|
| 60 |
-
if not categorized_descriptions.empty:
|
| 61 |
-
tx_list['category'] = tx_list['category'].fillna(
|
| 62 |
-
tx_list['name/description'].map(
|
| 63 |
-
categorized_descriptions.set_index('name/description')['category']
|
| 64 |
-
)
|
| 65 |
-
)
|
| 66 |
-
|
| 67 |
-
# Fill remaining NaN values in 'category' with 'Other'
|
| 68 |
-
tx_list['category'] = tx_list['category'].fillna('Other')
|
| 69 |
-
|
| 70 |
-
return tx_list
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/categorization/config.py
DELETED
|
@@ -1,6 +0,0 @@
|
|
| 1 |
-
# Data Folders
|
| 2 |
-
RESULT_OUTPUT_FILE = 'data/tx_data/output/result_master_data.csv'
|
| 3 |
-
CATEGORY_REFERENCE_OUTPUT_FILE = 'data/ref_data/category_ref_master_data.csv'
|
| 4 |
-
|
| 5 |
-
# LLM CONFIG
|
| 6 |
-
TX_PER_LLM_RUN = 10
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/categorization/file_processing.py
DELETED
|
@@ -1,128 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
import glob
|
| 3 |
-
import shutil
|
| 4 |
-
import logging
|
| 5 |
-
import warnings
|
| 6 |
-
from typing import Optional, Union, Dict, List
|
| 7 |
-
from datetime import datetime
|
| 8 |
-
|
| 9 |
-
import pandas as pd
|
| 10 |
-
from dateparser import parse
|
| 11 |
-
|
| 12 |
-
from app.categorization.categorizer_list import categorize_list
|
| 13 |
-
from app.categorization.config import RESULT_OUTPUT_FILE, CATEGORY_REFERENCE_OUTPUT_FILE
|
| 14 |
-
from app.model.transaction import Transaction
|
| 15 |
-
from app.schema.index import TransactionCreate
|
| 16 |
-
|
| 17 |
-
from sqlalchemy.ext.asyncio import AsyncSession
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
# Read file and process it (e.g. categorize transactions)
|
| 21 |
-
async def process_file(file_path: str) -> Dict[str, Union[str, pd.DataFrame]]:
|
| 22 |
-
"""
|
| 23 |
-
Process the input file by reading, cleaning, standardizing, and categorizing the transactions.
|
| 24 |
-
|
| 25 |
-
Args:
|
| 26 |
-
file_path (str): Path to the input file.
|
| 27 |
-
|
| 28 |
-
Returns:
|
| 29 |
-
Dict[str, Union[str, pd.DataFrame]]: Dictionary containing the file name, processed output, and error information if any
|
| 30 |
-
"""
|
| 31 |
-
|
| 32 |
-
file_name = os.path.basename(file_path)
|
| 33 |
-
result = {"file_name": file_name, "output": pd.DataFrame(), "error": ""}
|
| 34 |
-
try:
|
| 35 |
-
# Read file into standardized tx format: source, date, type, category, description, amount
|
| 36 |
-
tx_list = standardize_csv_file(file_path)
|
| 37 |
-
|
| 38 |
-
# Categorize transactions
|
| 39 |
-
result["output"] = await categorize_list(tx_list)
|
| 40 |
-
print(f"File processed sucessfully: {file_name}")
|
| 41 |
-
|
| 42 |
-
except Exception as e:
|
| 43 |
-
# Return an error indicator and exception info
|
| 44 |
-
logging.log(logging.ERROR, f"| File: {file_name} | Unexpected Error: {e}")
|
| 45 |
-
print(f"ERROR processing file {file_name}: {e}")
|
| 46 |
-
result["error"] = str(e)
|
| 47 |
-
|
| 48 |
-
return result
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
def standardize_csv_file(file_path: str) -> pd.DataFrame:
|
| 52 |
-
"""
|
| 53 |
-
Read and prepare the data from the input file.
|
| 54 |
-
|
| 55 |
-
Args:
|
| 56 |
-
file_path (str): Path to the input file.
|
| 57 |
-
|
| 58 |
-
Returns:
|
| 59 |
-
pd.DataFrame: Prepared transaction data.
|
| 60 |
-
"""
|
| 61 |
-
|
| 62 |
-
tx_list = pd.read_csv(file_path, index_col=False)
|
| 63 |
-
tx_list.attrs["file_name"] = file_path
|
| 64 |
-
tx_list.columns = tx_list.columns.str.lower().str.strip()
|
| 65 |
-
|
| 66 |
-
# Standardize dates to YYYY/MM/DD format
|
| 67 |
-
tx_list["date"] = pd.to_datetime(tx_list["date"]).dt.strftime("%Y/%m/%d")
|
| 68 |
-
|
| 69 |
-
# Add source and reindex to desired tx format; category column is new and therefore empty
|
| 70 |
-
tx_list.loc[:, "source"] = os.path.basename(file_path)
|
| 71 |
-
tx_list = tx_list.reindex(columns=["date", "expense/income", "category", "name/description", "amount"])
|
| 72 |
-
|
| 73 |
-
return tx_list
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
async def save_results(db: AsyncSession, results: List) -> None:
|
| 77 |
-
"""
|
| 78 |
-
Merge all interim results in the input folder and write the merged results to the output file.
|
| 79 |
-
|
| 80 |
-
Args:
|
| 81 |
-
in_folder (str): Path to the input folder containing interim results.
|
| 82 |
-
out_file (str): Path to the output file.
|
| 83 |
-
|
| 84 |
-
Returns:
|
| 85 |
-
None
|
| 86 |
-
"""
|
| 87 |
-
|
| 88 |
-
# Concatenate all (valid) results into a single DataFrame
|
| 89 |
-
# Print errors to console
|
| 90 |
-
ok_files = []
|
| 91 |
-
ko_files = []
|
| 92 |
-
error_messages = []
|
| 93 |
-
|
| 94 |
-
col_list = ["transaction_date", "type", "category", "name_description", "amount"]
|
| 95 |
-
tx_list = pd.DataFrame(columns=col_list)
|
| 96 |
-
for result in results:
|
| 97 |
-
if not result["error"]:
|
| 98 |
-
ok_files.append(result["file_name"])
|
| 99 |
-
result_df = result["output"]
|
| 100 |
-
result_df.columns = col_list
|
| 101 |
-
tx_list = pd.concat([tx_list, result_df], ignore_index=True)
|
| 102 |
-
else:
|
| 103 |
-
ko_files.append(result["file_name"])
|
| 104 |
-
error_messages.append(f"{result['file_name']}: {result['error']}")
|
| 105 |
-
|
| 106 |
-
# Save to database
|
| 107 |
-
# FIXME: get user_id from session
|
| 108 |
-
txn_list_to_save = [TransactionCreate(**row.to_dict(), user_id=1) for _, row in tx_list.iterrows()]
|
| 109 |
-
await Transaction.bulk_create(db, txn_list_to_save)
|
| 110 |
-
|
| 111 |
-
new_ref_data = tx_list[["name/description", "category"]]
|
| 112 |
-
if os.path.exists(CATEGORY_REFERENCE_OUTPUT_FILE):
|
| 113 |
-
# If it exists, add master file to interim results
|
| 114 |
-
old_ref_data = pd.read_csv(CATEGORY_REFERENCE_OUTPUT_FILE, names=["name/description", "category"], header=0)
|
| 115 |
-
new_ref_data = pd.concat([old_ref_data, new_ref_data], ignore_index=True)
|
| 116 |
-
|
| 117 |
-
# Drop duplicates, sort, and write to create new Master File
|
| 118 |
-
new_ref_data.drop_duplicates(subset=["name/description"]).sort_values(by=["name/description"]).to_csv(
|
| 119 |
-
CATEGORY_REFERENCE_OUTPUT_FILE, mode="w", index=False, header=True
|
| 120 |
-
)
|
| 121 |
-
|
| 122 |
-
# Summarize results
|
| 123 |
-
print(f"\nProcessed {len(results)} files: {len(ok_files)} successful, {len(ko_files)} with errors\n")
|
| 124 |
-
if len(ko_files):
|
| 125 |
-
print(f"Errors in the following files:")
|
| 126 |
-
for message in error_messages:
|
| 127 |
-
print(f" {message}")
|
| 128 |
-
print("\n")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/categorization/template.py
DELETED
|
@@ -1,56 +0,0 @@
|
|
| 1 |
-
CATEGORY_TEMPLATE = """
|
| 2 |
-
<context>
|
| 3 |
-
Your task is to create a list of [description, category] lists, where in each list item:
|
| 4 |
-
- the description is exactly the same you receive as input
|
| 5 |
-
- the category of the transaction (choose one from the list below based on the description)
|
| 6 |
-
</context>
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
<categories>
|
| 10 |
-
The following list contains the categories and associated keywords you often see in transaction descriptions:
|
| 11 |
-
-ATM: atm, cash, withdraw
|
| 12 |
-
-Auto: auto body
|
| 13 |
-
-Bars: bar, pubs, irish, brewery
|
| 14 |
-
-Beauty: body
|
| 15 |
-
-Cashback: cashback, reward, bonus, cash back
|
| 16 |
-
-Clothing: clothing, shoes, accessories
|
| 17 |
-
-Coffee Shops: coffee, cafe, tea, Starbucks, Dunkin
|
| 18 |
-
-Credit Card Payment: card payment, autopay
|
| 19 |
-
-Education: kindle, tuition
|
| 20 |
-
-Entertainment: event, show, movies, cinema, theater
|
| 21 |
-
-Fees: Fee
|
| 22 |
-
-Food: snack, Donalds, Burger King, KFC, Subway, Pizza, Domino, Taco Bell, Wendy, Chick-fil-A, Popeyes, Arby's, Chipotle
|
| 23 |
-
-Fuel: fuel, gas, petrol
|
| 24 |
-
-Gifts: donation, gift
|
| 25 |
-
-Groceries: groceries, supermarket, food, familia
|
| 26 |
-
-Gym: gym, fitness, yoga, pilates, crossfit
|
| 27 |
-
-Home: Ikea
|
| 28 |
-
-Housing: rent, mortgage
|
| 29 |
-
-Income: refund, deposit, paycheck
|
| 30 |
-
-Insurance: insurance
|
| 31 |
-
-Medical: medical, doctor, dentist, hospital, clinic
|
| 32 |
-
-Pets: vet, veterinary, pet, dog, cat
|
| 33 |
-
-Pharmacy: pharmacy, drugstore, cvs, walgreens, rite aid, duane
|
| 34 |
-
-Restaurants: restaurant, lunch, dinner
|
| 35 |
-
-Services: service, laundry, dry cleaning
|
| 36 |
-
-Shopping: shopping, amazon, walmart, target, safeway
|
| 37 |
-
-Streaming: Netflix, Spotify, Hulu, HBO
|
| 38 |
-
-Taxes: tax, irs
|
| 39 |
-
-Technology: technology, software, hardware, electronics
|
| 40 |
-
-Transportation: bus, train, subway, metro, airline, uber, lyft, taxi
|
| 41 |
-
-Travel: travel, holiday, trip, airbnb, kiwi, hotel, hostel, resort, kiwi, kayak, expedia, booking.com
|
| 42 |
-
-Transfer: payment from
|
| 43 |
-
-Utilities: electricity, water, gas, ting, verizon, comcast, sprint, t-mobile, at&t, mint
|
| 44 |
-
-Other: use this when very uncertain about the category
|
| 45 |
-
</categories>
|
| 46 |
-
|
| 47 |
-
<formatting_instructions>
|
| 48 |
-
Your output should be a valid list of lists (e.g. [[description1, category1], [description2, category2], ...]] parse-able by the command ast.literal_eval(output)
|
| 49 |
-
Don't include any kind of commentary, return carriages, spaces or other characters in the output
|
| 50 |
-
Fill all categories; if you don't know which one to choose, choose 'Other'
|
| 51 |
-
</formatting_instructions>
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
<financial_transactions>
|
| 55 |
-
{input_data}
|
| 56 |
-
</financial_transactions>"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/engine/postgresdb.py
DELETED
|
@@ -1,81 +0,0 @@
|
|
| 1 |
-
import logging
|
| 2 |
-
import contextlib
|
| 3 |
-
from typing import Any, AsyncIterator
|
| 4 |
-
|
| 5 |
-
from sqlalchemy.ext.asyncio import (
|
| 6 |
-
AsyncConnection,
|
| 7 |
-
AsyncSession,
|
| 8 |
-
AsyncEngine,
|
| 9 |
-
async_sessionmaker,
|
| 10 |
-
create_async_engine,
|
| 11 |
-
)
|
| 12 |
-
from sqlalchemy.orm import declarative_base
|
| 13 |
-
|
| 14 |
-
from config.index import config as env
|
| 15 |
-
|
| 16 |
-
logger = logging.getLogger(__name__)
|
| 17 |
-
|
| 18 |
-
Base = declarative_base()
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
class PostgresDatabase:
|
| 22 |
-
|
| 23 |
-
def __init__(self):
|
| 24 |
-
self._engine: AsyncEngine | None = None
|
| 25 |
-
self._sessionmaker: async_sessionmaker | None = None
|
| 26 |
-
|
| 27 |
-
def init(self, host: str, engine_kwargs: dict[str, Any] = {}):
|
| 28 |
-
self._engine = create_async_engine(host, **engine_kwargs)
|
| 29 |
-
self._sessionmaker = async_sessionmaker(autocommit=False, bind=self._engine)
|
| 30 |
-
|
| 31 |
-
async def close(self):
|
| 32 |
-
if self._engine is None:
|
| 33 |
-
raise Exception("DatabaseSessionManager is not initialized")
|
| 34 |
-
await self._engine.dispose()
|
| 35 |
-
|
| 36 |
-
self._engine = None
|
| 37 |
-
self._sessionmaker = None
|
| 38 |
-
|
| 39 |
-
@contextlib.asynccontextmanager
|
| 40 |
-
async def connect(self) -> AsyncIterator[AsyncConnection]:
|
| 41 |
-
if self._engine is None:
|
| 42 |
-
raise Exception("DatabaseSessionManager is not initialized")
|
| 43 |
-
|
| 44 |
-
async with self._engine.begin() as connection:
|
| 45 |
-
try:
|
| 46 |
-
yield connection
|
| 47 |
-
except Exception:
|
| 48 |
-
await connection.rollback()
|
| 49 |
-
raise
|
| 50 |
-
|
| 51 |
-
@contextlib.asynccontextmanager
|
| 52 |
-
async def session(self) -> AsyncIterator[AsyncSession]:
|
| 53 |
-
if self._sessionmaker is None:
|
| 54 |
-
raise Exception("DatabaseSessionManager is not initialized")
|
| 55 |
-
|
| 56 |
-
session = self._sessionmaker()
|
| 57 |
-
try:
|
| 58 |
-
yield session
|
| 59 |
-
except Exception:
|
| 60 |
-
await session.rollback()
|
| 61 |
-
raise
|
| 62 |
-
finally:
|
| 63 |
-
await session.close()
|
| 64 |
-
|
| 65 |
-
def get_engine(self):
|
| 66 |
-
return self._engine
|
| 67 |
-
|
| 68 |
-
# Used for testing
|
| 69 |
-
async def create_all(self, connection: AsyncConnection):
|
| 70 |
-
await connection.run_sync(Base.metadata.create_all)
|
| 71 |
-
|
| 72 |
-
async def drop_all(self, connection: AsyncConnection):
|
| 73 |
-
await connection.run_sync(Base.metadata.drop_all)
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
postgresdb = PostgresDatabase()
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
async def get_db_session():
|
| 80 |
-
async with postgresdb.session() as session:
|
| 81 |
-
yield session
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/model/base.py
DELETED
|
@@ -1,15 +0,0 @@
|
|
| 1 |
-
from datetime import datetime, timezone
|
| 2 |
-
from sqlalchemy import DateTime
|
| 3 |
-
from sqlalchemy.orm import Mapped, mapped_column
|
| 4 |
-
|
| 5 |
-
from app.engine.postgresdb import Base
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
class BaseModel:
|
| 9 |
-
id: Mapped[int] = mapped_column(primary_key=True, index=True, autoincrement=True)
|
| 10 |
-
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=datetime.now(timezone.utc))
|
| 11 |
-
updated_at: Mapped[datetime] = mapped_column(
|
| 12 |
-
DateTime(timezone=True),
|
| 13 |
-
default=datetime.now(timezone.utc),
|
| 14 |
-
onupdate=datetime.now(timezone.utc),
|
| 15 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/model/income_statement.py
DELETED
|
@@ -1,46 +0,0 @@
|
|
| 1 |
-
from datetime import datetime
|
| 2 |
-
from typing import Any, Dict, List
|
| 3 |
-
from sqlalchemy import ForeignKey
|
| 4 |
-
from sqlalchemy.sql import expression as sql
|
| 5 |
-
from sqlalchemy.orm import relationship, Mapped, mapped_column
|
| 6 |
-
from sqlalchemy.ext.asyncio import AsyncSession
|
| 7 |
-
from sqlalchemy.dialects.postgresql import JSON
|
| 8 |
-
|
| 9 |
-
from app.model.base import BaseModel
|
| 10 |
-
from app.engine.postgresdb import Base
|
| 11 |
-
from app.schema.index import IncomeStatement as IncomeStatementSchema
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
class IncomeStatement(Base, BaseModel):
|
| 15 |
-
__tablename__ = "income_statement"
|
| 16 |
-
|
| 17 |
-
date_from: Mapped[datetime]
|
| 18 |
-
date_to: Mapped[datetime]
|
| 19 |
-
income: Mapped[Dict] = mapped_column(JSON, nullable=True)
|
| 20 |
-
expenses: Mapped[Dict] = mapped_column(JSON, nullable=True)
|
| 21 |
-
|
| 22 |
-
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
|
| 23 |
-
user = relationship("User", back_populates="income_statements")
|
| 24 |
-
|
| 25 |
-
def __str__(self) -> str:
|
| 26 |
-
return f"IncomeStatement(id={self.id}, user_id={self.user_id}, date_from={self.date_from}, date_to={self.date_to}, income={self.income}, expenses={self.expenses})"
|
| 27 |
-
|
| 28 |
-
@classmethod
|
| 29 |
-
async def create(cls: "type[IncomeStatement]", db: AsyncSession, **kwargs: IncomeStatementSchema) -> "IncomeStatement":
|
| 30 |
-
income_statement = cls(**kwargs)
|
| 31 |
-
db.add(income_statement)
|
| 32 |
-
await db.commit()
|
| 33 |
-
await db.refresh(income_statement)
|
| 34 |
-
return income_statement
|
| 35 |
-
|
| 36 |
-
@classmethod
|
| 37 |
-
async def get_by_user(cls: "type[IncomeStatement]", db: AsyncSession, user_id: int) -> "List[IncomeStatement]":
|
| 38 |
-
query = sql.select(cls).where(cls.user_id == user_id)
|
| 39 |
-
income_statements = await db.scalars(query)
|
| 40 |
-
return income_statements.all()
|
| 41 |
-
|
| 42 |
-
@classmethod
|
| 43 |
-
async def get(cls: "type[IncomeStatement]", db: AsyncSession, id: int) -> "IncomeStatement":
|
| 44 |
-
query = sql.select(cls).where(cls.id == id)
|
| 45 |
-
income_statement = await db.scalar(query)
|
| 46 |
-
return income_statement
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/model/transaction.py
DELETED
|
@@ -1,62 +0,0 @@
|
|
| 1 |
-
from datetime import datetime
|
| 2 |
-
from typing import List
|
| 3 |
-
from sqlalchemy import ForeignKey
|
| 4 |
-
from sqlalchemy.orm import relationship, Mapped, mapped_column
|
| 5 |
-
from sqlalchemy.sql import expression as sql
|
| 6 |
-
from sqlalchemy.ext.asyncio import AsyncSession
|
| 7 |
-
|
| 8 |
-
from app.model.base import BaseModel
|
| 9 |
-
from app.engine.postgresdb import Base
|
| 10 |
-
from app.schema.index import TransactionCreate
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
class Transaction(Base, BaseModel):
|
| 14 |
-
__tablename__ = "transactions"
|
| 15 |
-
transaction_date: Mapped[datetime]
|
| 16 |
-
category: Mapped[str]
|
| 17 |
-
name_description: Mapped[str]
|
| 18 |
-
amount: Mapped[float]
|
| 19 |
-
type: Mapped[str]
|
| 20 |
-
|
| 21 |
-
user_id = mapped_column(ForeignKey("users.id"))
|
| 22 |
-
user = relationship("User", back_populates="transactions")
|
| 23 |
-
|
| 24 |
-
def __str__(self) -> str:
|
| 25 |
-
return f"{self.transaction_date}, {self.category}, {self.name_description}, {self.amount}, {self.type}"
|
| 26 |
-
|
| 27 |
-
@classmethod
|
| 28 |
-
async def create(cls: "type[Transaction]", db: AsyncSession, **kwargs) -> "Transaction":
|
| 29 |
-
query = sql.insert(cls).values(**kwargs)
|
| 30 |
-
transactions = await db.execute(query)
|
| 31 |
-
transaction = transactions.first()
|
| 32 |
-
await db.commit()
|
| 33 |
-
return transaction
|
| 34 |
-
|
| 35 |
-
@classmethod
|
| 36 |
-
async def bulk_create(cls: "type[Transaction]", db: AsyncSession, transactions: List[TransactionCreate]) -> None:
|
| 37 |
-
values = [transaction.model_dump() for transaction in transactions]
|
| 38 |
-
query = sql.insert(cls).values(values)
|
| 39 |
-
await db.execute(query)
|
| 40 |
-
await db.commit()
|
| 41 |
-
|
| 42 |
-
@classmethod
|
| 43 |
-
async def update(cls: "type[Transaction]", db: AsyncSession, id: int, **kwargs) -> "Transaction":
|
| 44 |
-
query = sql.update(cls).where(cls.id == id).values(**kwargs).execution_options(synchronize_session="fetch")
|
| 45 |
-
transactions = await db.scalars(query)
|
| 46 |
-
transaction = transactions.first()
|
| 47 |
-
await db.commit()
|
| 48 |
-
return transaction
|
| 49 |
-
|
| 50 |
-
@classmethod
|
| 51 |
-
async def get_by_user(cls: "type[Transaction]", db: AsyncSession, user_id: int) -> "List[Transaction]":
|
| 52 |
-
query = sql.select(cls).where(cls.user_id == user_id)
|
| 53 |
-
transactions = await db.scalars(query)
|
| 54 |
-
return transactions
|
| 55 |
-
|
| 56 |
-
@classmethod
|
| 57 |
-
async def get_by_user_between_dates(
|
| 58 |
-
cls: "type[Transaction]", db: AsyncSession, user_id: int, start_date: datetime, end_date: datetime
|
| 59 |
-
) -> "List[Transaction]":
|
| 60 |
-
query = sql.select(cls).where(cls.user_id == user_id).where(cls.transaction_date.between(start_date, end_date))
|
| 61 |
-
transactions = await db.scalars(query)
|
| 62 |
-
return transactions.all()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/model/user.py
DELETED
|
@@ -1,110 +0,0 @@
|
|
| 1 |
-
import logging
|
| 2 |
-
from sqlalchemy.orm import relationship, Mapped, mapped_column
|
| 3 |
-
from sqlalchemy.sql import expression as sql
|
| 4 |
-
from sqlalchemy.ext.asyncio import AsyncSession
|
| 5 |
-
|
| 6 |
-
from app.model.base import BaseModel
|
| 7 |
-
from app.engine.postgresdb import Base
|
| 8 |
-
from app.schema.index import UserCreate, UserUpdate
|
| 9 |
-
|
| 10 |
-
logger = logging.getLogger(__name__)
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
class User(Base, BaseModel):
|
| 14 |
-
__tablename__ = "users"
|
| 15 |
-
|
| 16 |
-
name: Mapped[str]
|
| 17 |
-
email: Mapped[str]
|
| 18 |
-
hashed_password: Mapped[str]
|
| 19 |
-
is_deleted: Mapped[bool] = mapped_column(default=False)
|
| 20 |
-
|
| 21 |
-
transactions = relationship("Transaction", back_populates="user")
|
| 22 |
-
income_statements = relationship("IncomeStatement", back_populates="user")
|
| 23 |
-
|
| 24 |
-
@classmethod
|
| 25 |
-
async def create(cls: "type[User]", db: AsyncSession, **kwargs: UserCreate) -> "User":
|
| 26 |
-
"""
|
| 27 |
-
Creates a new user in the database.
|
| 28 |
-
|
| 29 |
-
Args:
|
| 30 |
-
cls (type[User]): The class object representing the User model.
|
| 31 |
-
db (AsyncSession): The asynchronous session object for interacting with the database.
|
| 32 |
-
**kwargs (UserCreate): The keyword arguments representing the user's attributes.
|
| 33 |
-
|
| 34 |
-
Returns:
|
| 35 |
-
User
|
| 36 |
-
"""
|
| 37 |
-
|
| 38 |
-
print(f"Creating user: {kwargs}")
|
| 39 |
-
# query = sql.insert(cls).values(**kwargs)
|
| 40 |
-
user = cls(name=kwargs["name"], email=kwargs["email"], hashed_password=kwargs["hashed_password"])
|
| 41 |
-
db.add(user)
|
| 42 |
-
await db.commit()
|
| 43 |
-
await db.refresh(user)
|
| 44 |
-
return user
|
| 45 |
-
|
| 46 |
-
@classmethod
|
| 47 |
-
async def update(cls: "type[User]", db: AsyncSession, id: int, **kwargs: UserUpdate) -> "User":
|
| 48 |
-
"""
|
| 49 |
-
Updates a user in the database with the given ID and keyword arguments.
|
| 50 |
-
|
| 51 |
-
Args:
|
| 52 |
-
cls (type[User]): The class object representing the User model.
|
| 53 |
-
db (AsyncSession): The asynchronous session object for interacting with the database.
|
| 54 |
-
id (int): The ID of the user to update.
|
| 55 |
-
**kwargs (UserUpdate): The keyword arguments representing the user's attributes to update.
|
| 56 |
-
|
| 57 |
-
Returns:
|
| 58 |
-
User
|
| 59 |
-
"""
|
| 60 |
-
|
| 61 |
-
query = sql.update(cls).where(cls.id == id).values(**kwargs).execution_options(synchronize_session="fetch")
|
| 62 |
-
results = await db.execute(query)
|
| 63 |
-
user = results.fetchone()
|
| 64 |
-
await db.commit()
|
| 65 |
-
return user
|
| 66 |
-
|
| 67 |
-
@classmethod
|
| 68 |
-
async def get(cls: "type[User]", db: AsyncSession, email: str) -> "User":
|
| 69 |
-
"""
|
| 70 |
-
Retrieves a user from the database based on their email.
|
| 71 |
-
|
| 72 |
-
Args:
|
| 73 |
-
cls (type[User]): The class object representing the User model.
|
| 74 |
-
db (AsyncSession): The asynchronous session object for interacting with the database.
|
| 75 |
-
email (str): The email of the user to retrieve.
|
| 76 |
-
|
| 77 |
-
Returns:
|
| 78 |
-
User
|
| 79 |
-
"""
|
| 80 |
-
|
| 81 |
-
logging.info(f"Getting user: {email}")
|
| 82 |
-
query = sql.select(cls).where(cls.email == email)
|
| 83 |
-
logging.info(f"Query: {query}")
|
| 84 |
-
users = await db.scalars(query)
|
| 85 |
-
logging.info(f"Users: {users}")
|
| 86 |
-
return users.first()
|
| 87 |
-
|
| 88 |
-
@classmethod
|
| 89 |
-
async def delete(cls: "type[User]", db: AsyncSession, email: str) -> "User":
|
| 90 |
-
"""
|
| 91 |
-
Deletes a user from the database based on their email.
|
| 92 |
-
|
| 93 |
-
Args:
|
| 94 |
-
cls (type[User]): The class object representing the User model.
|
| 95 |
-
db (AsyncSession): The asynchronous session object for interacting with the database.
|
| 96 |
-
email (str): The email of the user to delete.
|
| 97 |
-
|
| 98 |
-
Returns:
|
| 99 |
-
User
|
| 100 |
-
"""
|
| 101 |
-
query = (
|
| 102 |
-
sql.update(cls)
|
| 103 |
-
.where(cls.email == email)
|
| 104 |
-
.values(is_deleted=True)
|
| 105 |
-
.execution_options(synchronize_session="fetch")
|
| 106 |
-
)
|
| 107 |
-
result = await db.execute(query)
|
| 108 |
-
user = result.fetchone()
|
| 109 |
-
await db.commit()
|
| 110 |
-
return user
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/schema/base.py
DELETED
|
@@ -1,14 +0,0 @@
|
|
| 1 |
-
from datetime import datetime
|
| 2 |
-
from pydantic import BaseModel, ConfigDict
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
class PydanticBaseModel(BaseModel):
|
| 6 |
-
pass
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
class BaseModel(PydanticBaseModel):
|
| 10 |
-
model_config = ConfigDict(from_attributes=True)
|
| 11 |
-
|
| 12 |
-
id: int
|
| 13 |
-
created_at: datetime
|
| 14 |
-
updated_at: datetime
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/schema/index.py
DELETED
|
@@ -1,85 +0,0 @@
|
|
| 1 |
-
from enum import Enum
|
| 2 |
-
from datetime import datetime
|
| 3 |
-
from typing import Dict, List
|
| 4 |
-
from typing_extensions import TypedDict
|
| 5 |
-
|
| 6 |
-
from app.schema.base import BaseModel, PydanticBaseModel
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
class TransactionType(str, Enum):
|
| 10 |
-
INCOME = "income"
|
| 11 |
-
EXPENSE = "expense"
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
class UserCreate(PydanticBaseModel):
|
| 15 |
-
name: str
|
| 16 |
-
email: str
|
| 17 |
-
hashed_password: str
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
class UserUpdate(PydanticBaseModel):
|
| 21 |
-
name: str
|
| 22 |
-
email: str
|
| 23 |
-
hashed_password: str
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
class UserResponse(PydanticBaseModel):
|
| 27 |
-
id: int
|
| 28 |
-
name: str
|
| 29 |
-
email: str
|
| 30 |
-
is_deleted: bool
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
class User(BaseModel):
|
| 34 |
-
name: str
|
| 35 |
-
email: str
|
| 36 |
-
hashed_password: str
|
| 37 |
-
is_deleted: bool = False
|
| 38 |
-
transactions: "List[Transaction]" = []
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
class TransactionResponse(PydanticBaseModel):
|
| 42 |
-
transaction_date: datetime
|
| 43 |
-
category: str
|
| 44 |
-
name_description: str
|
| 45 |
-
amount: float
|
| 46 |
-
type: TransactionType
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
class TransactionCreate(TransactionResponse):
|
| 50 |
-
user_id: int
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
class Transaction(TransactionResponse):
|
| 54 |
-
user: User
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
class FileUploadCreate(PydanticBaseModel):
|
| 58 |
-
source: str
|
| 59 |
-
date: datetime
|
| 60 |
-
category: str
|
| 61 |
-
name_description: str
|
| 62 |
-
amount: float
|
| 63 |
-
type: str
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
class IncomeStatementCreateRequest(PydanticBaseModel):
|
| 67 |
-
user_id: int
|
| 68 |
-
date_from: datetime
|
| 69 |
-
date_to: datetime
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
class IncomeStatementDetail(TypedDict):
|
| 73 |
-
total: float
|
| 74 |
-
category_totals: List[Dict[str, str | float]]
|
| 75 |
-
|
| 76 |
-
class IncomeStatementLLMResponse(PydanticBaseModel):
|
| 77 |
-
income: IncomeStatementDetail
|
| 78 |
-
expenses: IncomeStatementDetail
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
class IncomeStatement(IncomeStatementCreateRequest, IncomeStatementLLMResponse):
|
| 82 |
-
pass
|
| 83 |
-
|
| 84 |
-
class IncomeStatementResponse(IncomeStatement):
|
| 85 |
-
id: int
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/service/income_statement.py
DELETED
|
@@ -1,37 +0,0 @@
|
|
| 1 |
-
import logging
|
| 2 |
-
from app.schema.index import IncomeStatement, IncomeStatementCreateRequest
|
| 3 |
-
from app.model.transaction import Transaction as TransactionModel
|
| 4 |
-
from app.model.income_statement import IncomeStatement as IncomeStatementModel
|
| 5 |
-
from sqlalchemy.ext.asyncio import AsyncSession
|
| 6 |
-
|
| 7 |
-
from app.service.llm import call_llm
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
async def call_llm_to_create_income_statement(payload: IncomeStatementCreateRequest, db: AsyncSession) -> None:
|
| 11 |
-
transactions = await TransactionModel.get_by_user_between_dates(
|
| 12 |
-
db, payload.user_id, payload.date_from, payload.date_to
|
| 13 |
-
)
|
| 14 |
-
|
| 15 |
-
if not transactions:
|
| 16 |
-
print("No transactions found")
|
| 17 |
-
return
|
| 18 |
-
|
| 19 |
-
response = await call_llm(transactions)
|
| 20 |
-
|
| 21 |
-
income = response.dict()['income']
|
| 22 |
-
expenses = response.dict()['expenses']
|
| 23 |
-
|
| 24 |
-
try:
|
| 25 |
-
income_statement_create_payload = IncomeStatement(
|
| 26 |
-
user_id=payload.user_id,
|
| 27 |
-
date_from=payload.date_from,
|
| 28 |
-
date_to=payload.date_to,
|
| 29 |
-
income=income,
|
| 30 |
-
expenses=expenses,
|
| 31 |
-
)
|
| 32 |
-
|
| 33 |
-
income_statement = await IncomeStatementModel.create(db, **income_statement_create_payload.model_dump())
|
| 34 |
-
print(f"Income statement created: {income_statement}")
|
| 35 |
-
except Exception as e:
|
| 36 |
-
print(e)
|
| 37 |
-
raise e
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/service/llm.py
DELETED
|
@@ -1,55 +0,0 @@
|
|
| 1 |
-
from typing import List
|
| 2 |
-
from langchain_core.prompts import ChatPromptTemplate
|
| 3 |
-
from langchain_core.runnables.base import RunnableSequence
|
| 4 |
-
from langchain_openai import OpenAI
|
| 5 |
-
from langchain.globals import set_llm_cache
|
| 6 |
-
|
| 7 |
-
from app.model.transaction import Transaction
|
| 8 |
-
from app.schema.index import IncomeStatementLLMResponse
|
| 9 |
-
from config.index import config as env
|
| 10 |
-
|
| 11 |
-
from langchain_core.output_parsers import PydanticOutputParser
|
| 12 |
-
|
| 13 |
-
set_llm_cache(None)
|
| 14 |
-
|
| 15 |
-
def income_statement_prompt () -> ChatPromptTemplate:
|
| 16 |
-
context_str = """
|
| 17 |
-
You are an accountant skilled at organizing transactions from multiple different bank
|
| 18 |
-
accounts and credit card statements to prepare an income statement.
|
| 19 |
-
|
| 20 |
-
Input data is in the below csv format:
|
| 21 |
-
transaction_date, category, name_description, amount, type\n
|
| 22 |
-
{input_data_csv}
|
| 23 |
-
|
| 24 |
-
Your task is to prepare an income statement. The output should be in the following format: {format_instructions}
|
| 25 |
-
|
| 26 |
-
"""
|
| 27 |
-
prompt = ChatPromptTemplate.from_template(context_str)
|
| 28 |
-
return prompt
|
| 29 |
-
|
| 30 |
-
async def call_llm(inputData: List[Transaction]) -> str:
|
| 31 |
-
input_data_csv = '\n'.join(str(x) for x in inputData)
|
| 32 |
-
|
| 33 |
-
output_parser = PydanticOutputParser(pydantic_object=IncomeStatementLLMResponse)
|
| 34 |
-
|
| 35 |
-
prompt = income_statement_prompt().partial(format_instructions=output_parser.get_format_instructions())
|
| 36 |
-
|
| 37 |
-
llm = OpenAI(name='Income Statement Generation Bot',
|
| 38 |
-
api_key=env.OPENAI_API_KEY,
|
| 39 |
-
# cache=True,
|
| 40 |
-
temperature=0.7,
|
| 41 |
-
verbose=True)
|
| 42 |
-
|
| 43 |
-
try:
|
| 44 |
-
runnable_chain = RunnableSequence(prompt, llm, output_parser)
|
| 45 |
-
except Exception as e:
|
| 46 |
-
print(f"runnable_chain error: {str(e)}")
|
| 47 |
-
raise e
|
| 48 |
-
|
| 49 |
-
try:
|
| 50 |
-
output_chunks = runnable_chain.invoke({"input_data_csv": input_data_csv})
|
| 51 |
-
return output_chunks
|
| 52 |
-
|
| 53 |
-
except Exception as e:
|
| 54 |
-
print(f"runnable_chain.invoke error: {str(e)}")
|
| 55 |
-
raise e
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/transactions_rag/__init__.py
DELETED
|
File without changes
|
app/transactions_rag/categorize_transactions.ipynb
DELETED
|
@@ -1,571 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"cells": [
|
| 3 |
-
{
|
| 4 |
-
"cell_type": "code",
|
| 5 |
-
"execution_count": 2,
|
| 6 |
-
"metadata": {},
|
| 7 |
-
"outputs": [
|
| 8 |
-
{
|
| 9 |
-
"name": "stdout",
|
| 10 |
-
"output_type": "stream",
|
| 11 |
-
"text": [
|
| 12 |
-
"Defaulting to user installation because normal site-packages is not writeable\n",
|
| 13 |
-
"Requirement already satisfied: pandas in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (2.0.3)\n",
|
| 14 |
-
"Requirement already satisfied: python-dateutil>=2.8.2 in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from pandas) (2.9.0.post0)\n",
|
| 15 |
-
"Requirement already satisfied: pytz>=2020.1 in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from pandas) (2024.1)\n",
|
| 16 |
-
"Requirement already satisfied: tzdata>=2022.1 in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from pandas) (2024.1)\n",
|
| 17 |
-
"Requirement already satisfied: numpy>=1.20.3 in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from pandas) (1.24.4)\n",
|
| 18 |
-
"Requirement already satisfied: six>=1.5 in /Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/site-packages (from python-dateutil>=2.8.2->pandas) (1.15.0)\n"
|
| 19 |
-
]
|
| 20 |
-
},
|
| 21 |
-
{
|
| 22 |
-
"data": {
|
| 23 |
-
"text/html": [
|
| 24 |
-
"<div>\n",
|
| 25 |
-
"<style scoped>\n",
|
| 26 |
-
" .dataframe tbody tr th:only-of-type {\n",
|
| 27 |
-
" vertical-align: middle;\n",
|
| 28 |
-
" }\n",
|
| 29 |
-
"\n",
|
| 30 |
-
" .dataframe tbody tr th {\n",
|
| 31 |
-
" vertical-align: top;\n",
|
| 32 |
-
" }\n",
|
| 33 |
-
"\n",
|
| 34 |
-
" .dataframe thead th {\n",
|
| 35 |
-
" text-align: right;\n",
|
| 36 |
-
" }\n",
|
| 37 |
-
"</style>\n",
|
| 38 |
-
"<table border=\"1\" class=\"dataframe\">\n",
|
| 39 |
-
" <thead>\n",
|
| 40 |
-
" <tr style=\"text-align: right;\">\n",
|
| 41 |
-
" <th></th>\n",
|
| 42 |
-
" <th>Date</th>\n",
|
| 43 |
-
" <th>Name / Description</th>\n",
|
| 44 |
-
" <th>Expense/Income</th>\n",
|
| 45 |
-
" <th>Amount</th>\n",
|
| 46 |
-
" </tr>\n",
|
| 47 |
-
" </thead>\n",
|
| 48 |
-
" <tbody>\n",
|
| 49 |
-
" <tr>\n",
|
| 50 |
-
" <th>0</th>\n",
|
| 51 |
-
" <td>2023-12-30</td>\n",
|
| 52 |
-
" <td>Comcast Internet</td>\n",
|
| 53 |
-
" <td>Expense</td>\n",
|
| 54 |
-
" <td>9.96</td>\n",
|
| 55 |
-
" </tr>\n",
|
| 56 |
-
" <tr>\n",
|
| 57 |
-
" <th>1</th>\n",
|
| 58 |
-
" <td>2023-12-30</td>\n",
|
| 59 |
-
" <td>Lemonade Home Insurance</td>\n",
|
| 60 |
-
" <td>Expense</td>\n",
|
| 61 |
-
" <td>17.53</td>\n",
|
| 62 |
-
" </tr>\n",
|
| 63 |
-
" <tr>\n",
|
| 64 |
-
" <th>2</th>\n",
|
| 65 |
-
" <td>2023-12-30</td>\n",
|
| 66 |
-
" <td>Monthly Appartment Rent</td>\n",
|
| 67 |
-
" <td>Expense</td>\n",
|
| 68 |
-
" <td>2000.00</td>\n",
|
| 69 |
-
" </tr>\n",
|
| 70 |
-
" <tr>\n",
|
| 71 |
-
" <th>3</th>\n",
|
| 72 |
-
" <td>2023-12-30</td>\n",
|
| 73 |
-
" <td>Staples Office Supplies</td>\n",
|
| 74 |
-
" <td>Expense</td>\n",
|
| 75 |
-
" <td>12.46</td>\n",
|
| 76 |
-
" </tr>\n",
|
| 77 |
-
" <tr>\n",
|
| 78 |
-
" <th>4</th>\n",
|
| 79 |
-
" <td>2023-12-29</td>\n",
|
| 80 |
-
" <td>Selling Paintings</td>\n",
|
| 81 |
-
" <td>Income</td>\n",
|
| 82 |
-
" <td>13.63</td>\n",
|
| 83 |
-
" </tr>\n",
|
| 84 |
-
" </tbody>\n",
|
| 85 |
-
"</table>\n",
|
| 86 |
-
"</div>"
|
| 87 |
-
],
|
| 88 |
-
"text/plain": [
|
| 89 |
-
" Date Name / Description Expense/Income Amount\n",
|
| 90 |
-
"0 2023-12-30 Comcast Internet Expense 9.96\n",
|
| 91 |
-
"1 2023-12-30 Lemonade Home Insurance Expense 17.53\n",
|
| 92 |
-
"2 2023-12-30 Monthly Appartment Rent Expense 2000.00\n",
|
| 93 |
-
"3 2023-12-30 Staples Office Supplies Expense 12.46\n",
|
| 94 |
-
"4 2023-12-29 Selling Paintings Income 13.63"
|
| 95 |
-
]
|
| 96 |
-
},
|
| 97 |
-
"execution_count": 2,
|
| 98 |
-
"metadata": {},
|
| 99 |
-
"output_type": "execute_result"
|
| 100 |
-
}
|
| 101 |
-
],
|
| 102 |
-
"source": [
|
| 103 |
-
"# Read the transactions csv\n",
|
| 104 |
-
"!pip3 install pandas\n",
|
| 105 |
-
"\n",
|
| 106 |
-
"import pandas as pd\n",
|
| 107 |
-
"df = pd.read_csv(\"transactions_2024.csv\")\n",
|
| 108 |
-
"df.head()"
|
| 109 |
-
]
|
| 110 |
-
},
|
| 111 |
-
{
|
| 112 |
-
"cell_type": "code",
|
| 113 |
-
"execution_count": 3,
|
| 114 |
-
"metadata": {},
|
| 115 |
-
"outputs": [
|
| 116 |
-
{
|
| 117 |
-
"data": {
|
| 118 |
-
"text/plain": [
|
| 119 |
-
"array(['Lemonade Home Insurance', 'Monthly Appartment Rent',\n",
|
| 120 |
-
" 'Staples Office Supplies', 'Selling Paintings', 'Spotify',\n",
|
| 121 |
-
" 'Target', 'IT Consulting', 'Phone', 'ML Consulting'], dtype=object)"
|
| 122 |
-
]
|
| 123 |
-
},
|
| 124 |
-
"execution_count": 3,
|
| 125 |
-
"metadata": {},
|
| 126 |
-
"output_type": "execute_result"
|
| 127 |
-
}
|
| 128 |
-
],
|
| 129 |
-
"source": [
|
| 130 |
-
"# Get Unique transactions in the Name/Description column\n",
|
| 131 |
-
"unique_transactions = df[\"Name / Description\"].unique()\n",
|
| 132 |
-
"unique_transactions[1:10]"
|
| 133 |
-
]
|
| 134 |
-
},
|
| 135 |
-
{
|
| 136 |
-
"cell_type": "code",
|
| 137 |
-
"execution_count": 15,
|
| 138 |
-
"metadata": {},
|
| 139 |
-
"outputs": [],
|
| 140 |
-
"source": [
|
| 141 |
-
"!python3 -m venv venv"
|
| 142 |
-
]
|
| 143 |
-
},
|
| 144 |
-
{
|
| 145 |
-
"cell_type": "code",
|
| 146 |
-
"execution_count": 16,
|
| 147 |
-
"metadata": {},
|
| 148 |
-
"outputs": [],
|
| 149 |
-
"source": [
|
| 150 |
-
"!source venv/bin/activate"
|
| 151 |
-
]
|
| 152 |
-
},
|
| 153 |
-
{
|
| 154 |
-
"cell_type": "code",
|
| 155 |
-
"execution_count": 23,
|
| 156 |
-
"metadata": {},
|
| 157 |
-
"outputs": [
|
| 158 |
-
{
|
| 159 |
-
"name": "stdout",
|
| 160 |
-
"output_type": "stream",
|
| 161 |
-
"text": [
|
| 162 |
-
"Defaulting to user installation because normal site-packages is not writeable\n",
|
| 163 |
-
"Requirement already satisfied: setuptools in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (70.0.0)\n"
|
| 164 |
-
]
|
| 165 |
-
}
|
| 166 |
-
],
|
| 167 |
-
"source": [
|
| 168 |
-
"!pip3 install --upgrade setuptools"
|
| 169 |
-
]
|
| 170 |
-
},
|
| 171 |
-
{
|
| 172 |
-
"cell_type": "code",
|
| 173 |
-
"execution_count": 2,
|
| 174 |
-
"metadata": {},
|
| 175 |
-
"outputs": [
|
| 176 |
-
{
|
| 177 |
-
"data": {
|
| 178 |
-
"text/plain": [
|
| 179 |
-
"['Defaulting to user installation because normal site-packages is not writeable',\n",
|
| 180 |
-
" 'Requirement already satisfied: langchain in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from -r requirements.txt (line 1)) (0.2.1)',\n",
|
| 181 |
-
" 'Collecting langchain_openai (from -r requirements.txt (line 2))',\n",
|
| 182 |
-
" ' Downloading langchain_openai-0.1.7-py3-none-any.whl.metadata (2.5 kB)',\n",
|
| 183 |
-
" 'Collecting python-dotenv (from -r requirements.txt (line 3))',\n",
|
| 184 |
-
" ' Using cached python_dotenv-1.0.1-py3-none-any.whl.metadata (23 kB)',\n",
|
| 185 |
-
" 'Collecting openai (from -r requirements.txt (line 4))',\n",
|
| 186 |
-
" ' Using cached openai-1.30.4-py3-none-any.whl.metadata (21 kB)',\n",
|
| 187 |
-
" 'Requirement already satisfied: tenacity in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from -r requirements.txt (line 5)) (8.3.0)',\n",
|
| 188 |
-
" 'Requirement already satisfied: rapidfuzz in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from -r requirements.txt (line 6)) (3.9.1)',\n",
|
| 189 |
-
" 'Requirement already satisfied: pydantic in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from -r requirements.txt (line 7)) (2.7.1)',\n",
|
| 190 |
-
" 'Requirement already satisfied: dateparser in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from -r requirements.txt (line 8)) (1.2.0)',\n",
|
| 191 |
-
" 'Requirement already satisfied: pandas in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from -r requirements.txt (line 9)) (2.0.3)',\n",
|
| 192 |
-
" 'Collecting path (from -r requirements.txt (line 10))',\n",
|
| 193 |
-
" ' Using cached path-16.14.0-py3-none-any.whl.metadata (6.3 kB)',\n",
|
| 194 |
-
" 'Collecting aiostream==0.5.2 (from -r requirements.txt (line 12))',\n",
|
| 195 |
-
" ' Using cached aiostream-0.5.2-py3-none-any.whl.metadata (9.9 kB)',\n",
|
| 196 |
-
" 'Collecting cachetools==5.3.3 (from -r requirements.txt (line 13))',\n",
|
| 197 |
-
" ' Using cached cachetools-5.3.3-py3-none-any.whl.metadata (5.3 kB)',\n",
|
| 198 |
-
" 'Collecting docx2txt==0.8 (from -r requirements.txt (line 14))',\n",
|
| 199 |
-
" ' Using cached docx2txt-0.8.tar.gz (2.8 kB)',\n",
|
| 200 |
-
" ' Preparing metadata (setup.py): started',\n",
|
| 201 |
-
" \" Preparing metadata (setup.py): finished with status 'done'\",\n",
|
| 202 |
-
" 'Collecting fastapi==0.109.1 (from -r requirements.txt (line 15))',\n",
|
| 203 |
-
" ' Using cached fastapi-0.109.1-py3-none-any.whl.metadata (25 kB)',\n",
|
| 204 |
-
" 'Collecting llama-index-agent-openai==0.2.2 (from -r requirements.txt (line 16))',\n",
|
| 205 |
-
" ' Using cached llama_index_agent_openai-0.2.2-py3-none-any.whl.metadata (677 bytes)',\n",
|
| 206 |
-
" 'Collecting llama-index-core==0.10.28 (from -r requirements.txt (line 17))',\n",
|
| 207 |
-
" ' Using cached llama_index_core-0.10.28-py3-none-any.whl.metadata (3.6 kB)',\n",
|
| 208 |
-
" 'Collecting llama-index-vector-stores-pinecone==0.1.3 (from -r requirements.txt (line 18))',\n",
|
| 209 |
-
" ' Using cached llama_index_vector_stores_pinecone-0.1.3-py3-none-any.whl.metadata (725 bytes)',\n",
|
| 210 |
-
" 'Collecting llama-index==0.10.28 (from -r requirements.txt (line 19))',\n",
|
| 211 |
-
" ' Using cached llama_index-0.10.28-py3-none-any.whl.metadata (11 kB)',\n",
|
| 212 |
-
" 'Collecting python-dotenv (from -r requirements.txt (line 3))',\n",
|
| 213 |
-
" ' Using cached python_dotenv-1.0.0-py3-none-any.whl.metadata (21 kB)',\n",
|
| 214 |
-
" '\\x1b[31mERROR: Ignored the following versions that require a different python version: 0.0.10 Requires-Python >=3.9,<4.0; 0.0.11 Requires-Python >=3.9,<4.0; 0.0.12 Requires-Python >=3.9,<4.0; 0.0.13 Requires-Python >=3.9,<4.0; 0.0.14 Requires-Python >=3.9,<4.0; 0.0.15 Requires-Python >=3.9,<4.0; 0.0.16 Requires-Python >=3.9,<4.0; 0.0.17 Requires-Python >=3.9,<4.0; 0.0.18 Requires-Python >=3.9,<4.0; 0.0.19 Requires-Python >=3.9,<4.0; 0.0.20 Requires-Python >=3.9,<4.0; 0.0.21 Requires-Python >=3.9,<4.0; 0.0.22 Requires-Python >=3.9,<4.0; 0.0.23 Requires-Python >=3.9,<4.0; 0.0.24 Requires-Python >=3.9,<4.0; 0.0.25 Requires-Python >=3.9,<4.0; 0.14.3 Requires-Python >=3.9,<4; 0.14.4 Requires-Python <4,>=3.9; 0.14.5 Requires-Python <4,>=3.9; 0.15.0 Requires-Python <4,>=3.9; 0.15.1 Requires-Python <4,>=3.9; 0.15.10 Requires-Python <4,>=3.9; 0.15.11 Requires-Python <4,>=3.9; 0.15.12 Requires-Python <4,>=3.9; 0.15.13 Requires-Python <4,>=3.9; 0.15.2 Requires-Python <4,>=3.9; 0.15.3 Requires-Python <4,>=3.9; 0.15.4 Requires-Python <4,>=3.9; 0.15.5 Requires-Python <4,>=3.9; 0.15.6 Requires-Python <4,>=3.9; 0.15.7 Requires-Python <4,>=3.9; 0.15.8 Requires-Python <4,>=3.9; 0.15.9 Requires-Python <4,>=3.9; 0.16.0 Requires-Python <4,>=3.9; 0.16.1 Requires-Python <4,>=3.9; 0.16.2 Requires-Python <4,>=3.9; 0.16.3 Requires-Python <4,>=3.9; 0.16.4 Requires-Python <4,>=3.9; 0.16.5 Requires-Python <4,>=3.9; 0.16.6 Requires-Python <4,>=3.9; 0.16.7 Requires-Python <4,>=3.9; 0.16.8 Requires-Python <4,>=3.9; 0.16.9 Requires-Python <4,>=3.9; 0.17.0 Requires-Python <4,>=3.9; 0.17.1 Requires-Python <4,>=3.9; 0.17.2 Requires-Python <4,>=3.9; 0.17.3 Requires-Python <4,>=3.9; 0.17.4 Requires-Python <4,>=3.9; 0.17.5 Requires-Python <4,>=3.9; 0.17.6 Requires-Python <4,>=3.9; 0.17.7 Requires-Python <4,>=3.9; 0.18.0 Requires-Python <4,>=3.9; 0.18.1 Requires-Python <4,>=3.9; 0.18.2 Requires-Python <4,>=3.9; 0.19.0 Requires-Python <4,>=3.9; 0.20.0 Requires-Python <4,>=3.9; 0.21.0 Requires-Python <4,>=3.9\\x1b[0m\\x1b[31m',\n",
|
| 215 |
-
" '\\x1b[0m\\x1b[31mERROR: Could not find a version that satisfies the requirement traceloop-sdk==0.15.11 (from versions: 0.0.26, 0.0.27b0, 0.0.27, 0.0.28, 0.0.29, 0.0.31, 0.0.32, 0.0.33, 0.0.34, 0.0.35, 0.0.36, 0.0.37, 0.0.38, 0.0.39, 0.0.40, 0.0.41, 0.0.42, 0.0.43b0, 0.0.43b2, 0.0.43b3, 0.0.43b4, 0.0.43, 0.0.44b0, 0.0.44, 0.0.46, 0.0.47, 0.0.48, 0.0.49, 0.0.50, 0.0.51, 0.0.52, 0.0.53, 0.0.54, 0.0.55, 0.0.56a0, 0.0.56a1, 0.0.56a2, 0.0.56a3, 0.0.56a4, 0.0.56a5, 0.0.56a6, 0.0.56, 0.0.57, 0.0.58, 0.0.59, 0.0.60a0, 0.0.60a1, 0.0.60, 0.0.61a0, 0.0.61, 0.0.62, 0.0.63, 0.0.64a0, 0.0.64, 0.0.65, 0.0.66, 0.0.67, 0.0.68, 0.0.69, 0.0.70, 0.1.3, 0.1.5, 0.1.6, 0.1.7, 0.1.8, 0.1.9, 0.1.10, 0.1.11, 0.1.12, 0.2.0, 0.2.1, 0.3.0, 0.3.1, 0.3.2, 0.3.3, 0.3.4, 0.3.5, 0.3.6, 0.4.0, 0.4.1, 0.4.2, 0.5.0, 0.5.1, 0.5.2, 0.5.3, 0.6.0, 0.7.0, 0.8.0, 0.8.2, 0.9.0, 0.9.1, 0.9.2, 0.9.3, 0.9.4, 0.10.0, 0.10.1, 0.10.2, 0.10.3, 0.10.4, 0.10.5, 0.11.0, 0.11.1, 0.11.2, 0.11.3, 0.12.0, 0.12.1, 0.12.2, 0.12.3, 0.12.4, 0.12.5, 0.13.0, 0.13.1, 0.13.2, 0.13.3, 0.14.0, 0.14.1, 0.14.2)\\x1b[0m\\x1b[31m',\n",
|
| 216 |
-
" '\\x1b[0m\\x1b[31mERROR: No matching distribution found for traceloop-sdk==0.15.11\\x1b[0m\\x1b[31m',\n",
|
| 217 |
-
" '\\x1b[0m']"
|
| 218 |
-
]
|
| 219 |
-
},
|
| 220 |
-
"execution_count": 2,
|
| 221 |
-
"metadata": {},
|
| 222 |
-
"output_type": "execute_result"
|
| 223 |
-
}
|
| 224 |
-
],
|
| 225 |
-
"source": [
|
| 226 |
-
"!!pip3 install -r requirements.txt"
|
| 227 |
-
]
|
| 228 |
-
},
|
| 229 |
-
{
|
| 230 |
-
"cell_type": "code",
|
| 231 |
-
"execution_count": 28,
|
| 232 |
-
"metadata": {},
|
| 233 |
-
"outputs": [
|
| 234 |
-
{
|
| 235 |
-
"name": "stdout",
|
| 236 |
-
"output_type": "stream",
|
| 237 |
-
"text": [
|
| 238 |
-
"Defaulting to user installation because normal site-packages is not writeable\n",
|
| 239 |
-
"Collecting dateparser\n",
|
| 240 |
-
" Using cached dateparser-1.2.0-py2.py3-none-any.whl.metadata (28 kB)\n",
|
| 241 |
-
"Requirement already satisfied: python-dateutil in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from dateparser) (2.9.0.post0)\n",
|
| 242 |
-
"Requirement already satisfied: pytz in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from dateparser) (2024.1)\n",
|
| 243 |
-
"Collecting regex!=2019.02.19,!=2021.8.27 (from dateparser)\n",
|
| 244 |
-
" Downloading regex-2024.5.15-cp38-cp38-macosx_10_9_x86_64.whl.metadata (40 kB)\n",
|
| 245 |
-
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m40.9/40.9 kB\u001b[0m \u001b[31m95.7 kB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0ma \u001b[36m0:00:01\u001b[0m\n",
|
| 246 |
-
"\u001b[?25hCollecting tzlocal (from dateparser)\n",
|
| 247 |
-
" Downloading tzlocal-5.2-py3-none-any.whl.metadata (7.8 kB)\n",
|
| 248 |
-
"Requirement already satisfied: six>=1.5 in /Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/site-packages (from python-dateutil->dateparser) (1.15.0)\n",
|
| 249 |
-
"Collecting backports.zoneinfo (from tzlocal->dateparser)\n",
|
| 250 |
-
" Downloading backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl.metadata (4.7 kB)\n",
|
| 251 |
-
"Downloading dateparser-1.2.0-py2.py3-none-any.whl (294 kB)\n",
|
| 252 |
-
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m295.0/295.0 kB\u001b[0m \u001b[31m510.0 kB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0ma \u001b[36m0:00:01\u001b[0m\n",
|
| 253 |
-
"\u001b[?25hDownloading regex-2024.5.15-cp38-cp38-macosx_10_9_x86_64.whl (281 kB)\n",
|
| 254 |
-
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m281.8/281.8 kB\u001b[0m \u001b[31m2.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m00:01\u001b[0m\n",
|
| 255 |
-
"\u001b[?25hDownloading tzlocal-5.2-py3-none-any.whl (17 kB)\n",
|
| 256 |
-
"Downloading backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl (35 kB)\n",
|
| 257 |
-
"Installing collected packages: regex, backports.zoneinfo, tzlocal, dateparser\n",
|
| 258 |
-
"\u001b[33m WARNING: The script dateparser-download is installed in '/Users/patrickalexis/Library/Python/3.8/bin' which is not on PATH.\n",
|
| 259 |
-
" Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.\u001b[0m\u001b[33m\n",
|
| 260 |
-
"\u001b[0mSuccessfully installed backports.zoneinfo-0.2.1 dateparser-1.2.0 regex-2024.5.15 tzlocal-5.2\n"
|
| 261 |
-
]
|
| 262 |
-
}
|
| 263 |
-
],
|
| 264 |
-
"source": [
|
| 265 |
-
"!pip3 install dateparser"
|
| 266 |
-
]
|
| 267 |
-
},
|
| 268 |
-
{
|
| 269 |
-
"cell_type": "code",
|
| 270 |
-
"execution_count": 2,
|
| 271 |
-
"metadata": {},
|
| 272 |
-
"outputs": [
|
| 273 |
-
{
|
| 274 |
-
"name": "stdout",
|
| 275 |
-
"output_type": "stream",
|
| 276 |
-
"text": [
|
| 277 |
-
"Defaulting to user installation because normal site-packages is not writeable\n",
|
| 278 |
-
"Collecting rapidfuzz\n",
|
| 279 |
-
" Using cached rapidfuzz-3.9.1-cp38-cp38-macosx_10_9_x86_64.whl.metadata (11 kB)\n",
|
| 280 |
-
"Using cached rapidfuzz-3.9.1-cp38-cp38-macosx_10_9_x86_64.whl (2.1 MB)\n",
|
| 281 |
-
"Installing collected packages: rapidfuzz\n",
|
| 282 |
-
"Successfully installed rapidfuzz-3.9.1\n"
|
| 283 |
-
]
|
| 284 |
-
}
|
| 285 |
-
],
|
| 286 |
-
"source": [
|
| 287 |
-
"!pip3 install rapidfuzz"
|
| 288 |
-
]
|
| 289 |
-
},
|
| 290 |
-
{
|
| 291 |
-
"cell_type": "code",
|
| 292 |
-
"execution_count": 2,
|
| 293 |
-
"metadata": {},
|
| 294 |
-
"outputs": [
|
| 295 |
-
{
|
| 296 |
-
"name": "stdout",
|
| 297 |
-
"output_type": "stream",
|
| 298 |
-
"text": [
|
| 299 |
-
"Defaulting to user installation because normal site-packages is not writeable\n",
|
| 300 |
-
"Collecting dotenv\n",
|
| 301 |
-
" Downloading dotenv-0.0.5.tar.gz (2.4 kB)\n",
|
| 302 |
-
" Preparing metadata (setup.py) ... \u001b[?25lerror\n",
|
| 303 |
-
" \u001b[1;31merror\u001b[0m: \u001b[1msubprocess-exited-with-error\u001b[0m\n",
|
| 304 |
-
" \n",
|
| 305 |
-
" \u001b[31m×\u001b[0m \u001b[32mpython setup.py egg_info\u001b[0m did not run successfully.\n",
|
| 306 |
-
" \u001b[31m│\u001b[0m exit code: \u001b[1;36m1\u001b[0m\n",
|
| 307 |
-
" \u001b[31m╰─>\u001b[0m \u001b[31m[76 lines of output]\u001b[0m\n",
|
| 308 |
-
" \u001b[31m \u001b[0m /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages/setuptools/__init__.py:80: _DeprecatedInstaller: setuptools.installer and fetch_build_eggs are deprecated.\n",
|
| 309 |
-
" \u001b[31m \u001b[0m !!\n",
|
| 310 |
-
" \u001b[31m \u001b[0m \n",
|
| 311 |
-
" \u001b[31m \u001b[0m ********************************************************************************\n",
|
| 312 |
-
" \u001b[31m \u001b[0m Requirements should be satisfied by a PEP 517 installer.\n",
|
| 313 |
-
" \u001b[31m \u001b[0m If you are using pip, you can try `pip install --use-pep517`.\n",
|
| 314 |
-
" \u001b[31m \u001b[0m ********************************************************************************\n",
|
| 315 |
-
" \u001b[31m \u001b[0m \n",
|
| 316 |
-
" \u001b[31m \u001b[0m !!\n",
|
| 317 |
-
" \u001b[31m \u001b[0m dist.fetch_build_eggs(dist.setup_requires)\n",
|
| 318 |
-
" \u001b[31m \u001b[0m \u001b[1;31merror\u001b[0m: \u001b[1msubprocess-exited-with-error\u001b[0m\n",
|
| 319 |
-
" \u001b[31m \u001b[0m \n",
|
| 320 |
-
" \u001b[31m \u001b[0m \u001b[31m×\u001b[0m \u001b[32mpython setup.py egg_info\u001b[0m did not run successfully.\n",
|
| 321 |
-
" \u001b[31m \u001b[0m \u001b[31m│\u001b[0m exit code: \u001b[1;36m1\u001b[0m\n",
|
| 322 |
-
" \u001b[31m \u001b[0m \u001b[31m╰─>\u001b[0m \u001b[31m[16 lines of output]\u001b[0m\n",
|
| 323 |
-
" \u001b[31m \u001b[0m \u001b[31m \u001b[0m Traceback (most recent call last):\n",
|
| 324 |
-
" \u001b[31m \u001b[0m \u001b[31m \u001b[0m File \"<string>\", line 2, in <module>\n",
|
| 325 |
-
" \u001b[31m \u001b[0m \u001b[31m \u001b[0m File \"<pip-setuptools-caller>\", line 14, in <module>\n",
|
| 326 |
-
" \u001b[31m \u001b[0m \u001b[31m \u001b[0m File \"/private/var/folders/g9/flbt409n1b7g0ry4b9ffdvnc0000gn/T/pip-wheel-66v7g1h6/distribute_6e0b47b8750a4aa8bfff62a3f867a97f/setuptools/__init__.py\", line 2, in <module>\n",
|
| 327 |
-
" \u001b[31m \u001b[0m \u001b[31m \u001b[0m from setuptools.extension import Extension, Library\n",
|
| 328 |
-
" \u001b[31m \u001b[0m \u001b[31m \u001b[0m File \"/private/var/folders/g9/flbt409n1b7g0ry4b9ffdvnc0000gn/T/pip-wheel-66v7g1h6/distribute_6e0b47b8750a4aa8bfff62a3f867a97f/setuptools/extension.py\", line 5, in <module>\n",
|
| 329 |
-
" \u001b[31m \u001b[0m \u001b[31m \u001b[0m from setuptools.dist import _get_unpatched\n",
|
| 330 |
-
" \u001b[31m \u001b[0m \u001b[31m \u001b[0m File \"/private/var/folders/g9/flbt409n1b7g0ry4b9ffdvnc0000gn/T/pip-wheel-66v7g1h6/distribute_6e0b47b8750a4aa8bfff62a3f867a97f/setuptools/dist.py\", line 7, in <module>\n",
|
| 331 |
-
" \u001b[31m \u001b[0m \u001b[31m \u001b[0m from setuptools.command.install import install\n",
|
| 332 |
-
" \u001b[31m \u001b[0m \u001b[31m \u001b[0m File \"/private/var/folders/g9/flbt409n1b7g0ry4b9ffdvnc0000gn/T/pip-wheel-66v7g1h6/distribute_6e0b47b8750a4aa8bfff62a3f867a97f/setuptools/command/__init__.py\", line 8, in <module>\n",
|
| 333 |
-
" \u001b[31m \u001b[0m \u001b[31m \u001b[0m from setuptools.command import install_scripts\n",
|
| 334 |
-
" \u001b[31m \u001b[0m \u001b[31m \u001b[0m File \"/private/var/folders/g9/flbt409n1b7g0ry4b9ffdvnc0000gn/T/pip-wheel-66v7g1h6/distribute_6e0b47b8750a4aa8bfff62a3f867a97f/setuptools/command/install_scripts.py\", line 3, in <module>\n",
|
| 335 |
-
" \u001b[31m \u001b[0m \u001b[31m \u001b[0m from pkg_resources import Distribution, PathMetadata, ensure_directory\n",
|
| 336 |
-
" \u001b[31m \u001b[0m \u001b[31m \u001b[0m File \"/private/var/folders/g9/flbt409n1b7g0ry4b9ffdvnc0000gn/T/pip-wheel-66v7g1h6/distribute_6e0b47b8750a4aa8bfff62a3f867a97f/pkg_resources.py\", line 1518, in <module>\n",
|
| 337 |
-
" \u001b[31m \u001b[0m \u001b[31m \u001b[0m register_loader_type(importlib_bootstrap.SourceFileLoader, DefaultProvider)\n",
|
| 338 |
-
" \u001b[31m \u001b[0m \u001b[31m \u001b[0m AttributeError: module 'importlib._bootstrap' has no attribute 'SourceFileLoader'\n",
|
| 339 |
-
" \u001b[31m \u001b[0m \u001b[31m \u001b[0m \u001b[31m[end of output]\u001b[0m\n",
|
| 340 |
-
" \u001b[31m \u001b[0m \n",
|
| 341 |
-
" \u001b[31m \u001b[0m \u001b[1;35mnote\u001b[0m: This error originates from a subprocess, and is likely not a problem with pip.\n",
|
| 342 |
-
" \u001b[31m \u001b[0m \u001b[1;31merror\u001b[0m: \u001b[1mmetadata-generation-failed\u001b[0m\n",
|
| 343 |
-
" \u001b[31m \u001b[0m \n",
|
| 344 |
-
" \u001b[31m \u001b[0m \u001b[31m×\u001b[0m Encountered error while generating package metadata.\n",
|
| 345 |
-
" \u001b[31m \u001b[0m \u001b[31m╰─>\u001b[0m See above for output.\n",
|
| 346 |
-
" \u001b[31m \u001b[0m \n",
|
| 347 |
-
" \u001b[31m \u001b[0m \u001b[1;35mnote\u001b[0m: This is an issue with the package mentioned above, not pip.\n",
|
| 348 |
-
" \u001b[31m \u001b[0m \u001b[1;36mhint\u001b[0m: See above for details.\n",
|
| 349 |
-
" \u001b[31m \u001b[0m Traceback (most recent call last):\n",
|
| 350 |
-
" \u001b[31m \u001b[0m File \"/Users/patrickalexis/Library/Python/3.8/lib/python/site-packages/setuptools/installer.py\", line 101, in _fetch_build_egg_no_warn\n",
|
| 351 |
-
" \u001b[31m \u001b[0m subprocess.check_call(cmd)\n",
|
| 352 |
-
" \u001b[31m \u001b[0m File \"/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/subprocess.py\", line 364, in check_call\n",
|
| 353 |
-
" \u001b[31m \u001b[0m raise CalledProcessError(retcode, cmd)\n",
|
| 354 |
-
" \u001b[31m \u001b[0m subprocess.CalledProcessError: Command '['/Applications/Xcode.app/Contents/Developer/usr/bin/python3', '-m', 'pip', '--disable-pip-version-check', 'wheel', '--no-deps', '-w', '/var/folders/g9/flbt409n1b7g0ry4b9ffdvnc0000gn/T/tmpnw2yadh3', '--quiet', 'distribute']' returned non-zero exit status 1.\n",
|
| 355 |
-
" \u001b[31m \u001b[0m \n",
|
| 356 |
-
" \u001b[31m \u001b[0m The above exception was the direct cause of the following exception:\n",
|
| 357 |
-
" \u001b[31m \u001b[0m \n",
|
| 358 |
-
" \u001b[31m \u001b[0m Traceback (most recent call last):\n",
|
| 359 |
-
" \u001b[31m \u001b[0m File \"<string>\", line 2, in <module>\n",
|
| 360 |
-
" \u001b[31m \u001b[0m File \"<pip-setuptools-caller>\", line 34, in <module>\n",
|
| 361 |
-
" \u001b[31m \u001b[0m File \"/private/var/folders/g9/flbt409n1b7g0ry4b9ffdvnc0000gn/T/pip-install-ac3_ml88/dotenv_7766945cfd7d4e62965375b30e9a0c21/setup.py\", line 13, in <module>\n",
|
| 362 |
-
" \u001b[31m \u001b[0m setup(name='dotenv',\n",
|
| 363 |
-
" \u001b[31m \u001b[0m File \"/Users/patrickalexis/Library/Python/3.8/lib/python/site-packages/setuptools/__init__.py\", line 102, in setup\n",
|
| 364 |
-
" \u001b[31m \u001b[0m _install_setup_requires(attrs)\n",
|
| 365 |
-
" \u001b[31m \u001b[0m File \"/Users/patrickalexis/Library/Python/3.8/lib/python/site-packages/setuptools/__init__.py\", line 75, in _install_setup_requires\n",
|
| 366 |
-
" \u001b[31m \u001b[0m _fetch_build_eggs(dist)\n",
|
| 367 |
-
" \u001b[31m \u001b[0m File \"/Users/patrickalexis/Library/Python/3.8/lib/python/site-packages/setuptools/__init__.py\", line 80, in _fetch_build_eggs\n",
|
| 368 |
-
" \u001b[31m \u001b[0m dist.fetch_build_eggs(dist.setup_requires)\n",
|
| 369 |
-
" \u001b[31m \u001b[0m File \"/Users/patrickalexis/Library/Python/3.8/lib/python/site-packages/setuptools/dist.py\", line 641, in fetch_build_eggs\n",
|
| 370 |
-
" \u001b[31m \u001b[0m return _fetch_build_eggs(self, requires)\n",
|
| 371 |
-
" \u001b[31m \u001b[0m File \"/Users/patrickalexis/Library/Python/3.8/lib/python/site-packages/setuptools/installer.py\", line 38, in _fetch_build_eggs\n",
|
| 372 |
-
" \u001b[31m \u001b[0m resolved_dists = pkg_resources.working_set.resolve(\n",
|
| 373 |
-
" \u001b[31m \u001b[0m File \"/Users/patrickalexis/Library/Python/3.8/lib/python/site-packages/pkg_resources/__init__.py\", line 787, in resolve\n",
|
| 374 |
-
" \u001b[31m \u001b[0m dist = self._resolve_dist(\n",
|
| 375 |
-
" \u001b[31m \u001b[0m File \"/Users/patrickalexis/Library/Python/3.8/lib/python/site-packages/pkg_resources/__init__.py\", line 823, in _resolve_dist\n",
|
| 376 |
-
" \u001b[31m \u001b[0m dist = best[req.key] = env.best_match(\n",
|
| 377 |
-
" \u001b[31m \u001b[0m File \"/Users/patrickalexis/Library/Python/3.8/lib/python/site-packages/pkg_resources/__init__.py\", line 1093, in best_match\n",
|
| 378 |
-
" \u001b[31m \u001b[0m return self.obtain(req, installer)\n",
|
| 379 |
-
" \u001b[31m \u001b[0m File \"/Users/patrickalexis/Library/Python/3.8/lib/python/site-packages/pkg_resources/__init__.py\", line 1104, in obtain\n",
|
| 380 |
-
" \u001b[31m \u001b[0m return installer(requirement) if installer else None\n",
|
| 381 |
-
" \u001b[31m \u001b[0m File \"/Users/patrickalexis/Library/Python/3.8/lib/python/site-packages/setuptools/installer.py\", line 103, in _fetch_build_egg_no_warn\n",
|
| 382 |
-
" \u001b[31m \u001b[0m raise DistutilsError(str(e)) from e\n",
|
| 383 |
-
" \u001b[31m \u001b[0m distutils.errors.DistutilsError: Command '['/Applications/Xcode.app/Contents/Developer/usr/bin/python3', '-m', 'pip', '--disable-pip-version-check', 'wheel', '--no-deps', '-w', '/var/folders/g9/flbt409n1b7g0ry4b9ffdvnc0000gn/T/tmpnw2yadh3', '--quiet', 'distribute']' returned non-zero exit status 1.\n",
|
| 384 |
-
" \u001b[31m \u001b[0m \u001b[31m[end of output]\u001b[0m\n",
|
| 385 |
-
" \n",
|
| 386 |
-
" \u001b[1;35mnote\u001b[0m: This error originates from a subprocess, and is likely not a problem with pip.\n",
|
| 387 |
-
"\u001b[?25h\u001b[1;31merror\u001b[0m: \u001b[1mmetadata-generation-failed\u001b[0m\n",
|
| 388 |
-
"\n",
|
| 389 |
-
"\u001b[31m×\u001b[0m Encountered error while generating package metadata.\n",
|
| 390 |
-
"\u001b[31m╰─>\u001b[0m See above for output.\n",
|
| 391 |
-
"\n",
|
| 392 |
-
"\u001b[1;35mnote\u001b[0m: This is an issue with the package mentioned above, not pip.\n",
|
| 393 |
-
"\u001b[1;36mhint\u001b[0m: See above for details.\n"
|
| 394 |
-
]
|
| 395 |
-
}
|
| 396 |
-
],
|
| 397 |
-
"source": [
|
| 398 |
-
"!pip3 install dotenv"
|
| 399 |
-
]
|
| 400 |
-
},
|
| 401 |
-
{
|
| 402 |
-
"cell_type": "code",
|
| 403 |
-
"execution_count": 10,
|
| 404 |
-
"metadata": {},
|
| 405 |
-
"outputs": [],
|
| 406 |
-
"source": [
|
| 407 |
-
"# Create data folder\n",
|
| 408 |
-
"!mkdir -p data/tx_data/output\n",
|
| 409 |
-
"!mkdir -p data/ref_data"
|
| 410 |
-
]
|
| 411 |
-
},
|
| 412 |
-
{
|
| 413 |
-
"cell_type": "code",
|
| 414 |
-
"execution_count": 4,
|
| 415 |
-
"metadata": {},
|
| 416 |
-
"outputs": [
|
| 417 |
-
{
|
| 418 |
-
"name": "stdout",
|
| 419 |
-
"output_type": "stream",
|
| 420 |
-
"text": [
|
| 421 |
-
"Defaulting to user installation because normal site-packages is not writeable\n",
|
| 422 |
-
"Collecting openai\n",
|
| 423 |
-
" Using cached openai-1.30.4-py3-none-any.whl.metadata (21 kB)\n",
|
| 424 |
-
"Collecting anyio<5,>=3.5.0 (from openai)\n",
|
| 425 |
-
" Downloading anyio-4.4.0-py3-none-any.whl.metadata (4.6 kB)\n",
|
| 426 |
-
"Collecting distro<2,>=1.7.0 (from openai)\n",
|
| 427 |
-
" Downloading distro-1.9.0-py3-none-any.whl.metadata (6.8 kB)\n",
|
| 428 |
-
"Collecting httpx<1,>=0.23.0 (from openai)\n",
|
| 429 |
-
" Downloading httpx-0.27.0-py3-none-any.whl.metadata (7.2 kB)\n",
|
| 430 |
-
"Requirement already satisfied: pydantic<3,>=1.9.0 in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from openai) (2.7.1)\n",
|
| 431 |
-
"Collecting sniffio (from openai)\n",
|
| 432 |
-
" Downloading sniffio-1.3.1-py3-none-any.whl.metadata (3.9 kB)\n",
|
| 433 |
-
"Requirement already satisfied: tqdm>4 in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from openai) (4.66.4)\n",
|
| 434 |
-
"Requirement already satisfied: typing-extensions<5,>=4.7 in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from openai) (4.12.0)\n",
|
| 435 |
-
"Requirement already satisfied: idna>=2.8 in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from anyio<5,>=3.5.0->openai) (3.7)\n",
|
| 436 |
-
"Collecting exceptiongroup>=1.0.2 (from anyio<5,>=3.5.0->openai)\n",
|
| 437 |
-
" Downloading exceptiongroup-1.2.1-py3-none-any.whl.metadata (6.6 kB)\n",
|
| 438 |
-
"Requirement already satisfied: certifi in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from httpx<1,>=0.23.0->openai) (2024.2.2)\n",
|
| 439 |
-
"Collecting httpcore==1.* (from httpx<1,>=0.23.0->openai)\n",
|
| 440 |
-
" Downloading httpcore-1.0.5-py3-none-any.whl.metadata (20 kB)\n",
|
| 441 |
-
"Collecting h11<0.15,>=0.13 (from httpcore==1.*->httpx<1,>=0.23.0->openai)\n",
|
| 442 |
-
" Downloading h11-0.14.0-py3-none-any.whl.metadata (8.2 kB)\n",
|
| 443 |
-
"Requirement already satisfied: annotated-types>=0.4.0 in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from pydantic<3,>=1.9.0->openai) (0.7.0)\n",
|
| 444 |
-
"Requirement already satisfied: pydantic-core==2.18.2 in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from pydantic<3,>=1.9.0->openai) (2.18.2)\n",
|
| 445 |
-
"Downloading openai-1.30.4-py3-none-any.whl (320 kB)\n",
|
| 446 |
-
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m320.6/320.6 kB\u001b[0m \u001b[31m5.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m:00:01\u001b[0m\n",
|
| 447 |
-
"\u001b[?25hDownloading anyio-4.4.0-py3-none-any.whl (86 kB)\n",
|
| 448 |
-
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m86.8/86.8 kB\u001b[0m \u001b[31m3.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
|
| 449 |
-
"\u001b[?25hDownloading distro-1.9.0-py3-none-any.whl (20 kB)\n",
|
| 450 |
-
"Downloading httpx-0.27.0-py3-none-any.whl (75 kB)\n",
|
| 451 |
-
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m75.6/75.6 kB\u001b[0m \u001b[31m2.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
|
| 452 |
-
"\u001b[?25hDownloading httpcore-1.0.5-py3-none-any.whl (77 kB)\n",
|
| 453 |
-
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m77.9/77.9 kB\u001b[0m \u001b[31m4.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
|
| 454 |
-
"\u001b[?25hDownloading sniffio-1.3.1-py3-none-any.whl (10 kB)\n",
|
| 455 |
-
"Downloading exceptiongroup-1.2.1-py3-none-any.whl (16 kB)\n",
|
| 456 |
-
"Downloading h11-0.14.0-py3-none-any.whl (58 kB)\n",
|
| 457 |
-
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m58.3/58.3 kB\u001b[0m \u001b[31m2.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
|
| 458 |
-
"\u001b[?25hInstalling collected packages: sniffio, h11, exceptiongroup, distro, httpcore, anyio, httpx, openai\n",
|
| 459 |
-
"\u001b[33m WARNING: The script distro is installed in '/Users/patrickalexis/Library/Python/3.8/bin' which is not on PATH.\n",
|
| 460 |
-
" Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.\u001b[0m\u001b[33m\n",
|
| 461 |
-
"\u001b[0m\u001b[33m WARNING: The script httpx is installed in '/Users/patrickalexis/Library/Python/3.8/bin' which is not on PATH.\n",
|
| 462 |
-
" Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.\u001b[0m\u001b[33m\n",
|
| 463 |
-
"\u001b[0m\u001b[33m WARNING: The script openai is installed in '/Users/patrickalexis/Library/Python/3.8/bin' which is not on PATH.\n",
|
| 464 |
-
" Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.\u001b[0m\u001b[33m\n",
|
| 465 |
-
"\u001b[0mSuccessfully installed anyio-4.4.0 distro-1.9.0 exceptiongroup-1.2.1 h11-0.14.0 httpcore-1.0.5 httpx-0.27.0 openai-1.30.4 sniffio-1.3.1\n"
|
| 466 |
-
]
|
| 467 |
-
}
|
| 468 |
-
],
|
| 469 |
-
"source": [
|
| 470 |
-
"!pip3 install openai"
|
| 471 |
-
]
|
| 472 |
-
},
|
| 473 |
-
{
|
| 474 |
-
"cell_type": "code",
|
| 475 |
-
"execution_count": 5,
|
| 476 |
-
"metadata": {},
|
| 477 |
-
"outputs": [
|
| 478 |
-
{
|
| 479 |
-
"name": "stdout",
|
| 480 |
-
"output_type": "stream",
|
| 481 |
-
"text": [
|
| 482 |
-
"\n",
|
| 483 |
-
"Processing file\n"
|
| 484 |
-
]
|
| 485 |
-
},
|
| 486 |
-
{
|
| 487 |
-
"name": "stderr",
|
| 488 |
-
"output_type": "stream",
|
| 489 |
-
"text": [
|
| 490 |
-
"ERROR:root:| File: transactions_2024.csv | Unexpected Error: expected str, got module\n"
|
| 491 |
-
]
|
| 492 |
-
},
|
| 493 |
-
{
|
| 494 |
-
"name": "stdout",
|
| 495 |
-
"output_type": "stream",
|
| 496 |
-
"text": [
|
| 497 |
-
"ERROR processing file transactions_2024.csv: expected str, got module\n",
|
| 498 |
-
"\n",
|
| 499 |
-
"Processed 1 files: 0 successful, 1 with errors\n",
|
| 500 |
-
"\n",
|
| 501 |
-
"Errors in the following files:\n",
|
| 502 |
-
" transactions_2024.csv: expected str, got module\n",
|
| 503 |
-
"\n",
|
| 504 |
-
"\n",
|
| 505 |
-
"[{'file_name': 'transactions_2024.csv', 'output': Empty DataFrame\n",
|
| 506 |
-
"Columns: []\n",
|
| 507 |
-
"Index: [], 'error': 'expected str, got module'}]\n",
|
| 508 |
-
"['name/description,category\\n']\n",
|
| 509 |
-
"None\n"
|
| 510 |
-
]
|
| 511 |
-
}
|
| 512 |
-
],
|
| 513 |
-
"source": [
|
| 514 |
-
"# Process the transactions csv and get the results from the categorization llm utility\n",
|
| 515 |
-
"from app.categorization.file_processing import process_file, save_results\n",
|
| 516 |
-
"from app.categorization.config import CATEGORY_REFERENCE_OUTPUT_FILE\n",
|
| 517 |
-
"# from dotenv import load_dotenv\n",
|
| 518 |
-
"import asyncio\n",
|
| 519 |
-
"import os.path\n",
|
| 520 |
-
"\n",
|
| 521 |
-
"# load_dotenv()\n",
|
| 522 |
-
"\n",
|
| 523 |
-
"# relative_dir = os.path.dirname(__file__)\n",
|
| 524 |
-
"# abs_file_path = os.path.join(relative_dir, CATEGORY_REFERENCE_OUTPUT_FILE)\n",
|
| 525 |
-
"\n",
|
| 526 |
-
"async def apply_categorization():\n",
|
| 527 |
-
" processed_file = process_file(\"app/transactions_rag/transactions_2024.csv\")\n",
|
| 528 |
-
"\n",
|
| 529 |
-
" print(\"\\nProcessing file\")\n",
|
| 530 |
-
" result = await asyncio.gather(processed_file)\n",
|
| 531 |
-
"\n",
|
| 532 |
-
" await save_results(result)\n",
|
| 533 |
-
" print(result)\n",
|
| 534 |
-
"\n",
|
| 535 |
-
" output_file = open(CATEGORY_REFERENCE_OUTPUT_FILE, \"r+\")\n",
|
| 536 |
-
" print(output_file.readlines())\n",
|
| 537 |
-
"\n",
|
| 538 |
-
"\n",
|
| 539 |
-
"result = await apply_categorization()\n",
|
| 540 |
-
"print(result)\n"
|
| 541 |
-
]
|
| 542 |
-
}
|
| 543 |
-
],
|
| 544 |
-
"metadata": {
|
| 545 |
-
"kernelspec": {
|
| 546 |
-
"display_name": "Python 3.8.9 64-bit",
|
| 547 |
-
"language": "python",
|
| 548 |
-
"name": "python3"
|
| 549 |
-
},
|
| 550 |
-
"language_info": {
|
| 551 |
-
"codemirror_mode": {
|
| 552 |
-
"name": "ipython",
|
| 553 |
-
"version": 3
|
| 554 |
-
},
|
| 555 |
-
"file_extension": ".py",
|
| 556 |
-
"mimetype": "text/x-python",
|
| 557 |
-
"name": "python",
|
| 558 |
-
"nbconvert_exporter": "python",
|
| 559 |
-
"pygments_lexer": "ipython3",
|
| 560 |
-
"version": "3.8.9"
|
| 561 |
-
},
|
| 562 |
-
"orig_nbformat": 4,
|
| 563 |
-
"vscode": {
|
| 564 |
-
"interpreter": {
|
| 565 |
-
"hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6"
|
| 566 |
-
}
|
| 567 |
-
}
|
| 568 |
-
},
|
| 569 |
-
"nbformat": 4,
|
| 570 |
-
"nbformat_minor": 2
|
| 571 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/transactions_rag/transactions_2024.csv
DELETED
|
@@ -1,29 +0,0 @@
|
|
| 1 |
-
Date,Name / Description,Expense/Income,Amount
|
| 2 |
-
2023-12-30,Comcast Internet,Expense,9.96
|
| 3 |
-
2023-12-30,Lemonade Home Insurance,Expense,17.53
|
| 4 |
-
2023-12-30,Monthly Appartment Rent,Expense,2000.0
|
| 5 |
-
2023-12-30,Staples Office Supplies,Expense,12.46
|
| 6 |
-
2023-12-29,Selling Paintings,Income,13.63
|
| 7 |
-
2023-12-29,Spotify,Expense,12.19
|
| 8 |
-
2023-12-23,Target,Expense,27.08
|
| 9 |
-
2023-12-22,IT Consulting,Income,541.57
|
| 10 |
-
2023-12-22,Phone,Expense,10.7
|
| 11 |
-
2023-12-20,ML Consulting,Income,2641.93
|
| 12 |
-
2023-12-19,Chipotle,Expense,18.9
|
| 13 |
-
2023-12-18,Cold Brew Coffee,Expense,17.67
|
| 14 |
-
2023-12-18,Gas,Expense,8.80
|
| 15 |
-
2023-12-18,CA Property Tax,Expense,1670.34
|
| 16 |
-
2022-11-26,Salary,Income,4000.36
|
| 17 |
-
2022-11-26,Cellphone,Expense,19.27
|
| 18 |
-
2022-11-26,Gym Membership,Expense,24.71
|
| 19 |
-
2022-11-25,Wholefoods,Expense,17.35
|
| 20 |
-
2022-11-24,Freelancing,Income,2409.55
|
| 21 |
-
2022-11-19,Spotify,Expense,20.76
|
| 22 |
-
2022-10-25,Blogging,Income,4044.27
|
| 23 |
-
2022-10-24,Uber Taxi,Expense,18.9
|
| 24 |
-
2022-10-23,Uber Taxi,Expense,27.54
|
| 25 |
-
2022-10-22,Apple Services,Expense,41.25
|
| 26 |
-
2022-10-21,Netflix,Expense,22.8
|
| 27 |
-
2022-01-16,Amazon Lux,Expense,24.11
|
| 28 |
-
2022-01-15,Burger King,Expense,30.08
|
| 29 |
-
2022-01-14,Amazon,Expense,11.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
config/index.py
DELETED
|
@@ -1,34 +0,0 @@
|
|
| 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 = int(os.getenv("POSTGRES_DB_PORT", 5432))
|
| 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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data/.gitkeep
DELETED
|
File without changes
|
data/tx_data/input/transactions_2024.csv
DELETED
|
@@ -1,28 +0,0 @@
|
|
| 1 |
-
2023-12-30,Comcast Internet,Expense,9.96
|
| 2 |
-
2023-12-30,Lemonade Home Insurance,Expense,17.53
|
| 3 |
-
2023-12-30,Monthly Appartment Rent,Expense,2000.0
|
| 4 |
-
2023-12-30,Staples Office Supplies,Expense,12.46
|
| 5 |
-
2023-12-29,Selling Paintings,Income,13.63
|
| 6 |
-
2023-12-29,Spotify,Expense,12.19
|
| 7 |
-
2023-12-23,Target,Expense,27.08
|
| 8 |
-
2023-12-22,IT Consulting,Income,541.57
|
| 9 |
-
2023-12-22,Phone,Expense,10.7
|
| 10 |
-
2023-12-20,ML Consulting,Income,2641.93
|
| 11 |
-
2023-12-19,Chipotle,Expense,18.9
|
| 12 |
-
2023-12-18,Cold Brew Coffee,Expense,17.67
|
| 13 |
-
2023-12-18,Gas,Expense,8.80
|
| 14 |
-
2023-12-18,CA Property Tax,Expense,1670.34
|
| 15 |
-
2022-11-26,Salary,Income,4000.36
|
| 16 |
-
2022-11-26,Cellphone,Expense,19.27
|
| 17 |
-
2022-11-26,Gym Membership,Expense,24.71
|
| 18 |
-
2022-11-25,Wholefoods,Expense,17.35
|
| 19 |
-
2022-11-24,Freelancing,Income,2409.55
|
| 20 |
-
2022-11-19,Spotify,Expense,20.76
|
| 21 |
-
2022-10-25,Blogging,Income,4044.27
|
| 22 |
-
2022-10-24,Uber Taxi,Expense,18.9
|
| 23 |
-
2022-10-23,Uber Taxi,Expense,27.54
|
| 24 |
-
2022-10-22,Apple Services,Expense,41.25
|
| 25 |
-
2022-10-21,Netflix,Expense,22.8
|
| 26 |
-
2022-01-16,Amazon Lux,Expense,24.11
|
| 27 |
-
2022-01-15,Burger King,Expense,30.08
|
| 28 |
-
2022-01-14,Amazon,Expense,11.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
main.py
CHANGED
|
@@ -1,74 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.api.routers.transaction import transaction_router
|
| 11 |
-
from app.api.routers.file_upload import file_upload_router
|
| 12 |
-
from app.api.routers.income_statement import income_statement_router
|
| 13 |
from app.settings import init_settings
|
|
|
|
| 14 |
from fastapi.staticfiles import StaticFiles
|
| 15 |
-
from alembic.config import Config
|
| 16 |
-
from alembic import command
|
| 17 |
-
|
| 18 |
|
| 19 |
-
from app.engine.postgresdb import postgresdb
|
| 20 |
-
from config.index import config as env
|
| 21 |
-
|
| 22 |
-
logger = logging.getLogger("uvicorn")
|
| 23 |
|
|
|
|
| 24 |
|
| 25 |
init_settings()
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
def init_app(init_db: bool = True) -> FastAPI:
|
| 30 |
-
lifespan = None
|
| 31 |
|
| 32 |
-
|
| 33 |
-
postgresdb.init(env.SQLALCHEMY_DATABASE_URL, {"echo": True, "future": True})
|
| 34 |
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
|
| 41 |
-
|
|
|
|
|
|
|
|
|
|
| 42 |
|
| 43 |
-
if env.ENVIRONMENT == "dev":
|
| 44 |
-
logger.warning("Running in development mode - allowing CORS for all origins")
|
| 45 |
-
app.add_middleware(
|
| 46 |
-
CORSMiddleware,
|
| 47 |
-
allow_origins=["*"],
|
| 48 |
-
allow_credentials=True,
|
| 49 |
-
allow_methods=["*"],
|
| 50 |
-
allow_headers=["*"],
|
| 51 |
-
)
|
| 52 |
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
app.include_router(user_router)
|
| 57 |
-
app.include_router(transaction_router)
|
| 58 |
-
app.include_router(file_upload_router)
|
| 59 |
-
app.include_router(income_statement_router)
|
| 60 |
-
|
| 61 |
-
return app
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
app = init_app()
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
# Redirect to documentation page when accessing base URL
|
| 68 |
-
@app.get("/")
|
| 69 |
-
async def redirect_to_docs():
|
| 70 |
-
return RedirectResponse(url="/docs")
|
| 71 |
|
| 72 |
|
| 73 |
if __name__ == "__main__":
|
| 74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from dotenv import load_dotenv
|
| 2 |
+
|
| 3 |
+
load_dotenv()
|
| 4 |
+
|
| 5 |
import logging
|
| 6 |
import os
|
| 7 |
import uvicorn
|
|
|
|
| 8 |
from fastapi import FastAPI
|
| 9 |
from fastapi.middleware.cors import CORSMiddleware
|
| 10 |
from fastapi.responses import RedirectResponse
|
| 11 |
from app.api.routers.chat import chat_router
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
from app.settings import init_settings
|
| 13 |
+
from app.observability import init_observability
|
| 14 |
from fastapi.staticfiles import StaticFiles
|
|
|
|
|
|
|
|
|
|
| 15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
+
app = FastAPI()
|
| 18 |
|
| 19 |
init_settings()
|
| 20 |
+
init_observability()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
+
environment = os.getenv("ENVIRONMENT", "dev") # Default to 'development' if not set
|
|
|
|
| 23 |
|
| 24 |
+
if environment == "dev":
|
| 25 |
+
logger = logging.getLogger("uvicorn")
|
| 26 |
+
logger.warning("Running in development mode - allowing CORS for all origins")
|
| 27 |
+
app.add_middleware(
|
| 28 |
+
CORSMiddleware,
|
| 29 |
+
allow_origins=["*"],
|
| 30 |
+
allow_credentials=True,
|
| 31 |
+
allow_methods=["*"],
|
| 32 |
+
allow_headers=["*"],
|
| 33 |
+
)
|
| 34 |
|
| 35 |
+
# Redirect to documentation page when accessing base URL
|
| 36 |
+
@app.get("/")
|
| 37 |
+
async def redirect_to_docs():
|
| 38 |
+
return RedirectResponse(url="/docs")
|
| 39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
|
| 41 |
+
if os.path.exists("data"):
|
| 42 |
+
app.mount("/api/data", StaticFiles(directory="data"), name="static")
|
| 43 |
+
app.include_router(chat_router, prefix="/api/chat")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
|
| 45 |
|
| 46 |
if __name__ == "__main__":
|
| 47 |
+
app_host = os.getenv("APP_HOST", "0.0.0.0")
|
| 48 |
+
app_port = int(os.getenv("APP_PORT", "8000"))
|
| 49 |
+
reload = True if environment == "dev" else False
|
| 50 |
+
|
| 51 |
+
uvicorn.run(app="main:app", host=app_host, port=app_port, reload=reload)
|
migration/README
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
Generic single-database configuration.
|
|
|
|
|
|
migration/env.py
DELETED
|
@@ -1,91 +0,0 @@
|
|
| 1 |
-
import asyncio
|
| 2 |
-
from logging.config import fileConfig
|
| 3 |
-
|
| 4 |
-
from app.engine.postgresdb import Base
|
| 5 |
-
from sqlalchemy import Connection
|
| 6 |
-
from sqlalchemy import pool
|
| 7 |
-
from sqlalchemy.ext.asyncio import 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.
|
| 21 |
-
if config.config_file_name is not None:
|
| 22 |
-
fileConfig(config.config_file_name)
|
| 23 |
-
|
| 24 |
-
# Import all models so they're registered with SQLAlchemy.
|
| 25 |
-
import app.model.user
|
| 26 |
-
import app.model.transaction
|
| 27 |
-
import app.model.income_statement
|
| 28 |
-
|
| 29 |
-
# add your model's MetaData object here
|
| 30 |
-
# for 'autogenerate' support
|
| 31 |
-
# from myapp import mymodel
|
| 32 |
-
# target_metadata = mymodel.Base.metadata
|
| 33 |
-
target_metadata = Base.metadata
|
| 34 |
-
|
| 35 |
-
# other values from the config, defined by the needs of env.py,
|
| 36 |
-
# can be acquired:
|
| 37 |
-
# my_important_option = config.get_main_option("my_important_option")
|
| 38 |
-
# ... etc.
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
def run_migrations_offline():
|
| 42 |
-
"""Run migrations in 'offline' mode.
|
| 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 |
-
Calls to context.execute() here emit the given string to the
|
| 48 |
-
script output.
|
| 49 |
-
"""
|
| 50 |
-
url = config.get_main_option("sqlalchemy.url")
|
| 51 |
-
context.configure(
|
| 52 |
-
url=url,
|
| 53 |
-
target_metadata=target_metadata,
|
| 54 |
-
literal_binds=True,
|
| 55 |
-
dialect_opts={"paramstyle": "named"},
|
| 56 |
-
)
|
| 57 |
-
|
| 58 |
-
with context.begin_transaction():
|
| 59 |
-
context.run_migrations()
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
def do_run_migrations(connection: Connection) -> None:
|
| 63 |
-
context.configure(connection=connection, target_metadata=target_metadata)
|
| 64 |
-
|
| 65 |
-
with context.begin_transaction():
|
| 66 |
-
context.run_migrations()
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
async def run_migrations_online():
|
| 70 |
-
"""Run migrations in 'online' mode.
|
| 71 |
-
In this scenario we need to create an Engine
|
| 72 |
-
and associate a connection with the context.
|
| 73 |
-
"""
|
| 74 |
-
configuration = config.get_section(config.config_ini_section)
|
| 75 |
-
configuration["sqlalchemy.url"] = env.SQLALCHEMY_DATABASE_URL
|
| 76 |
-
connectable = async_engine_from_config(
|
| 77 |
-
configuration,
|
| 78 |
-
prefix="sqlalchemy.",
|
| 79 |
-
poolclass=pool.NullPool,
|
| 80 |
-
)
|
| 81 |
-
|
| 82 |
-
async with connectable.connect() as connection:
|
| 83 |
-
await connection.run_sync(do_run_migrations)
|
| 84 |
-
|
| 85 |
-
await connectable.dispose()
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
if context.is_offline_mode():
|
| 89 |
-
run_migrations_offline()
|
| 90 |
-
else:
|
| 91 |
-
asyncio.run(run_migrations_online())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
migration/script.py.mako
DELETED
|
@@ -1,26 +0,0 @@
|
|
| 1 |
-
"""${message}
|
| 2 |
-
|
| 3 |
-
Revision ID: ${up_revision}
|
| 4 |
-
Revises: ${down_revision | comma,n}
|
| 5 |
-
Create Date: ${create_date}
|
| 6 |
-
|
| 7 |
-
"""
|
| 8 |
-
from typing import Sequence, Union
|
| 9 |
-
|
| 10 |
-
from alembic import op
|
| 11 |
-
import sqlalchemy as sa
|
| 12 |
-
${imports if imports else ""}
|
| 13 |
-
|
| 14 |
-
# revision identifiers, used by Alembic.
|
| 15 |
-
revision: str = ${repr(up_revision)}
|
| 16 |
-
down_revision: Union[str, None] = ${repr(down_revision)}
|
| 17 |
-
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
|
| 18 |
-
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
def upgrade() -> None:
|
| 22 |
-
${upgrades if upgrades else "pass"}
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
def downgrade() -> None:
|
| 26 |
-
${downgrades if downgrades else "pass"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
migration/versions/4e76691ab103_add_income_statement_table.py
DELETED
|
@@ -1,43 +0,0 @@
|
|
| 1 |
-
"""add_income_statement_table
|
| 2 |
-
|
| 3 |
-
Revision ID: 4e76691ab103
|
| 4 |
-
Revises: 7ea44cbc5b1f
|
| 5 |
-
Create Date: 2024-06-03 23:59:03.717778
|
| 6 |
-
|
| 7 |
-
"""
|
| 8 |
-
from typing import Sequence, Union
|
| 9 |
-
|
| 10 |
-
from alembic import op
|
| 11 |
-
import sqlalchemy as sa
|
| 12 |
-
from sqlalchemy.dialects import postgresql
|
| 13 |
-
|
| 14 |
-
# revision identifiers, used by Alembic.
|
| 15 |
-
revision: str = '4e76691ab103'
|
| 16 |
-
down_revision: Union[str, None] = '7ea44cbc5b1f'
|
| 17 |
-
branch_labels: Union[str, Sequence[str], None] = None
|
| 18 |
-
depends_on: Union[str, Sequence[str], None] = None
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
def upgrade() -> None:
|
| 22 |
-
# ### commands auto generated by Alembic - please adjust! ###
|
| 23 |
-
op.create_table('income_statement',
|
| 24 |
-
sa.Column('income', postgresql.JSON(astext_type=sa.Text()), nullable=True),
|
| 25 |
-
sa.Column('expenses', postgresql.JSON(astext_type=sa.Text()), nullable=True),
|
| 26 |
-
sa.Column('date_from', sa.DateTime(), nullable=False),
|
| 27 |
-
sa.Column('date_to', sa.DateTime(), nullable=False),
|
| 28 |
-
sa.Column('user_id', sa.Integer(), nullable=False),
|
| 29 |
-
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
| 30 |
-
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
|
| 31 |
-
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
|
| 32 |
-
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
| 33 |
-
sa.PrimaryKeyConstraint('id')
|
| 34 |
-
)
|
| 35 |
-
op.create_index(op.f('ix_income_statement_id'), 'income_statement', ['id'], unique=False)
|
| 36 |
-
# ### end Alembic commands ###
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
def downgrade() -> None:
|
| 40 |
-
# ### commands auto generated by Alembic - please adjust! ###
|
| 41 |
-
op.drop_index(op.f('ix_income_statement_id'), table_name='income_statement')
|
| 42 |
-
op.drop_table('income_statement')
|
| 43 |
-
# ### end Alembic commands ###
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
migration/versions/7ea44cbc5b1f_default_timezone.py
DELETED
|
@@ -1,69 +0,0 @@
|
|
| 1 |
-
"""default_timezone
|
| 2 |
-
|
| 3 |
-
Revision ID: 7ea44cbc5b1f
|
| 4 |
-
Revises: 8feaedca36f9
|
| 5 |
-
Create Date: 2024-06-02 14:36:01.552518
|
| 6 |
-
|
| 7 |
-
"""
|
| 8 |
-
|
| 9 |
-
from typing import Sequence, Union
|
| 10 |
-
|
| 11 |
-
from alembic import op
|
| 12 |
-
import sqlalchemy as sa
|
| 13 |
-
from sqlalchemy.dialects import postgresql
|
| 14 |
-
|
| 15 |
-
# revision identifiers, used by Alembic.
|
| 16 |
-
revision: str = "7ea44cbc5b1f"
|
| 17 |
-
down_revision: Union[str, None] = "8feaedca36f9"
|
| 18 |
-
branch_labels: Union[str, Sequence[str], None] = None
|
| 19 |
-
depends_on: Union[str, Sequence[str], None] = None
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
def upgrade() -> None:
|
| 23 |
-
# ### commands auto generated by Alembic - please adjust! ###
|
| 24 |
-
op.alter_column(
|
| 25 |
-
"transactions",
|
| 26 |
-
"created_at",
|
| 27 |
-
existing_type=postgresql.TIMESTAMP(),
|
| 28 |
-
type_=sa.DateTime(timezone=True),
|
| 29 |
-
nullable=False,
|
| 30 |
-
)
|
| 31 |
-
op.alter_column(
|
| 32 |
-
"transactions",
|
| 33 |
-
"updated_at",
|
| 34 |
-
existing_type=postgresql.TIMESTAMP(),
|
| 35 |
-
type_=sa.DateTime(timezone=True),
|
| 36 |
-
nullable=False,
|
| 37 |
-
)
|
| 38 |
-
op.alter_column(
|
| 39 |
-
"users", "created_at", existing_type=postgresql.TIMESTAMP(), type_=sa.DateTime(timezone=True), nullable=False
|
| 40 |
-
)
|
| 41 |
-
op.alter_column(
|
| 42 |
-
"users", "updated_at", existing_type=postgresql.TIMESTAMP(), type_=sa.DateTime(timezone=True), nullable=False
|
| 43 |
-
)
|
| 44 |
-
# ### end Alembic commands ###
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
def downgrade() -> None:
|
| 48 |
-
# ### commands auto generated by Alembic - please adjust! ###
|
| 49 |
-
op.alter_column(
|
| 50 |
-
"users", "updated_at", existing_type=sa.DateTime(timezone=True), type_=postgresql.TIMESTAMP(), nullable=True
|
| 51 |
-
)
|
| 52 |
-
op.alter_column(
|
| 53 |
-
"users", "created_at", existing_type=sa.DateTime(timezone=True), type_=postgresql.TIMESTAMP(), nullable=True
|
| 54 |
-
)
|
| 55 |
-
op.alter_column(
|
| 56 |
-
"transactions",
|
| 57 |
-
"updated_at",
|
| 58 |
-
existing_type=sa.DateTime(timezone=True),
|
| 59 |
-
type_=postgresql.TIMESTAMP(),
|
| 60 |
-
nullable=True,
|
| 61 |
-
)
|
| 62 |
-
op.alter_column(
|
| 63 |
-
"transactions",
|
| 64 |
-
"created_at",
|
| 65 |
-
existing_type=sa.DateTime(timezone=True),
|
| 66 |
-
type_=postgresql.TIMESTAMP(),
|
| 67 |
-
nullable=True,
|
| 68 |
-
)
|
| 69 |
-
# ### end Alembic commands ###
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
migration/versions/8feaedca36f9_users_datatype_updates.py
DELETED
|
@@ -1,74 +0,0 @@
|
|
| 1 |
-
"""Remove_Transactions_From_Users
|
| 2 |
-
|
| 3 |
-
Revision ID: 8feaedca36f9
|
| 4 |
-
Revises: cd515c44401d
|
| 5 |
-
Create Date: 2024-06-02 01:26:54.731002
|
| 6 |
-
|
| 7 |
-
"""
|
| 8 |
-
from typing import Sequence, Union
|
| 9 |
-
|
| 10 |
-
from alembic import op
|
| 11 |
-
import sqlalchemy as sa
|
| 12 |
-
from sqlalchemy.dialects import postgresql
|
| 13 |
-
|
| 14 |
-
# revision identifiers, used by Alembic.
|
| 15 |
-
revision: str = '8feaedca36f9'
|
| 16 |
-
down_revision: Union[str, None] = 'cd515c44401d'
|
| 17 |
-
branch_labels: Union[str, Sequence[str], None] = None
|
| 18 |
-
depends_on: Union[str, Sequence[str], None] = None
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
def upgrade() -> None:
|
| 22 |
-
# ### commands auto generated by Alembic - please adjust! ###
|
| 23 |
-
op.alter_column('transactions', 'transaction_date',
|
| 24 |
-
existing_type=postgresql.TIMESTAMP(),
|
| 25 |
-
nullable=False)
|
| 26 |
-
op.alter_column('transactions', 'category',
|
| 27 |
-
existing_type=sa.VARCHAR(),
|
| 28 |
-
nullable=False)
|
| 29 |
-
op.alter_column('transactions', 'name_description',
|
| 30 |
-
existing_type=sa.VARCHAR(),
|
| 31 |
-
nullable=False)
|
| 32 |
-
op.alter_column('transactions', 'amount',
|
| 33 |
-
existing_type=sa.DOUBLE_PRECISION(precision=53),
|
| 34 |
-
nullable=False)
|
| 35 |
-
op.alter_column('transactions', 'type',
|
| 36 |
-
existing_type=sa.VARCHAR(),
|
| 37 |
-
nullable=False)
|
| 38 |
-
op.add_column('users', sa.Column('hashed_password', sa.String(), nullable=False))
|
| 39 |
-
op.add_column('users', sa.Column('is_deleted', sa.Boolean(), nullable=False))
|
| 40 |
-
op.alter_column('users', 'name',
|
| 41 |
-
existing_type=sa.VARCHAR(),
|
| 42 |
-
nullable=False)
|
| 43 |
-
op.alter_column('users', 'email',
|
| 44 |
-
existing_type=sa.VARCHAR(),
|
| 45 |
-
nullable=False)
|
| 46 |
-
# ### end Alembic commands ###
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
def downgrade() -> None:
|
| 50 |
-
# ### commands auto generated by Alembic - please adjust! ###
|
| 51 |
-
op.alter_column('users', 'email',
|
| 52 |
-
existing_type=sa.VARCHAR(),
|
| 53 |
-
nullable=True)
|
| 54 |
-
op.alter_column('users', 'name',
|
| 55 |
-
existing_type=sa.VARCHAR(),
|
| 56 |
-
nullable=True)
|
| 57 |
-
op.drop_column('users', 'is_deleted')
|
| 58 |
-
op.drop_column('users', 'hashed_password')
|
| 59 |
-
op.alter_column('transactions', 'type',
|
| 60 |
-
existing_type=sa.VARCHAR(),
|
| 61 |
-
nullable=True)
|
| 62 |
-
op.alter_column('transactions', 'amount',
|
| 63 |
-
existing_type=sa.DOUBLE_PRECISION(precision=53),
|
| 64 |
-
nullable=True)
|
| 65 |
-
op.alter_column('transactions', 'name_description',
|
| 66 |
-
existing_type=sa.VARCHAR(),
|
| 67 |
-
nullable=True)
|
| 68 |
-
op.alter_column('transactions', 'category',
|
| 69 |
-
existing_type=sa.VARCHAR(),
|
| 70 |
-
nullable=True)
|
| 71 |
-
op.alter_column('transactions', 'transaction_date',
|
| 72 |
-
existing_type=postgresql.TIMESTAMP(),
|
| 73 |
-
nullable=True)
|
| 74 |
-
# ### end Alembic commands ###
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
migration/versions/cd515c44401d_add_users_transactions_tables.py
DELETED
|
@@ -1,55 +0,0 @@
|
|
| 1 |
-
"""Add users, transactions tables
|
| 2 |
-
|
| 3 |
-
Revision ID: cd515c44401d
|
| 4 |
-
Revises:
|
| 5 |
-
Create Date: 2024-06-01 01:32:42.500524
|
| 6 |
-
|
| 7 |
-
"""
|
| 8 |
-
from typing import Sequence, Union
|
| 9 |
-
|
| 10 |
-
from alembic import op
|
| 11 |
-
import sqlalchemy as sa
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
# revision identifiers, used by Alembic.
|
| 15 |
-
revision: str = 'cd515c44401d'
|
| 16 |
-
down_revision: Union[str, None] = None
|
| 17 |
-
branch_labels: Union[str, Sequence[str], None] = None
|
| 18 |
-
depends_on: Union[str, Sequence[str], None] = None
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
def upgrade() -> None:
|
| 22 |
-
# ### commands auto generated by Alembic - please adjust! ###
|
| 23 |
-
op.create_table('users',
|
| 24 |
-
sa.Column('name', sa.String(), nullable=True),
|
| 25 |
-
sa.Column('email', sa.String(), nullable=True),
|
| 26 |
-
sa.Column('id', sa.Integer(), nullable=False),
|
| 27 |
-
sa.Column('created_at', sa.DateTime(), nullable=True),
|
| 28 |
-
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
| 29 |
-
sa.PrimaryKeyConstraint('id')
|
| 30 |
-
)
|
| 31 |
-
op.create_index(op.f('ix_users_id'), 'users', ['id'], unique=False)
|
| 32 |
-
op.create_table('transactions',
|
| 33 |
-
sa.Column('transaction_date', sa.DateTime(), nullable=True),
|
| 34 |
-
sa.Column('category', sa.String(), nullable=True),
|
| 35 |
-
sa.Column('name_description', sa.String(), nullable=True),
|
| 36 |
-
sa.Column('amount', sa.Float(), nullable=True),
|
| 37 |
-
sa.Column('type', sa.String(), nullable=True),
|
| 38 |
-
sa.Column('user_id', sa.Integer(), nullable=True),
|
| 39 |
-
sa.Column('id', sa.Integer(), nullable=False),
|
| 40 |
-
sa.Column('created_at', sa.DateTime(), nullable=True),
|
| 41 |
-
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
| 42 |
-
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
| 43 |
-
sa.PrimaryKeyConstraint('id')
|
| 44 |
-
)
|
| 45 |
-
op.create_index(op.f('ix_transactions_id'), 'transactions', ['id'], unique=False)
|
| 46 |
-
# ### end Alembic commands ###
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
def downgrade() -> None:
|
| 50 |
-
# ### commands auto generated by Alembic - please adjust! ###
|
| 51 |
-
op.drop_index(op.f('ix_transactions_id'), table_name='transactions')
|
| 52 |
-
op.drop_table('transactions')
|
| 53 |
-
op.drop_index(op.f('ix_users_id'), table_name='users')
|
| 54 |
-
op.drop_table('users')
|
| 55 |
-
# ### end Alembic commands ###
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
poetry.lock
CHANGED
|
@@ -123,25 +123,6 @@ files = [
|
|
| 123 |
[package.dependencies]
|
| 124 |
typing-extensions = "*"
|
| 125 |
|
| 126 |
-
[[package]]
|
| 127 |
-
name = "alembic"
|
| 128 |
-
version = "1.13.1"
|
| 129 |
-
description = "A database migration tool for SQLAlchemy."
|
| 130 |
-
optional = false
|
| 131 |
-
python-versions = ">=3.8"
|
| 132 |
-
files = [
|
| 133 |
-
{file = "alembic-1.13.1-py3-none-any.whl", hash = "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43"},
|
| 134 |
-
{file = "alembic-1.13.1.tar.gz", hash = "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595"},
|
| 135 |
-
]
|
| 136 |
-
|
| 137 |
-
[package.dependencies]
|
| 138 |
-
Mako = "*"
|
| 139 |
-
SQLAlchemy = ">=1.3.0"
|
| 140 |
-
typing-extensions = ">=4"
|
| 141 |
-
|
| 142 |
-
[package.extras]
|
| 143 |
-
tz = ["backports.zoneinfo"]
|
| 144 |
-
|
| 145 |
[[package]]
|
| 146 |
name = "annotated-types"
|
| 147 |
version = "0.7.0"
|
|
@@ -198,87 +179,6 @@ 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 = "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"
|
|
@@ -480,28 +380,6 @@ files = [
|
|
| 480 |
marshmallow = ">=3.18.0,<4.0.0"
|
| 481 |
typing-inspect = ">=0.4.0,<1"
|
| 482 |
|
| 483 |
-
[[package]]
|
| 484 |
-
name = "dateparser"
|
| 485 |
-
version = "1.2.0"
|
| 486 |
-
description = "Date parsing library designed to parse dates from HTML pages"
|
| 487 |
-
optional = false
|
| 488 |
-
python-versions = ">=3.7"
|
| 489 |
-
files = [
|
| 490 |
-
{file = "dateparser-1.2.0-py2.py3-none-any.whl", hash = "sha256:0b21ad96534e562920a0083e97fd45fa959882d4162acc358705144520a35830"},
|
| 491 |
-
{file = "dateparser-1.2.0.tar.gz", hash = "sha256:7975b43a4222283e0ae15be7b4999d08c9a70e2d378ac87385b1ccf2cffbbb30"},
|
| 492 |
-
]
|
| 493 |
-
|
| 494 |
-
[package.dependencies]
|
| 495 |
-
python-dateutil = "*"
|
| 496 |
-
pytz = "*"
|
| 497 |
-
regex = "<2019.02.19 || >2019.02.19,<2021.8.27 || >2021.8.27"
|
| 498 |
-
tzlocal = "*"
|
| 499 |
-
|
| 500 |
-
[package.extras]
|
| 501 |
-
calendars = ["convertdate", "hijri-converter"]
|
| 502 |
-
fasttext = ["fasttext"]
|
| 503 |
-
langdetect = ["langdetect"]
|
| 504 |
-
|
| 505 |
[[package]]
|
| 506 |
name = "deprecated"
|
| 507 |
version = "1.2.14"
|
|
@@ -541,20 +419,6 @@ files = [
|
|
| 541 |
{file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"},
|
| 542 |
]
|
| 543 |
|
| 544 |
-
[[package]]
|
| 545 |
-
name = "execnet"
|
| 546 |
-
version = "2.1.1"
|
| 547 |
-
description = "execnet: rapid multi-Python deployment"
|
| 548 |
-
optional = false
|
| 549 |
-
python-versions = ">=3.8"
|
| 550 |
-
files = [
|
| 551 |
-
{file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"},
|
| 552 |
-
{file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"},
|
| 553 |
-
]
|
| 554 |
-
|
| 555 |
-
[package.extras]
|
| 556 |
-
testing = ["hatch", "pre-commit", "pytest", "tox"]
|
| 557 |
-
|
| 558 |
[[package]]
|
| 559 |
name = "fastapi"
|
| 560 |
version = "0.109.2"
|
|
@@ -1039,17 +903,6 @@ files = [
|
|
| 1039 |
{file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"},
|
| 1040 |
]
|
| 1041 |
|
| 1042 |
-
[[package]]
|
| 1043 |
-
name = "iniconfig"
|
| 1044 |
-
version = "2.0.0"
|
| 1045 |
-
description = "brain-dead simple config-ini parsing"
|
| 1046 |
-
optional = false
|
| 1047 |
-
python-versions = ">=3.7"
|
| 1048 |
-
files = [
|
| 1049 |
-
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
|
| 1050 |
-
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
|
| 1051 |
-
]
|
| 1052 |
-
|
| 1053 |
[[package]]
|
| 1054 |
name = "jinja2"
|
| 1055 |
version = "3.1.4"
|
|
@@ -1148,139 +1001,6 @@ files = [
|
|
| 1148 |
{file = "joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e"},
|
| 1149 |
]
|
| 1150 |
|
| 1151 |
-
[[package]]
|
| 1152 |
-
name = "jsonpatch"
|
| 1153 |
-
version = "1.33"
|
| 1154 |
-
description = "Apply JSON-Patches (RFC 6902)"
|
| 1155 |
-
optional = false
|
| 1156 |
-
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*"
|
| 1157 |
-
files = [
|
| 1158 |
-
{file = "jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade"},
|
| 1159 |
-
{file = "jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c"},
|
| 1160 |
-
]
|
| 1161 |
-
|
| 1162 |
-
[package.dependencies]
|
| 1163 |
-
jsonpointer = ">=1.9"
|
| 1164 |
-
|
| 1165 |
-
[[package]]
|
| 1166 |
-
name = "jsonpointer"
|
| 1167 |
-
version = "2.4"
|
| 1168 |
-
description = "Identify specific nodes in a JSON document (RFC 6901)"
|
| 1169 |
-
optional = false
|
| 1170 |
-
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*"
|
| 1171 |
-
files = [
|
| 1172 |
-
{file = "jsonpointer-2.4-py2.py3-none-any.whl", hash = "sha256:15d51bba20eea3165644553647711d150376234112651b4f1811022aecad7d7a"},
|
| 1173 |
-
{file = "jsonpointer-2.4.tar.gz", hash = "sha256:585cee82b70211fa9e6043b7bb89db6e1aa49524340dde8ad6b63206ea689d88"},
|
| 1174 |
-
]
|
| 1175 |
-
|
| 1176 |
-
[[package]]
|
| 1177 |
-
name = "langchain"
|
| 1178 |
-
version = "0.2.1"
|
| 1179 |
-
description = "Building applications with LLMs through composability"
|
| 1180 |
-
optional = false
|
| 1181 |
-
python-versions = "<4.0,>=3.8.1"
|
| 1182 |
-
files = [
|
| 1183 |
-
{file = "langchain-0.2.1-py3-none-any.whl", hash = "sha256:3e13bf97c5717bce2c281f5117e8778823e8ccf62d949e73d3869448962b1c97"},
|
| 1184 |
-
{file = "langchain-0.2.1.tar.gz", hash = "sha256:5758a315e1ac92eb26dafec5ad0fafa03cafa686aba197d5bb0b1dd28cc03ebe"},
|
| 1185 |
-
]
|
| 1186 |
-
|
| 1187 |
-
[package.dependencies]
|
| 1188 |
-
aiohttp = ">=3.8.3,<4.0.0"
|
| 1189 |
-
langchain-core = ">=0.2.0,<0.3.0"
|
| 1190 |
-
langchain-text-splitters = ">=0.2.0,<0.3.0"
|
| 1191 |
-
langsmith = ">=0.1.17,<0.2.0"
|
| 1192 |
-
numpy = ">=1,<2"
|
| 1193 |
-
pydantic = ">=1,<3"
|
| 1194 |
-
PyYAML = ">=5.3"
|
| 1195 |
-
requests = ">=2,<3"
|
| 1196 |
-
SQLAlchemy = ">=1.4,<3"
|
| 1197 |
-
tenacity = ">=8.1.0,<9.0.0"
|
| 1198 |
-
|
| 1199 |
-
[package.extras]
|
| 1200 |
-
azure = ["azure-ai-formrecognizer (>=3.2.1,<4.0.0)", "azure-ai-textanalytics (>=5.3.0,<6.0.0)", "azure-cognitiveservices-speech (>=1.28.0,<2.0.0)", "azure-core (>=1.26.4,<2.0.0)", "azure-cosmos (>=4.4.0b1,<5.0.0)", "azure-identity (>=1.12.0,<2.0.0)", "azure-search-documents (==11.4.0b8)", "openai (<2)"]
|
| 1201 |
-
clarifai = ["clarifai (>=9.1.0)"]
|
| 1202 |
-
cli = ["typer (>=0.9.0,<0.10.0)"]
|
| 1203 |
-
cohere = ["cohere (>=4,<6)"]
|
| 1204 |
-
docarray = ["docarray[hnswlib] (>=0.32.0,<0.33.0)"]
|
| 1205 |
-
embeddings = ["sentence-transformers (>=2,<3)"]
|
| 1206 |
-
extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.0,<3.0.0)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.0,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "cohere (>=4,<6)", "couchbase (>=4.1.9,<5.0.0)", "dashvector (>=1.0.1,<2.0.0)", "databricks-vectorsearch (>=0.21,<0.22)", "datasets (>=2.15.0,<3.0.0)", "dgml-utils (>=0.3.0,<0.4.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "fireworks-ai (>=0.9.0,<0.10.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "google-cloud-documentai (>=2.20.1,<3.0.0)", "gql (>=3.4.1,<4.0.0)", "hologres-vector (>=0.0.6,<0.0.7)", "html2text (>=2020.1.16,<2021.0.0)", "javelin-sdk (>=0.1.8,<0.2.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "jsonschema (>1)", "langchain-openai (>=0.1,<0.2)", "lxml (>=4.9.3,<6.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "msal (>=1.25.0,<2.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "openai (<2)", "openapi-pydantic (>=0.3.2,<0.4.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "praw (>=7.7.1,<8.0.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "rdflib (==7.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "rspace_client (>=2.5.0,<3.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "upstash-redis (>=0.15.0,<0.16.0)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)"]
|
| 1207 |
-
javascript = ["esprima (>=4.0.1,<5.0.0)"]
|
| 1208 |
-
llms = ["clarifai (>=9.1.0)", "cohere (>=4,<6)", "huggingface_hub (>=0,<1)", "manifest-ml (>=0.0.1,<0.0.2)", "nlpcloud (>=1,<2)", "openai (<2)", "openlm (>=0.0.5,<0.0.6)", "torch (>=1,<3)", "transformers (>=4,<5)"]
|
| 1209 |
-
openai = ["openai (<2)", "tiktoken (>=0.7,<1.0)"]
|
| 1210 |
-
qdrant = ["qdrant-client (>=1.3.1,<2.0.0)"]
|
| 1211 |
-
text-helpers = ["chardet (>=5.1.0,<6.0.0)"]
|
| 1212 |
-
|
| 1213 |
-
[[package]]
|
| 1214 |
-
name = "langchain-core"
|
| 1215 |
-
version = "0.2.3"
|
| 1216 |
-
description = "Building applications with LLMs through composability"
|
| 1217 |
-
optional = false
|
| 1218 |
-
python-versions = "<4.0,>=3.8.1"
|
| 1219 |
-
files = [
|
| 1220 |
-
{file = "langchain_core-0.2.3-py3-none-any.whl", hash = "sha256:22189b5a3a30bfd65eb995f95e627f7c2c3acb322feb89f5f5f2fb7df21833a7"},
|
| 1221 |
-
{file = "langchain_core-0.2.3.tar.gz", hash = "sha256:fbc75a64b9c0b7655d96ca57a707df1e6c09efc1539c36adbd73260612549810"},
|
| 1222 |
-
]
|
| 1223 |
-
|
| 1224 |
-
[package.dependencies]
|
| 1225 |
-
jsonpatch = ">=1.33,<2.0"
|
| 1226 |
-
langsmith = ">=0.1.65,<0.2.0"
|
| 1227 |
-
packaging = ">=23.2,<24.0"
|
| 1228 |
-
pydantic = ">=1,<3"
|
| 1229 |
-
PyYAML = ">=5.3"
|
| 1230 |
-
tenacity = ">=8.1.0,<9.0.0"
|
| 1231 |
-
|
| 1232 |
-
[package.extras]
|
| 1233 |
-
extended-testing = ["jinja2 (>=3,<4)"]
|
| 1234 |
-
|
| 1235 |
-
[[package]]
|
| 1236 |
-
name = "langchain-openai"
|
| 1237 |
-
version = "0.1.8"
|
| 1238 |
-
description = "An integration package connecting OpenAI and LangChain"
|
| 1239 |
-
optional = false
|
| 1240 |
-
python-versions = "<4.0,>=3.8.1"
|
| 1241 |
-
files = [
|
| 1242 |
-
{file = "langchain_openai-0.1.8-py3-none-any.whl", hash = "sha256:8125c84223e9f43b05defbca64eedbcf362fd78a680de6c25e64f973b34a8063"},
|
| 1243 |
-
{file = "langchain_openai-0.1.8.tar.gz", hash = "sha256:a11fcce15def7917c44232abda6baaa63dfc79fe44be1531eea650d39a44cd95"},
|
| 1244 |
-
]
|
| 1245 |
-
|
| 1246 |
-
[package.dependencies]
|
| 1247 |
-
langchain-core = ">=0.2.2,<0.3"
|
| 1248 |
-
openai = ">=1.26.0,<2.0.0"
|
| 1249 |
-
tiktoken = ">=0.7,<1"
|
| 1250 |
-
|
| 1251 |
-
[[package]]
|
| 1252 |
-
name = "langchain-text-splitters"
|
| 1253 |
-
version = "0.2.0"
|
| 1254 |
-
description = "LangChain text splitting utilities"
|
| 1255 |
-
optional = false
|
| 1256 |
-
python-versions = "<4.0,>=3.8.1"
|
| 1257 |
-
files = [
|
| 1258 |
-
{file = "langchain_text_splitters-0.2.0-py3-none-any.whl", hash = "sha256:7b4c6a45f8471630a882b321e138329b6897102a5bc62f4c12be1c0b05bb9199"},
|
| 1259 |
-
{file = "langchain_text_splitters-0.2.0.tar.gz", hash = "sha256:b32ab4f7397f7d42c1fa3283fefc2547ba356bd63a68ee9092865e5ad83c82f9"},
|
| 1260 |
-
]
|
| 1261 |
-
|
| 1262 |
-
[package.dependencies]
|
| 1263 |
-
langchain-core = ">=0.2.0,<0.3.0"
|
| 1264 |
-
|
| 1265 |
-
[package.extras]
|
| 1266 |
-
extended-testing = ["beautifulsoup4 (>=4.12.3,<5.0.0)", "lxml (>=4.9.3,<6.0)"]
|
| 1267 |
-
|
| 1268 |
-
[[package]]
|
| 1269 |
-
name = "langsmith"
|
| 1270 |
-
version = "0.1.65"
|
| 1271 |
-
description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform."
|
| 1272 |
-
optional = false
|
| 1273 |
-
python-versions = "<4.0,>=3.8.1"
|
| 1274 |
-
files = [
|
| 1275 |
-
{file = "langsmith-0.1.65-py3-none-any.whl", hash = "sha256:ab4487029240e69cca30da1065f1e9138e5a7ca2bbe8c697f0bd7d5839f71cf7"},
|
| 1276 |
-
{file = "langsmith-0.1.65.tar.gz", hash = "sha256:d3c2eb2391478bd79989f02652cf66e29a7959d677614b6993a47cef43f7f43b"},
|
| 1277 |
-
]
|
| 1278 |
-
|
| 1279 |
-
[package.dependencies]
|
| 1280 |
-
orjson = ">=3.9.14,<4.0.0"
|
| 1281 |
-
pydantic = ">=1,<3"
|
| 1282 |
-
requests = ">=2,<3"
|
| 1283 |
-
|
| 1284 |
[[package]]
|
| 1285 |
name = "llama-index"
|
| 1286 |
version = "0.10.28"
|
|
@@ -1590,25 +1310,6 @@ files = [
|
|
| 1590 |
httpx = ">=0.20.0"
|
| 1591 |
pydantic = ">=1.10"
|
| 1592 |
|
| 1593 |
-
[[package]]
|
| 1594 |
-
name = "mako"
|
| 1595 |
-
version = "1.3.5"
|
| 1596 |
-
description = "A super-fast templating language that borrows the best ideas from the existing templating languages."
|
| 1597 |
-
optional = false
|
| 1598 |
-
python-versions = ">=3.8"
|
| 1599 |
-
files = [
|
| 1600 |
-
{file = "Mako-1.3.5-py3-none-any.whl", hash = "sha256:260f1dbc3a519453a9c856dedfe4beb4e50bd5a26d96386cb6c80856556bb91a"},
|
| 1601 |
-
{file = "Mako-1.3.5.tar.gz", hash = "sha256:48dbc20568c1d276a2698b36d968fa76161bf127194907ea6fc594fa81f943bc"},
|
| 1602 |
-
]
|
| 1603 |
-
|
| 1604 |
-
[package.dependencies]
|
| 1605 |
-
MarkupSafe = ">=0.9.2"
|
| 1606 |
-
|
| 1607 |
-
[package.extras]
|
| 1608 |
-
babel = ["Babel"]
|
| 1609 |
-
lingua = ["lingua"]
|
| 1610 |
-
testing = ["pytest"]
|
| 1611 |
-
|
| 1612 |
[[package]]
|
| 1613 |
name = "markupsafe"
|
| 1614 |
version = "2.1.5"
|
|
@@ -1697,20 +1398,6 @@ dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"]
|
|
| 1697 |
docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.3.7)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"]
|
| 1698 |
tests = ["pytest", "pytz", "simplejson"]
|
| 1699 |
|
| 1700 |
-
[[package]]
|
| 1701 |
-
name = "mirakuru"
|
| 1702 |
-
version = "2.5.2"
|
| 1703 |
-
description = "Process executor (not only) for tests."
|
| 1704 |
-
optional = false
|
| 1705 |
-
python-versions = ">=3.8"
|
| 1706 |
-
files = [
|
| 1707 |
-
{file = "mirakuru-2.5.2-py3-none-any.whl", hash = "sha256:90c2d90a8cf14349b2f33e6db30a16acd855499811e0312e56cf80ceacf2d3e5"},
|
| 1708 |
-
{file = "mirakuru-2.5.2.tar.gz", hash = "sha256:41ca583d355eb7a6cfdc21c1aea549979d685c27b57239b88725434f115a7132"},
|
| 1709 |
-
]
|
| 1710 |
-
|
| 1711 |
-
[package.dependencies]
|
| 1712 |
-
psutil = {version = ">=4.0.0", markers = "sys_platform != \"cygwin\""}
|
| 1713 |
-
|
| 1714 |
[[package]]
|
| 1715 |
name = "monotonic"
|
| 1716 |
version = "1.6"
|
|
@@ -1933,13 +1620,13 @@ files = [
|
|
| 1933 |
|
| 1934 |
[[package]]
|
| 1935 |
name = "openai"
|
| 1936 |
-
version = "1.30.
|
| 1937 |
description = "The official Python library for the openai API"
|
| 1938 |
optional = false
|
| 1939 |
python-versions = ">=3.7.1"
|
| 1940 |
files = [
|
| 1941 |
-
{file = "openai-1.30.
|
| 1942 |
-
{file = "openai-1.30.
|
| 1943 |
]
|
| 1944 |
|
| 1945 |
[package.dependencies]
|
|
@@ -2442,70 +2129,15 @@ files = [
|
|
| 2442 |
{file = "opentelemetry_util_http-0.45b0.tar.gz", hash = "sha256:4ce08b6a7d52dd7c96b7705b5b4f06fdb6aa3eac1233b3b0bfef8a0cab9a92cd"},
|
| 2443 |
]
|
| 2444 |
|
| 2445 |
-
[[package]]
|
| 2446 |
-
name = "orjson"
|
| 2447 |
-
version = "3.10.3"
|
| 2448 |
-
description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy"
|
| 2449 |
-
optional = false
|
| 2450 |
-
python-versions = ">=3.8"
|
| 2451 |
-
files = [
|
| 2452 |
-
{file = "orjson-3.10.3-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9fb6c3f9f5490a3eb4ddd46fc1b6eadb0d6fc16fb3f07320149c3286a1409dd8"},
|
| 2453 |
-
{file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:252124b198662eee80428f1af8c63f7ff077c88723fe206a25df8dc57a57b1fa"},
|
| 2454 |
-
{file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9f3e87733823089a338ef9bbf363ef4de45e5c599a9bf50a7a9b82e86d0228da"},
|
| 2455 |
-
{file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8334c0d87103bb9fbbe59b78129f1f40d1d1e8355bbed2ca71853af15fa4ed3"},
|
| 2456 |
-
{file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1952c03439e4dce23482ac846e7961f9d4ec62086eb98ae76d97bd41d72644d7"},
|
| 2457 |
-
{file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c0403ed9c706dcd2809f1600ed18f4aae50be263bd7112e54b50e2c2bc3ebd6d"},
|
| 2458 |
-
{file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:382e52aa4270a037d41f325e7d1dfa395b7de0c367800b6f337d8157367bf3a7"},
|
| 2459 |
-
{file = "orjson-3.10.3-cp310-none-win32.whl", hash = "sha256:be2aab54313752c04f2cbaab4515291ef5af8c2256ce22abc007f89f42f49109"},
|
| 2460 |
-
{file = "orjson-3.10.3-cp310-none-win_amd64.whl", hash = "sha256:416b195f78ae461601893f482287cee1e3059ec49b4f99479aedf22a20b1098b"},
|
| 2461 |
-
{file = "orjson-3.10.3-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:73100d9abbbe730331f2242c1fc0bcb46a3ea3b4ae3348847e5a141265479700"},
|
| 2462 |
-
{file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544a12eee96e3ab828dbfcb4d5a0023aa971b27143a1d35dc214c176fdfb29b3"},
|
| 2463 |
-
{file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:520de5e2ef0b4ae546bea25129d6c7c74edb43fc6cf5213f511a927f2b28148b"},
|
| 2464 |
-
{file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ccaa0a401fc02e8828a5bedfd80f8cd389d24f65e5ca3954d72c6582495b4bcf"},
|
| 2465 |
-
{file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7bc9e8bc11bac40f905640acd41cbeaa87209e7e1f57ade386da658092dc16"},
|
| 2466 |
-
{file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3582b34b70543a1ed6944aca75e219e1192661a63da4d039d088a09c67543b08"},
|
| 2467 |
-
{file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c23dfa91481de880890d17aa7b91d586a4746a4c2aa9a145bebdbaf233768d5"},
|
| 2468 |
-
{file = "orjson-3.10.3-cp311-none-win32.whl", hash = "sha256:1770e2a0eae728b050705206d84eda8b074b65ee835e7f85c919f5705b006c9b"},
|
| 2469 |
-
{file = "orjson-3.10.3-cp311-none-win_amd64.whl", hash = "sha256:93433b3c1f852660eb5abdc1f4dd0ced2be031ba30900433223b28ee0140cde5"},
|
| 2470 |
-
{file = "orjson-3.10.3-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a39aa73e53bec8d410875683bfa3a8edf61e5a1c7bb4014f65f81d36467ea098"},
|
| 2471 |
-
{file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0943a96b3fa09bee1afdfccc2cb236c9c64715afa375b2af296c73d91c23eab2"},
|
| 2472 |
-
{file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e852baafceff8da3c9defae29414cc8513a1586ad93e45f27b89a639c68e8176"},
|
| 2473 |
-
{file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18566beb5acd76f3769c1d1a7ec06cdb81edc4d55d2765fb677e3eaa10fa99e0"},
|
| 2474 |
-
{file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bd2218d5a3aa43060efe649ec564ebedec8ce6ae0a43654b81376216d5ebd42"},
|
| 2475 |
-
{file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cf20465e74c6e17a104ecf01bf8cd3b7b252565b4ccee4548f18b012ff2f8069"},
|
| 2476 |
-
{file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba7f67aa7f983c4345eeda16054a4677289011a478ca947cd69c0a86ea45e534"},
|
| 2477 |
-
{file = "orjson-3.10.3-cp312-none-win32.whl", hash = "sha256:17e0713fc159abc261eea0f4feda611d32eabc35708b74bef6ad44f6c78d5ea0"},
|
| 2478 |
-
{file = "orjson-3.10.3-cp312-none-win_amd64.whl", hash = "sha256:4c895383b1ec42b017dd2c75ae8a5b862fc489006afde06f14afbdd0309b2af0"},
|
| 2479 |
-
{file = "orjson-3.10.3-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:be2719e5041e9fb76c8c2c06b9600fe8e8584e6980061ff88dcbc2691a16d20d"},
|
| 2480 |
-
{file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0175a5798bdc878956099f5c54b9837cb62cfbf5d0b86ba6d77e43861bcec2"},
|
| 2481 |
-
{file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:978be58a68ade24f1af7758626806e13cff7748a677faf95fbb298359aa1e20d"},
|
| 2482 |
-
{file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16bda83b5c61586f6f788333d3cf3ed19015e3b9019188c56983b5a299210eb5"},
|
| 2483 |
-
{file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ad1f26bea425041e0a1adad34630c4825a9e3adec49079b1fb6ac8d36f8b754"},
|
| 2484 |
-
{file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9e253498bee561fe85d6325ba55ff2ff08fb5e7184cd6a4d7754133bd19c9195"},
|
| 2485 |
-
{file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0a62f9968bab8a676a164263e485f30a0b748255ee2f4ae49a0224be95f4532b"},
|
| 2486 |
-
{file = "orjson-3.10.3-cp38-none-win32.whl", hash = "sha256:8d0b84403d287d4bfa9bf7d1dc298d5c1c5d9f444f3737929a66f2fe4fb8f134"},
|
| 2487 |
-
{file = "orjson-3.10.3-cp38-none-win_amd64.whl", hash = "sha256:8bc7a4df90da5d535e18157220d7915780d07198b54f4de0110eca6b6c11e290"},
|
| 2488 |
-
{file = "orjson-3.10.3-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9059d15c30e675a58fdcd6f95465c1522b8426e092de9fff20edebfdc15e1cb0"},
|
| 2489 |
-
{file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d40c7f7938c9c2b934b297412c067936d0b54e4b8ab916fd1a9eb8f54c02294"},
|
| 2490 |
-
{file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4a654ec1de8fdaae1d80d55cee65893cb06494e124681ab335218be6a0691e7"},
|
| 2491 |
-
{file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:831c6ef73f9aa53c5f40ae8f949ff7681b38eaddb6904aab89dca4d85099cb78"},
|
| 2492 |
-
{file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99b880d7e34542db89f48d14ddecbd26f06838b12427d5a25d71baceb5ba119d"},
|
| 2493 |
-
{file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2e5e176c994ce4bd434d7aafb9ecc893c15f347d3d2bbd8e7ce0b63071c52e25"},
|
| 2494 |
-
{file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b69a58a37dab856491bf2d3bbf259775fdce262b727f96aafbda359cb1d114d8"},
|
| 2495 |
-
{file = "orjson-3.10.3-cp39-none-win32.whl", hash = "sha256:b8d4d1a6868cde356f1402c8faeb50d62cee765a1f7ffcfd6de732ab0581e063"},
|
| 2496 |
-
{file = "orjson-3.10.3-cp39-none-win_amd64.whl", hash = "sha256:5102f50c5fc46d94f2033fe00d392588564378260d64377aec702f21a7a22912"},
|
| 2497 |
-
{file = "orjson-3.10.3.tar.gz", hash = "sha256:2b166507acae7ba2f7c315dcf185a9111ad5e992ac81f2d507aac39193c2c818"},
|
| 2498 |
-
]
|
| 2499 |
-
|
| 2500 |
[[package]]
|
| 2501 |
name = "packaging"
|
| 2502 |
-
version = "
|
| 2503 |
description = "Core utilities for Python packages"
|
| 2504 |
optional = false
|
| 2505 |
python-versions = ">=3.7"
|
| 2506 |
files = [
|
| 2507 |
-
{file = "packaging-
|
| 2508 |
-
{file = "packaging-
|
| 2509 |
]
|
| 2510 |
|
| 2511 |
[[package]]
|
|
@@ -2577,21 +2209,6 @@ sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-d
|
|
| 2577 |
test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"]
|
| 2578 |
xml = ["lxml (>=4.9.2)"]
|
| 2579 |
|
| 2580 |
-
[[package]]
|
| 2581 |
-
name = "path"
|
| 2582 |
-
version = "16.14.0"
|
| 2583 |
-
description = "A module wrapper for os.path"
|
| 2584 |
-
optional = false
|
| 2585 |
-
python-versions = ">=3.8"
|
| 2586 |
-
files = [
|
| 2587 |
-
{file = "path-16.14.0-py3-none-any.whl", hash = "sha256:8ee37703cbdc7cc83835ed4ecc6b638226fb2b43b7b45f26b620589981a109a5"},
|
| 2588 |
-
{file = "path-16.14.0.tar.gz", hash = "sha256:dbaaa7efd4602fd6ba8d82890dc7823d69e5de740a6e842d9919b0faaf2b6a8e"},
|
| 2589 |
-
]
|
| 2590 |
-
|
| 2591 |
-
[package.extras]
|
| 2592 |
-
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
| 2593 |
-
testing = ["appdirs", "more-itertools", "packaging", "pygments", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "pywin32"]
|
| 2594 |
-
|
| 2595 |
[[package]]
|
| 2596 |
name = "pillow"
|
| 2597 |
version = "10.3.0"
|
|
@@ -2698,32 +2315,6 @@ urllib3 = {version = ">=1.26.0", markers = "python_version >= \"3.8\" and python
|
|
| 2698 |
[package.extras]
|
| 2699 |
grpc = ["googleapis-common-protos (>=1.53.0)", "grpc-gateway-protoc-gen-openapiv2 (==0.1.0)", "grpcio (>=1.44.0)", "grpcio (>=1.59.0)", "lz4 (>=3.1.3)", "protobuf (>=3.20.0,<3.21.0)"]
|
| 2700 |
|
| 2701 |
-
[[package]]
|
| 2702 |
-
name = "pluggy"
|
| 2703 |
-
version = "1.5.0"
|
| 2704 |
-
description = "plugin and hook calling mechanisms for python"
|
| 2705 |
-
optional = false
|
| 2706 |
-
python-versions = ">=3.8"
|
| 2707 |
-
files = [
|
| 2708 |
-
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
|
| 2709 |
-
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
|
| 2710 |
-
]
|
| 2711 |
-
|
| 2712 |
-
[package.extras]
|
| 2713 |
-
dev = ["pre-commit", "tox"]
|
| 2714 |
-
testing = ["pytest", "pytest-benchmark"]
|
| 2715 |
-
|
| 2716 |
-
[[package]]
|
| 2717 |
-
name = "port-for"
|
| 2718 |
-
version = "0.7.2"
|
| 2719 |
-
description = "Utility that helps with local TCP ports management. It can find an unused TCP localhost port and remember the association."
|
| 2720 |
-
optional = false
|
| 2721 |
-
python-versions = ">=3.8"
|
| 2722 |
-
files = [
|
| 2723 |
-
{file = "port-for-0.7.2.tar.gz", hash = "sha256:074f29335130578aa42fef3726985e57d01c15189e509633a8a1b0b7f9226349"},
|
| 2724 |
-
{file = "port_for-0.7.2-py3-none-any.whl", hash = "sha256:16b279ab4f210bad33515c45bd9af0c6e048ab24c3b6bbd9cfc7e451782617df"},
|
| 2725 |
-
]
|
| 2726 |
-
|
| 2727 |
[[package]]
|
| 2728 |
name = "posthog"
|
| 2729 |
version = "3.5.0"
|
|
@@ -2767,143 +2358,20 @@ files = [
|
|
| 2767 |
{file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"},
|
| 2768 |
]
|
| 2769 |
|
| 2770 |
-
[[package]]
|
| 2771 |
-
name = "psutil"
|
| 2772 |
-
version = "5.9.8"
|
| 2773 |
-
description = "Cross-platform lib for process and system monitoring in Python."
|
| 2774 |
-
optional = false
|
| 2775 |
-
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
|
| 2776 |
-
files = [
|
| 2777 |
-
{file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"},
|
| 2778 |
-
{file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"},
|
| 2779 |
-
{file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"},
|
| 2780 |
-
{file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"},
|
| 2781 |
-
{file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"},
|
| 2782 |
-
{file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"},
|
| 2783 |
-
{file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"},
|
| 2784 |
-
{file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"},
|
| 2785 |
-
{file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"},
|
| 2786 |
-
{file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"},
|
| 2787 |
-
{file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"},
|
| 2788 |
-
{file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"},
|
| 2789 |
-
{file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"},
|
| 2790 |
-
{file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"},
|
| 2791 |
-
{file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"},
|
| 2792 |
-
{file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"},
|
| 2793 |
-
]
|
| 2794 |
-
|
| 2795 |
-
[package.extras]
|
| 2796 |
-
test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"]
|
| 2797 |
-
|
| 2798 |
-
[[package]]
|
| 2799 |
-
name = "psycopg"
|
| 2800 |
-
version = "3.1.19"
|
| 2801 |
-
description = "PostgreSQL database adapter for Python"
|
| 2802 |
-
optional = false
|
| 2803 |
-
python-versions = ">=3.7"
|
| 2804 |
-
files = [
|
| 2805 |
-
{file = "psycopg-3.1.19-py3-none-any.whl", hash = "sha256:dca5e5521c859f6606686432ae1c94e8766d29cc91f2ee595378c510cc5b0731"},
|
| 2806 |
-
{file = "psycopg-3.1.19.tar.gz", hash = "sha256:92d7b78ad82426cdcf1a0440678209faa890c6e1721361c2f8901f0dccd62961"},
|
| 2807 |
-
]
|
| 2808 |
-
|
| 2809 |
-
[package.dependencies]
|
| 2810 |
-
typing-extensions = ">=4.1"
|
| 2811 |
-
tzdata = {version = "*", markers = "sys_platform == \"win32\""}
|
| 2812 |
-
|
| 2813 |
-
[package.extras]
|
| 2814 |
-
binary = ["psycopg-binary (==3.1.19)"]
|
| 2815 |
-
c = ["psycopg-c (==3.1.19)"]
|
| 2816 |
-
dev = ["black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=1.4.1)", "types-setuptools (>=57.4)", "wheel (>=0.37)"]
|
| 2817 |
-
docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"]
|
| 2818 |
-
pool = ["psycopg-pool"]
|
| 2819 |
-
test = ["anyio (>=3.6.2,<4.0)", "mypy (>=1.4.1)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"]
|
| 2820 |
-
|
| 2821 |
-
[[package]]
|
| 2822 |
-
name = "psycopg-binary"
|
| 2823 |
-
version = "3.1.19"
|
| 2824 |
-
description = "PostgreSQL database adapter for Python -- C optimisation distribution"
|
| 2825 |
-
optional = false
|
| 2826 |
-
python-versions = ">=3.7"
|
| 2827 |
-
files = [
|
| 2828 |
-
{file = "psycopg_binary-3.1.19-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7204818f05151dd08f8f851defb01972ec9d2cc925608eb0de232563f203f354"},
|
| 2829 |
-
{file = "psycopg_binary-3.1.19-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d4e67fd86758dbeac85641419a54f84d74495a8683b58ad5dfad08b7fc37a8f"},
|
| 2830 |
-
{file = "psycopg_binary-3.1.19-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e12173e34b176e93ad2da913de30f774d5119c2d4d4640c6858d2d77dfa6c9bf"},
|
| 2831 |
-
{file = "psycopg_binary-3.1.19-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:052f5193304066318853b4b2e248f523c8f52b371fc4e95d4ef63baee3f30955"},
|
| 2832 |
-
{file = "psycopg_binary-3.1.19-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29008f3f8977f600b8a7fb07c2e041b01645b08121760609cc45e861a0364dc9"},
|
| 2833 |
-
{file = "psycopg_binary-3.1.19-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c6a9a651a08d876303ed059c9553df18b3c13c3406584a70a8f37f1a1fe2709"},
|
| 2834 |
-
{file = "psycopg_binary-3.1.19-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:91a645e6468c4f064b7f4f3b81074bdd68fe5aa2b8c5107de15dcd85ba6141be"},
|
| 2835 |
-
{file = "psycopg_binary-3.1.19-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5c6956808fd5cf0576de5a602243af8e04594b25b9a28675feddc71c5526410a"},
|
| 2836 |
-
{file = "psycopg_binary-3.1.19-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:1622ca27d5a7a98f7d8f35e8b146dc7efda4a4b6241d2edf7e076bd6bcecbeb4"},
|
| 2837 |
-
{file = "psycopg_binary-3.1.19-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a100482950a55228f648bd382bb71bfaff520002f29845274fccbbf02e28bd52"},
|
| 2838 |
-
{file = "psycopg_binary-3.1.19-cp310-cp310-win_amd64.whl", hash = "sha256:955ca8905c0251fc4af7ce0a20999e824a25652f53a558ab548b60969f1f368e"},
|
| 2839 |
-
{file = "psycopg_binary-3.1.19-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cf49e91dcf699b8a449944ed898ef1466b39b92720613838791a551bc8f587a"},
|
| 2840 |
-
{file = "psycopg_binary-3.1.19-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:964c307e400c5f33fa762ba1e19853e048814fcfbd9679cc923431adb7a2ead2"},
|
| 2841 |
-
{file = "psycopg_binary-3.1.19-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3433924e1b14074798331dc2bfae2af452ed7888067f2fc145835704d8981b15"},
|
| 2842 |
-
{file = "psycopg_binary-3.1.19-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00879d4c6be4b3afc510073f48a5e960f797200e261ab3d9bd9b7746a08c669d"},
|
| 2843 |
-
{file = "psycopg_binary-3.1.19-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34a6997c80f86d3dd80a4f078bb3b200079c47eeda4fd409d8899b883c90d2ac"},
|
| 2844 |
-
{file = "psycopg_binary-3.1.19-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0106e42b481677c41caa69474fe530f786dcef88b11b70000f0e45a03534bc8f"},
|
| 2845 |
-
{file = "psycopg_binary-3.1.19-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81efe09ba27533e35709905c3061db4dc9fb814f637360578d065e2061fbb116"},
|
| 2846 |
-
{file = "psycopg_binary-3.1.19-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d312d6dddc18d9c164e1893706269c293cba1923118349d375962b1188dafb01"},
|
| 2847 |
-
{file = "psycopg_binary-3.1.19-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:bfd2c734da9950f7afaad5f132088e0e1478f32f042881fca6651bb0c8d14206"},
|
| 2848 |
-
{file = "psycopg_binary-3.1.19-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8a732610a5a6b4f06dadcf9288688a8ff202fd556d971436a123b7adb85596e2"},
|
| 2849 |
-
{file = "psycopg_binary-3.1.19-cp311-cp311-win_amd64.whl", hash = "sha256:321814a9a3ad785855a821b842aba08ca1b7de7dfb2979a2f0492dca9ec4ae70"},
|
| 2850 |
-
{file = "psycopg_binary-3.1.19-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4aa0ca13bb8a725bb6d12c13999217fd5bc8b86a12589f28a74b93e076fbb959"},
|
| 2851 |
-
{file = "psycopg_binary-3.1.19-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:469424e354ebcec949aa6aa30e5a9edc352a899d9a68ad7a48f97df83cc914cf"},
|
| 2852 |
-
{file = "psycopg_binary-3.1.19-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b04f5349313529ae1f1c42fe1aa0443faaf50fdf12d13866c2cc49683bfa53d0"},
|
| 2853 |
-
{file = "psycopg_binary-3.1.19-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:959feabddc7fffac89b054d6f23f3b3c62d7d3c90cd414a02e3747495597f150"},
|
| 2854 |
-
{file = "psycopg_binary-3.1.19-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e9da624a6ca4bc5f7fa1f03f8485446b5b81d5787b6beea2b4f8d9dbef878ad7"},
|
| 2855 |
-
{file = "psycopg_binary-3.1.19-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1823221a6b96e38b15686170d4fc5b36073efcb87cce7d3da660440b50077f6"},
|
| 2856 |
-
{file = "psycopg_binary-3.1.19-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:866db42f986298f0cf15d805225eb8df2228bf19f7997d7f1cb5f388cbfc6a0f"},
|
| 2857 |
-
{file = "psycopg_binary-3.1.19-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:738c34657305b5973af6dbb6711b07b179dfdd21196d60039ca30a74bafe9648"},
|
| 2858 |
-
{file = "psycopg_binary-3.1.19-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb9758473200384a04374d0e0cac6f451218ff6945a024f65a1526802c34e56e"},
|
| 2859 |
-
{file = "psycopg_binary-3.1.19-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0e991632777e217953ac960726158987da684086dd813ac85038c595e7382c91"},
|
| 2860 |
-
{file = "psycopg_binary-3.1.19-cp312-cp312-win_amd64.whl", hash = "sha256:1d87484dd42c8783c44a30400949efb3d81ef2487eaa7d64d1c54df90cf8b97a"},
|
| 2861 |
-
{file = "psycopg_binary-3.1.19-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d1d1723d7449c12bb61aca7eb6e0c6ab2863cd8dc0019273cc4d4a1982f84bdb"},
|
| 2862 |
-
{file = "psycopg_binary-3.1.19-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e538a8671005641fa195eab962f85cf0504defbd3b548c4c8fc27102a59f687b"},
|
| 2863 |
-
{file = "psycopg_binary-3.1.19-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c50592bc8517092f40979e4a5d934f96a1737a77724bb1d121eb78b614b30fc8"},
|
| 2864 |
-
{file = "psycopg_binary-3.1.19-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:95f16ae82bc242b76cd3c3e5156441e2bd85ff9ec3a9869d750aad443e46073c"},
|
| 2865 |
-
{file = "psycopg_binary-3.1.19-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aebd1e98e865e9a28ce0cb2c25b7dfd752f0d1f0a423165b55cd32a431dcc0f4"},
|
| 2866 |
-
{file = "psycopg_binary-3.1.19-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:49cd7af7d49e438a39593d1dd8cab106a1912536c2b78a4d814ebdff2786094e"},
|
| 2867 |
-
{file = "psycopg_binary-3.1.19-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:affebd61aa3b7a8880fd4ac3ee94722940125ff83ff485e1a7c76be9adaabb38"},
|
| 2868 |
-
{file = "psycopg_binary-3.1.19-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:d1bac282f140fa092f2bbb6c36ed82270b4a21a6fc55d4b16748ed9f55e50fdb"},
|
| 2869 |
-
{file = "psycopg_binary-3.1.19-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1285aa54449e362b1d30d92b2dc042ad3ee80f479cc4e323448d0a0a8a1641fa"},
|
| 2870 |
-
{file = "psycopg_binary-3.1.19-cp37-cp37m-win_amd64.whl", hash = "sha256:6cff31af8155dc9ee364098a328bab688c887c732c66b8d027e5b03818ca0287"},
|
| 2871 |
-
{file = "psycopg_binary-3.1.19-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d9b689c4a17dd3130791dcbb8c30dbf05602f7c2d56c792e193fb49adc7bf5f8"},
|
| 2872 |
-
{file = "psycopg_binary-3.1.19-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:017518bd2de4851adc826a224fb105411e148ad845e11355edd6786ba3dfedf5"},
|
| 2873 |
-
{file = "psycopg_binary-3.1.19-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c35fd811f339a3cbe7f9b54b2d9a5e592e57426c6cc1051632a62c59c4810208"},
|
| 2874 |
-
{file = "psycopg_binary-3.1.19-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38ed45ec9673709bfa5bc17f140e71dd4cca56d4e58ef7fd50d5a5043a4f55c6"},
|
| 2875 |
-
{file = "psycopg_binary-3.1.19-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:433f1c256108f9e26f480a8cd6ddb0fb37dbc87d7f5a97e4540a9da9b881f23f"},
|
| 2876 |
-
{file = "psycopg_binary-3.1.19-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ed61e43bf5dc8d0936daf03a19fef3168d64191dbe66483f7ad08c4cea0bc36b"},
|
| 2877 |
-
{file = "psycopg_binary-3.1.19-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4ae8109ff9fdf1fa0cb87ab6645298693fdd2666a7f5f85660df88f6965e0bb7"},
|
| 2878 |
-
{file = "psycopg_binary-3.1.19-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:a53809ee02e3952fae7977c19b30fd828bd117b8f5edf17a3a94212feb57faaf"},
|
| 2879 |
-
{file = "psycopg_binary-3.1.19-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9d39d5ffc151fb33bcd55b99b0e8957299c0b1b3e5a1a5f4399c1287ef0051a9"},
|
| 2880 |
-
{file = "psycopg_binary-3.1.19-cp38-cp38-win_amd64.whl", hash = "sha256:e14bc8250000921fcccd53722f86b3b3d1b57db901e206e49e2ab2afc5919c2d"},
|
| 2881 |
-
{file = "psycopg_binary-3.1.19-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cd88c5cea4efe614d5004fb5f5dcdea3d7d59422be796689e779e03363102d24"},
|
| 2882 |
-
{file = "psycopg_binary-3.1.19-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:621a814e60825162d38760c66351b4df679fd422c848b7c2f86ad399bff27145"},
|
| 2883 |
-
{file = "psycopg_binary-3.1.19-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46e50c05952b59a214e27d3606f6d510aaa429daed898e16b8a37bfbacc81acc"},
|
| 2884 |
-
{file = "psycopg_binary-3.1.19-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03354a9db667c27946e70162cb0042c3929154167f3678a30d23cebfe0ad55b5"},
|
| 2885 |
-
{file = "psycopg_binary-3.1.19-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:703c2f3b79037581afec7baa2bdbcb0a1787f1758744a7662099b0eca2d721cb"},
|
| 2886 |
-
{file = "psycopg_binary-3.1.19-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6469ebd9e93327e9f5f36dcf8692fb1e7aeaf70087c1c15d4f2c020e0be3a891"},
|
| 2887 |
-
{file = "psycopg_binary-3.1.19-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:85bca9765c04b6be90cb46e7566ffe0faa2d7480ff5c8d5e055ac427f039fd24"},
|
| 2888 |
-
{file = "psycopg_binary-3.1.19-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:a836610d5c75e9cff98b9fdb3559c007c785c09eaa84a60d5d10ef6f85f671e8"},
|
| 2889 |
-
{file = "psycopg_binary-3.1.19-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ef8de7a1d9fb3518cc6b58e3c80b75a824209ad52b90c542686c912db8553dad"},
|
| 2890 |
-
{file = "psycopg_binary-3.1.19-cp39-cp39-win_amd64.whl", hash = "sha256:76fcd33342f38e35cd6b5408f1bc117d55ab8b16e5019d99b6d3ce0356c51717"},
|
| 2891 |
-
]
|
| 2892 |
-
|
| 2893 |
[[package]]
|
| 2894 |
name = "pydantic"
|
| 2895 |
-
version = "2.7.
|
| 2896 |
description = "Data validation using Python type hints"
|
| 2897 |
optional = false
|
| 2898 |
python-versions = ">=3.8"
|
| 2899 |
files = [
|
| 2900 |
-
{file = "pydantic-2.7.
|
| 2901 |
-
{file = "pydantic-2.7.
|
| 2902 |
]
|
| 2903 |
|
| 2904 |
[package.dependencies]
|
| 2905 |
annotated-types = ">=0.4.0"
|
| 2906 |
-
pydantic-core = "2.18.
|
| 2907 |
typing-extensions = ">=4.6.1"
|
| 2908 |
|
| 2909 |
[package.extras]
|
|
@@ -2911,90 +2379,90 @@ email = ["email-validator (>=2.0.0)"]
|
|
| 2911 |
|
| 2912 |
[[package]]
|
| 2913 |
name = "pydantic-core"
|
| 2914 |
-
version = "2.18.
|
| 2915 |
description = "Core functionality for Pydantic validation and serialization"
|
| 2916 |
optional = false
|
| 2917 |
python-versions = ">=3.8"
|
| 2918 |
files = [
|
| 2919 |
-
{file = "pydantic_core-2.18.
|
| 2920 |
-
{file = "pydantic_core-2.18.
|
| 2921 |
-
{file = "pydantic_core-2.18.
|
| 2922 |
-
{file = "pydantic_core-2.18.
|
| 2923 |
-
{file = "pydantic_core-2.18.
|
| 2924 |
-
{file = "pydantic_core-2.18.
|
| 2925 |
-
{file = "pydantic_core-2.18.
|
| 2926 |
-
{file = "pydantic_core-2.18.
|
| 2927 |
-
{file = "pydantic_core-2.18.
|
| 2928 |
-
{file = "pydantic_core-2.18.
|
| 2929 |
-
{file = "pydantic_core-2.18.
|
| 2930 |
-
{file = "pydantic_core-2.18.
|
| 2931 |
-
{file = "pydantic_core-2.18.
|
| 2932 |
-
{file = "pydantic_core-2.18.
|
| 2933 |
-
{file = "pydantic_core-2.18.
|
| 2934 |
-
{file = "pydantic_core-2.18.
|
| 2935 |
-
{file = "pydantic_core-2.18.
|
| 2936 |
-
{file = "pydantic_core-2.18.
|
| 2937 |
-
{file = "pydantic_core-2.18.
|
| 2938 |
-
{file = "pydantic_core-2.18.
|
| 2939 |
-
{file = "pydantic_core-2.18.
|
| 2940 |
-
{file = "pydantic_core-2.18.
|
| 2941 |
-
{file = "pydantic_core-2.18.
|
| 2942 |
-
{file = "pydantic_core-2.18.
|
| 2943 |
-
{file = "pydantic_core-2.18.
|
| 2944 |
-
{file = "pydantic_core-2.18.
|
| 2945 |
-
{file = "pydantic_core-2.18.
|
| 2946 |
-
{file = "pydantic_core-2.18.
|
| 2947 |
-
{file = "pydantic_core-2.18.
|
| 2948 |
-
{file = "pydantic_core-2.18.
|
| 2949 |
-
{file = "pydantic_core-2.18.
|
| 2950 |
-
{file = "pydantic_core-2.18.
|
| 2951 |
-
{file = "pydantic_core-2.18.
|
| 2952 |
-
{file = "pydantic_core-2.18.
|
| 2953 |
-
{file = "pydantic_core-2.18.
|
| 2954 |
-
{file = "pydantic_core-2.18.
|
| 2955 |
-
{file = "pydantic_core-2.18.
|
| 2956 |
-
{file = "pydantic_core-2.18.
|
| 2957 |
-
{file = "pydantic_core-2.18.
|
| 2958 |
-
{file = "pydantic_core-2.18.
|
| 2959 |
-
{file = "pydantic_core-2.18.
|
| 2960 |
-
{file = "pydantic_core-2.18.
|
| 2961 |
-
{file = "pydantic_core-2.18.
|
| 2962 |
-
{file = "pydantic_core-2.18.
|
| 2963 |
-
{file = "pydantic_core-2.18.
|
| 2964 |
-
{file = "pydantic_core-2.18.
|
| 2965 |
-
{file = "pydantic_core-2.18.
|
| 2966 |
-
{file = "pydantic_core-2.18.
|
| 2967 |
-
{file = "pydantic_core-2.18.
|
| 2968 |
-
{file = "pydantic_core-2.18.
|
| 2969 |
-
{file = "pydantic_core-2.18.
|
| 2970 |
-
{file = "pydantic_core-2.18.
|
| 2971 |
-
{file = "pydantic_core-2.18.
|
| 2972 |
-
{file = "pydantic_core-2.18.
|
| 2973 |
-
{file = "pydantic_core-2.18.
|
| 2974 |
-
{file = "pydantic_core-2.18.
|
| 2975 |
-
{file = "pydantic_core-2.18.
|
| 2976 |
-
{file = "pydantic_core-2.18.
|
| 2977 |
-
{file = "pydantic_core-2.18.
|
| 2978 |
-
{file = "pydantic_core-2.18.
|
| 2979 |
-
{file = "pydantic_core-2.18.
|
| 2980 |
-
{file = "pydantic_core-2.18.
|
| 2981 |
-
{file = "pydantic_core-2.18.
|
| 2982 |
-
{file = "pydantic_core-2.18.
|
| 2983 |
-
{file = "pydantic_core-2.18.
|
| 2984 |
-
{file = "pydantic_core-2.18.
|
| 2985 |
-
{file = "pydantic_core-2.18.
|
| 2986 |
-
{file = "pydantic_core-2.18.
|
| 2987 |
-
{file = "pydantic_core-2.18.
|
| 2988 |
-
{file = "pydantic_core-2.18.
|
| 2989 |
-
{file = "pydantic_core-2.18.
|
| 2990 |
-
{file = "pydantic_core-2.18.
|
| 2991 |
-
{file = "pydantic_core-2.18.
|
| 2992 |
-
{file = "pydantic_core-2.18.
|
| 2993 |
-
{file = "pydantic_core-2.18.
|
| 2994 |
-
{file = "pydantic_core-2.18.
|
| 2995 |
-
{file = "pydantic_core-2.18.
|
| 2996 |
-
{file = "pydantic_core-2.18.
|
| 2997 |
-
{file = "pydantic_core-2.18.
|
| 2998 |
]
|
| 2999 |
|
| 3000 |
[package.dependencies]
|
|
@@ -3018,82 +2486,6 @@ docs = ["myst_parser", "sphinx", "sphinx_rtd_theme"]
|
|
| 3018 |
full = ["Pillow (>=8.0.0)", "PyCryptodome", "cryptography"]
|
| 3019 |
image = ["Pillow (>=8.0.0)"]
|
| 3020 |
|
| 3021 |
-
[[package]]
|
| 3022 |
-
name = "pytest"
|
| 3023 |
-
version = "8.2.1"
|
| 3024 |
-
description = "pytest: simple powerful testing with Python"
|
| 3025 |
-
optional = false
|
| 3026 |
-
python-versions = ">=3.8"
|
| 3027 |
-
files = [
|
| 3028 |
-
{file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"},
|
| 3029 |
-
{file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"},
|
| 3030 |
-
]
|
| 3031 |
-
|
| 3032 |
-
[package.dependencies]
|
| 3033 |
-
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
| 3034 |
-
iniconfig = "*"
|
| 3035 |
-
packaging = "*"
|
| 3036 |
-
pluggy = ">=1.5,<2.0"
|
| 3037 |
-
|
| 3038 |
-
[package.extras]
|
| 3039 |
-
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
|
| 3040 |
-
|
| 3041 |
-
[[package]]
|
| 3042 |
-
name = "pytest-asyncio"
|
| 3043 |
-
version = "0.23.7"
|
| 3044 |
-
description = "Pytest support for asyncio"
|
| 3045 |
-
optional = false
|
| 3046 |
-
python-versions = ">=3.8"
|
| 3047 |
-
files = [
|
| 3048 |
-
{file = "pytest_asyncio-0.23.7-py3-none-any.whl", hash = "sha256:009b48127fbe44518a547bddd25611551b0e43ccdbf1e67d12479f569832c20b"},
|
| 3049 |
-
{file = "pytest_asyncio-0.23.7.tar.gz", hash = "sha256:5f5c72948f4c49e7db4f29f2521d4031f1c27f86e57b046126654083d4770268"},
|
| 3050 |
-
]
|
| 3051 |
-
|
| 3052 |
-
[package.dependencies]
|
| 3053 |
-
pytest = ">=7.0.0,<9"
|
| 3054 |
-
|
| 3055 |
-
[package.extras]
|
| 3056 |
-
docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"]
|
| 3057 |
-
testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"]
|
| 3058 |
-
|
| 3059 |
-
[[package]]
|
| 3060 |
-
name = "pytest-postgresql"
|
| 3061 |
-
version = "6.0.0"
|
| 3062 |
-
description = "Postgresql fixtures and fixture factories for Pytest."
|
| 3063 |
-
optional = false
|
| 3064 |
-
python-versions = ">=3.8"
|
| 3065 |
-
files = [
|
| 3066 |
-
{file = "pytest-postgresql-6.0.0.tar.gz", hash = "sha256:6a4d8e600a2eef273f3c0e846cd0b2ea577282e252de29b4ca854bfb929bb682"},
|
| 3067 |
-
{file = "pytest_postgresql-6.0.0-py3-none-any.whl", hash = "sha256:f14272bffad16a74d9a63f4cc828f243a12ae92995e236b68fd53154760e6a5a"},
|
| 3068 |
-
]
|
| 3069 |
-
|
| 3070 |
-
[package.dependencies]
|
| 3071 |
-
mirakuru = "*"
|
| 3072 |
-
port-for = ">=0.6.0"
|
| 3073 |
-
psycopg = ">=3.0.0"
|
| 3074 |
-
pytest = ">=6.2"
|
| 3075 |
-
setuptools = "*"
|
| 3076 |
-
|
| 3077 |
-
[[package]]
|
| 3078 |
-
name = "pytest-xdist"
|
| 3079 |
-
version = "3.6.1"
|
| 3080 |
-
description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs"
|
| 3081 |
-
optional = false
|
| 3082 |
-
python-versions = ">=3.8"
|
| 3083 |
-
files = [
|
| 3084 |
-
{file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"},
|
| 3085 |
-
{file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"},
|
| 3086 |
-
]
|
| 3087 |
-
|
| 3088 |
-
[package.dependencies]
|
| 3089 |
-
execnet = ">=2.1"
|
| 3090 |
-
pytest = ">=7.0.0"
|
| 3091 |
-
|
| 3092 |
-
[package.extras]
|
| 3093 |
-
psutil = ["psutil (>=3.0)"]
|
| 3094 |
-
setproctitle = ["setproctitle"]
|
| 3095 |
-
testing = ["filelock"]
|
| 3096 |
-
|
| 3097 |
[[package]]
|
| 3098 |
name = "python-dateutil"
|
| 3099 |
version = "2.9.0.post0"
|
|
@@ -3122,20 +2514,6 @@ files = [
|
|
| 3122 |
[package.extras]
|
| 3123 |
cli = ["click (>=5.0)"]
|
| 3124 |
|
| 3125 |
-
[[package]]
|
| 3126 |
-
name = "python-multipart"
|
| 3127 |
-
version = "0.0.9"
|
| 3128 |
-
description = "A streaming multipart parser for Python"
|
| 3129 |
-
optional = false
|
| 3130 |
-
python-versions = ">=3.8"
|
| 3131 |
-
files = [
|
| 3132 |
-
{file = "python_multipart-0.0.9-py3-none-any.whl", hash = "sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215"},
|
| 3133 |
-
{file = "python_multipart-0.0.9.tar.gz", hash = "sha256:03f54688c663f1b7977105f021043b0793151e4cb1c1a9d4a11fc13d622c4026"},
|
| 3134 |
-
]
|
| 3135 |
-
|
| 3136 |
-
[package.extras]
|
| 3137 |
-
dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatch", "invoke (==2.2.0)", "more-itertools (==10.2.0)", "pbr (==6.0.0)", "pluggy (==1.4.0)", "py (==1.11.0)", "pytest (==8.0.0)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.2.0)", "pyyaml (==6.0.1)", "ruff (==0.2.1)"]
|
| 3138 |
-
|
| 3139 |
[[package]]
|
| 3140 |
name = "pytz"
|
| 3141 |
version = "2024.1"
|
|
@@ -3207,111 +2585,6 @@ files = [
|
|
| 3207 |
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
|
| 3208 |
]
|
| 3209 |
|
| 3210 |
-
[[package]]
|
| 3211 |
-
name = "rapidfuzz"
|
| 3212 |
-
version = "3.9.2"
|
| 3213 |
-
description = "rapid fuzzy string matching"
|
| 3214 |
-
optional = false
|
| 3215 |
-
python-versions = ">=3.8"
|
| 3216 |
-
files = [
|
| 3217 |
-
{file = "rapidfuzz-3.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:45e0c3e279e70589381f47ad410de7211bac943e827eb09eb8339d2124abca90"},
|
| 3218 |
-
{file = "rapidfuzz-3.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:280ef2f3066df9c486ffd3874d2489978fb8021044c47c006eb96be8d47917d7"},
|
| 3219 |
-
{file = "rapidfuzz-3.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe128ac0e05ca3a71d8ff18e70884a64fde00b6fbd2b4d9f59f7a4d798257c55"},
|
| 3220 |
-
{file = "rapidfuzz-3.9.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8fbc0f6e1b6f4063b937d0edcf0a56cbc1d7179ade9b7d6c849c94e44a7b20f6"},
|
| 3221 |
-
{file = "rapidfuzz-3.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:df19455c2fb85e86a721111b84ac8dd3685194f0edc9faefb226731ad3e134a7"},
|
| 3222 |
-
{file = "rapidfuzz-3.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:801a5d97c465a3467b3cdf50cdcdadec129ddca582b24430f5d24c715c80be9b"},
|
| 3223 |
-
{file = "rapidfuzz-3.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81f218524596d261a6cb33cda965687e62dd30def478d39f0befa243642c3985"},
|
| 3224 |
-
{file = "rapidfuzz-3.9.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5c61d53f293b4e3286919b0e081513367afabcb5aef0b6f899d006117778e558"},
|
| 3225 |
-
{file = "rapidfuzz-3.9.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0ed70fc6627ae37319f822e5d8d21d561044e0b3331b6f0e6904476faa8d8ed7"},
|
| 3226 |
-
{file = "rapidfuzz-3.9.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:96fa229d06ee005d2f46374fb2af65590a590a6fa2fd56e66474829f5fa9adfe"},
|
| 3227 |
-
{file = "rapidfuzz-3.9.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6609e881b57cabb40d515cc226bbf570e32e768bd2cc688ba026a45ffbc60875"},
|
| 3228 |
-
{file = "rapidfuzz-3.9.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:204fd4d293ef4d409c4142ddf830b7613924b998670f67e512ab1f880a60218a"},
|
| 3229 |
-
{file = "rapidfuzz-3.9.2-cp310-cp310-win32.whl", hash = "sha256:5b331a09446bc8f8971cf488c9e6c0f7dbf2739828588e063cf08fd400638a24"},
|
| 3230 |
-
{file = "rapidfuzz-3.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:01a9975984953fe549649e6a4c3f0d9c60707acf458184ec09678d6a57560112"},
|
| 3231 |
-
{file = "rapidfuzz-3.9.2-cp310-cp310-win_arm64.whl", hash = "sha256:ca4af5d7fc9c17bdc498aa1cab9ecf5140c8535c9cedeba1990bbe4b8be75098"},
|
| 3232 |
-
{file = "rapidfuzz-3.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:300ab53981a5d6831fe7e0f30c407c79520ad0f0ab51b2cece8717689026f495"},
|
| 3233 |
-
{file = "rapidfuzz-3.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f4828642acdb075154ce2ff3260f8afb6a17b5b0c8a437efbadac06e9995dd7b"},
|
| 3234 |
-
{file = "rapidfuzz-3.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b262883c3ce93dee1a9a974992961c8098e96b8142e2e01cabdb15ea8105c4a"},
|
| 3235 |
-
{file = "rapidfuzz-3.9.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf8582d85e35641734d6c1f43eb37c1f2a5eda338d3cfa8e651e078246b9ec58"},
|
| 3236 |
-
{file = "rapidfuzz-3.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e33b61ef87e1876d216c479fa2256233b3bb0424465ab2db1d94ab7b8649ae1c"},
|
| 3237 |
-
{file = "rapidfuzz-3.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fa1b3eb21756003a6a3977847dd4e0e9a26e2e02731d9daa5e92a9258e7f0db"},
|
| 3238 |
-
{file = "rapidfuzz-3.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:923ae0301a56356364f1159e3005fbeb2191e7a0e8705d5cc1b481d9eea27b97"},
|
| 3239 |
-
{file = "rapidfuzz-3.9.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8e4041cfd87f0a022aa8a9a187d3b0824e35be2bd9b3bceada11578ddd9ad65"},
|
| 3240 |
-
{file = "rapidfuzz-3.9.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1f832b430f976727bdbba009ee64acda25412602976fbfb2113d41e765d81849"},
|
| 3241 |
-
{file = "rapidfuzz-3.9.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6ce5e57e0c6acf5a98ffbdfaf8bccb6e41fbddb9eda3e041f4cc69b7cade5fa0"},
|
| 3242 |
-
{file = "rapidfuzz-3.9.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d65f34e71102d9cbe733d4ba1c645e7623eef850562501bab1ac79d217831436"},
|
| 3243 |
-
{file = "rapidfuzz-3.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5dd9ba4df0db46b9f909289e4687cc7721c622985c4cd169969005dd30fc1e24"},
|
| 3244 |
-
{file = "rapidfuzz-3.9.2-cp311-cp311-win32.whl", hash = "sha256:34c8bca3fef33d7e71f290de68be2184fac7a9e136fa0ed22b17ec597e181406"},
|
| 3245 |
-
{file = "rapidfuzz-3.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:91e1a8872c0b8aef95c33db86d25e8bdea6f557b9cdf683123c25035b2bcfb8e"},
|
| 3246 |
-
{file = "rapidfuzz-3.9.2-cp311-cp311-win_arm64.whl", hash = "sha256:ed02d73e46b7a4604d2bc1e0364b25f204862d40dd162f6b36ee22b9bf6d9df2"},
|
| 3247 |
-
{file = "rapidfuzz-3.9.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ae6c4ba2778b097397968130f2b0cb795cdc415c115539a49ce798f606152ad5"},
|
| 3248 |
-
{file = "rapidfuzz-3.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7270556ddebaa98fb777f493f17ed6a733b3527de16c43342bce1db109042845"},
|
| 3249 |
-
{file = "rapidfuzz-3.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4625273447bdd94f2ab06b2951cd8b74356c3a48552208279a3ec2947ceee141"},
|
| 3250 |
-
{file = "rapidfuzz-3.9.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5107b5ec8821453f7cac70b2d0bc4866699b25bff4819ada8b28bf2b11e87f65"},
|
| 3251 |
-
{file = "rapidfuzz-3.9.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b04c851d309df8261ed42951444db657936234ceddf4032f4409b0214c95ecbe"},
|
| 3252 |
-
{file = "rapidfuzz-3.9.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aeefff80f3f5d6841c30ffe0cdc84d62874de5a64cff509ae26fbd7478297af8"},
|
| 3253 |
-
{file = "rapidfuzz-3.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cdc106b5a99edd46443449c767287dbb5d4464a7536475a365e368e7ee4d651"},
|
| 3254 |
-
{file = "rapidfuzz-3.9.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ce253a2b7a71a01a4abac71ac31fd05f6ac1f1cd2af2d98fa80fe5c402175e54"},
|
| 3255 |
-
{file = "rapidfuzz-3.9.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5c30407cadbfe99753b7a996f0dd6da490b1e27d318c01db227e8f49770a01ec"},
|
| 3256 |
-
{file = "rapidfuzz-3.9.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:fb3fc387783f70387a91aababd8a5faeb230931b655ad99bcf838cd72404ba66"},
|
| 3257 |
-
{file = "rapidfuzz-3.9.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:c409852a89535ec8720301a847bab198c1c14d0f34ed07dfabbb90b1dbfc506d"},
|
| 3258 |
-
{file = "rapidfuzz-3.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8603050e547249c1cc8a8dc6a49917076572ea69b04bc51eb1748c403cfc9f46"},
|
| 3259 |
-
{file = "rapidfuzz-3.9.2-cp312-cp312-win32.whl", hash = "sha256:77bdb96e82d8831f0dd6db83e2ff0d4a731cff53e926d029c65a1dc3ae0f160a"},
|
| 3260 |
-
{file = "rapidfuzz-3.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:09f354fa28e0fd170c6e4eea5e97eea0dba43761067df93109f49a5414ca8584"},
|
| 3261 |
-
{file = "rapidfuzz-3.9.2-cp312-cp312-win_arm64.whl", hash = "sha256:168299c9a2b4f20f10c1bb96d8da0bb05bf1f3b9957be3a0bae5db65ce9f095f"},
|
| 3262 |
-
{file = "rapidfuzz-3.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d87621d60078f87cb52082b1cbf9849afeaa1cb6d0a2b072fce25fe21c8675b4"},
|
| 3263 |
-
{file = "rapidfuzz-3.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c447d0e534418ef3eaabcd890d85c7e9f289c1c6ef6e060a0b1f239799781747"},
|
| 3264 |
-
{file = "rapidfuzz-3.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7161b205f25eff5f88ab809fb05a2a102634e06f452c0deb9535c9f41cd7b0a"},
|
| 3265 |
-
{file = "rapidfuzz-3.9.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f13a6bbadba8fdd42676c1213ebc692bba9fac00f7db0ae92acc06bb734294c4"},
|
| 3266 |
-
{file = "rapidfuzz-3.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54534743820a15bd0dc30a0a0010825be337973236550fd63587700a7950bbca"},
|
| 3267 |
-
{file = "rapidfuzz-3.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bea61851a4c2f93148aa2779458fb3f70a62342d77c9ec3d9d08445c8485b738"},
|
| 3268 |
-
{file = "rapidfuzz-3.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e941f81a60351a842976fea208e6a6701a5899eb8a80b907e57d7c3099337900"},
|
| 3269 |
-
{file = "rapidfuzz-3.9.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:1bbfaf439e48efe3a48cada946cf7678b09c818ce9668e09dac40d05b772f6f8"},
|
| 3270 |
-
{file = "rapidfuzz-3.9.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:574f464da18d660712e9776072572d462cf6a26144c833d18d9c93778286e023"},
|
| 3271 |
-
{file = "rapidfuzz-3.9.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:8a56c494246d29aacf5ac93ca3cf338d79588a1a5c05d8f496c3f4d7127e9031"},
|
| 3272 |
-
{file = "rapidfuzz-3.9.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:2943b0f17195c000948a7668bb11979ea0e50079a3d3db9d139e51b68c3a7c26"},
|
| 3273 |
-
{file = "rapidfuzz-3.9.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:27214f93555d4f9b7b1baf107a6ba13e9daee21f1ec6e36418556d04a7ee4d9b"},
|
| 3274 |
-
{file = "rapidfuzz-3.9.2-cp38-cp38-win32.whl", hash = "sha256:876c6628fec6241262c27f8fda3c73bab88e205e9b9394c8868361e2eda59048"},
|
| 3275 |
-
{file = "rapidfuzz-3.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:cf1952b486589ffcfbde2015ca9be15e0f4b0e63d1e2c25f3daec0263fda4e69"},
|
| 3276 |
-
{file = "rapidfuzz-3.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1ca9a135060ee4d887d6af86493c3e0eb1c99ca205bca943fe5994dc93e648d5"},
|
| 3277 |
-
{file = "rapidfuzz-3.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:723518c9a18e8bda996d77aa9307b6f8b0e77905702b2772b020adf24191073a"},
|
| 3278 |
-
{file = "rapidfuzz-3.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65eb9aeae73ac60e53a9d6c509daaa217ea256a5e184eb8920c9b15295c48677"},
|
| 3279 |
-
{file = "rapidfuzz-3.9.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef2964f4eb9a37487c96e5e32167a3c4fa51bf8e899853d0ac67e0465a27702c"},
|
| 3280 |
-
{file = "rapidfuzz-3.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c64a252c96f29667c206726903bb9705c5195f01850360c9b9268de92ac878dc"},
|
| 3281 |
-
{file = "rapidfuzz-3.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b32b03398517b5e33c7f36d625a00fcb1c955b9fe3c939325688175fb21730"},
|
| 3282 |
-
{file = "rapidfuzz-3.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec5f7b1bac77439b624f5acbd8bfe61e7b833678701068b43f7a489c151427c0"},
|
| 3283 |
-
{file = "rapidfuzz-3.9.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5fd1b49fba8b4b9172eed5b131c1e9864d4d76bebea34359274f16a3591e5f44"},
|
| 3284 |
-
{file = "rapidfuzz-3.9.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c05b033fc3ff043f48e744f67038af7fd34003047c7810f24bec7c01ce7da05b"},
|
| 3285 |
-
{file = "rapidfuzz-3.9.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c3bea20db89b510d78d017b349b9d87159c32418693ddf091d9035dbe20b4dc0"},
|
| 3286 |
-
{file = "rapidfuzz-3.9.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:77226a77590f83ee073f4f8cc86a1232da88e24d19d349361faa169fb17ba1cd"},
|
| 3287 |
-
{file = "rapidfuzz-3.9.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:83ed8bc2c942dc61ab739bbca1ead791143b4639dc92156d3060bd0b6f4541ea"},
|
| 3288 |
-
{file = "rapidfuzz-3.9.2-cp39-cp39-win32.whl", hash = "sha256:2db70f64974c10b76ae37d5cff6124dce791def815d4fdf5ac16fe60be88d905"},
|
| 3289 |
-
{file = "rapidfuzz-3.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:bdead23114206dea4a22ed3aad6565b99a9e4b3fff9837c423afc556d2814b1a"},
|
| 3290 |
-
{file = "rapidfuzz-3.9.2-cp39-cp39-win_arm64.whl", hash = "sha256:0ec69ad076cfc7c88323d671613e40bb8754ba95a203556d9a7759e60f0544e8"},
|
| 3291 |
-
{file = "rapidfuzz-3.9.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:018360654881e75131b227aa96cdaba543c438da881c70a12ca0c86e2c4083b2"},
|
| 3292 |
-
{file = "rapidfuzz-3.9.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:eaa8178ec9238f32f15b6e49f70b852accda0a848448c4e30bce77c6624ebaba"},
|
| 3293 |
-
{file = "rapidfuzz-3.9.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32dd79b0f90ce609df96d0d48ef4327cf1f0415b9274588a466d3610a775d2f9"},
|
| 3294 |
-
{file = "rapidfuzz-3.9.2-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:04a1c38a72a50f3e6d346a33d53fa51ba390552b3592fca64a07e54d749b439b"},
|
| 3295 |
-
{file = "rapidfuzz-3.9.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77ca96eec40e815f0cf10b00008f295fd26ca43792a844cf62588a8ea614e160"},
|
| 3296 |
-
{file = "rapidfuzz-3.9.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c01c515a928f295f49d588b6523f44b474f047f9f2de0079bc57bcd00b870778"},
|
| 3297 |
-
{file = "rapidfuzz-3.9.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:07e14ef260b6f4ee03dff07a0ac95a16aff1ddbc7e6171e07e49d2d61526f3be"},
|
| 3298 |
-
{file = "rapidfuzz-3.9.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:64f3480bddc12b89969930f12a50a1aeb53e09aad41cf8b27694d83ca1cc7864"},
|
| 3299 |
-
{file = "rapidfuzz-3.9.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3c9e33ec21755bda1878095537cb84848e9cf6510d4837d22144ba04e33df29"},
|
| 3300 |
-
{file = "rapidfuzz-3.9.2-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a70045e84225697ddf67d656aa25b70d6802e2ff339d51f9545fca5b9b13fb8c"},
|
| 3301 |
-
{file = "rapidfuzz-3.9.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9ec1fd328518c33adb9171afe8735137cb7b492e4a81cddc23568f9980c235c"},
|
| 3302 |
-
{file = "rapidfuzz-3.9.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:1fd8458fdac232766d55593c1228c70968f382fdc376c25685273f99b5d1d921"},
|
| 3303 |
-
{file = "rapidfuzz-3.9.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a373748fddb5403b562b6d682082de360bb08395f44e3cb7e74819461e39a16c"},
|
| 3304 |
-
{file = "rapidfuzz-3.9.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:45f80856db3e22cb5f96ad1572aa1d004714514625ed4668144661d8a7c7e61f"},
|
| 3305 |
-
{file = "rapidfuzz-3.9.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:663e52cf878e0ccbbad0744eb3e2bb83a784645b146f15611bac225bc218f19b"},
|
| 3306 |
-
{file = "rapidfuzz-3.9.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbe4d3034a8cfe59a2b477375ad7d739b3e5935f10af08abdf64aae55780cad"},
|
| 3307 |
-
{file = "rapidfuzz-3.9.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd38abfda97e42b30093f207108dcba944beab1edf6624ba757cf57354063177"},
|
| 3308 |
-
{file = "rapidfuzz-3.9.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:16b41fe360387283a3184ce72d4d26d1928e7ce809268a88e8491a776dd770af"},
|
| 3309 |
-
{file = "rapidfuzz-3.9.2.tar.gz", hash = "sha256:c899d78709f8d4bd0059784fa27a9f6c53d04fc4aeaa21de7c0c8e34a7154e88"},
|
| 3310 |
-
]
|
| 3311 |
-
|
| 3312 |
-
[package.extras]
|
| 3313 |
-
full = ["numpy"]
|
| 3314 |
-
|
| 3315 |
[[package]]
|
| 3316 |
name = "regex"
|
| 3317 |
version = "2024.5.15"
|
|
@@ -3919,23 +3192,6 @@ files = [
|
|
| 3919 |
{file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"},
|
| 3920 |
]
|
| 3921 |
|
| 3922 |
-
[[package]]
|
| 3923 |
-
name = "tzlocal"
|
| 3924 |
-
version = "5.2"
|
| 3925 |
-
description = "tzinfo object for the local timezone"
|
| 3926 |
-
optional = false
|
| 3927 |
-
python-versions = ">=3.8"
|
| 3928 |
-
files = [
|
| 3929 |
-
{file = "tzlocal-5.2-py3-none-any.whl", hash = "sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8"},
|
| 3930 |
-
{file = "tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e"},
|
| 3931 |
-
]
|
| 3932 |
-
|
| 3933 |
-
[package.dependencies]
|
| 3934 |
-
tzdata = {version = "*", markers = "platform_system == \"Windows\""}
|
| 3935 |
-
|
| 3936 |
-
[package.extras]
|
| 3937 |
-
devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"]
|
| 3938 |
-
|
| 3939 |
[[package]]
|
| 3940 |
name = "urllib3"
|
| 3941 |
version = "2.2.1"
|
|
@@ -4390,4 +3646,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more
|
|
| 4390 |
[metadata]
|
| 4391 |
lock-version = "2.0"
|
| 4392 |
python-versions = "^3.11,<3.12"
|
| 4393 |
-
content-hash = "
|
|
|
|
| 123 |
[package.dependencies]
|
| 124 |
typing-extensions = "*"
|
| 125 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
[[package]]
|
| 127 |
name = "annotated-types"
|
| 128 |
version = "0.7.0"
|
|
|
|
| 179 |
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)"]
|
| 180 |
trio = ["trio (>=0.23)"]
|
| 181 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
[[package]]
|
| 183 |
name = "attrs"
|
| 184 |
version = "23.2.0"
|
|
|
|
| 380 |
marshmallow = ">=3.18.0,<4.0.0"
|
| 381 |
typing-inspect = ">=0.4.0,<1"
|
| 382 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 383 |
[[package]]
|
| 384 |
name = "deprecated"
|
| 385 |
version = "1.2.14"
|
|
|
|
| 419 |
{file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"},
|
| 420 |
]
|
| 421 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 422 |
[[package]]
|
| 423 |
name = "fastapi"
|
| 424 |
version = "0.109.2"
|
|
|
|
| 903 |
{file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"},
|
| 904 |
]
|
| 905 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 906 |
[[package]]
|
| 907 |
name = "jinja2"
|
| 908 |
version = "3.1.4"
|
|
|
|
| 1001 |
{file = "joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e"},
|
| 1002 |
]
|
| 1003 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1004 |
[[package]]
|
| 1005 |
name = "llama-index"
|
| 1006 |
version = "0.10.28"
|
|
|
|
| 1310 |
httpx = ">=0.20.0"
|
| 1311 |
pydantic = ">=1.10"
|
| 1312 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1313 |
[[package]]
|
| 1314 |
name = "markupsafe"
|
| 1315 |
version = "2.1.5"
|
|
|
|
| 1398 |
docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.3.7)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"]
|
| 1399 |
tests = ["pytest", "pytz", "simplejson"]
|
| 1400 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1401 |
[[package]]
|
| 1402 |
name = "monotonic"
|
| 1403 |
version = "1.6"
|
|
|
|
| 1620 |
|
| 1621 |
[[package]]
|
| 1622 |
name = "openai"
|
| 1623 |
+
version = "1.30.1"
|
| 1624 |
description = "The official Python library for the openai API"
|
| 1625 |
optional = false
|
| 1626 |
python-versions = ">=3.7.1"
|
| 1627 |
files = [
|
| 1628 |
+
{file = "openai-1.30.1-py3-none-any.whl", hash = "sha256:c9fb3c3545c118bbce8deb824397b9433a66d0d0ede6a96f7009c95b76de4a46"},
|
| 1629 |
+
{file = "openai-1.30.1.tar.gz", hash = "sha256:4f85190e577cba0b066e1950b8eb9b11d25bc7ebcc43a86b326ce1bfa564ec74"},
|
| 1630 |
]
|
| 1631 |
|
| 1632 |
[package.dependencies]
|
|
|
|
| 2129 |
{file = "opentelemetry_util_http-0.45b0.tar.gz", hash = "sha256:4ce08b6a7d52dd7c96b7705b5b4f06fdb6aa3eac1233b3b0bfef8a0cab9a92cd"},
|
| 2130 |
]
|
| 2131 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2132 |
[[package]]
|
| 2133 |
name = "packaging"
|
| 2134 |
+
version = "24.0"
|
| 2135 |
description = "Core utilities for Python packages"
|
| 2136 |
optional = false
|
| 2137 |
python-versions = ">=3.7"
|
| 2138 |
files = [
|
| 2139 |
+
{file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
|
| 2140 |
+
{file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
|
| 2141 |
]
|
| 2142 |
|
| 2143 |
[[package]]
|
|
|
|
| 2209 |
test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"]
|
| 2210 |
xml = ["lxml (>=4.9.2)"]
|
| 2211 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2212 |
[[package]]
|
| 2213 |
name = "pillow"
|
| 2214 |
version = "10.3.0"
|
|
|
|
| 2315 |
[package.extras]
|
| 2316 |
grpc = ["googleapis-common-protos (>=1.53.0)", "grpc-gateway-protoc-gen-openapiv2 (==0.1.0)", "grpcio (>=1.44.0)", "grpcio (>=1.59.0)", "lz4 (>=3.1.3)", "protobuf (>=3.20.0,<3.21.0)"]
|
| 2317 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2318 |
[[package]]
|
| 2319 |
name = "posthog"
|
| 2320 |
version = "3.5.0"
|
|
|
|
| 2358 |
{file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"},
|
| 2359 |
]
|
| 2360 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2361 |
[[package]]
|
| 2362 |
name = "pydantic"
|
| 2363 |
+
version = "2.7.1"
|
| 2364 |
description = "Data validation using Python type hints"
|
| 2365 |
optional = false
|
| 2366 |
python-versions = ">=3.8"
|
| 2367 |
files = [
|
| 2368 |
+
{file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"},
|
| 2369 |
+
{file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"},
|
| 2370 |
]
|
| 2371 |
|
| 2372 |
[package.dependencies]
|
| 2373 |
annotated-types = ">=0.4.0"
|
| 2374 |
+
pydantic-core = "2.18.2"
|
| 2375 |
typing-extensions = ">=4.6.1"
|
| 2376 |
|
| 2377 |
[package.extras]
|
|
|
|
| 2379 |
|
| 2380 |
[[package]]
|
| 2381 |
name = "pydantic-core"
|
| 2382 |
+
version = "2.18.2"
|
| 2383 |
description = "Core functionality for Pydantic validation and serialization"
|
| 2384 |
optional = false
|
| 2385 |
python-versions = ">=3.8"
|
| 2386 |
files = [
|
| 2387 |
+
{file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"},
|
| 2388 |
+
{file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"},
|
| 2389 |
+
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d"},
|
| 2390 |
+
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250"},
|
| 2391 |
+
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038"},
|
| 2392 |
+
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74"},
|
| 2393 |
+
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af"},
|
| 2394 |
+
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857"},
|
| 2395 |
+
{file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563"},
|
| 2396 |
+
{file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38"},
|
| 2397 |
+
{file = "pydantic_core-2.18.2-cp310-none-win32.whl", hash = "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027"},
|
| 2398 |
+
{file = "pydantic_core-2.18.2-cp310-none-win_amd64.whl", hash = "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543"},
|
| 2399 |
+
{file = "pydantic_core-2.18.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3"},
|
| 2400 |
+
{file = "pydantic_core-2.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4"},
|
| 2401 |
+
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90"},
|
| 2402 |
+
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd"},
|
| 2403 |
+
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150"},
|
| 2404 |
+
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413"},
|
| 2405 |
+
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6"},
|
| 2406 |
+
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c"},
|
| 2407 |
+
{file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0"},
|
| 2408 |
+
{file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664"},
|
| 2409 |
+
{file = "pydantic_core-2.18.2-cp311-none-win32.whl", hash = "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e"},
|
| 2410 |
+
{file = "pydantic_core-2.18.2-cp311-none-win_amd64.whl", hash = "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3"},
|
| 2411 |
+
{file = "pydantic_core-2.18.2-cp311-none-win_arm64.whl", hash = "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d"},
|
| 2412 |
+
{file = "pydantic_core-2.18.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242"},
|
| 2413 |
+
{file = "pydantic_core-2.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043"},
|
| 2414 |
+
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182"},
|
| 2415 |
+
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f"},
|
| 2416 |
+
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3"},
|
| 2417 |
+
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f"},
|
| 2418 |
+
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72"},
|
| 2419 |
+
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c"},
|
| 2420 |
+
{file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241"},
|
| 2421 |
+
{file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3"},
|
| 2422 |
+
{file = "pydantic_core-2.18.2-cp312-none-win32.whl", hash = "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038"},
|
| 2423 |
+
{file = "pydantic_core-2.18.2-cp312-none-win_amd64.whl", hash = "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438"},
|
| 2424 |
+
{file = "pydantic_core-2.18.2-cp312-none-win_arm64.whl", hash = "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec"},
|
| 2425 |
+
{file = "pydantic_core-2.18.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439"},
|
| 2426 |
+
{file = "pydantic_core-2.18.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347"},
|
| 2427 |
+
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91"},
|
| 2428 |
+
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb"},
|
| 2429 |
+
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd"},
|
| 2430 |
+
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b"},
|
| 2431 |
+
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70"},
|
| 2432 |
+
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b"},
|
| 2433 |
+
{file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761"},
|
| 2434 |
+
{file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788"},
|
| 2435 |
+
{file = "pydantic_core-2.18.2-cp38-none-win32.whl", hash = "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350"},
|
| 2436 |
+
{file = "pydantic_core-2.18.2-cp38-none-win_amd64.whl", hash = "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e"},
|
| 2437 |
+
{file = "pydantic_core-2.18.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8"},
|
| 2438 |
+
{file = "pydantic_core-2.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a"},
|
| 2439 |
+
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804"},
|
| 2440 |
+
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b"},
|
| 2441 |
+
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0"},
|
| 2442 |
+
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845"},
|
| 2443 |
+
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0"},
|
| 2444 |
+
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4"},
|
| 2445 |
+
{file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399"},
|
| 2446 |
+
{file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b"},
|
| 2447 |
+
{file = "pydantic_core-2.18.2-cp39-none-win32.whl", hash = "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e"},
|
| 2448 |
+
{file = "pydantic_core-2.18.2-cp39-none-win_amd64.whl", hash = "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641"},
|
| 2449 |
+
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75"},
|
| 2450 |
+
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d"},
|
| 2451 |
+
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9"},
|
| 2452 |
+
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7"},
|
| 2453 |
+
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb"},
|
| 2454 |
+
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a"},
|
| 2455 |
+
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b"},
|
| 2456 |
+
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3"},
|
| 2457 |
+
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c"},
|
| 2458 |
+
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce"},
|
| 2459 |
+
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400"},
|
| 2460 |
+
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349"},
|
| 2461 |
+
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c"},
|
| 2462 |
+
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592"},
|
| 2463 |
+
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae"},
|
| 2464 |
+
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374"},
|
| 2465 |
+
{file = "pydantic_core-2.18.2.tar.gz", hash = "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e"},
|
| 2466 |
]
|
| 2467 |
|
| 2468 |
[package.dependencies]
|
|
|
|
| 2486 |
full = ["Pillow (>=8.0.0)", "PyCryptodome", "cryptography"]
|
| 2487 |
image = ["Pillow (>=8.0.0)"]
|
| 2488 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2489 |
[[package]]
|
| 2490 |
name = "python-dateutil"
|
| 2491 |
version = "2.9.0.post0"
|
|
|
|
| 2514 |
[package.extras]
|
| 2515 |
cli = ["click (>=5.0)"]
|
| 2516 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2517 |
[[package]]
|
| 2518 |
name = "pytz"
|
| 2519 |
version = "2024.1"
|
|
|
|
| 2585 |
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
|
| 2586 |
]
|
| 2587 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2588 |
[[package]]
|
| 2589 |
name = "regex"
|
| 2590 |
version = "2024.5.15"
|
|
|
|
| 3192 |
{file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"},
|
| 3193 |
]
|
| 3194 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3195 |
[[package]]
|
| 3196 |
name = "urllib3"
|
| 3197 |
version = "2.2.1"
|
|
|
|
| 3646 |
[metadata]
|
| 3647 |
lock-version = "2.0"
|
| 3648 |
python-versions = "^3.11,<3.12"
|
| 3649 |
+
content-hash = "e1009d6d5a3112c225a6916b387f78d5e01bbda9edbd6ee1b78759540da5e72e"
|
pyproject.toml
CHANGED
|
@@ -3,7 +3,7 @@
|
|
| 3 |
name = "app"
|
| 4 |
version = "0.1.0"
|
| 5 |
description = ""
|
| 6 |
-
authors = [ "
|
| 7 |
readme = "README.md"
|
| 8 |
package-mode = false
|
| 9 |
|
|
@@ -19,42 +19,14 @@ llama-index = "0.10.28"
|
|
| 19 |
llama-index-core = "0.10.28"
|
| 20 |
llama-index-vector-stores-pinecone = "^0.1.7"
|
| 21 |
traceloop-sdk = "^0.19.0"
|
| 22 |
-
langchain = "^0.2.1"
|
| 23 |
-
langchain-openai = "^0.1.8"
|
| 24 |
-
openai = "^1.30.5"
|
| 25 |
-
tenacity = "^8.3.0"
|
| 26 |
-
rapidfuzz = "^3.9.2"
|
| 27 |
-
pydantic = "^2.7.2"
|
| 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 |
-
pytest-postgresql = "^6.0.0"
|
| 38 |
-
psycopg-binary = "^3.1.19"
|
| 39 |
-
python-multipart = "^0.0.9"
|
| 40 |
|
| 41 |
[tool.poetry.dependencies.uvicorn]
|
| 42 |
extras = [ "standard" ]
|
| 43 |
version = "^0.23.2"
|
| 44 |
|
| 45 |
-
[tool.poetry.dependencies.sqlalchemy]
|
| 46 |
-
extras = [ "asyncio" ]
|
| 47 |
-
version = "^2.0.30"
|
| 48 |
-
|
| 49 |
[tool.poetry.dependencies.llama-index-agent-openai]
|
| 50 |
version = "0.2.2"
|
| 51 |
|
| 52 |
-
[tool.black]
|
| 53 |
-
line-length = 119
|
| 54 |
-
|
| 55 |
-
[tool.pytest.ini_options]
|
| 56 |
-
asyncio_mode = "auto"
|
| 57 |
-
|
| 58 |
[build-system]
|
| 59 |
requires = [ "poetry-core" ]
|
| 60 |
build-backend = "poetry.core.masonry.api"
|
|
|
|
| 3 |
name = "app"
|
| 4 |
version = "0.1.0"
|
| 5 |
description = ""
|
| 6 |
+
authors = [ "Marcus Schiesser <mail@marcusschiesser.de>" ]
|
| 7 |
readme = "README.md"
|
| 8 |
package-mode = false
|
| 9 |
|
|
|
|
| 19 |
llama-index-core = "0.10.28"
|
| 20 |
llama-index-vector-stores-pinecone = "^0.1.7"
|
| 21 |
traceloop-sdk = "^0.19.0"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
|
| 23 |
[tool.poetry.dependencies.uvicorn]
|
| 24 |
extras = [ "standard" ]
|
| 25 |
version = "^0.23.2"
|
| 26 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
[tool.poetry.dependencies.llama-index-agent-openai]
|
| 28 |
version = "0.2.2"
|
| 29 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
[build-system]
|
| 31 |
requires = [ "poetry-core" ]
|
| 32 |
build-backend = "poetry.core.masonry.api"
|
requirements.txt
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
aiostream==0.5.2
|
| 2 |
+
cachetools==5.3.3
|
| 3 |
+
docx2txt==0.8
|
| 4 |
+
fastapi==0.109.1
|
| 5 |
+
llama-index-agent-openai==0.2.2
|
| 6 |
+
llama-index-core==0.10.28
|
| 7 |
+
llama-index-vector-stores-pinecone==0.1.3
|
| 8 |
+
llama-index==0.10.28
|
| 9 |
+
python-dotenv==1.0.0
|
| 10 |
+
traceloop-sdk==0.15.11
|
| 11 |
+
uvicorn==0.23.2
|
tests/conftest.py
DELETED
|
@@ -1,83 +0,0 @@
|
|
| 1 |
-
# Credits: https://github.com/ThomasAitken/demo-fastapi-async-sqlalchemy/blob/main/backend/app/conftest.py
|
| 2 |
-
|
| 3 |
-
import asyncio
|
| 4 |
-
from contextlib import ExitStack
|
| 5 |
-
|
| 6 |
-
import pytest
|
| 7 |
-
from alembic.config import Config
|
| 8 |
-
from alembic.migration import MigrationContext
|
| 9 |
-
from alembic.operations import Operations
|
| 10 |
-
from alembic.script import ScriptDirectory
|
| 11 |
-
from config.index import config as settings
|
| 12 |
-
from app.engine.postgresdb import Base, get_db_session, postgresdb as sessionmanager
|
| 13 |
-
from main import init_app
|
| 14 |
-
from asyncpg import Connection
|
| 15 |
-
from fastapi.testclient import TestClient
|
| 16 |
-
from pytest_postgresql import factories
|
| 17 |
-
from pytest_postgresql.factories.noprocess import postgresql_noproc
|
| 18 |
-
from pytest_postgresql.janitor import DatabaseJanitor
|
| 19 |
-
from sqlalchemy.testing.entities import ComparableEntity
|
| 20 |
-
|
| 21 |
-
from config.index import config as env
|
| 22 |
-
|
| 23 |
-
test_db = factories.postgresql_proc(dbname="test_db", port=5433)
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
@pytest.fixture(autouse=True)
|
| 27 |
-
def app():
|
| 28 |
-
with ExitStack():
|
| 29 |
-
# Don't initialize database connection.
|
| 30 |
-
# This is because we want to initialize the database connection manually, so that we can create the test database.
|
| 31 |
-
yield init_app(init_db=False)
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
@pytest.fixture
|
| 35 |
-
def client(app):
|
| 36 |
-
with TestClient(app) as c:
|
| 37 |
-
yield c
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
@pytest.fixture(scope="session")
|
| 41 |
-
def event_loop(request):
|
| 42 |
-
loop = asyncio.get_event_loop_policy().new_event_loop()
|
| 43 |
-
yield loop
|
| 44 |
-
loop.close()
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
@pytest.fixture(scope="function", autouse=True)
|
| 48 |
-
async def connection_test(test_db, event_loop):
|
| 49 |
-
pg_host = test_db.host
|
| 50 |
-
pg_port = test_db.port
|
| 51 |
-
pg_user = test_db.user
|
| 52 |
-
pg_db = test_db.dbname
|
| 53 |
-
pg_password = test_db.password
|
| 54 |
-
|
| 55 |
-
with DatabaseJanitor(user=pg_user, host=pg_host, port=pg_port, dbname=pg_db, version=test_db.version, password=pg_password):
|
| 56 |
-
connection_str = f"postgresql+psycopg://{pg_user}:@{pg_host}:{pg_port}/{pg_db}"
|
| 57 |
-
sessionmanager.init(connection_str,
|
| 58 |
-
# {"echo": True, "future": True}
|
| 59 |
-
)
|
| 60 |
-
yield
|
| 61 |
-
await sessionmanager.close()
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
@pytest.fixture(scope="function", autouse=True)
|
| 65 |
-
async def create_tables(connection_test):
|
| 66 |
-
async with sessionmanager.connect() as connection:
|
| 67 |
-
await sessionmanager.drop_all(connection)
|
| 68 |
-
await sessionmanager.create_all(connection)
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
@pytest.fixture(scope="function", autouse=True)
|
| 72 |
-
async def session_override(app, connection_test):
|
| 73 |
-
async def get_db_session_override():
|
| 74 |
-
async with sessionmanager.session() as session:
|
| 75 |
-
yield session
|
| 76 |
-
|
| 77 |
-
app.dependency_overrides[get_db_session] = get_db_session_override
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
@pytest.fixture(scope="function", autouse=True)
|
| 81 |
-
async def get_db_session_fixture():
|
| 82 |
-
async with sessionmanager.session() as session:
|
| 83 |
-
yield session
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/pytest.ini
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
[pytest]
|
| 2 |
-
asyncio_mode = auto
|
| 3 |
-
timeout = 60
|
| 4 |
-
addopts = -vv --disable-warnings --durations=10 --durations-min=1.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_income_statement.py
DELETED
|
@@ -1,51 +0,0 @@
|
|
| 1 |
-
from fastapi.testclient import TestClient
|
| 2 |
-
import pytest
|
| 3 |
-
|
| 4 |
-
from sqlalchemy.ext.asyncio import AsyncSession
|
| 5 |
-
from app.model.transaction import Transaction
|
| 6 |
-
from app.model.user import User
|
| 7 |
-
from tests.utils import get_fake_transactions
|
| 8 |
-
|
| 9 |
-
@pytest.mark.asyncio
|
| 10 |
-
async def test_income_statement(client: TestClient, get_db_session_fixture: AsyncSession) -> None:
|
| 11 |
-
session_override = get_db_session_fixture
|
| 12 |
-
|
| 13 |
-
# 1. Create a user
|
| 14 |
-
user = await User.create(session_override, name="user", email="email", hashed_password="password")
|
| 15 |
-
|
| 16 |
-
# 2. Create a bunch of transactions
|
| 17 |
-
fake_transactions = get_fake_transactions(user.id)
|
| 18 |
-
await Transaction.bulk_create(session_override, fake_transactions)
|
| 19 |
-
|
| 20 |
-
# 3. Create an income statement
|
| 21 |
-
min_date = min(t.transaction_date for t in fake_transactions)
|
| 22 |
-
max_date = max(t.transaction_date for t in fake_transactions)
|
| 23 |
-
|
| 24 |
-
print(f"min_date: {min_date}, max_date: {max_date}")
|
| 25 |
-
response = client.post(
|
| 26 |
-
"/api/v1/income_statement",
|
| 27 |
-
json={
|
| 28 |
-
"user_id": 1,
|
| 29 |
-
"date_from": str(min_date),
|
| 30 |
-
"date_to": str(max_date),
|
| 31 |
-
},
|
| 32 |
-
)
|
| 33 |
-
|
| 34 |
-
assert response.status_code == 200
|
| 35 |
-
|
| 36 |
-
# 4. Verify that the income statement matches the transactions
|
| 37 |
-
response = client.get(f"/api/v1/income_statement/user/1")
|
| 38 |
-
print(response.json())
|
| 39 |
-
assert response.status_code == 200
|
| 40 |
-
assert response.json()[0].get("income")
|
| 41 |
-
assert response.json()[0].get("expenses")
|
| 42 |
-
|
| 43 |
-
report_id = response.json()[0].get("id")
|
| 44 |
-
|
| 45 |
-
# # 5. Verify that the income statement can be retrieved
|
| 46 |
-
if report_id is not None:
|
| 47 |
-
response = client.get(f"/api/v1/income_statement/report/{report_id}")
|
| 48 |
-
assert response.status_code == 200
|
| 49 |
-
assert response.json().get("income")
|
| 50 |
-
assert response.json().get("expenses")
|
| 51 |
-
assert response.json().get("id") == report_id
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_transactions.py
DELETED
|
@@ -1,25 +0,0 @@
|
|
| 1 |
-
from fastapi.testclient import TestClient
|
| 2 |
-
import pytest
|
| 3 |
-
|
| 4 |
-
from app.model.transaction import Transaction
|
| 5 |
-
from app.model.user import User
|
| 6 |
-
from sqlalchemy.ext.asyncio import AsyncSession
|
| 7 |
-
from tests.utils import get_fake_transactions
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
@pytest.mark.asyncio
|
| 11 |
-
async def test_transactions(client: TestClient, get_db_session_fixture: AsyncSession) -> None:
|
| 12 |
-
|
| 13 |
-
session_override = get_db_session_fixture
|
| 14 |
-
|
| 15 |
-
# 1. Create a user
|
| 16 |
-
user = await User.create(session_override, name="user", email="email", hashed_password="password")
|
| 17 |
-
|
| 18 |
-
# 2. Create a bunch of transactions
|
| 19 |
-
fake_transactions = get_fake_transactions(user.id)
|
| 20 |
-
await Transaction.bulk_create(session_override, fake_transactions)
|
| 21 |
-
|
| 22 |
-
# 3. Verify that the transactions are returned
|
| 23 |
-
response = client.get("/api/v1/transactions/1")
|
| 24 |
-
assert response.status_code == 200
|
| 25 |
-
assert len(response.json()) == 10
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|