Spaces:
Runtime error
Runtime error
merge to main
#1
by AryanSifars - opened
This view is limited to 50 files because it contains too many changes. See the raw diff here.
- .env.example +0 -1
- .gitignore +0 -140
- Dockerfile +0 -54
- alembic.ini +0 -141
- alembic/README +0 -1
- alembic/env.py +0 -95
- alembic/script.py.mako +0 -28
- alembic/versions/0402a665d940_update_rfp_table.py +0 -28
- alembic/versions/11d43047d470_initial_tables.py +0 -118
- alembic/versions/3ba37b484f31_update_enum_type.py +0 -56
- alembic/versions/718e96a6869a_update_tables.py +0 -125
- alembic/versions/7fae52a30825_update_rfp_table.py +0 -31
- alembic/versions/7fb86c16841c_add_flag_in_rfp_table.py +0 -36
- alembic/versions/80b53babd4e6_update_flag_column_name.py +0 -36
- alembic/versions/a3bbab758ddb_categorize_the_analysis.py +0 -91
- alembic/versions/b5d2f8b83d7e_add_flag_and_status.py +0 -41
- alembic/versions/c70330d677c0_add_evaluation_criteria.py +0 -38
- alembic/versions/e825902b2e8a_alter_tables.py +0 -49
- poetry.lock +0 -0
- pyproject.toml +0 -32
- src/__init__.py +0 -15
- src/app.py +0 -65
- src/config/__init__.py +0 -9
- src/config/_databse_config.py +0 -37
- src/config/_logger.py +0 -8
- src/controllers/__init__.py +0 -28
- src/controllers/_analysis_conteoller.py +0 -249
- src/controllers/_comparative_weight_controller.py +0 -237
- src/controllers/_evaluation_controller.py +0 -299
- src/controllers/_file_controller.py +0 -34
- src/controllers/_letter_controller.py +0 -263
- src/controllers/_proposal_ai_analysis_controller.py +0 -1783
- src/controllers/_proposal_controller.py +0 -338
- src/controllers/_proposal_detailed_analysis_controller.py +0 -291
- src/controllers/_proposal_evaluation_controller.py +0 -209
- src/controllers/_rfp_controller.py +0 -170
- src/models/__init__.py +0 -50
- src/models/_analysis.py +0 -42
- src/models/_base.py +0 -3
- src/models/_comparitive_weights.py +0 -47
- src/models/_evaluation.py +0 -34
- src/models/_letters.py +0 -36
- src/models/_proposal.py +0 -46
- src/models/_proposal_ai_analysis.py +0 -103
- src/models/_proposal_detailed_analysis.py +0 -39
- src/models/_proposal_evaluation.py +0 -40
- src/models/_proposal_query_models.py +0 -13
- src/models/_rfp.py +0 -47
- src/repositories/__init__.py +0 -23
- src/repositories/_analysis_repository.py +0 -99
.env.example
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
SQLALCHEMY_DATABASE_URI=sqlite+aiosqlite:///:memory:
|
|
|
|
|
|
.gitignore
DELETED
|
@@ -1,140 +0,0 @@
|
|
| 1 |
-
# Byte-compiled / optimized / DLL files
|
| 2 |
-
__pycache__/
|
| 3 |
-
*.py[cod]
|
| 4 |
-
*$py.class
|
| 5 |
-
|
| 6 |
-
# C extensions
|
| 7 |
-
*.so
|
| 8 |
-
|
| 9 |
-
# Distribution / packaging
|
| 10 |
-
.Python
|
| 11 |
-
build/
|
| 12 |
-
develop-eggs/
|
| 13 |
-
dist/
|
| 14 |
-
downloads/
|
| 15 |
-
eggs/
|
| 16 |
-
.eggs/
|
| 17 |
-
lib/
|
| 18 |
-
lib64/
|
| 19 |
-
parts/
|
| 20 |
-
sdist/
|
| 21 |
-
var/
|
| 22 |
-
wheels/
|
| 23 |
-
share/python-wheels/
|
| 24 |
-
*.egg-info/
|
| 25 |
-
.installed.cfg
|
| 26 |
-
*.egg
|
| 27 |
-
MANIFEST
|
| 28 |
-
|
| 29 |
-
# PyInstaller
|
| 30 |
-
# Usually these files are written by a python script from a template
|
| 31 |
-
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
| 32 |
-
*.manifest
|
| 33 |
-
*.spec
|
| 34 |
-
|
| 35 |
-
# Installer logs
|
| 36 |
-
pip-log.txt
|
| 37 |
-
pip-delete-this-directory.txt
|
| 38 |
-
|
| 39 |
-
# Unit test / coverage reports
|
| 40 |
-
htmlcov/
|
| 41 |
-
.tox/
|
| 42 |
-
.nox/
|
| 43 |
-
.coverage
|
| 44 |
-
.coverage.*
|
| 45 |
-
.cache
|
| 46 |
-
nosetests.xml
|
| 47 |
-
coverage.xml
|
| 48 |
-
*.cover
|
| 49 |
-
*.py,cover
|
| 50 |
-
.hypothesis/
|
| 51 |
-
.pytest_cache/
|
| 52 |
-
cover/
|
| 53 |
-
|
| 54 |
-
# Translations
|
| 55 |
-
*.mo
|
| 56 |
-
*.pot
|
| 57 |
-
|
| 58 |
-
# Django stuff:
|
| 59 |
-
*.log
|
| 60 |
-
local_settings.py
|
| 61 |
-
db.sqlite3
|
| 62 |
-
db.sqlite3-journal
|
| 63 |
-
|
| 64 |
-
# Flask stuff:
|
| 65 |
-
instance/
|
| 66 |
-
.webassets-cache
|
| 67 |
-
|
| 68 |
-
# Scrapy stuff:
|
| 69 |
-
.scrapy
|
| 70 |
-
|
| 71 |
-
# Sphinx documentation
|
| 72 |
-
docs/_build/
|
| 73 |
-
|
| 74 |
-
# PyBuilder
|
| 75 |
-
.pybuilder/
|
| 76 |
-
target/
|
| 77 |
-
|
| 78 |
-
# Jupyter Notebook
|
| 79 |
-
.ipynb_checkpoints
|
| 80 |
-
|
| 81 |
-
# IPython
|
| 82 |
-
profile_default/
|
| 83 |
-
ipython_config.py
|
| 84 |
-
|
| 85 |
-
# pyenv
|
| 86 |
-
# For a library or package, you might want to ignore these files since the code is
|
| 87 |
-
# intended to run in multiple environments; otherwise, check them in:
|
| 88 |
-
# .python-version
|
| 89 |
-
|
| 90 |
-
# pipenv
|
| 91 |
-
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
| 92 |
-
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
| 93 |
-
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
| 94 |
-
# install all needed dependencies.
|
| 95 |
-
#Pipfile.lock
|
| 96 |
-
|
| 97 |
-
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
| 98 |
-
__pypackages__/
|
| 99 |
-
|
| 100 |
-
# Celery stuff
|
| 101 |
-
celerybeat-schedule
|
| 102 |
-
celerybeat.pid
|
| 103 |
-
|
| 104 |
-
# SageMath parsed files
|
| 105 |
-
*.sage.py
|
| 106 |
-
|
| 107 |
-
# Environments
|
| 108 |
-
.env
|
| 109 |
-
.venv
|
| 110 |
-
env/
|
| 111 |
-
venv/
|
| 112 |
-
ENV/
|
| 113 |
-
env.bak/
|
| 114 |
-
venv.bak/
|
| 115 |
-
|
| 116 |
-
# Spyder project settings
|
| 117 |
-
.spyderproject
|
| 118 |
-
.spyproject
|
| 119 |
-
|
| 120 |
-
# Rope project settings
|
| 121 |
-
.ropeproject
|
| 122 |
-
|
| 123 |
-
# mkdocs documentation
|
| 124 |
-
/site
|
| 125 |
-
|
| 126 |
-
# mypy
|
| 127 |
-
.mypy_cache/
|
| 128 |
-
.dmypy.json
|
| 129 |
-
dmypy.json
|
| 130 |
-
|
| 131 |
-
# Pyre type checker
|
| 132 |
-
.pyre/
|
| 133 |
-
|
| 134 |
-
# pytype static type analyzer
|
| 135 |
-
.pytype/
|
| 136 |
-
|
| 137 |
-
# Cython debug symbols
|
| 138 |
-
cython_debug/
|
| 139 |
-
|
| 140 |
-
.vscode
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Dockerfile
DELETED
|
@@ -1,54 +0,0 @@
|
|
| 1 |
-
# Use official Python 3.12 slim image
|
| 2 |
-
FROM python:3.12-slim
|
| 3 |
-
|
| 4 |
-
# Set environment variables
|
| 5 |
-
ENV SQLALCHEMY_DATABASE_URI=postgresql+asyncpg://avnadmin:AVNS_W4PzkCRVJw0ASHFYc8i@pg-5cdc7c9-aryanjain19082002-7775.j.aivencloud.com:25607/defaultdb
|
| 6 |
-
ENV PYTHONUNBUFFERED=1
|
| 7 |
-
ENV POETRY_VERSION=1.8.2
|
| 8 |
-
|
| 9 |
-
# Set work directory
|
| 10 |
-
WORKDIR /app
|
| 11 |
-
|
| 12 |
-
# Install system dependencies
|
| 13 |
-
RUN apt-get update && apt-get install -y \
|
| 14 |
-
curl \
|
| 15 |
-
build-essential \
|
| 16 |
-
libreoffice \
|
| 17 |
-
default-jre-headless \
|
| 18 |
-
fonts-dejavu \
|
| 19 |
-
fonts-liberation \
|
| 20 |
-
fontconfig \
|
| 21 |
-
xvfb \
|
| 22 |
-
dbus-x11 \
|
| 23 |
-
&& apt-get clean \
|
| 24 |
-
&& rm -rf /var/lib/apt/lists/*
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
# Install Poetry
|
| 28 |
-
RUN curl -sSL https://install.python-poetry.org | python3 -
|
| 29 |
-
ENV PATH="/root/.local/bin:$PATH"
|
| 30 |
-
|
| 31 |
-
# Copy only dependency files first to leverage Docker cache
|
| 32 |
-
COPY pyproject.toml poetry.lock* /app/
|
| 33 |
-
|
| 34 |
-
# Install dependencies (without creating a virtualenv)
|
| 35 |
-
RUN poetry config virtualenvs.create false \
|
| 36 |
-
&& poetry install --no-interaction --no-ansi
|
| 37 |
-
|
| 38 |
-
RUN mkdir -p /tmp/.config/libreoffice && \
|
| 39 |
-
chmod 755 /tmp/.config/libreoffice
|
| 40 |
-
|
| 41 |
-
RUN mkdir -p /files && chmod 777 /files
|
| 42 |
-
|
| 43 |
-
ENV HOME=/tmp \
|
| 44 |
-
TMPDIR=/tmp \
|
| 45 |
-
JAVA_HOME=/usr/lib/jvm/default-java
|
| 46 |
-
|
| 47 |
-
# Copy the entire project
|
| 48 |
-
COPY . /app/
|
| 49 |
-
|
| 50 |
-
# Expose the port Uvicorn will run on
|
| 51 |
-
EXPOSE 7860
|
| 52 |
-
|
| 53 |
-
# Run the FastAPI application
|
| 54 |
-
CMD ["uvicorn", "src.app:app", "--reload", "--host", "0.0.0.0", "--port", "7860"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
alembic.ini
DELETED
|
@@ -1,141 +0,0 @@
|
|
| 1 |
-
# A generic, single database configuration.
|
| 2 |
-
|
| 3 |
-
[alembic]
|
| 4 |
-
# path to migration scripts.
|
| 5 |
-
# this is typically a path given in POSIX (e.g. forward slashes)
|
| 6 |
-
# format, relative to the token %(here)s which refers to the location of this
|
| 7 |
-
# ini file
|
| 8 |
-
script_location = %(here)s/alembic
|
| 9 |
-
|
| 10 |
-
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
|
| 11 |
-
# Uncomment the line below if you want the files to be prepended with date and time
|
| 12 |
-
# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
|
| 13 |
-
# for all available tokens
|
| 14 |
-
# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
|
| 15 |
-
|
| 16 |
-
# sys.path path, will be prepended to sys.path if present.
|
| 17 |
-
# defaults to the current working directory. for multiple paths, the path separator
|
| 18 |
-
# is defined by "path_separator" below.
|
| 19 |
-
prepend_sys_path = .
|
| 20 |
-
|
| 21 |
-
# timezone to use when rendering the date within the migration file
|
| 22 |
-
# as well as the filename.
|
| 23 |
-
# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library.
|
| 24 |
-
# Any required deps can installed by adding `alembic[tz]` to the pip requirements
|
| 25 |
-
# string value is passed to ZoneInfo()
|
| 26 |
-
# leave blank for localtime
|
| 27 |
-
# timezone =
|
| 28 |
-
|
| 29 |
-
# max length of characters to apply to the "slug" field
|
| 30 |
-
# truncate_slug_length = 40
|
| 31 |
-
|
| 32 |
-
# set to 'true' to run the environment during
|
| 33 |
-
# the 'revision' command, regardless of autogenerate
|
| 34 |
-
# revision_environment = false
|
| 35 |
-
|
| 36 |
-
# set to 'true' to allow .pyc and .pyo files without
|
| 37 |
-
# a source .py file to be detected as revisions in the
|
| 38 |
-
# versions/ directory
|
| 39 |
-
# sourceless = false
|
| 40 |
-
|
| 41 |
-
# version location specification; This defaults
|
| 42 |
-
# to <script_location>/versions. When using multiple version
|
| 43 |
-
# directories, initial revisions must be specified with --version-path.
|
| 44 |
-
# The path separator used here should be the separator specified by "path_separator"
|
| 45 |
-
# below.
|
| 46 |
-
# version_locations = %(here)s/bar:%(here)s/bat:%(here)s/alembic/versions
|
| 47 |
-
|
| 48 |
-
# path_separator; This indicates what character is used to split lists of file
|
| 49 |
-
# paths, including version_locations and prepend_sys_path within configparser
|
| 50 |
-
# files such as alembic.ini.
|
| 51 |
-
# The default rendered in new alembic.ini files is "os", which uses os.pathsep
|
| 52 |
-
# to provide os-dependent path splitting.
|
| 53 |
-
#
|
| 54 |
-
# Note that in order to support legacy alembic.ini files, this default does NOT
|
| 55 |
-
# take place if path_separator is not present in alembic.ini. If this
|
| 56 |
-
# option is omitted entirely, fallback logic is as follows:
|
| 57 |
-
#
|
| 58 |
-
# 1. Parsing of the version_locations option falls back to using the legacy
|
| 59 |
-
# "version_path_separator" key, which if absent then falls back to the legacy
|
| 60 |
-
# behavior of splitting on spaces and/or commas.
|
| 61 |
-
# 2. Parsing of the prepend_sys_path option falls back to the legacy
|
| 62 |
-
# behavior of splitting on spaces, commas, or colons.
|
| 63 |
-
#
|
| 64 |
-
# Valid values for path_separator are:
|
| 65 |
-
#
|
| 66 |
-
# path_separator = :
|
| 67 |
-
# path_separator = ;
|
| 68 |
-
# path_separator = space
|
| 69 |
-
# path_separator = newline
|
| 70 |
-
#
|
| 71 |
-
# Use os.pathsep. Default configuration used for new projects.
|
| 72 |
-
path_separator = os
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
# set to 'true' to search source files recursively
|
| 76 |
-
# in each "version_locations" directory
|
| 77 |
-
# new in Alembic version 1.10
|
| 78 |
-
# recursive_version_locations = false
|
| 79 |
-
|
| 80 |
-
# the output encoding used when revision files
|
| 81 |
-
# are written from script.py.mako
|
| 82 |
-
# output_encoding = utf-8
|
| 83 |
-
|
| 84 |
-
# database URL. This is consumed by the user-maintained env.py script only.
|
| 85 |
-
# other means of configuring database URLs may be customized within the env.py
|
| 86 |
-
# file.
|
| 87 |
-
sqlalchemy.url = driver://user:pass@host/db
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
[post_write_hooks]
|
| 91 |
-
# post_write_hooks defines scripts or Python functions that are run
|
| 92 |
-
# on newly generated revision scripts. See the documentation for further
|
| 93 |
-
# detail and examples
|
| 94 |
-
|
| 95 |
-
# format using "black" - use the console_scripts runner, against the "black" entrypoint
|
| 96 |
-
# hooks = black
|
| 97 |
-
# black.type = console_scripts
|
| 98 |
-
# black.entrypoint = black
|
| 99 |
-
# black.options = -l 79 REVISION_SCRIPT_FILENAME
|
| 100 |
-
|
| 101 |
-
# lint with attempts to fix using "ruff" - use the exec runner, execute a binary
|
| 102 |
-
# hooks = ruff
|
| 103 |
-
# ruff.type = exec
|
| 104 |
-
# ruff.executable = %(here)s/.venv/bin/ruff
|
| 105 |
-
# ruff.options = check --fix REVISION_SCRIPT_FILENAME
|
| 106 |
-
|
| 107 |
-
# Logging configuration. This is also consumed by the user-maintained
|
| 108 |
-
# env.py script only.
|
| 109 |
-
[loggers]
|
| 110 |
-
keys = root,sqlalchemy,alembic
|
| 111 |
-
|
| 112 |
-
[handlers]
|
| 113 |
-
keys = console
|
| 114 |
-
|
| 115 |
-
[formatters]
|
| 116 |
-
keys = generic
|
| 117 |
-
|
| 118 |
-
[logger_root]
|
| 119 |
-
level = WARNING
|
| 120 |
-
handlers = console
|
| 121 |
-
qualname =
|
| 122 |
-
|
| 123 |
-
[logger_sqlalchemy]
|
| 124 |
-
level = WARNING
|
| 125 |
-
handlers =
|
| 126 |
-
qualname = sqlalchemy.engine
|
| 127 |
-
|
| 128 |
-
[logger_alembic]
|
| 129 |
-
level = INFO
|
| 130 |
-
handlers =
|
| 131 |
-
qualname = alembic
|
| 132 |
-
|
| 133 |
-
[handler_console]
|
| 134 |
-
class = StreamHandler
|
| 135 |
-
args = (sys.stderr,)
|
| 136 |
-
level = NOTSET
|
| 137 |
-
formatter = generic
|
| 138 |
-
|
| 139 |
-
[formatter_generic]
|
| 140 |
-
format = %(levelname)-5.5s [%(name)s] %(message)s
|
| 141 |
-
datefmt = %H:%M:%S
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
alembic/README
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
Generic single-database configuration with an async dbapi.
|
|
|
|
|
|
alembic/env.py
DELETED
|
@@ -1,95 +0,0 @@
|
|
| 1 |
-
import asyncio
|
| 2 |
-
from logging.config import fileConfig
|
| 3 |
-
|
| 4 |
-
from alembic import context
|
| 5 |
-
from sqlalchemy import pool
|
| 6 |
-
from sqlalchemy.engine import Connection
|
| 7 |
-
from sqlalchemy.ext.asyncio import async_engine_from_config
|
| 8 |
-
|
| 9 |
-
from src.models import *
|
| 10 |
-
|
| 11 |
-
# this is the Alembic Config object, which provides
|
| 12 |
-
# access to the values within the .ini file in use.
|
| 13 |
-
config = context.config
|
| 14 |
-
|
| 15 |
-
# Interpret the config file for Python logging.
|
| 16 |
-
# This line sets up loggers basically.
|
| 17 |
-
if config.config_file_name is not None:
|
| 18 |
-
fileConfig(config.config_file_name)
|
| 19 |
-
|
| 20 |
-
# add your model's MetaData object here
|
| 21 |
-
# for 'autogenerate' support
|
| 22 |
-
# from myapp import mymodel
|
| 23 |
-
# target_metadata = mymodel.Base.metadata
|
| 24 |
-
target_metadata = Base.metadata
|
| 25 |
-
|
| 26 |
-
# other values from the config, defined by the needs of env.py,
|
| 27 |
-
# can be acquired:
|
| 28 |
-
# my_important_option = config.get_main_option("my_important_option")
|
| 29 |
-
# ... etc.
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
def run_migrations_offline() -> None:
|
| 33 |
-
"""Run migrations in 'offline' mode.
|
| 34 |
-
|
| 35 |
-
This configures the context with just a URL
|
| 36 |
-
and not an Engine, though an Engine is acceptable
|
| 37 |
-
here as well. By skipping the Engine creation
|
| 38 |
-
we don't even need a DBAPI to be available.
|
| 39 |
-
|
| 40 |
-
Calls to context.execute() here emit the given string to the
|
| 41 |
-
script output.
|
| 42 |
-
|
| 43 |
-
"""
|
| 44 |
-
url = config.get_main_option("sqlalchemy.url")
|
| 45 |
-
context.configure(
|
| 46 |
-
url=url,
|
| 47 |
-
target_metadata=target_metadata,
|
| 48 |
-
literal_binds=True,
|
| 49 |
-
dialect_opts={"paramstyle": "named"},
|
| 50 |
-
)
|
| 51 |
-
|
| 52 |
-
with context.begin_transaction():
|
| 53 |
-
context.run_migrations()
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
def do_run_migrations(connection: Connection) -> None:
|
| 57 |
-
context.configure(connection=connection, target_metadata=target_metadata)
|
| 58 |
-
|
| 59 |
-
with context.begin_transaction():
|
| 60 |
-
context.run_migrations()
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
async def run_async_migrations() -> None:
|
| 64 |
-
"""In this scenario we need to create an Engine
|
| 65 |
-
and associate a connection with the context.
|
| 66 |
-
|
| 67 |
-
"""
|
| 68 |
-
|
| 69 |
-
connectable = async_engine_from_config(
|
| 70 |
-
config.get_section(config.config_ini_section, {}),
|
| 71 |
-
prefix="sqlalchemy.",
|
| 72 |
-
poolclass=pool.NullPool,
|
| 73 |
-
)
|
| 74 |
-
|
| 75 |
-
async with connectable.connect() as connection:
|
| 76 |
-
await connection.run_sync(do_run_migrations)
|
| 77 |
-
|
| 78 |
-
await connectable.dispose()
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
def run_migrations_online() -> None:
|
| 82 |
-
"""Run migrations in 'online' mode."""
|
| 83 |
-
|
| 84 |
-
connectable = config.attributes.get("connection", None)
|
| 85 |
-
|
| 86 |
-
if connectable is None:
|
| 87 |
-
asyncio.run(run_async_migrations())
|
| 88 |
-
else:
|
| 89 |
-
do_run_migrations(connectable)
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
if context.is_offline_mode():
|
| 93 |
-
run_migrations_offline()
|
| 94 |
-
else:
|
| 95 |
-
run_migrations_online()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
alembic/script.py.mako
DELETED
|
@@ -1,28 +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 |
-
"""Upgrade schema."""
|
| 23 |
-
${upgrades if upgrades else "pass"}
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
def downgrade() -> None:
|
| 27 |
-
"""Downgrade schema."""
|
| 28 |
-
${downgrades if downgrades else "pass"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
alembic/versions/0402a665d940_update_rfp_table.py
DELETED
|
@@ -1,28 +0,0 @@
|
|
| 1 |
-
"""update rfp table
|
| 2 |
-
|
| 3 |
-
Revision ID: 0402a665d940
|
| 4 |
-
Revises: c70330d677c0
|
| 5 |
-
Create Date: 2025-06-04 10:36:51.925465
|
| 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 = '0402a665d940'
|
| 16 |
-
down_revision: Union[str, None] = 'c70330d677c0'
|
| 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 |
-
"""Upgrade schema."""
|
| 23 |
-
op.add_column("rfps", sa.Column("target_award_date", sa.DateTime(), nullable=True))
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
def downgrade() -> None:
|
| 27 |
-
"""Downgrade schema."""
|
| 28 |
-
op.drop_column("rfps", "target_award_date")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
alembic/versions/11d43047d470_initial_tables.py
DELETED
|
@@ -1,118 +0,0 @@
|
|
| 1 |
-
"""Initial tables
|
| 2 |
-
|
| 3 |
-
Revision ID: 11d43047d470
|
| 4 |
-
Revises:
|
| 5 |
-
Create Date: 2025-05-29 13:59:12.209373
|
| 6 |
-
|
| 7 |
-
"""
|
| 8 |
-
from typing import Sequence, Union
|
| 9 |
-
|
| 10 |
-
from alembic import op
|
| 11 |
-
import sqlalchemy as sa
|
| 12 |
-
|
| 13 |
-
from sqlalchemy.dialects import postgresql
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
# revision identifiers, used by Alembic.
|
| 17 |
-
revision: str = '11d43047d470'
|
| 18 |
-
down_revision: Union[str, None] = None
|
| 19 |
-
branch_labels: Union[str, Sequence[str], None] = None
|
| 20 |
-
depends_on: Union[str, Sequence[str], None] = None
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
def upgrade() -> None:
|
| 24 |
-
"""Upgrade schema."""
|
| 25 |
-
# ### commands auto generated by Alembic - please adjust! ###
|
| 26 |
-
op.create_table(
|
| 27 |
-
'rfps',
|
| 28 |
-
sa.Column('id', postgresql.UUID(as_uuid=True), primary_key=True),
|
| 29 |
-
sa.Column('rfp_number', sa.String(), nullable=True),
|
| 30 |
-
sa.Column('received_proposals', sa.Integer(), nullable=True),
|
| 31 |
-
sa.Column('evaluated_count', sa.Integer(), nullable=True),
|
| 32 |
-
sa.Column('awaiting_evaluation', sa.Integer(), nullable=True),
|
| 33 |
-
sa.Column('created_at', sa.DateTime(), nullable=False),
|
| 34 |
-
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
| 35 |
-
)
|
| 36 |
-
|
| 37 |
-
op.create_table(
|
| 38 |
-
'proposals',
|
| 39 |
-
sa.Column('id', postgresql.UUID(as_uuid=True), primary_key=True),
|
| 40 |
-
sa.Column('rfp_id', postgresql.UUID(as_uuid=True), sa.ForeignKey('rfps.id', ondelete="CASCADE"), nullable=False),
|
| 41 |
-
sa.Column('name', sa.String(), nullable=True),
|
| 42 |
-
sa.Column('status', sa.Enum('APPROVED', 'EVALUATED', 'IN_REVIEW', name='proposalstatus'), nullable=False),
|
| 43 |
-
sa.Column('created_at', sa.DateTime(), nullable=False),
|
| 44 |
-
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
| 45 |
-
)
|
| 46 |
-
|
| 47 |
-
op.create_table(
|
| 48 |
-
'proposal_ai_analyses',
|
| 49 |
-
sa.Column('id', postgresql.UUID(as_uuid=True), primary_key=True),
|
| 50 |
-
sa.Column('proposal_id', postgresql.UUID(as_uuid=True), sa.ForeignKey('proposals.id', ondelete="CASCADE"), nullable=False),
|
| 51 |
-
sa.Column('ai_score', sa.Float(), nullable=True),
|
| 52 |
-
sa.Column('created_at', sa.DateTime(), nullable=False),
|
| 53 |
-
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
| 54 |
-
)
|
| 55 |
-
|
| 56 |
-
op.create_table(
|
| 57 |
-
'proposal_strengths',
|
| 58 |
-
sa.Column('id', postgresql.UUID(as_uuid=True), primary_key=True),
|
| 59 |
-
sa.Column('proposal_id', postgresql.UUID(as_uuid=True), sa.ForeignKey('proposals.id', ondelete="CASCADE"), nullable=False),
|
| 60 |
-
sa.Column('strength_point', sa.String(), nullable=True),
|
| 61 |
-
sa.Column('created_at', sa.DateTime(), nullable=False),
|
| 62 |
-
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
| 63 |
-
)
|
| 64 |
-
|
| 65 |
-
op.create_table(
|
| 66 |
-
'proposal_rejection_reasons',
|
| 67 |
-
sa.Column('id', postgresql.UUID(as_uuid=True), primary_key=True),
|
| 68 |
-
sa.Column('proposal_id', postgresql.UUID(as_uuid=True), sa.ForeignKey('proposals.id', ondelete="CASCADE"), nullable=False),
|
| 69 |
-
sa.Column('reason', sa.String(), nullable=True),
|
| 70 |
-
sa.Column('created_at', sa.DateTime(), nullable=False),
|
| 71 |
-
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
| 72 |
-
)
|
| 73 |
-
|
| 74 |
-
op.create_table(
|
| 75 |
-
'proposal_weaknesses',
|
| 76 |
-
sa.Column('id', postgresql.UUID(as_uuid=True), primary_key=True),
|
| 77 |
-
sa.Column('proposal_id', postgresql.UUID(as_uuid=True), sa.ForeignKey('proposals.id', ondelete="CASCADE"), nullable=False),
|
| 78 |
-
sa.Column('weakness_point', sa.String(), nullable=True),
|
| 79 |
-
sa.Column('created_at', sa.DateTime(), nullable=False),
|
| 80 |
-
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
| 81 |
-
)
|
| 82 |
-
|
| 83 |
-
op.create_table(
|
| 84 |
-
'proposal_improvements',
|
| 85 |
-
sa.Column('id', postgresql.UUID(as_uuid=True), primary_key=True),
|
| 86 |
-
sa.Column('proposal_id', postgresql.UUID(as_uuid=True), sa.ForeignKey('proposals.id', ondelete="CASCADE"), nullable=False),
|
| 87 |
-
sa.Column('improvement_point', sa.String(), nullable=True),
|
| 88 |
-
sa.Column('created_at', sa.DateTime(), nullable=False),
|
| 89 |
-
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
| 90 |
-
)
|
| 91 |
-
|
| 92 |
-
op.create_table(
|
| 93 |
-
'proposal_detail_analyses',
|
| 94 |
-
sa.Column('id', postgresql.UUID(as_uuid=True), primary_key=True),
|
| 95 |
-
sa.Column('proposal_id', postgresql.UUID(as_uuid=True), sa.ForeignKey('proposals.id', ondelete="CASCADE"), nullable=False),
|
| 96 |
-
sa.Column('ai_assessment_result', sa.String(), nullable=True),
|
| 97 |
-
sa.Column('confidence', sa.Float(), nullable=True),
|
| 98 |
-
sa.Column('operation_criteria', sa.Enum(
|
| 99 |
-
'MISSION_ALIGNMENT', 'TECHNICAL_FEASIBILITY', 'COST_EFFICIENCY', 'RISK_ASSESSMENT',
|
| 100 |
-
name='operationcriteria'), nullable=False),
|
| 101 |
-
sa.Column('created_at', sa.DateTime(), nullable=False),
|
| 102 |
-
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
| 103 |
-
)
|
| 104 |
-
# ### end Alembic commands ###
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
def downgrade() -> None:
|
| 108 |
-
"""Downgrade schema."""
|
| 109 |
-
# ### commands auto generated by Alembic - please adjust! ###
|
| 110 |
-
op.drop_table('proposal_detail_analyses')
|
| 111 |
-
op.drop_table('proposal_improvements')
|
| 112 |
-
op.drop_table('proposal_weaknesses')
|
| 113 |
-
op.drop_table('proposal_rejection_reasons')
|
| 114 |
-
op.drop_table('proposal_strengths')
|
| 115 |
-
op.drop_table('proposal_ai_analyses')
|
| 116 |
-
op.drop_table('proposals')
|
| 117 |
-
op.drop_table('rfps')
|
| 118 |
-
# ### end Alembic commands ###
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
alembic/versions/3ba37b484f31_update_enum_type.py
DELETED
|
@@ -1,56 +0,0 @@
|
|
| 1 |
-
"""update enum type
|
| 2 |
-
|
| 3 |
-
Revision ID: 3ba37b484f31
|
| 4 |
-
Revises: 718e96a6869a
|
| 5 |
-
Create Date: 2025-06-05 10:04:03.238833
|
| 6 |
-
|
| 7 |
-
"""
|
| 8 |
-
|
| 9 |
-
from typing import Sequence, Union
|
| 10 |
-
|
| 11 |
-
from alembic import op
|
| 12 |
-
import sqlalchemy as sa
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
# revision identifiers, used by Alembic.
|
| 16 |
-
revision: str = "3ba37b484f31"
|
| 17 |
-
down_revision: Union[str, None] = "718e96a6869a"
|
| 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 |
-
"""Upgrade schema."""
|
| 24 |
-
op.execute(
|
| 25 |
-
"CREATE TYPE analysistype_new AS ENUM ('STRENGTHS', 'WEAKNESSES', 'DEFICIENCIES')"
|
| 26 |
-
)
|
| 27 |
-
|
| 28 |
-
op.execute(
|
| 29 |
-
"""
|
| 30 |
-
ALTER TABLE analysis
|
| 31 |
-
ALTER COLUMN analysis_type TYPE analysistype_new
|
| 32 |
-
USING analysis_type::text::analysistype_new
|
| 33 |
-
"""
|
| 34 |
-
)
|
| 35 |
-
|
| 36 |
-
op.execute("DROP TYPE analysistype")
|
| 37 |
-
|
| 38 |
-
op.execute("ALTER TYPE analysistype_new RENAME TO analysistype")
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
def downgrade() -> None:
|
| 42 |
-
"""Downgrade schema."""
|
| 43 |
-
op.execute(
|
| 44 |
-
"CREATE TYPE analysistype_old AS ENUM ('STRENGHTS', 'WEAKNESSES', 'DEFICIENCIES')"
|
| 45 |
-
)
|
| 46 |
-
|
| 47 |
-
op.execute(
|
| 48 |
-
"""
|
| 49 |
-
ALTER TABLE analysis
|
| 50 |
-
ALTER COLUMN analysis_type TYPE analysistype_old
|
| 51 |
-
USING analysis_type::text::analysistype_old
|
| 52 |
-
"""
|
| 53 |
-
)
|
| 54 |
-
|
| 55 |
-
op.execute("DROP TYPE analysistype")
|
| 56 |
-
op.execute("ALTER TYPE analysistype_old RENAME TO analysistype")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
alembic/versions/718e96a6869a_update_tables.py
DELETED
|
@@ -1,125 +0,0 @@
|
|
| 1 |
-
"""update tables
|
| 2 |
-
|
| 3 |
-
Revision ID: 718e96a6869a
|
| 4 |
-
Revises: a3bbab758ddb
|
| 5 |
-
Create Date: 2025-06-05 05:27:57.408552
|
| 6 |
-
|
| 7 |
-
"""
|
| 8 |
-
|
| 9 |
-
from typing import Sequence, Union
|
| 10 |
-
|
| 11 |
-
from alembic import op
|
| 12 |
-
import sqlalchemy as sa
|
| 13 |
-
|
| 14 |
-
from sqlalchemy.dialects import postgresql
|
| 15 |
-
|
| 16 |
-
# revision identifiers, used by Alembic.
|
| 17 |
-
revision: str = "718e96a6869a"
|
| 18 |
-
down_revision: Union[str, None] = "a3bbab758ddb"
|
| 19 |
-
branch_labels: Union[str, Sequence[str], None] = None
|
| 20 |
-
depends_on: Union[str, Sequence[str], None] = None
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
def upgrade() -> None:
|
| 24 |
-
"""Upgrade schema."""
|
| 25 |
-
op.add_column("rfps", sa.Column("ko_name", sa.String(), nullable=True))
|
| 26 |
-
op.add_column("rfps", sa.Column("award_type", sa.String(), nullable=True))
|
| 27 |
-
op.add_column("rfps", sa.Column("google_drive_id", sa.String(), nullable=True))
|
| 28 |
-
|
| 29 |
-
op.add_column("proposals", sa.Column("ai_score", sa.Float(), nullable=True))
|
| 30 |
-
op.add_column("proposals", sa.Column("final_score", sa.Float(), nullable=True))
|
| 31 |
-
|
| 32 |
-
op.create_table(
|
| 33 |
-
"comparative_weights",
|
| 34 |
-
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
|
| 35 |
-
sa.Column(
|
| 36 |
-
"rfp_id",
|
| 37 |
-
postgresql.UUID(as_uuid=True),
|
| 38 |
-
sa.ForeignKey("rfps.id", ondelete="CASCADE"),
|
| 39 |
-
nullable=False,
|
| 40 |
-
unique=True,
|
| 41 |
-
),
|
| 42 |
-
sa.Column(
|
| 43 |
-
"technical_weight",
|
| 44 |
-
sa.Enum("NONE", "LOW", "MEDIUM", "HIGH", name="comparativeweights"),
|
| 45 |
-
nullable=False,
|
| 46 |
-
),
|
| 47 |
-
sa.Column(
|
| 48 |
-
"management_weight",
|
| 49 |
-
sa.Enum("NONE", "LOW", "MEDIUM", "HIGH", name="comparativeweights"),
|
| 50 |
-
nullable=False,
|
| 51 |
-
),
|
| 52 |
-
sa.Column(
|
| 53 |
-
"past_performance_weight",
|
| 54 |
-
sa.Enum("NONE", "LOW", "MEDIUM", "HIGH", name="comparativeweights"),
|
| 55 |
-
nullable=False,
|
| 56 |
-
),
|
| 57 |
-
sa.Column(
|
| 58 |
-
"price_weight",
|
| 59 |
-
sa.Enum("NONE", "LOW", "MEDIUM", "HIGH", name="comparativeweights"),
|
| 60 |
-
nullable=False,
|
| 61 |
-
),
|
| 62 |
-
sa.Column("strengths_weight", sa.Float(), nullable=False, default=15),
|
| 63 |
-
sa.Column("weaknesses_weight", sa.Float(), nullable=False),
|
| 64 |
-
sa.Column("created_at", sa.DateTime(), nullable=False),
|
| 65 |
-
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
| 66 |
-
)
|
| 67 |
-
|
| 68 |
-
op.create_table(
|
| 69 |
-
"evaluations",
|
| 70 |
-
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
|
| 71 |
-
sa.Column(
|
| 72 |
-
"proposal_id",
|
| 73 |
-
postgresql.UUID(as_uuid=True),
|
| 74 |
-
sa.ForeignKey("proposals.id", ondelete="CASCADE"),
|
| 75 |
-
nullable=False,
|
| 76 |
-
),
|
| 77 |
-
sa.Column(
|
| 78 |
-
"evaluation_type",
|
| 79 |
-
sa.Enum(
|
| 80 |
-
"TECHNICAL",
|
| 81 |
-
"MANAGEMENT",
|
| 82 |
-
"PAST_PERFORMANCE",
|
| 83 |
-
"PRICE",
|
| 84 |
-
name="evaluationtype",
|
| 85 |
-
),
|
| 86 |
-
nullable=False,
|
| 87 |
-
),
|
| 88 |
-
sa.Column("ai_score", sa.Float(), nullable=True),
|
| 89 |
-
sa.Column("adjusted_score", sa.Float(), nullable=True),
|
| 90 |
-
sa.Column("created_at", sa.DateTime(), nullable=False),
|
| 91 |
-
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
| 92 |
-
)
|
| 93 |
-
|
| 94 |
-
op.create_table(
|
| 95 |
-
"analysis",
|
| 96 |
-
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
|
| 97 |
-
sa.Column(
|
| 98 |
-
"evaluation_id",
|
| 99 |
-
postgresql.UUID(as_uuid=True),
|
| 100 |
-
sa.ForeignKey("evaluations.id", ondelete="CASCADE"),
|
| 101 |
-
nullable=False,
|
| 102 |
-
),
|
| 103 |
-
sa.Column("insights", sa.Text(), nullable=True),
|
| 104 |
-
sa.Column(
|
| 105 |
-
"analysis_type",
|
| 106 |
-
sa.Enum("STRENGHTS", "WEAKNESSES", "DEFICIENCIES", name="analysistype"),
|
| 107 |
-
nullable=False,
|
| 108 |
-
),
|
| 109 |
-
sa.Column("created_at", sa.DateTime(), nullable=False),
|
| 110 |
-
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
| 111 |
-
)
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
def downgrade() -> None:
|
| 115 |
-
"""Downgrade schema."""
|
| 116 |
-
op.drop_column("rfps", "ko_name")
|
| 117 |
-
op.drop_column("rfps", "award_type")
|
| 118 |
-
op.drop_column("rfps", "google_drive_id")
|
| 119 |
-
|
| 120 |
-
op.drop_column("proposals", "ai_score")
|
| 121 |
-
op.drop_column("proposals", "final_score")
|
| 122 |
-
|
| 123 |
-
op.drop_table("comparative_weights")
|
| 124 |
-
op.drop_table("evaluations")
|
| 125 |
-
op.drop_table("analysis")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
alembic/versions/7fae52a30825_update_rfp_table.py
DELETED
|
@@ -1,31 +0,0 @@
|
|
| 1 |
-
"""update rfp table
|
| 2 |
-
|
| 3 |
-
Revision ID: 7fae52a30825
|
| 4 |
-
Revises: 0402a665d940
|
| 5 |
-
Create Date: 2025-06-04 11:00:24.081115
|
| 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 = '7fae52a30825'
|
| 16 |
-
down_revision: Union[str, None] = '0402a665d940'
|
| 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 |
-
"""Upgrade schema."""
|
| 23 |
-
status = sa.Enum("ACTIVE", "IN_PROGRESS", "NOT_YET_STARTED", "CUSTOMER_HOLD", "UNSOLVED_ISSUES", "AWARDED", "PROTESTED", name="rfpstatus")
|
| 24 |
-
status.create(op.get_bind())
|
| 25 |
-
op.add_column("rfps", sa.Column("status", status, nullable=False, server_default="ACTIVE"))
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
def downgrade() -> None:
|
| 29 |
-
"""Downgrade schema."""
|
| 30 |
-
sa.Enum(name='rfpstatus').drop(op.get_bind(), checkfirst=True)
|
| 31 |
-
op.drop_column("rfps", "status")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
alembic/versions/7fb86c16841c_add_flag_in_rfp_table.py
DELETED
|
@@ -1,36 +0,0 @@
|
|
| 1 |
-
"""add flag in rfp table
|
| 2 |
-
|
| 3 |
-
Revision ID: 7fb86c16841c
|
| 4 |
-
Revises: b5d2f8b83d7e
|
| 5 |
-
Create Date: 2025-06-06 11:06:57.084499
|
| 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 = '7fb86c16841c'
|
| 16 |
-
down_revision: Union[str, None] = 'b5d2f8b83d7e'
|
| 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 |
-
"""Upgrade schema."""
|
| 23 |
-
op.add_column("rfps", sa.Column("flag", sa.Boolean(), nullable=True, server_default=sa.text("FALSE")))
|
| 24 |
-
op.execute("UPDATE rfps SET flag = FALSE")
|
| 25 |
-
op.alter_column(
|
| 26 |
-
"rfps",
|
| 27 |
-
"flag",
|
| 28 |
-
existing_type=sa.Boolean(),
|
| 29 |
-
nullable=False,
|
| 30 |
-
server_default=sa.text("FALSE"),
|
| 31 |
-
)
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
def downgrade() -> None:
|
| 35 |
-
"""Downgrade schema."""
|
| 36 |
-
op.drop_column("rfps", "flag")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
alembic/versions/80b53babd4e6_update_flag_column_name.py
DELETED
|
@@ -1,36 +0,0 @@
|
|
| 1 |
-
"""update flag column name
|
| 2 |
-
|
| 3 |
-
Revision ID: 80b53babd4e6
|
| 4 |
-
Revises: 7fb86c16841c
|
| 5 |
-
Create Date: 2025-06-06 11:29:29.960724
|
| 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 = '80b53babd4e6'
|
| 16 |
-
down_revision: Union[str, None] = '7fb86c16841c'
|
| 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 |
-
"""Upgrade schema."""
|
| 23 |
-
op.alter_column(
|
| 24 |
-
'rfps',
|
| 25 |
-
'flag',
|
| 26 |
-
new_column_name='comparative_weights_locked'
|
| 27 |
-
)
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
def downgrade() -> None:
|
| 31 |
-
"""Downgrade schema."""
|
| 32 |
-
op.alter_column(
|
| 33 |
-
'rfps',
|
| 34 |
-
'comparative_weights_locked',
|
| 35 |
-
new_column_name='flag'
|
| 36 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
alembic/versions/a3bbab758ddb_categorize_the_analysis.py
DELETED
|
@@ -1,91 +0,0 @@
|
|
| 1 |
-
"""categorize the analysis
|
| 2 |
-
|
| 3 |
-
Revision ID: a3bbab758ddb
|
| 4 |
-
Revises: 7fae52a30825
|
| 5 |
-
Create Date: 2025-06-04 14:01:24.366580
|
| 6 |
-
|
| 7 |
-
"""
|
| 8 |
-
|
| 9 |
-
from typing import Sequence, Union
|
| 10 |
-
|
| 11 |
-
from alembic import op
|
| 12 |
-
import sqlalchemy as sa
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
# revision identifiers, used by Alembic.
|
| 16 |
-
revision: str = "a3bbab758ddb"
|
| 17 |
-
down_revision: Union[str, None] = "7fae52a30825"
|
| 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 |
-
"""Upgrade schema."""
|
| 24 |
-
strength_category_enum = sa.Enum(
|
| 25 |
-
"TECHNICAL", "MANAGEMENT", "PAST_PERFORMANCE", "PRICE", name="strengthcategory"
|
| 26 |
-
)
|
| 27 |
-
strength_category_enum.create(op.get_bind())
|
| 28 |
-
op.add_column(
|
| 29 |
-
"proposal_strengths",
|
| 30 |
-
sa.Column(
|
| 31 |
-
"category",
|
| 32 |
-
strength_category_enum,
|
| 33 |
-
nullable=False,
|
| 34 |
-
server_default="TECHNICAL",
|
| 35 |
-
),
|
| 36 |
-
)
|
| 37 |
-
weakness_category_enum = sa.Enum(
|
| 38 |
-
"TECHNICAL", "MANAGEMENT", "PAST_PERFORMANCE", "PRICE", name="weaknesscategory"
|
| 39 |
-
)
|
| 40 |
-
weakness_category_enum.create(op.get_bind())
|
| 41 |
-
op.add_column(
|
| 42 |
-
"proposal_weaknesses",
|
| 43 |
-
sa.Column(
|
| 44 |
-
"category",
|
| 45 |
-
weakness_category_enum,
|
| 46 |
-
nullable=False,
|
| 47 |
-
server_default="TECHNICAL",
|
| 48 |
-
),
|
| 49 |
-
)
|
| 50 |
-
rejection_category_enum = sa.Enum(
|
| 51 |
-
"TECHNICAL", "MANAGEMENT", "PAST_PERFORMANCE", "PRICE", name="rejectioncategory"
|
| 52 |
-
)
|
| 53 |
-
rejection_category_enum.create(op.get_bind())
|
| 54 |
-
op.add_column(
|
| 55 |
-
"proposal_rejection_reasons",
|
| 56 |
-
sa.Column(
|
| 57 |
-
"category",
|
| 58 |
-
rejection_category_enum,
|
| 59 |
-
nullable=False,
|
| 60 |
-
server_default="TECHNICAL",
|
| 61 |
-
),
|
| 62 |
-
)
|
| 63 |
-
improvement_category_enum = sa.Enum(
|
| 64 |
-
"TECHNICAL",
|
| 65 |
-
"MANAGEMENT",
|
| 66 |
-
"PAST_PERFORMANCE",
|
| 67 |
-
"PRICE",
|
| 68 |
-
name="improvementcategory",
|
| 69 |
-
)
|
| 70 |
-
improvement_category_enum.create(op.get_bind())
|
| 71 |
-
op.add_column(
|
| 72 |
-
"proposal_improvements",
|
| 73 |
-
sa.Column(
|
| 74 |
-
"category",
|
| 75 |
-
improvement_category_enum,
|
| 76 |
-
nullable=False,
|
| 77 |
-
server_default="TECHNICAL",
|
| 78 |
-
),
|
| 79 |
-
)
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
def downgrade() -> None:
|
| 83 |
-
"""Downgrade schema."""
|
| 84 |
-
sa.Enum(name="strengthcategory").drop(op.get_bind(), checkfirst=True)
|
| 85 |
-
op.drop_column("proposal_strengths", "category")
|
| 86 |
-
sa.Enum(name="weaknesscategory").drop(op.get_bind(), checkfirst=True)
|
| 87 |
-
op.drop_column("proposal_weaknesses", "category")
|
| 88 |
-
sa.Enum(name="rejectioncategory").drop(op.get_bind(), checkfirst=True)
|
| 89 |
-
op.drop_column("proposal_rejection_reasons", "category")
|
| 90 |
-
sa.Enum(name="improvementcategory").drop(op.get_bind(), checkfirst=True)
|
| 91 |
-
op.drop_column("proposal_improvements", "category")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
alembic/versions/b5d2f8b83d7e_add_flag_and_status.py
DELETED
|
@@ -1,41 +0,0 @@
|
|
| 1 |
-
"""add flag and status
|
| 2 |
-
|
| 3 |
-
Revision ID: b5d2f8b83d7e
|
| 4 |
-
Revises: 3ba37b484f31
|
| 5 |
-
Create Date: 2025-06-05 20:00:52.297117
|
| 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 = 'b5d2f8b83d7e'
|
| 16 |
-
down_revision: Union[str, None] = '3ba37b484f31'
|
| 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 |
-
"""Upgrade schema."""
|
| 23 |
-
op.add_column("comparative_weights", sa.Column("flag", sa.Boolean(), nullable=True, server_default=sa.text("FALSE")))
|
| 24 |
-
op.execute("UPDATE comparative_weights SET flag = FALSE")
|
| 25 |
-
op.alter_column(
|
| 26 |
-
"comparative_weights",
|
| 27 |
-
"flag",
|
| 28 |
-
existing_type=sa.Boolean(),
|
| 29 |
-
nullable=False,
|
| 30 |
-
server_default=sa.text("FALSE"),
|
| 31 |
-
)
|
| 32 |
-
analysis_enum = sa.Enum("ACCEPTED", "REJECTED", "PENDING", name="analysisstatus")
|
| 33 |
-
analysis_enum.create(op.get_bind())
|
| 34 |
-
op.add_column("analysis", sa.Column("status", analysis_enum, nullable=False, server_default="PENDING"))
|
| 35 |
-
|
| 36 |
-
def downgrade() -> None:
|
| 37 |
-
"""Downgrade schema."""
|
| 38 |
-
op.drop_column("comparative_weights", "flag")
|
| 39 |
-
|
| 40 |
-
sa.Enum(name="analysisstatus").drop(op.get_bind(), checkfirst=True)
|
| 41 |
-
op.drop_column("analysis", "status")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
alembic/versions/c70330d677c0_add_evaluation_criteria.py
DELETED
|
@@ -1,38 +0,0 @@
|
|
| 1 |
-
"""add evaluation_criteria
|
| 2 |
-
|
| 3 |
-
Revision ID: c70330d677c0
|
| 4 |
-
Revises: e825902b2e8a
|
| 5 |
-
Create Date: 2025-06-02 19:38:53.073958
|
| 6 |
-
|
| 7 |
-
"""
|
| 8 |
-
from typing import Sequence, Union
|
| 9 |
-
|
| 10 |
-
from alembic import op
|
| 11 |
-
import sqlalchemy as sa
|
| 12 |
-
|
| 13 |
-
from sqlalchemy.dialects import postgresql
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
# revision identifiers, used by Alembic.
|
| 17 |
-
revision: str = 'c70330d677c0'
|
| 18 |
-
down_revision: Union[str, None] = 'e825902b2e8a'
|
| 19 |
-
branch_labels: Union[str, Sequence[str], None] = None
|
| 20 |
-
depends_on: Union[str, Sequence[str], None] = None
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
def upgrade() -> None:
|
| 24 |
-
"""Upgrade schema."""
|
| 25 |
-
op.create_table(
|
| 26 |
-
"evaluation_criteria_details",
|
| 27 |
-
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
|
| 28 |
-
sa.Column("rfp_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("rfps.id", ondelete="CASCADE"), nullable=False),
|
| 29 |
-
sa.Column("evaluation_criteria", sa.String(), nullable=True),
|
| 30 |
-
sa.Column("evaluation_criteria_type", sa.Enum("EVALUATION", "GATE", name="evaluationcriteriatype"), nullable=False),
|
| 31 |
-
sa.Column("created_at", sa.DateTime(), nullable=False),
|
| 32 |
-
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
| 33 |
-
)
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
def downgrade() -> None:
|
| 37 |
-
"""Downgrade schema."""
|
| 38 |
-
op.drop_table("evaluation_criteria_details")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
alembic/versions/e825902b2e8a_alter_tables.py
DELETED
|
@@ -1,49 +0,0 @@
|
|
| 1 |
-
"""alter tables
|
| 2 |
-
|
| 3 |
-
Revision ID: e825902b2e8a
|
| 4 |
-
Revises: 11d43047d470
|
| 5 |
-
Create Date: 2025-06-02 16:32:45.552509
|
| 6 |
-
|
| 7 |
-
"""
|
| 8 |
-
from typing import Sequence, Union
|
| 9 |
-
|
| 10 |
-
from alembic import op
|
| 11 |
-
import sqlalchemy as sa
|
| 12 |
-
|
| 13 |
-
from sqlalchemy.dialects import postgresql
|
| 14 |
-
|
| 15 |
-
# revision identifiers, used by Alembic.
|
| 16 |
-
revision: str = 'e825902b2e8a'
|
| 17 |
-
down_revision: Union[str, None] = '11d43047d470'
|
| 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 |
-
"""Upgrade schema."""
|
| 24 |
-
op.add_column("rfps", sa.Column("name", sa.String(), nullable=True))
|
| 25 |
-
|
| 26 |
-
op.add_column("proposals", sa.Column("tep", sa.String(), nullable=True))
|
| 27 |
-
|
| 28 |
-
gate_criteria_enum = sa.Enum('PASS', 'FAIL', 'IN_REVIEW', name='gatecriteria')
|
| 29 |
-
gate_criteria_enum.create(op.get_bind())
|
| 30 |
-
op.add_column('proposals', sa.Column('gate_criteria', gate_criteria_enum, nullable=False, server_default='IN_REVIEW'))
|
| 31 |
-
|
| 32 |
-
op.create_table(
|
| 33 |
-
"letters",
|
| 34 |
-
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
|
| 35 |
-
sa.Column("proposal_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("proposals.id", ondelete="CASCADE"), nullable=False),
|
| 36 |
-
sa.Column("letter", sa.Text(), nullable=True),
|
| 37 |
-
sa.Column("letter_type", sa.Enum("UO", "DEBRIEF", name="lettertype"), nullable=False),
|
| 38 |
-
sa.Column("created_at", sa.DateTime(), nullable=False),
|
| 39 |
-
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
| 40 |
-
)
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
def downgrade() -> None:
|
| 44 |
-
"""Downgrade schema."""
|
| 45 |
-
op.drop_table("letters")
|
| 46 |
-
sa.Enum(name='gatecriteria').drop(op.get_bind(), checkfirst=True)
|
| 47 |
-
op.drop_column("proposals", "gate_criteria")
|
| 48 |
-
op.drop_column("proposals", "tep")
|
| 49 |
-
op.drop_column("rfps", "name")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
poetry.lock
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
pyproject.toml
DELETED
|
@@ -1,32 +0,0 @@
|
|
| 1 |
-
[tool.poetry]
|
| 2 |
-
name = "proposal-evaluation"
|
| 3 |
-
version = "0.1.0"
|
| 4 |
-
description = ""
|
| 5 |
-
authors = ["Aryan Jain <77013565+aryanjain1908@users.noreply.github.com>"]
|
| 6 |
-
readme = "README.md"
|
| 7 |
-
|
| 8 |
-
[tool.poetry.dependencies]
|
| 9 |
-
python = "^3.12"
|
| 10 |
-
uvicorn = "^0.34.2"
|
| 11 |
-
fastapi = "^0.115.12"
|
| 12 |
-
python-dotenv = "^1.1.0"
|
| 13 |
-
alembic = "^1.16.1"
|
| 14 |
-
asyncpg = "^0.30.0"
|
| 15 |
-
loguru = "^0.7.3"
|
| 16 |
-
pydantic = "^2.11.5"
|
| 17 |
-
aiosqlite = "^0.21.0"
|
| 18 |
-
python-multipart = "^0.0.20"
|
| 19 |
-
pypdf2 = "^3.0.1"
|
| 20 |
-
pymupdf = "^1.26.0"
|
| 21 |
-
python-docx = "^1.1.2"
|
| 22 |
-
docx2pdf = "^0.1.8"
|
| 23 |
-
openpyxl = "^3.1.5"
|
| 24 |
-
pandas = "^2.3.0"
|
| 25 |
-
numpy = "^2.2.6"
|
| 26 |
-
aiofiles = "^24.1.0"
|
| 27 |
-
markdown2 = "^2.5.3"
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
[build-system]
|
| 31 |
-
requires = ["poetry-core"]
|
| 32 |
-
build-backend = "poetry.core.masonry.api"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/__init__.py
DELETED
|
@@ -1,15 +0,0 @@
|
|
| 1 |
-
from dotenv import load_dotenv
|
| 2 |
-
import os
|
| 3 |
-
|
| 4 |
-
from .app import app
|
| 5 |
-
from .config import logger
|
| 6 |
-
|
| 7 |
-
load_dotenv()
|
| 8 |
-
|
| 9 |
-
if not os.getenv("SQLALCHEMY_DATABASE_URI"):
|
| 10 |
-
logger.error("SQLALCHEMY_DATABASE_URI is not set")
|
| 11 |
-
raise ValueError("SQLALCHEMY_DATABASE_URI is not set")
|
| 12 |
-
|
| 13 |
-
__all__ = ["app"]
|
| 14 |
-
__version__ = "0.1.0"
|
| 15 |
-
__author__ = "Aryan Jain"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/app.py
DELETED
|
@@ -1,65 +0,0 @@
|
|
| 1 |
-
import asyncio
|
| 2 |
-
import os
|
| 3 |
-
from contextlib import asynccontextmanager
|
| 4 |
-
from threading import Thread
|
| 5 |
-
|
| 6 |
-
from alembic import command
|
| 7 |
-
from alembic.config import Config
|
| 8 |
-
from fastapi import FastAPI
|
| 9 |
-
from fastapi.middleware.cors import CORSMiddleware
|
| 10 |
-
|
| 11 |
-
from src.config import DatabaseConfig, logger
|
| 12 |
-
from src.controllers import api_router
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
def run_upgrade(connection, alembic_config: Config):
|
| 17 |
-
alembic_config.attributes["connection"] = connection
|
| 18 |
-
command.upgrade(alembic_config, "head")
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
async def run_migrations():
|
| 22 |
-
logger.info("Running migrations if any...")
|
| 23 |
-
alembic_config = Config("alembic.ini")
|
| 24 |
-
alembic_config.set_main_option(
|
| 25 |
-
"sqlalchemy.url", os.getenv("SQLALCHEMY_DATABASE_URI")
|
| 26 |
-
)
|
| 27 |
-
async with DatabaseConfig.get_engine().begin() as session:
|
| 28 |
-
await session.run_sync(run_upgrade, alembic_config)
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
@asynccontextmanager
|
| 32 |
-
async def lifespan(app: FastAPI):
|
| 33 |
-
try:
|
| 34 |
-
logger.info("Starting up the application...")
|
| 35 |
-
await run_migrations()
|
| 36 |
-
logger.info("Application started successfully...")
|
| 37 |
-
yield
|
| 38 |
-
except Exception as e:
|
| 39 |
-
logger.error(f"Error during startup: {str(e)}")
|
| 40 |
-
raise
|
| 41 |
-
finally:
|
| 42 |
-
logger.info("Shutting down the application...")
|
| 43 |
-
logger.info("Application shutdown complete.")
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
app = FastAPI(lifespan=lifespan)
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
app.add_middleware(
|
| 50 |
-
CORSMiddleware,
|
| 51 |
-
allow_origins=os.getenv(
|
| 52 |
-
"CORS_ALLOW_ORIGINS", "http://localhost, http://127.0.0.1"
|
| 53 |
-
).split(", "),
|
| 54 |
-
allow_credentials=True,
|
| 55 |
-
allow_methods=["*"],
|
| 56 |
-
allow_headers=["*"],
|
| 57 |
-
)
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
@app.get("/")
|
| 61 |
-
async def check_health():
|
| 62 |
-
return {"response": "Service is healthy!"}
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
app.include_router(api_router, prefix="/api/v1")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/config/__init__.py
DELETED
|
@@ -1,9 +0,0 @@
|
|
| 1 |
-
from ._databse_config import DatabaseConfig
|
| 2 |
-
from ._logger import logger
|
| 3 |
-
|
| 4 |
-
__all__ = [
|
| 5 |
-
"DatabaseConfig",
|
| 6 |
-
"logger",
|
| 7 |
-
]
|
| 8 |
-
__version__ = "0.1.0"
|
| 9 |
-
__author__ = "Aryan Jain"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/config/_databse_config.py
DELETED
|
@@ -1,37 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
import threading
|
| 3 |
-
from contextlib import asynccontextmanager
|
| 4 |
-
|
| 5 |
-
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
class DatabaseConfig:
|
| 9 |
-
_thread_local = threading.local()
|
| 10 |
-
|
| 11 |
-
@classmethod
|
| 12 |
-
def get_engine(cls):
|
| 13 |
-
if not hasattr(cls._thread_local, "engine"):
|
| 14 |
-
cls._thread_local.engine = create_async_engine(
|
| 15 |
-
url=os.getenv("SQLALCHEMY_DATABASE_URI"),
|
| 16 |
-
pool_pre_ping=True,
|
| 17 |
-
pool_recycle=3600,
|
| 18 |
-
)
|
| 19 |
-
return cls._thread_local.engine
|
| 20 |
-
|
| 21 |
-
@classmethod
|
| 22 |
-
def _get_session_factory(cls):
|
| 23 |
-
if not hasattr(cls._thread_local, "session_factory"):
|
| 24 |
-
cls._thread_local.session_factory = async_sessionmaker(
|
| 25 |
-
bind=cls.get_engine(),
|
| 26 |
-
autoflush=False,
|
| 27 |
-
autocommit=False,
|
| 28 |
-
expire_on_commit=False,
|
| 29 |
-
)
|
| 30 |
-
return cls._thread_local.session_factory
|
| 31 |
-
|
| 32 |
-
@classmethod
|
| 33 |
-
@asynccontextmanager
|
| 34 |
-
async def async_session(cls):
|
| 35 |
-
session_factory = cls._get_session_factory()
|
| 36 |
-
async with session_factory() as session:
|
| 37 |
-
yield session
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/config/_logger.py
DELETED
|
@@ -1,8 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
|
| 3 |
-
from loguru import logger
|
| 4 |
-
|
| 5 |
-
LOG_FILE = os.getenv("LOG_FILE")
|
| 6 |
-
if LOG_FILE:
|
| 7 |
-
LOG_RETENTION = os.getenv("LOG_RETENTION", "90 days")
|
| 8 |
-
logger.add(LOG_FILE, retention=LOG_RETENTION)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/controllers/__init__.py
DELETED
|
@@ -1,28 +0,0 @@
|
|
| 1 |
-
from fastapi import APIRouter
|
| 2 |
-
from ._rfp_controller import RFPController
|
| 3 |
-
from ._proposal_controller import ProposalController
|
| 4 |
-
from ._proposal_ai_analysis_controller import ProposalAIController
|
| 5 |
-
from ._proposal_detailed_analysis_controller import ProposalDetailedController
|
| 6 |
-
from ._letter_controller import LetterController
|
| 7 |
-
from ._evaluation_controller import EvaluationController
|
| 8 |
-
from ._comparative_weight_controller import ComparativeWeightController
|
| 9 |
-
from ._proposal_evaluation_controller import ProposalEvaluationController
|
| 10 |
-
from ._analysis_conteoller import AnalysisController
|
| 11 |
-
from ._file_controller import FileController
|
| 12 |
-
|
| 13 |
-
api_router = APIRouter()
|
| 14 |
-
|
| 15 |
-
api_router.include_router(RFPController().router)
|
| 16 |
-
api_router.include_router(ProposalController().router)
|
| 17 |
-
# api_router.include_router(ProposalAIController().router)
|
| 18 |
-
# api_router.include_router(ProposalDetailedController().router)
|
| 19 |
-
api_router.include_router(LetterController().router)
|
| 20 |
-
api_router.include_router(EvaluationController().router)
|
| 21 |
-
api_router.include_router(ComparativeWeightController().router)
|
| 22 |
-
api_router.include_router(ProposalEvaluationController().router)
|
| 23 |
-
api_router.include_router(AnalysisController().router)
|
| 24 |
-
api_router.include_router(FileController().router)
|
| 25 |
-
|
| 26 |
-
__all__ = ["api_router"]
|
| 27 |
-
__version__ = "0.1.0"
|
| 28 |
-
__author__ = "Aryan Jain"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/controllers/_analysis_conteoller.py
DELETED
|
@@ -1,249 +0,0 @@
|
|
| 1 |
-
from fastapi import APIRouter, Body, HTTPException, Query, Path
|
| 2 |
-
from pydantic import BaseModel
|
| 3 |
-
from typing import List, Optional
|
| 4 |
-
from uuid import UUID
|
| 5 |
-
from datetime import datetime
|
| 6 |
-
|
| 7 |
-
from src.config import logger
|
| 8 |
-
from src.models import AnalysisType, AnalysisStatus
|
| 9 |
-
from src.services import AnalysisService
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
class Analysis(BaseModel):
|
| 13 |
-
id: UUID
|
| 14 |
-
evaluation_id: UUID
|
| 15 |
-
insights: str
|
| 16 |
-
analysis_type: AnalysisType
|
| 17 |
-
status: AnalysisStatus
|
| 18 |
-
created_at: datetime
|
| 19 |
-
updated_at: datetime
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
class Response(BaseModel):
|
| 23 |
-
status: str
|
| 24 |
-
data: Optional[List[Analysis]] = None
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
class CreateAnalysis(BaseModel):
|
| 28 |
-
insights: str
|
| 29 |
-
analysis_type: AnalysisType
|
| 30 |
-
status: Optional[AnalysisStatus] = None
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
class CreateAnalysisRequest(BaseModel):
|
| 34 |
-
data: List[CreateAnalysis]
|
| 35 |
-
analysis_score_type: Optional[str] = None
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
class UpdateAnalysis(BaseModel):
|
| 39 |
-
insights: Optional[str] = None
|
| 40 |
-
analysis_type: Optional[AnalysisType] = None
|
| 41 |
-
status: Optional[AnalysisStatus] = None
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
class Deleteresponse(BaseModel):
|
| 45 |
-
status: str
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
class AnalysisController:
|
| 49 |
-
def __init__(self):
|
| 50 |
-
self.service = AnalysisService
|
| 51 |
-
self.router = APIRouter()
|
| 52 |
-
self.router.add_api_route(
|
| 53 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/evaluations/{evaluation_id}/analysis",
|
| 54 |
-
self.get_analysis_by_proposal_and_rfp_id_and_evaluation_id,
|
| 55 |
-
methods=["GET"],
|
| 56 |
-
response_model=Response,
|
| 57 |
-
tags=["Analysis by Proposal and RFP ID and Evaluation ID"],
|
| 58 |
-
)
|
| 59 |
-
self.router.add_api_route(
|
| 60 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/evaluations/{evaluation_id}/analysis/{id}",
|
| 61 |
-
self.get_analysis_by_proposal_and_rfp_id_and_evaluation_id_and_id,
|
| 62 |
-
methods=["GET"],
|
| 63 |
-
response_model=Response,
|
| 64 |
-
tags=["Analysis by Proposal and RFP ID and Evaluation ID and ID"],
|
| 65 |
-
)
|
| 66 |
-
|
| 67 |
-
self.router.add_api_route(
|
| 68 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/evaluations/{evaluation_id}/analysis",
|
| 69 |
-
self.create_analysis_by_proposal_and_rfp_id_and_evaluation_id,
|
| 70 |
-
methods=["POST"],
|
| 71 |
-
response_model=Response,
|
| 72 |
-
tags=["Analysis by Proposal and RFP ID and Evaluation ID"],
|
| 73 |
-
)
|
| 74 |
-
|
| 75 |
-
self.router.add_api_route(
|
| 76 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/evaluations/{evaluation_id}/analysis",
|
| 77 |
-
self.update_analysis_by_proposal_and_rfp_id_and_evaluation_id,
|
| 78 |
-
methods=["PUT"],
|
| 79 |
-
response_model=Response,
|
| 80 |
-
tags=["Analysis by Proposal and RFP ID and Evaluation ID"],
|
| 81 |
-
)
|
| 82 |
-
self.router.add_api_route(
|
| 83 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/evaluations/{evaluation_id}/analysis/{id}",
|
| 84 |
-
self.update_analysis_by_proposal_and_rfp_id_and_evaluation_id_and_id,
|
| 85 |
-
methods=["PUT"],
|
| 86 |
-
response_model=Response,
|
| 87 |
-
tags=["Analysis by Proposal and RFP ID and Evaluation ID and ID"],
|
| 88 |
-
)
|
| 89 |
-
|
| 90 |
-
self.router.add_api_route(
|
| 91 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/evaluations/{evaluation_id}/analysis",
|
| 92 |
-
self.delete_analysis_by_proposal_and_rfp_id_and_evaluation_id,
|
| 93 |
-
methods=["DELETE"],
|
| 94 |
-
response_model=Deleteresponse,
|
| 95 |
-
tags=["Analysis by Proposal and RFP ID and Evaluation ID"],
|
| 96 |
-
)
|
| 97 |
-
self.router.add_api_route(
|
| 98 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/evaluations/{evaluation_id}/analysis/{id}",
|
| 99 |
-
self.delete_analysis_by_proposal_and_rfp_id_and_evaluation_id_and_id,
|
| 100 |
-
methods=["DELETE"],
|
| 101 |
-
response_model=Deleteresponse,
|
| 102 |
-
tags=["Analysis by Proposal and RFP ID and Evaluation ID and ID"],
|
| 103 |
-
)
|
| 104 |
-
|
| 105 |
-
async def get_analysis_by_proposal_and_rfp_id_and_evaluation_id(
|
| 106 |
-
self,
|
| 107 |
-
rfp_id: str = Path(...),
|
| 108 |
-
proposal_id: str = Path(...),
|
| 109 |
-
evaluation_id: str = Path(...),
|
| 110 |
-
analysis_type: AnalysisType = Query(None),
|
| 111 |
-
):
|
| 112 |
-
try:
|
| 113 |
-
async with self.service() as service:
|
| 114 |
-
results = await service.get_analysis(
|
| 115 |
-
evaluation_id=evaluation_id, analysis_type=analysis_type
|
| 116 |
-
)
|
| 117 |
-
return Response(
|
| 118 |
-
status="success",
|
| 119 |
-
data=[Analysis(**result) for result in results],
|
| 120 |
-
)
|
| 121 |
-
except Exception as e:
|
| 122 |
-
logger.error(e)
|
| 123 |
-
raise HTTPException(status_code=500, detail=str(e))
|
| 124 |
-
|
| 125 |
-
async def get_analysis_by_proposal_and_rfp_id_and_evaluation_id_and_id(
|
| 126 |
-
self,
|
| 127 |
-
rfp_id: str = Path(...),
|
| 128 |
-
proposal_id: str = Path(...),
|
| 129 |
-
evaluation_id: str = Path(...),
|
| 130 |
-
id: str = Path(...),
|
| 131 |
-
):
|
| 132 |
-
try:
|
| 133 |
-
async with self.service() as service:
|
| 134 |
-
results = await service.get_analysis(evaluation_id=evaluation_id, id=id)
|
| 135 |
-
return Response(
|
| 136 |
-
status="success",
|
| 137 |
-
data=[Analysis(**result) for result in results],
|
| 138 |
-
)
|
| 139 |
-
except Exception as e:
|
| 140 |
-
logger.error(e)
|
| 141 |
-
raise HTTPException(status_code=500, detail=str(e))
|
| 142 |
-
|
| 143 |
-
async def create_analysis_by_proposal_and_rfp_id_and_evaluation_id(
|
| 144 |
-
self,
|
| 145 |
-
rfp_id: str = Path(...),
|
| 146 |
-
proposal_id: str = Path(...),
|
| 147 |
-
evaluation_id: str = Path(...),
|
| 148 |
-
analysis: CreateAnalysisRequest = Body(...),
|
| 149 |
-
):
|
| 150 |
-
try:
|
| 151 |
-
async with self.service() as service:
|
| 152 |
-
evaluation_analysis = analysis.model_dump(
|
| 153 |
-
mode="json", exclude_unset=True
|
| 154 |
-
)
|
| 155 |
-
results = await service.create_analysis(
|
| 156 |
-
evaluation_id=evaluation_id,
|
| 157 |
-
evaluation_analysis=evaluation_analysis["data"],
|
| 158 |
-
analysis_score_type=(
|
| 159 |
-
evaluation_analysis["analysis_score_type"]
|
| 160 |
-
if "analysis_score_type" in evaluation_analysis
|
| 161 |
-
else None
|
| 162 |
-
),
|
| 163 |
-
)
|
| 164 |
-
return Response(
|
| 165 |
-
status="success",
|
| 166 |
-
data=[Analysis(**result) for result in results],
|
| 167 |
-
)
|
| 168 |
-
except Exception as e:
|
| 169 |
-
logger.error(e)
|
| 170 |
-
raise HTTPException(status_code=500, detail=str(e))
|
| 171 |
-
|
| 172 |
-
async def update_analysis_by_proposal_and_rfp_id_and_evaluation_id(
|
| 173 |
-
self,
|
| 174 |
-
rfp_id: str = Path(...),
|
| 175 |
-
proposal_id: str = Path(...),
|
| 176 |
-
evaluation_id: str = Path(...),
|
| 177 |
-
analysis: CreateAnalysisRequest = Body(...),
|
| 178 |
-
):
|
| 179 |
-
try:
|
| 180 |
-
async with self.service() as service:
|
| 181 |
-
results = await service.update_analysis(
|
| 182 |
-
evaluation_id=evaluation_id,
|
| 183 |
-
proposal_analysis=analysis.model_dump(
|
| 184 |
-
mode="json", exclude_unset=True
|
| 185 |
-
)["data"],
|
| 186 |
-
)
|
| 187 |
-
return Response(
|
| 188 |
-
status="success",
|
| 189 |
-
data=[Analysis(**result) for result in results],
|
| 190 |
-
)
|
| 191 |
-
except Exception as e:
|
| 192 |
-
logger.error(e)
|
| 193 |
-
raise HTTPException(status_code=500, detail=str(e))
|
| 194 |
-
|
| 195 |
-
async def update_analysis_by_proposal_and_rfp_id_and_evaluation_id_and_id(
|
| 196 |
-
self,
|
| 197 |
-
rfp_id: str = Path(...),
|
| 198 |
-
proposal_id: str = Path(...),
|
| 199 |
-
evaluation_id: str = Path(...),
|
| 200 |
-
id: str = Path(...),
|
| 201 |
-
analysis: UpdateAnalysis = Body(...),
|
| 202 |
-
):
|
| 203 |
-
try:
|
| 204 |
-
async with self.service() as service:
|
| 205 |
-
results = await service.update_analysis(
|
| 206 |
-
evaluation_id=evaluation_id,
|
| 207 |
-
id=id,
|
| 208 |
-
proposal_analysis=analysis.model_dump(
|
| 209 |
-
mode="json", exclude_unset=True
|
| 210 |
-
),
|
| 211 |
-
)
|
| 212 |
-
return Response(
|
| 213 |
-
status="success",
|
| 214 |
-
data=[Analysis(**result) for result in results],
|
| 215 |
-
)
|
| 216 |
-
except Exception as e:
|
| 217 |
-
logger.error(e)
|
| 218 |
-
raise HTTPException(status_code=500, detail=str(e))
|
| 219 |
-
|
| 220 |
-
async def delete_analysis_by_proposal_and_rfp_id_and_evaluation_id(
|
| 221 |
-
self,
|
| 222 |
-
rfp_id: str = Path(...),
|
| 223 |
-
proposal_id: str = Path(...),
|
| 224 |
-
evaluation_id: str = Path(...),
|
| 225 |
-
):
|
| 226 |
-
try:
|
| 227 |
-
async with self.service() as service:
|
| 228 |
-
results = await service.delete_analysis(evaluation_id=evaluation_id)
|
| 229 |
-
return Deleteresponse(status="success")
|
| 230 |
-
except Exception as e:
|
| 231 |
-
logger.error(e)
|
| 232 |
-
raise HTTPException(status_code=500, detail=str(e))
|
| 233 |
-
|
| 234 |
-
async def delete_analysis_by_proposal_and_rfp_id_and_evaluation_id_and_id(
|
| 235 |
-
self,
|
| 236 |
-
rfp_id: str = Path(...),
|
| 237 |
-
proposal_id: str = Path(...),
|
| 238 |
-
evaluation_id: str = Path(...),
|
| 239 |
-
id: str = Path(...),
|
| 240 |
-
):
|
| 241 |
-
try:
|
| 242 |
-
async with self.service() as service:
|
| 243 |
-
results = await service.delete_analysis(
|
| 244 |
-
evaluation_id=evaluation_id, id=id
|
| 245 |
-
)
|
| 246 |
-
return Deleteresponse(status="success")
|
| 247 |
-
except Exception as e:
|
| 248 |
-
logger.error(e)
|
| 249 |
-
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/controllers/_comparative_weight_controller.py
DELETED
|
@@ -1,237 +0,0 @@
|
|
| 1 |
-
from fastapi import APIRouter, HTTPException, Query, Path
|
| 2 |
-
from pydantic import BaseModel
|
| 3 |
-
from typing import List, Optional
|
| 4 |
-
from uuid import UUID
|
| 5 |
-
from datetime import datetime
|
| 6 |
-
|
| 7 |
-
from src.config import logger
|
| 8 |
-
from src.models import Weights
|
| 9 |
-
from src.services import ComparativeWeightService
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
class ComparitiveWeight(BaseModel):
|
| 13 |
-
id: UUID
|
| 14 |
-
rfp_id: UUID
|
| 15 |
-
technical_weight: Weights
|
| 16 |
-
management_weight: Weights
|
| 17 |
-
past_performance_weight: Weights
|
| 18 |
-
price_weight: Weights
|
| 19 |
-
strengths_weight: float
|
| 20 |
-
weaknesses_weight: float
|
| 21 |
-
flag: Optional[bool] = False
|
| 22 |
-
created_at: datetime
|
| 23 |
-
updated_at: datetime
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
class ResponseComparativeWeight(BaseModel):
|
| 27 |
-
status: str
|
| 28 |
-
data: Optional[List[ComparitiveWeight]]
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
class DeleteResponse(BaseModel):
|
| 32 |
-
status: str
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
class ComparativeWeightRequest(BaseModel):
|
| 36 |
-
technical_weight: Optional[Weights] = None
|
| 37 |
-
management_weight: Optional[Weights] = None
|
| 38 |
-
past_performance_weight: Optional[Weights] = None
|
| 39 |
-
price_weight: Optional[Weights] = None
|
| 40 |
-
strengths_weight: Optional[float] = None
|
| 41 |
-
weaknesses_weight: Optional[float] = None
|
| 42 |
-
flag: Optional[bool] = None
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
class ComparativeWeightController:
|
| 46 |
-
def __init__(self):
|
| 47 |
-
self.comparative_weight_service = ComparativeWeightService
|
| 48 |
-
self.router = APIRouter()
|
| 49 |
-
self.router.add_api_route(
|
| 50 |
-
"/rfps/{rfp_id}/comparative-weights",
|
| 51 |
-
self.get_comparative_weights_by_rfp_id,
|
| 52 |
-
methods=["GET"],
|
| 53 |
-
response_model=ResponseComparativeWeight,
|
| 54 |
-
tags=["Comparative Weights by RFP ID"],
|
| 55 |
-
)
|
| 56 |
-
self.router.add_api_route(
|
| 57 |
-
"/rfps/{rfp_id}/comparative-weights/{id}",
|
| 58 |
-
self.get_comparative_weights_by_id,
|
| 59 |
-
methods=["GET"],
|
| 60 |
-
response_model=ResponseComparativeWeight,
|
| 61 |
-
tags=["Comparative Weights by RFP ID and ID"],
|
| 62 |
-
)
|
| 63 |
-
self.router.add_api_route(
|
| 64 |
-
"/rfps/{rfp_id}/comparative-weights",
|
| 65 |
-
self.create_comparative_weights,
|
| 66 |
-
methods=["POST"],
|
| 67 |
-
response_model=ResponseComparativeWeight,
|
| 68 |
-
tags=["Comparative Weights by RFP ID"],
|
| 69 |
-
)
|
| 70 |
-
self.router.add_api_route(
|
| 71 |
-
"/rfps/{rfp_id}/comparative-weights/{id}",
|
| 72 |
-
self.update_comparative_weights_by_id,
|
| 73 |
-
methods=["PUT"],
|
| 74 |
-
response_model=ResponseComparativeWeight,
|
| 75 |
-
tags=["Comparative Weights by RFP ID and ID"],
|
| 76 |
-
)
|
| 77 |
-
self.router.add_api_route(
|
| 78 |
-
"/rfps/{rfp_id}/comparative-weights",
|
| 79 |
-
self.update_comparative_weights,
|
| 80 |
-
methods=["PUT"],
|
| 81 |
-
response_model=ResponseComparativeWeight,
|
| 82 |
-
tags=["Comparative Weights by RFP ID"],
|
| 83 |
-
)
|
| 84 |
-
self.router.add_api_route(
|
| 85 |
-
"/rfps/{rfp_id}/comparative-weights",
|
| 86 |
-
self.delete_comparative_weights,
|
| 87 |
-
methods=["DELETE"],
|
| 88 |
-
response_model=DeleteResponse,
|
| 89 |
-
tags=["Comparative Weights by RFP ID"],
|
| 90 |
-
)
|
| 91 |
-
self.router.add_api_route(
|
| 92 |
-
"/rfps/{rfp_id}/comparative-weights/{id}",
|
| 93 |
-
self.delete_comparative_weights_by_id,
|
| 94 |
-
methods=["DELETE"],
|
| 95 |
-
response_model=DeleteResponse,
|
| 96 |
-
tags=["Comparative Weights by RFP ID and ID"],
|
| 97 |
-
)
|
| 98 |
-
|
| 99 |
-
async def get_comparative_weights_by_rfp_id(self, rfp_id: str = Path(...)):
|
| 100 |
-
try:
|
| 101 |
-
async with self.comparative_weight_service() as service:
|
| 102 |
-
comparative_weights = await service.get_comparative_weights(
|
| 103 |
-
rfp_id=rfp_id
|
| 104 |
-
)
|
| 105 |
-
return ResponseComparativeWeight(
|
| 106 |
-
status="success",
|
| 107 |
-
data=[
|
| 108 |
-
ComparitiveWeight(**comparative_weight)
|
| 109 |
-
for comparative_weight in comparative_weights
|
| 110 |
-
],
|
| 111 |
-
)
|
| 112 |
-
except Exception as e:
|
| 113 |
-
logger.error(f"Error getting comparative weights by rfp id: {e}")
|
| 114 |
-
raise HTTPException(
|
| 115 |
-
status_code=500, detail="Error getting comparative weights by rfp id"
|
| 116 |
-
)
|
| 117 |
-
|
| 118 |
-
async def get_comparative_weights_by_id(
|
| 119 |
-
self, rfp_id: str = Path(...), id: str = Path(...)
|
| 120 |
-
):
|
| 121 |
-
try:
|
| 122 |
-
async with self.comparative_weight_service() as service:
|
| 123 |
-
comparative_weights = await service.get_comparative_weights(
|
| 124 |
-
rfp_id=rfp_id, id=id
|
| 125 |
-
)
|
| 126 |
-
return ResponseComparativeWeight(
|
| 127 |
-
status="success",
|
| 128 |
-
data=[
|
| 129 |
-
ComparitiveWeight(**comparative_weight)
|
| 130 |
-
for comparative_weight in comparative_weights
|
| 131 |
-
],
|
| 132 |
-
)
|
| 133 |
-
except Exception as e:
|
| 134 |
-
logger.error(f"Error getting comparative weights by id: {e}")
|
| 135 |
-
raise HTTPException(
|
| 136 |
-
status_code=500, detail="Error getting comparative weights by id"
|
| 137 |
-
)
|
| 138 |
-
|
| 139 |
-
async def create_comparative_weights(
|
| 140 |
-
self, comparative_weights: ComparativeWeightRequest, rfp_id: str = Path(...)
|
| 141 |
-
):
|
| 142 |
-
try:
|
| 143 |
-
async with self.comparative_weight_service() as service:
|
| 144 |
-
comparative_weights = await service.create_comparative_weights(
|
| 145 |
-
rfp_id=rfp_id,
|
| 146 |
-
comparative_weights=comparative_weights.model_dump(
|
| 147 |
-
mode="json", exclude_unset=True
|
| 148 |
-
),
|
| 149 |
-
)
|
| 150 |
-
return ResponseComparativeWeight(
|
| 151 |
-
status="success",
|
| 152 |
-
data=[
|
| 153 |
-
ComparitiveWeight(**comparative_weight)
|
| 154 |
-
for comparative_weight in comparative_weights
|
| 155 |
-
],
|
| 156 |
-
)
|
| 157 |
-
except Exception as e:
|
| 158 |
-
logger.error(f"Error creating comparative weights: {e}")
|
| 159 |
-
raise HTTPException(
|
| 160 |
-
status_code=500, detail="Error creating comparative weights"
|
| 161 |
-
)
|
| 162 |
-
|
| 163 |
-
async def update_comparative_weights(
|
| 164 |
-
self, comparative_weights: ComparativeWeightRequest, rfp_id: str = Path(...)
|
| 165 |
-
):
|
| 166 |
-
try:
|
| 167 |
-
async with self.comparative_weight_service() as service:
|
| 168 |
-
comparative_weights = await service.update_comparative_weights(
|
| 169 |
-
rfp_id=rfp_id,
|
| 170 |
-
comparative_weights=comparative_weights.model_dump(
|
| 171 |
-
mode="json", exclude_unset=True
|
| 172 |
-
),
|
| 173 |
-
)
|
| 174 |
-
return ResponseComparativeWeight(
|
| 175 |
-
status="success",
|
| 176 |
-
data=[
|
| 177 |
-
ComparitiveWeight(**comparative_weight)
|
| 178 |
-
for comparative_weight in comparative_weights
|
| 179 |
-
],
|
| 180 |
-
)
|
| 181 |
-
except Exception as e:
|
| 182 |
-
logger.error(f"Error updating comparative weights: {e}")
|
| 183 |
-
raise HTTPException(
|
| 184 |
-
status_code=500, detail="Error updating comparative weights"
|
| 185 |
-
)
|
| 186 |
-
|
| 187 |
-
async def update_comparative_weights_by_id(
|
| 188 |
-
self,
|
| 189 |
-
comparative_weights: ComparativeWeightRequest,
|
| 190 |
-
rfp_id: str = Path(...),
|
| 191 |
-
id: str = Path(...),
|
| 192 |
-
):
|
| 193 |
-
try:
|
| 194 |
-
async with self.comparative_weight_service() as service:
|
| 195 |
-
comparative_weights = await service.update_comparative_weights(
|
| 196 |
-
rfp_id=rfp_id,
|
| 197 |
-
id=id,
|
| 198 |
-
comparative_weights=comparative_weights.model_dump(
|
| 199 |
-
mode="json", exclude_unset=True
|
| 200 |
-
),
|
| 201 |
-
)
|
| 202 |
-
return ResponseComparativeWeight(
|
| 203 |
-
status="success",
|
| 204 |
-
data=[
|
| 205 |
-
ComparitiveWeight(**comparative_weight)
|
| 206 |
-
for comparative_weight in comparative_weights
|
| 207 |
-
],
|
| 208 |
-
)
|
| 209 |
-
except Exception as e:
|
| 210 |
-
logger.error(f"Error updating comparative weights by id: {e}")
|
| 211 |
-
raise HTTPException(
|
| 212 |
-
status_code=500, detail="Error updating comparative weights by id"
|
| 213 |
-
)
|
| 214 |
-
|
| 215 |
-
async def delete_comparative_weights(self, rfp_id: str = Path(...)):
|
| 216 |
-
try:
|
| 217 |
-
async with self.comparative_weight_service() as service:
|
| 218 |
-
await service.delete_comparative_weights(rfp_id=rfp_id)
|
| 219 |
-
return DeleteResponse(status="success")
|
| 220 |
-
except Exception as e:
|
| 221 |
-
logger.error(f"Error deleting comparative weights: {e}")
|
| 222 |
-
raise HTTPException(
|
| 223 |
-
status_code=500, detail="Error deleting comparative weights"
|
| 224 |
-
)
|
| 225 |
-
|
| 226 |
-
async def delete_comparative_weights_by_id(
|
| 227 |
-
self, rfp_id: str = Path(...), id: str = Path(...)
|
| 228 |
-
):
|
| 229 |
-
try:
|
| 230 |
-
async with self.comparative_weight_service() as service:
|
| 231 |
-
await service.delete_comparative_weights(rfp_id=rfp_id, id=id)
|
| 232 |
-
return DeleteResponse(status="success")
|
| 233 |
-
except Exception as e:
|
| 234 |
-
logger.error(f"Error deleting comparative weights: {e}")
|
| 235 |
-
raise HTTPException(
|
| 236 |
-
status_code=500, detail="Error deleting comparative weights"
|
| 237 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/controllers/_evaluation_controller.py
DELETED
|
@@ -1,299 +0,0 @@
|
|
| 1 |
-
from fastapi import APIRouter, HTTPException, Query, Path
|
| 2 |
-
from pydantic import BaseModel
|
| 3 |
-
from typing import List, Optional
|
| 4 |
-
from uuid import UUID
|
| 5 |
-
from datetime import datetime
|
| 6 |
-
|
| 7 |
-
from src.config import logger
|
| 8 |
-
from src.services import EvaluationService
|
| 9 |
-
from src.models import EvaluationCriteriaType
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
class EvaluationCriteria(BaseModel):
|
| 13 |
-
id: UUID
|
| 14 |
-
rfp_id: UUID
|
| 15 |
-
evaluation_criteria: str
|
| 16 |
-
evaluation_criteria_type: EvaluationCriteriaType
|
| 17 |
-
created_at: datetime
|
| 18 |
-
updated_at: datetime
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
class EvaluationResponse(BaseModel):
|
| 22 |
-
status: str
|
| 23 |
-
data: List[EvaluationCriteria]
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
class EvaluationRequest(BaseModel):
|
| 27 |
-
rfp_id: UUID
|
| 28 |
-
evaluation_criteria: str
|
| 29 |
-
evaluation_criteria_type: EvaluationCriteriaType
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
class CreateEvaluationRequest(BaseModel):
|
| 33 |
-
evaluation_criteria: str
|
| 34 |
-
evaluation_criteria_type: EvaluationCriteriaType
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
class UpdateEvaluationRequest(BaseModel):
|
| 38 |
-
rfp_id: Optional[UUID] = None
|
| 39 |
-
evaluation_criteria: Optional[str] = None
|
| 40 |
-
evaluation_criteria_type: Optional[EvaluationCriteriaType] = None
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
class UpdateEvaluationCriteriaByRFPRequest(BaseModel):
|
| 44 |
-
evaluation_criteria: Optional[str] = None
|
| 45 |
-
evaluation_criteria_type: Optional[EvaluationCriteriaType] = None
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
class DeleteResponse(BaseModel):
|
| 49 |
-
status: str
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
class EvaluationController:
|
| 53 |
-
def __init__(self):
|
| 54 |
-
self.evaluation_service = EvaluationService
|
| 55 |
-
self.router = APIRouter()
|
| 56 |
-
# self.router.add_api_route(
|
| 57 |
-
# "/evaluation-criteria",
|
| 58 |
-
# self.get_criteria,
|
| 59 |
-
# methods=["GET"],
|
| 60 |
-
# response_model=EvaluationResponse,
|
| 61 |
-
# tags=["Evaluation Criteria"],
|
| 62 |
-
# )
|
| 63 |
-
# self.router.add_api_route(
|
| 64 |
-
# "/evaluation-criteria/{id}",
|
| 65 |
-
# self.get_criteria_by_id,
|
| 66 |
-
# methods=["GET"],
|
| 67 |
-
# response_model=EvaluationResponse,
|
| 68 |
-
# tags=["Evaluation Criteria by ID"],
|
| 69 |
-
# )
|
| 70 |
-
self.router.add_api_route(
|
| 71 |
-
"/rfps/{rfp_id}/evaluation-criteria",
|
| 72 |
-
self.get_criteria_by_rfp_id,
|
| 73 |
-
methods=["GET"],
|
| 74 |
-
response_model=EvaluationResponse,
|
| 75 |
-
tags=["Evaluation Criteria by RFP ID"],
|
| 76 |
-
)
|
| 77 |
-
self.router.add_api_route(
|
| 78 |
-
"/rfps/{rfp_id}/evaluation-criteria/{id}",
|
| 79 |
-
self.get_criteria_by_proposal_and_rfp_id,
|
| 80 |
-
methods=["GET"],
|
| 81 |
-
response_model=EvaluationResponse,
|
| 82 |
-
tags=["Evaluation Criteria by Proposal and RFP ID"],
|
| 83 |
-
)
|
| 84 |
-
# self.router.add_api_route(
|
| 85 |
-
# "/evaluation-criteria",
|
| 86 |
-
# self.create_criteria,
|
| 87 |
-
# methods=["POST"],
|
| 88 |
-
# response_model=EvaluationResponse,
|
| 89 |
-
# tags=["Evaluation Criteria"],
|
| 90 |
-
# )
|
| 91 |
-
self.router.add_api_route(
|
| 92 |
-
"/rfps/{rfp_id}/evaluation-criteria",
|
| 93 |
-
self.create_criteria_by_rfp_id,
|
| 94 |
-
methods=["POST"],
|
| 95 |
-
response_model=EvaluationResponse,
|
| 96 |
-
tags=["Evaluation Criteria by RFP ID"],
|
| 97 |
-
)
|
| 98 |
-
# self.router.add_api_route(
|
| 99 |
-
# "/evaluation-criteria/{id}",
|
| 100 |
-
# self.update_criteria,
|
| 101 |
-
# methods=["PUT"],
|
| 102 |
-
# response_model=EvaluationResponse,
|
| 103 |
-
# tags=["Evaluation Criteria by ID"],
|
| 104 |
-
# )
|
| 105 |
-
self.router.add_api_route(
|
| 106 |
-
"/rfps/{rfp_id}/evaluation-criteria/{id}",
|
| 107 |
-
self.update_criteria_by_proposal_and_rfp_id,
|
| 108 |
-
methods=["PUT"],
|
| 109 |
-
response_model=EvaluationResponse,
|
| 110 |
-
tags=["Evaluation Criteria by Proposal and RFP ID"],
|
| 111 |
-
)
|
| 112 |
-
# self.router.add_api_route(
|
| 113 |
-
# "/evaluation-criteria/{id}",
|
| 114 |
-
# self.delete_criteria,
|
| 115 |
-
# methods=["DELETE"],
|
| 116 |
-
# response_model=DeleteResponse,
|
| 117 |
-
# tags=["Evaluation Criteria by ID"],
|
| 118 |
-
# )
|
| 119 |
-
self.router.add_api_route(
|
| 120 |
-
"/rfps/{rfp_id}/evaluation-criteria",
|
| 121 |
-
self.delete_criteria_by_rfp_id,
|
| 122 |
-
methods=["DELETE"],
|
| 123 |
-
response_model=DeleteResponse,
|
| 124 |
-
tags=["Evaluation Criteria by RFP ID"],
|
| 125 |
-
)
|
| 126 |
-
self.router.add_api_route(
|
| 127 |
-
"/rfps/{rfp_id}/evaluation-criteria/{id}",
|
| 128 |
-
self.delete_criteria_by_proposal_and_rfp_id,
|
| 129 |
-
methods=["DELETE"],
|
| 130 |
-
response_model=DeleteResponse,
|
| 131 |
-
tags=["Evaluation Criteria by Proposal and RFP ID"],
|
| 132 |
-
)
|
| 133 |
-
|
| 134 |
-
async def get_criteria(self):
|
| 135 |
-
try:
|
| 136 |
-
async with self.evaluation_service() as service:
|
| 137 |
-
result = await service.get_criteria()
|
| 138 |
-
return EvaluationResponse(
|
| 139 |
-
status="success", data=[EvaluationCriteria(**r) for r in result]
|
| 140 |
-
)
|
| 141 |
-
except Exception as e:
|
| 142 |
-
logger.error(e)
|
| 143 |
-
raise HTTPException(
|
| 144 |
-
status_code=500, detail="Error fetching evaluation criteria"
|
| 145 |
-
)
|
| 146 |
-
|
| 147 |
-
async def get_criteria_by_id(self, id: str = Path(...)):
|
| 148 |
-
try:
|
| 149 |
-
async with self.evaluation_service() as service:
|
| 150 |
-
result = await service.get_criteria(id=id)
|
| 151 |
-
return EvaluationResponse(
|
| 152 |
-
status="success", data=[EvaluationCriteria(**r) for r in result]
|
| 153 |
-
)
|
| 154 |
-
except Exception as e:
|
| 155 |
-
logger.error(e)
|
| 156 |
-
raise HTTPException(
|
| 157 |
-
status_code=500, detail="Error fetching evaluation criteria"
|
| 158 |
-
)
|
| 159 |
-
|
| 160 |
-
async def get_criteria_by_rfp_id(
|
| 161 |
-
self,
|
| 162 |
-
rfp_id: str = Path(...),
|
| 163 |
-
criteria_type: Optional[EvaluationCriteriaType] = Query(None),
|
| 164 |
-
):
|
| 165 |
-
try:
|
| 166 |
-
async with self.evaluation_service() as service:
|
| 167 |
-
result = await service.get_criteria(
|
| 168 |
-
rfp_id=rfp_id, evaluation_criteria_type=criteria_type
|
| 169 |
-
)
|
| 170 |
-
return EvaluationResponse(
|
| 171 |
-
status="success", data=[EvaluationCriteria(**r) for r in result]
|
| 172 |
-
)
|
| 173 |
-
except Exception as e:
|
| 174 |
-
logger.error(e)
|
| 175 |
-
raise HTTPException(
|
| 176 |
-
status_code=500, detail="Error fetching evaluation criteria"
|
| 177 |
-
)
|
| 178 |
-
|
| 179 |
-
async def get_criteria_by_proposal_and_rfp_id(
|
| 180 |
-
self, rfp_id: str = Path(...), id: str = Path(...)
|
| 181 |
-
):
|
| 182 |
-
try:
|
| 183 |
-
async with self.evaluation_service() as service:
|
| 184 |
-
result = await service.get_criteria(rfp_id=rfp_id, id=id)
|
| 185 |
-
return EvaluationResponse(
|
| 186 |
-
status="success", data=[EvaluationCriteria(**r) for r in result]
|
| 187 |
-
)
|
| 188 |
-
except Exception as e:
|
| 189 |
-
logger.error(e)
|
| 190 |
-
raise HTTPException(
|
| 191 |
-
status_code=500, detail="Error fetching evaluation criteria"
|
| 192 |
-
)
|
| 193 |
-
|
| 194 |
-
async def create_criteria(self, criteria: EvaluationRequest):
|
| 195 |
-
try:
|
| 196 |
-
async with self.evaluation_service() as service:
|
| 197 |
-
result = await service.create_criteria(
|
| 198 |
-
criteria.model_dump(exclude_unset=True, mode="json")
|
| 199 |
-
)
|
| 200 |
-
return EvaluationResponse(
|
| 201 |
-
status="success", data=[EvaluationCriteria(**r) for r in result]
|
| 202 |
-
)
|
| 203 |
-
except Exception as e:
|
| 204 |
-
logger.error(e)
|
| 205 |
-
raise HTTPException(
|
| 206 |
-
status_code=500, detail="Error creating evaluation criteria"
|
| 207 |
-
)
|
| 208 |
-
|
| 209 |
-
async def create_criteria_by_rfp_id(
|
| 210 |
-
self, criteria: CreateEvaluationRequest, rfp_id: str = Path(...)
|
| 211 |
-
):
|
| 212 |
-
try:
|
| 213 |
-
async with self.evaluation_service() as service:
|
| 214 |
-
data = criteria.model_dump(exclude_unset=True, mode="json")
|
| 215 |
-
data["rfp_id"] = rfp_id
|
| 216 |
-
result = await service.create_criteria(evaluation_criteria=data)
|
| 217 |
-
return EvaluationResponse(
|
| 218 |
-
status="success", data=[EvaluationCriteria(**r) for r in result]
|
| 219 |
-
)
|
| 220 |
-
except Exception as e:
|
| 221 |
-
logger.error(e)
|
| 222 |
-
raise HTTPException(
|
| 223 |
-
status_code=500, detail="Error creating evaluation criteria"
|
| 224 |
-
)
|
| 225 |
-
|
| 226 |
-
async def update_criteria(
|
| 227 |
-
self, criteria: UpdateEvaluationRequest, id: str = Path(...)
|
| 228 |
-
):
|
| 229 |
-
try:
|
| 230 |
-
async with self.evaluation_service() as service:
|
| 231 |
-
result = await service.update_criteria(
|
| 232 |
-
id=id,
|
| 233 |
-
evaluation_criteria=criteria.model_dump(
|
| 234 |
-
exclude_unset=True, mode="json"
|
| 235 |
-
),
|
| 236 |
-
)
|
| 237 |
-
return EvaluationResponse(
|
| 238 |
-
status="success", data=[EvaluationCriteria(**r) for r in result]
|
| 239 |
-
)
|
| 240 |
-
except Exception as e:
|
| 241 |
-
logger.error(e)
|
| 242 |
-
raise HTTPException(
|
| 243 |
-
status_code=500, detail="Error updating evaluation criteria"
|
| 244 |
-
)
|
| 245 |
-
|
| 246 |
-
async def update_criteria_by_proposal_and_rfp_id(
|
| 247 |
-
self,
|
| 248 |
-
criteria: UpdateEvaluationCriteriaByRFPRequest,
|
| 249 |
-
rfp_id: str = Path(...),
|
| 250 |
-
id: str = Path(...),
|
| 251 |
-
):
|
| 252 |
-
try:
|
| 253 |
-
async with self.evaluation_service() as service:
|
| 254 |
-
data = criteria.model_dump(exclude_unset=True, mode="json")
|
| 255 |
-
data["rfp_id"] = rfp_id
|
| 256 |
-
result = await service.update_criteria(id=id, evaluation_criteria=data)
|
| 257 |
-
return EvaluationResponse(
|
| 258 |
-
status="success", data=[EvaluationCriteria(**r) for r in result]
|
| 259 |
-
)
|
| 260 |
-
except Exception as e:
|
| 261 |
-
logger.error(e)
|
| 262 |
-
raise HTTPException(
|
| 263 |
-
status_code=500, detail="Error updating evaluation criteria"
|
| 264 |
-
)
|
| 265 |
-
|
| 266 |
-
async def delete_criteria(self, id: str = Path(...)):
|
| 267 |
-
try:
|
| 268 |
-
async with self.evaluation_service() as service:
|
| 269 |
-
await service.delete_criteria(id=id)
|
| 270 |
-
return DeleteResponse(status="success")
|
| 271 |
-
except Exception as e:
|
| 272 |
-
logger.error(e)
|
| 273 |
-
raise HTTPException(
|
| 274 |
-
status_code=500, detail="Error deleting evaluation criteria"
|
| 275 |
-
)
|
| 276 |
-
|
| 277 |
-
async def delete_criteria_by_rfp_id(self, rfp_id: str = Path(...)):
|
| 278 |
-
try:
|
| 279 |
-
async with self.evaluation_service() as service:
|
| 280 |
-
await service.delete_criteria(rfp_id=rfp_id)
|
| 281 |
-
return DeleteResponse(status="success")
|
| 282 |
-
except Exception as e:
|
| 283 |
-
logger.error(e)
|
| 284 |
-
raise HTTPException(
|
| 285 |
-
status_code=500, detail="Error deleting evaluation criteria"
|
| 286 |
-
)
|
| 287 |
-
|
| 288 |
-
async def delete_criteria_by_proposal_and_rfp_id(
|
| 289 |
-
self, rfp_id: str = Path(...), id: str = Path(...)
|
| 290 |
-
):
|
| 291 |
-
try:
|
| 292 |
-
async with self.evaluation_service() as service:
|
| 293 |
-
await service.delete_criteria(rfp_id=rfp_id, id=id)
|
| 294 |
-
return DeleteResponse(status="success")
|
| 295 |
-
except Exception as e:
|
| 296 |
-
logger.error(e)
|
| 297 |
-
raise HTTPException(
|
| 298 |
-
status_code=500, detail="Error deleting evaluation criteria"
|
| 299 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/controllers/_file_controller.py
DELETED
|
@@ -1,34 +0,0 @@
|
|
| 1 |
-
from fastapi import APIRouter, HTTPException, Query, Path, UploadFile, File
|
| 2 |
-
from pydantic import BaseModel
|
| 3 |
-
from typing import List, Optional
|
| 4 |
-
from uuid import UUID
|
| 5 |
-
from datetime import datetime
|
| 6 |
-
|
| 7 |
-
from src.config import logger
|
| 8 |
-
from src.services import FileService
|
| 9 |
-
|
| 10 |
-
import io
|
| 11 |
-
|
| 12 |
-
class Response(BaseModel):
|
| 13 |
-
status: str
|
| 14 |
-
data: str
|
| 15 |
-
|
| 16 |
-
class FileController:
|
| 17 |
-
def __init__(self):
|
| 18 |
-
self.service = FileService
|
| 19 |
-
self.router = APIRouter()
|
| 20 |
-
self.router.add_api_route(
|
| 21 |
-
"/files",
|
| 22 |
-
self.get_files_extrcated_text,
|
| 23 |
-
methods=["POST"],
|
| 24 |
-
tags=["Files"],
|
| 25 |
-
)
|
| 26 |
-
|
| 27 |
-
async def get_files_extrcated_text(self, file: UploadFile = File(...)):
|
| 28 |
-
try:
|
| 29 |
-
async with self.service() as service:
|
| 30 |
-
result = await service.extract_file(file=file)
|
| 31 |
-
return result
|
| 32 |
-
except Exception as e:
|
| 33 |
-
logger.error(e)
|
| 34 |
-
raise HTTPException(status_code=500, detail="Error processing file")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/controllers/_letter_controller.py
DELETED
|
@@ -1,263 +0,0 @@
|
|
| 1 |
-
from fastapi import APIRouter, HTTPException, Query, Path
|
| 2 |
-
from pydantic import BaseModel
|
| 3 |
-
from typing import List, Optional
|
| 4 |
-
from uuid import UUID
|
| 5 |
-
from datetime import datetime
|
| 6 |
-
|
| 7 |
-
from src.config import logger
|
| 8 |
-
from src.services import LetterService
|
| 9 |
-
from src.models import LetterType
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
class Letter(BaseModel):
|
| 13 |
-
id: UUID
|
| 14 |
-
proposal_id: UUID
|
| 15 |
-
letter: str
|
| 16 |
-
letter_type: LetterType
|
| 17 |
-
created_at: datetime
|
| 18 |
-
updated_at: datetime
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
class Response(BaseModel):
|
| 22 |
-
status: str
|
| 23 |
-
data: List[Letter]
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
class LetterRequest(BaseModel):
|
| 27 |
-
proposal_id: UUID
|
| 28 |
-
letter: str
|
| 29 |
-
letter_type: LetterType
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
class CreateLetterRequest(BaseModel):
|
| 33 |
-
letter: str
|
| 34 |
-
letter_type: LetterType
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
class UpdateLetterRequest(BaseModel):
|
| 38 |
-
proposal_id: Optional[UUID] = None
|
| 39 |
-
letter: Optional[str] = None
|
| 40 |
-
letter_type: Optional[LetterType] = None
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
class UpdateLetterRequestByProposalAndId(BaseModel):
|
| 44 |
-
letter: Optional[str] = None
|
| 45 |
-
letter_type: Optional[LetterType] = None
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
class DeleteResponse(BaseModel):
|
| 49 |
-
status: str
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
class LetterController:
|
| 53 |
-
def __init__(self):
|
| 54 |
-
self.letter_service = LetterService
|
| 55 |
-
self.router = APIRouter()
|
| 56 |
-
# self.router.add_api_route(
|
| 57 |
-
# "/letters",
|
| 58 |
-
# self.get_letters,
|
| 59 |
-
# methods=["GET"],
|
| 60 |
-
# response_model=Response,
|
| 61 |
-
# tags=["Letters"],
|
| 62 |
-
# )
|
| 63 |
-
# self.router.add_api_route(
|
| 64 |
-
# "/letters/{id}",
|
| 65 |
-
# self.get_letters_by_id,
|
| 66 |
-
# methods=["GET"],
|
| 67 |
-
# response_model=Response,
|
| 68 |
-
# tags=["Letters by ID"],
|
| 69 |
-
# )
|
| 70 |
-
self.router.add_api_route(
|
| 71 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/letters",
|
| 72 |
-
self.get_letters_by_proposal_id,
|
| 73 |
-
methods=["GET"],
|
| 74 |
-
response_model=Response,
|
| 75 |
-
tags=["Letters by Proposal ID"],
|
| 76 |
-
)
|
| 77 |
-
self.router.add_api_route(
|
| 78 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/letters/{id}",
|
| 79 |
-
self.get_letters_by_proposal_and_id,
|
| 80 |
-
methods=["GET"],
|
| 81 |
-
response_model=Response,
|
| 82 |
-
tags=["Letters by Proposal and ID"],
|
| 83 |
-
)
|
| 84 |
-
# self.router.add_api_route(
|
| 85 |
-
# "/letters",
|
| 86 |
-
# self.create_letter,
|
| 87 |
-
# methods=["POST"],
|
| 88 |
-
# response_model=Response,
|
| 89 |
-
# tags=["Letters"],
|
| 90 |
-
# )
|
| 91 |
-
self.router.add_api_route(
|
| 92 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/letters",
|
| 93 |
-
self.create_letters_by_proposal_id,
|
| 94 |
-
methods=["POST"],
|
| 95 |
-
response_model=Response,
|
| 96 |
-
tags=["Letters by Proposal ID"],
|
| 97 |
-
)
|
| 98 |
-
# self.router.add_api_route(
|
| 99 |
-
# "/letters/{id}",
|
| 100 |
-
# self.update_letter,
|
| 101 |
-
# methods=["PUT"],
|
| 102 |
-
# response_model=Response,
|
| 103 |
-
# tags=["Letters by ID"],
|
| 104 |
-
# )
|
| 105 |
-
self.router.add_api_route(
|
| 106 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/letters/{id}",
|
| 107 |
-
self.update_letters_by_proposal_and_id,
|
| 108 |
-
methods=["PUT"],
|
| 109 |
-
response_model=Response,
|
| 110 |
-
tags=["Letters by Proposal and ID"],
|
| 111 |
-
)
|
| 112 |
-
# self.router.add_api_route(
|
| 113 |
-
# "/letters/{id}",
|
| 114 |
-
# self.delete_letter,
|
| 115 |
-
# methods=["DELETE"],
|
| 116 |
-
# response_model=DeleteResponse,
|
| 117 |
-
# tags=["Letters by ID"],
|
| 118 |
-
# )
|
| 119 |
-
self.router.add_api_route(
|
| 120 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/letters",
|
| 121 |
-
self.delete_letters_by_proposal_id,
|
| 122 |
-
methods=["DELETE"],
|
| 123 |
-
response_model=DeleteResponse,
|
| 124 |
-
tags=["Letters by Proposal ID"],
|
| 125 |
-
)
|
| 126 |
-
self.router.add_api_route(
|
| 127 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/letters/{id}",
|
| 128 |
-
self.delete_letters_by_proposal_and_id,
|
| 129 |
-
methods=["DELETE"],
|
| 130 |
-
response_model=DeleteResponse,
|
| 131 |
-
tags=["Letters by Proposal and ID"],
|
| 132 |
-
)
|
| 133 |
-
|
| 134 |
-
async def get_letters(self):
|
| 135 |
-
try:
|
| 136 |
-
async with self.letter_service() as service:
|
| 137 |
-
result = await service.get_letters()
|
| 138 |
-
return Response(status="success", data=[Letter(**r) for r in result])
|
| 139 |
-
except Exception as e:
|
| 140 |
-
logger.error(e)
|
| 141 |
-
raise HTTPException(status_code=500, detail="Error fetching letters")
|
| 142 |
-
|
| 143 |
-
async def get_letters_by_id(self, id: str = Path(...)):
|
| 144 |
-
try:
|
| 145 |
-
async with self.letter_service() as service:
|
| 146 |
-
result = await service.get_letters(id=id)
|
| 147 |
-
return Response(status="success", data=[Letter(**r) for r in result])
|
| 148 |
-
except Exception as e:
|
| 149 |
-
logger.error(e)
|
| 150 |
-
raise HTTPException(status_code=500, detail="Error fetching letter")
|
| 151 |
-
|
| 152 |
-
async def get_letters_by_proposal_id(
|
| 153 |
-
self,
|
| 154 |
-
proposal_id: str = Path(...),
|
| 155 |
-
letter_type: LetterType = Query(None),
|
| 156 |
-
rfp_id: str = Path(...),
|
| 157 |
-
):
|
| 158 |
-
try:
|
| 159 |
-
async with self.letter_service() as service:
|
| 160 |
-
result = await service.get_letters(
|
| 161 |
-
proposal_id=proposal_id, letter_type=letter_type
|
| 162 |
-
)
|
| 163 |
-
return Response(status="success", data=[Letter(**r) for r in result])
|
| 164 |
-
except Exception as e:
|
| 165 |
-
logger.error(e)
|
| 166 |
-
raise HTTPException(status_code=500, detail="Error fetching letter")
|
| 167 |
-
|
| 168 |
-
async def get_letters_by_proposal_and_id(
|
| 169 |
-
self, proposal_id: str = Path(...), id: str = Path(...), rfp_id: str = Path(...)
|
| 170 |
-
):
|
| 171 |
-
try:
|
| 172 |
-
async with self.letter_service() as service:
|
| 173 |
-
result = await service.get_letters(proposal_id=proposal_id, id=id)
|
| 174 |
-
return Response(status="success", data=[Letter(**r) for r in result])
|
| 175 |
-
except Exception as e:
|
| 176 |
-
logger.error(e)
|
| 177 |
-
raise HTTPException(status_code=500, detail="Error fetching letter")
|
| 178 |
-
|
| 179 |
-
async def create_letter(self, letter: LetterRequest):
|
| 180 |
-
try:
|
| 181 |
-
async with self.letter_service() as service:
|
| 182 |
-
result = await service.create_letter(
|
| 183 |
-
letter.model_dump(exclude_unset=True, mode="json")
|
| 184 |
-
)
|
| 185 |
-
return Response(status="success", data=[Letter(**r) for r in result])
|
| 186 |
-
except Exception as e:
|
| 187 |
-
logger.error(e)
|
| 188 |
-
raise HTTPException(status_code=500, detail="Error creating letter")
|
| 189 |
-
|
| 190 |
-
async def create_letters_by_proposal_id(
|
| 191 |
-
self,
|
| 192 |
-
letter: CreateLetterRequest,
|
| 193 |
-
proposal_id: str = Path(...),
|
| 194 |
-
rfp_id: str = Path(...),
|
| 195 |
-
):
|
| 196 |
-
try:
|
| 197 |
-
async with self.letter_service() as service:
|
| 198 |
-
data = letter.model_dump(exclude_unset=True, mode="json")
|
| 199 |
-
data["proposal_id"] = proposal_id
|
| 200 |
-
result = await service.create_letter(letter=data)
|
| 201 |
-
return Response(status="success", data=[Letter(**r) for r in result])
|
| 202 |
-
except Exception as e:
|
| 203 |
-
logger.error(e)
|
| 204 |
-
raise HTTPException(status_code=500, detail="Error creating letter")
|
| 205 |
-
|
| 206 |
-
async def update_letter(self, letter: UpdateLetterRequest, id: str = Path(...)):
|
| 207 |
-
try:
|
| 208 |
-
async with self.letter_service() as service:
|
| 209 |
-
result = await service.update_letter(
|
| 210 |
-
id=id, letter=letter.model_dump(exclude_unset=True, mode="json")
|
| 211 |
-
)
|
| 212 |
-
return Response(status="success", data=[Letter(**r) for r in result])
|
| 213 |
-
except Exception as e:
|
| 214 |
-
logger.error(e)
|
| 215 |
-
raise HTTPException(status_code=500, detail="Error updating letter")
|
| 216 |
-
|
| 217 |
-
async def update_letters_by_proposal_and_id(
|
| 218 |
-
self,
|
| 219 |
-
letter: UpdateLetterRequestByProposalAndId,
|
| 220 |
-
proposal_id: str = Path(...),
|
| 221 |
-
id: str = Path(...),
|
| 222 |
-
rfp_id: str = Path(...),
|
| 223 |
-
):
|
| 224 |
-
try:
|
| 225 |
-
async with self.letter_service() as service:
|
| 226 |
-
data = letter.model_dump(exclude_unset=True, mode="json")
|
| 227 |
-
data["proposal_id"] = proposal_id
|
| 228 |
-
result = await service.update_letter(id=id, letter=data)
|
| 229 |
-
return Response(status="success", data=[Letter(**r) for r in result])
|
| 230 |
-
except Exception as e:
|
| 231 |
-
logger.error(e)
|
| 232 |
-
raise HTTPException(status_code=500, detail="Error updating letter")
|
| 233 |
-
|
| 234 |
-
async def delete_letter(self, id: str = Path(...)):
|
| 235 |
-
try:
|
| 236 |
-
async with self.letter_service() as service:
|
| 237 |
-
result = await service.delete_letter(id=id)
|
| 238 |
-
return DeleteResponse(status="success")
|
| 239 |
-
except Exception as e:
|
| 240 |
-
logger.error(e)
|
| 241 |
-
raise HTTPException(status_code=500, detail="Error deleting letter")
|
| 242 |
-
|
| 243 |
-
async def delete_letters_by_proposal_id(
|
| 244 |
-
self, proposal_id: str = Path(...), rfp_id: str = Path(...)
|
| 245 |
-
):
|
| 246 |
-
try:
|
| 247 |
-
async with self.letter_service() as service:
|
| 248 |
-
result = await service.delete_letter(proposal_id=proposal_id)
|
| 249 |
-
return DeleteResponse(status="success")
|
| 250 |
-
except Exception as e:
|
| 251 |
-
logger.error(e)
|
| 252 |
-
raise HTTPException(status_code=500, detail="Error deleting letter")
|
| 253 |
-
|
| 254 |
-
async def delete_letters_by_proposal_and_id(
|
| 255 |
-
self, proposal_id: str = Path(...), id: str = Path(...), rfp_id: str = Path(...)
|
| 256 |
-
):
|
| 257 |
-
try:
|
| 258 |
-
async with self.letter_service() as service:
|
| 259 |
-
result = await service.delete_letter(proposal_id=proposal_id, id=id)
|
| 260 |
-
return DeleteResponse(status="success")
|
| 261 |
-
except Exception as e:
|
| 262 |
-
logger.error(e)
|
| 263 |
-
raise HTTPException(status_code=500, detail="Error deleting letter")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/controllers/_proposal_ai_analysis_controller.py
DELETED
|
@@ -1,1783 +0,0 @@
|
|
| 1 |
-
from fastapi import APIRouter, HTTPException, Query, Path
|
| 2 |
-
from pydantic import BaseModel
|
| 3 |
-
from typing import List, Optional
|
| 4 |
-
from uuid import UUID
|
| 5 |
-
from datetime import datetime
|
| 6 |
-
|
| 7 |
-
from src.config import logger
|
| 8 |
-
|
| 9 |
-
from src.services import ProposalAIAnalysisService
|
| 10 |
-
|
| 11 |
-
from src.models import QueryType, CreateOrUpdateType, Category
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
class ProposalAIAnalysis(BaseModel):
|
| 15 |
-
id: UUID
|
| 16 |
-
proposal_id: UUID
|
| 17 |
-
ai_score: float
|
| 18 |
-
created_at: datetime
|
| 19 |
-
updated_at: datetime
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
class ProposalStrength(BaseModel):
|
| 23 |
-
id: UUID
|
| 24 |
-
proposal_id: UUID
|
| 25 |
-
strength_point: str
|
| 26 |
-
category: Category
|
| 27 |
-
created_at: datetime
|
| 28 |
-
updated_at: datetime
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
class ProposalWeakness(BaseModel):
|
| 32 |
-
id: UUID
|
| 33 |
-
proposal_id: UUID
|
| 34 |
-
weakness_point: str
|
| 35 |
-
category: Category
|
| 36 |
-
created_at: datetime
|
| 37 |
-
updated_at: datetime
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
class ProposalRejectionReason(BaseModel):
|
| 41 |
-
id: UUID
|
| 42 |
-
proposal_id: UUID
|
| 43 |
-
reason: str
|
| 44 |
-
category: Category
|
| 45 |
-
created_at: datetime
|
| 46 |
-
updated_at: datetime
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
class ProposalImprovement(BaseModel):
|
| 50 |
-
id: UUID
|
| 51 |
-
proposal_id: UUID
|
| 52 |
-
improvement_point: str
|
| 53 |
-
category: Category
|
| 54 |
-
created_at: datetime
|
| 55 |
-
updated_at: datetime
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
class ProposalAllStrengthsResponse(BaseModel):
|
| 59 |
-
status: str
|
| 60 |
-
data: Optional[List[str]]
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
class ProposalAllWeaknessesResponse(BaseModel):
|
| 64 |
-
status: str
|
| 65 |
-
data: Optional[List[str]]
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
class ProposalAllRejectionReasonsResponse(BaseModel):
|
| 69 |
-
status: str
|
| 70 |
-
data: Optional[List[str]]
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
class ProposalAllImprovementsResponse(BaseModel):
|
| 74 |
-
status: str
|
| 75 |
-
data: Optional[List[str]]
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
class ProposalAIAnalysisResponse(BaseModel):
|
| 79 |
-
status: str
|
| 80 |
-
data: Optional[List[ProposalAIAnalysis]]
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
class ProposalStrengthResponse(BaseModel):
|
| 84 |
-
status: str
|
| 85 |
-
data: Optional[List[ProposalStrength]]
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
class ProposalWeaknessResponse(BaseModel):
|
| 89 |
-
status: str
|
| 90 |
-
data: Optional[List[ProposalWeakness]]
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
class ProposalRejectionReasonResponse(BaseModel):
|
| 94 |
-
status: str
|
| 95 |
-
data: Optional[List[ProposalRejectionReason]]
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
class ProposalImprovementResponse(BaseModel):
|
| 99 |
-
status: str
|
| 100 |
-
data: Optional[List[ProposalImprovement]]
|
| 101 |
-
|
| 102 |
-
class Analysis(BaseModel):
|
| 103 |
-
strengths: Optional[List[str]] = None
|
| 104 |
-
weaknesses: Optional[List[str]] = None
|
| 105 |
-
rejection_reasons: Optional[List[str]] = None
|
| 106 |
-
improvements: Optional[List[str]] = None
|
| 107 |
-
|
| 108 |
-
class ProposalCompleteAnalysis(BaseModel):
|
| 109 |
-
ai_score: float
|
| 110 |
-
technical: Optional[Analysis] = None
|
| 111 |
-
management: Optional[Analysis] = None
|
| 112 |
-
past_performance: Optional[Analysis] = None
|
| 113 |
-
price: Optional[Analysis] = None
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
class ProposalCompleteAnalysisResponse(BaseModel):
|
| 117 |
-
status: str
|
| 118 |
-
data: Optional[List[ProposalCompleteAnalysis]]
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
class DeleteResponse(BaseModel):
|
| 122 |
-
status: str
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
class ProposalStrengthRequest(BaseModel):
|
| 126 |
-
proposal_id: UUID
|
| 127 |
-
strength_point: str
|
| 128 |
-
category: Category
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
class UpdateProposalStrengthRequest(BaseModel):
|
| 132 |
-
proposal_id: Optional[UUID] = None
|
| 133 |
-
strength_point: Optional[str] = None
|
| 134 |
-
category: Optional[Category] = None
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
class ProposalWeaknessRequest(BaseModel):
|
| 138 |
-
proposal_id: UUID
|
| 139 |
-
weakness_point: str
|
| 140 |
-
category: Category
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
class UpdateProposalWeaknessRequest(BaseModel):
|
| 144 |
-
proposal_id: Optional[UUID] = None
|
| 145 |
-
weakness_point: Optional[str] = None
|
| 146 |
-
category: Optional[Category] = None
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
class ProposalRejectionReasonRequest(BaseModel):
|
| 150 |
-
proposal_id: UUID
|
| 151 |
-
reason: str
|
| 152 |
-
category: Category
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
class UpdateProposalRejectionReasonRequest(BaseModel):
|
| 156 |
-
proposal_id: Optional[UUID] = None
|
| 157 |
-
reason: Optional[str] = None
|
| 158 |
-
category: Optional[Category] = None
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
class ProposalImprovementRequest(BaseModel):
|
| 162 |
-
proposal_id: UUID
|
| 163 |
-
improvement_point: str
|
| 164 |
-
category: Category
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
class UpdateProposalImprovementRequest(BaseModel):
|
| 168 |
-
proposal_id: Optional[UUID] = None
|
| 169 |
-
improvement_point: Optional[str] = None
|
| 170 |
-
category: Optional[Category] = None
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
class CreateAiAnalysisRequest(BaseModel):
|
| 174 |
-
proposal_id: UUID
|
| 175 |
-
ai_score: float
|
| 176 |
-
|
| 177 |
-
class CreateAIAnalysisRequestByProposalId(BaseModel):
|
| 178 |
-
ai_score: float
|
| 179 |
-
|
| 180 |
-
class UpdateAiAnalysisRequest(BaseModel):
|
| 181 |
-
proposal_id: Optional[UUID] = None
|
| 182 |
-
ai_score: Optional[float] = None
|
| 183 |
-
|
| 184 |
-
class UpdateAIAnalysisRequestByProposalId(BaseModel):
|
| 185 |
-
ai_score: Optional[float] = None
|
| 186 |
-
|
| 187 |
-
class CreateAllProposalStrengthsRequest(BaseModel):
|
| 188 |
-
strength_point: List[str]
|
| 189 |
-
category: Category
|
| 190 |
-
|
| 191 |
-
class UpdateAllProposalStrengthsRequest(BaseModel):
|
| 192 |
-
strength_point: Optional[List[str]] = None
|
| 193 |
-
|
| 194 |
-
class CreateAllProposalWeaknessRequest(BaseModel):
|
| 195 |
-
weakness_point: List[str]
|
| 196 |
-
category: Category
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
class CreateAllProposalRejectionReasonsRequest(BaseModel):
|
| 200 |
-
reason: List[str]
|
| 201 |
-
category: Category
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
class CreateAllProposalImprovementsRequest(BaseModel):
|
| 205 |
-
improvement_point: List[str]
|
| 206 |
-
category: Category
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
class CreateAllProposalAnalysisRequest(BaseModel):
|
| 210 |
-
ai_score: float
|
| 211 |
-
technical: Optional[Analysis] = None
|
| 212 |
-
management: Optional[Analysis] = None
|
| 213 |
-
past_performance: Optional[Analysis] = None
|
| 214 |
-
price: Optional[Analysis] = None
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
class UpdateAllProposalAnalysisRequest(BaseModel):
|
| 218 |
-
ai_score: Optional[float] = None
|
| 219 |
-
technical: Optional[Analysis] = None
|
| 220 |
-
management: Optional[Analysis] = None
|
| 221 |
-
past_performance: Optional[Analysis] = None
|
| 222 |
-
price: Optional[Analysis] = None
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
class ProposalAIController:
|
| 226 |
-
def __init__(self):
|
| 227 |
-
self.__service = ProposalAIAnalysisService
|
| 228 |
-
self.router = APIRouter()
|
| 229 |
-
|
| 230 |
-
# self.router.add_api_route(
|
| 231 |
-
# "/proposal-strengths",
|
| 232 |
-
# self.get_proposal_strengths,
|
| 233 |
-
# methods=["GET"],
|
| 234 |
-
# tags=["Proposal Strengths"],
|
| 235 |
-
# response_model=ProposalStrengthResponse,
|
| 236 |
-
# )
|
| 237 |
-
# self.router.add_api_route(
|
| 238 |
-
# "/proposal-strengths/{id}",
|
| 239 |
-
# self.get_proposal_strengths_by_id,
|
| 240 |
-
# methods=["GET"],
|
| 241 |
-
# tags=["Proposal Strengths by ID"],
|
| 242 |
-
# response_model=ProposalStrengthResponse,
|
| 243 |
-
# )
|
| 244 |
-
self.router.add_api_route(
|
| 245 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/strengths",
|
| 246 |
-
self.get_proposal_strengths_by_proposal_id,
|
| 247 |
-
methods=["GET"],
|
| 248 |
-
tags=["Proposal Strengths by Proposal ID"],
|
| 249 |
-
response_model=ProposalAllStrengthsResponse,
|
| 250 |
-
)
|
| 251 |
-
self.router.add_api_route(
|
| 252 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/strengths/{category}",
|
| 253 |
-
self.get_proposal_strengths_by_proposal_and_category,
|
| 254 |
-
methods=["GET"],
|
| 255 |
-
tags=["Proposal Strengths by Proposal and Category"],
|
| 256 |
-
response_model=ProposalAllStrengthsResponse,
|
| 257 |
-
)
|
| 258 |
-
# self.router.add_api_route(
|
| 259 |
-
# "/rfps/{rfp_id}/proposals/{proposal_id}/strengths/{id}",
|
| 260 |
-
# self.get_proposal_strengths_by_proposal_and_id,
|
| 261 |
-
# methods=["GET"],
|
| 262 |
-
# tags=["Proposal Strengths by Proposal and ID"],
|
| 263 |
-
# response_model=ProposalStrengthResponse,
|
| 264 |
-
# )
|
| 265 |
-
# self.router.add_api_route(
|
| 266 |
-
# "/proposal-strengths",
|
| 267 |
-
# self.create_proposal_strengths,
|
| 268 |
-
# methods=["POST"],
|
| 269 |
-
# tags=["Proposal Strengths"],
|
| 270 |
-
# response_model=ProposalStrengthResponse,
|
| 271 |
-
# )
|
| 272 |
-
self.router.add_api_route(
|
| 273 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/strengths",
|
| 274 |
-
self.create_all_proposal_strengths,
|
| 275 |
-
methods=["POST"],
|
| 276 |
-
tags=["Proposal Strengths by Proposal ID"],
|
| 277 |
-
response_model=ProposalAllStrengthsResponse,
|
| 278 |
-
)
|
| 279 |
-
# self.router.add_api_route(
|
| 280 |
-
# "/proposal-strengths/{id}",
|
| 281 |
-
# self.update_proposal_strengths,
|
| 282 |
-
# methods=["PUT"],
|
| 283 |
-
# tags=["Proposal Strengths by ID"],
|
| 284 |
-
# response_model=ProposalStrengthResponse,
|
| 285 |
-
# )
|
| 286 |
-
self.router.add_api_route(
|
| 287 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/strengths/{category}",
|
| 288 |
-
self.update_all_proposal_strengths,
|
| 289 |
-
methods=["PUT"],
|
| 290 |
-
tags=["Proposal Strengths by Proposal and Category"],
|
| 291 |
-
response_model=ProposalAllStrengthsResponse,
|
| 292 |
-
)
|
| 293 |
-
# self.router.add_api_route(
|
| 294 |
-
# "/proposal-strengths/{id}",
|
| 295 |
-
# self.delete_proposal_strength,
|
| 296 |
-
# methods=["DELETE"],
|
| 297 |
-
# tags=["Proposal Strengths by ID"],
|
| 298 |
-
# response_model=DeleteResponse,
|
| 299 |
-
# )
|
| 300 |
-
self.router.add_api_route(
|
| 301 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/strengths",
|
| 302 |
-
self.delete_all_proposal_strengths,
|
| 303 |
-
methods=["DELETE"],
|
| 304 |
-
tags=["Proposal Strengths by Proposal ID"],
|
| 305 |
-
response_model=DeleteResponse,
|
| 306 |
-
)
|
| 307 |
-
self.router.add_api_route(
|
| 308 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/strengths/{category}",
|
| 309 |
-
self.delete_proposal_strengths_by_proposal_and_category,
|
| 310 |
-
methods=["DELETE"],
|
| 311 |
-
tags=["Proposal Strengths by Proposal and Category"],
|
| 312 |
-
response_model=DeleteResponse,
|
| 313 |
-
)
|
| 314 |
-
# self.router.add_api_route(
|
| 315 |
-
# "rfps/{rfp_id}/proposals/{proposal_id}/strengths/{id}",
|
| 316 |
-
# self.delete_proposal_strengths_by_proposal_and_id,
|
| 317 |
-
# methods=["DELETE"],
|
| 318 |
-
# tags=["Proposal Strengths by Proposal and ID"],
|
| 319 |
-
# response_model=DeleteResponse,
|
| 320 |
-
# )
|
| 321 |
-
|
| 322 |
-
# self.router.add_api_route(
|
| 323 |
-
# "/proposal-weakness",
|
| 324 |
-
# self.get_proposal_weakness,
|
| 325 |
-
# methods=["GET"],
|
| 326 |
-
# tags=["Proposal Weakness"],
|
| 327 |
-
# response_model=ProposalWeaknessResponse,
|
| 328 |
-
# )
|
| 329 |
-
# self.router.add_api_route(
|
| 330 |
-
# "/proposal-weakness/{id}",
|
| 331 |
-
# self.get_proposal_weakness_by_id,
|
| 332 |
-
# methods=["GET"],
|
| 333 |
-
# tags=["Proposal Weakness by ID"],
|
| 334 |
-
# response_model=ProposalWeaknessResponse,
|
| 335 |
-
# )
|
| 336 |
-
self.router.add_api_route(
|
| 337 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/weakness",
|
| 338 |
-
self.get_proposal_weakness_by_proposal_id,
|
| 339 |
-
methods=["GET"],
|
| 340 |
-
tags=["Proposal Weakness by Proposal ID"],
|
| 341 |
-
response_model=ProposalAllWeaknessesResponse,
|
| 342 |
-
)
|
| 343 |
-
# self.router.add_api_route(
|
| 344 |
-
# "/rfps/{rfp_id}/proposals/{proposal_id}/weakness/{id}",
|
| 345 |
-
# self.get_proposal_weakness_by_proposal_and_id,
|
| 346 |
-
# methods=["GET"],
|
| 347 |
-
# tags=["Proposal Weakness by Proposal and ID"],
|
| 348 |
-
# response_model=ProposalWeaknessResponse,
|
| 349 |
-
# )
|
| 350 |
-
# self.router.add_api_route(
|
| 351 |
-
# "/proposal-weakness",
|
| 352 |
-
# self.create_proposal_weakness,
|
| 353 |
-
# methods=["POST"],
|
| 354 |
-
# tags=["Proposal Weakness"],
|
| 355 |
-
# response_model=ProposalWeaknessResponse,
|
| 356 |
-
# )
|
| 357 |
-
self.router.add_api_route(
|
| 358 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/weakness",
|
| 359 |
-
self.create_all_proposal_weakness,
|
| 360 |
-
methods=["POST"],
|
| 361 |
-
tags=["Proposal Weakness by Proposal ID"],
|
| 362 |
-
response_model=ProposalAllWeaknessesResponse,
|
| 363 |
-
)
|
| 364 |
-
# self.router.add_api_route(
|
| 365 |
-
# "/proposal-weakness/{id}",
|
| 366 |
-
# self.update_proposal_weakness,
|
| 367 |
-
# methods=["PUT"],
|
| 368 |
-
# tags=["Proposal Weakness by ID"],
|
| 369 |
-
# response_model=ProposalWeaknessResponse,
|
| 370 |
-
# )
|
| 371 |
-
self.router.add_api_route(
|
| 372 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/weakness",
|
| 373 |
-
self.update_all_proposal_weakness,
|
| 374 |
-
methods=["PUT"],
|
| 375 |
-
tags=["Proposal Weakness by Proposal ID"],
|
| 376 |
-
response_model=ProposalAllWeaknessesResponse,
|
| 377 |
-
)
|
| 378 |
-
# self.router.add_api_route(
|
| 379 |
-
# "/proposal-weakness/{id}",
|
| 380 |
-
# self.delete_proposal_weakness,
|
| 381 |
-
# methods=["DELETE"],
|
| 382 |
-
# tags=["Proposal Weakness by ID"],
|
| 383 |
-
# response_model=DeleteResponse,
|
| 384 |
-
# )
|
| 385 |
-
self.router.add_api_route(
|
| 386 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/weakness",
|
| 387 |
-
self.delete_all_proposal_weakness,
|
| 388 |
-
methods=["DELETE"],
|
| 389 |
-
tags=["Proposal Weakness by Proposal ID"],
|
| 390 |
-
response_model=DeleteResponse,
|
| 391 |
-
)
|
| 392 |
-
self.router.add_api_route(
|
| 393 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/weakness/{category}",
|
| 394 |
-
self.delete_proposal_weakness_by_proposal_and_category,
|
| 395 |
-
methods=["DELETE"],
|
| 396 |
-
tags=["Proposal Weakness by Proposal and Category"],
|
| 397 |
-
response_model=DeleteResponse,
|
| 398 |
-
)
|
| 399 |
-
# self.router.add_api_route(
|
| 400 |
-
# "/rfps/{rfp_id}/proposals/{proposal_id}/weakness/{id}",
|
| 401 |
-
# self.delete_proposal_weakness_by_proposal_and_id,
|
| 402 |
-
# methods=["DELETE"],
|
| 403 |
-
# tags=["Proposal Weakness by Proposal and ID"],
|
| 404 |
-
# response_model=DeleteResponse,
|
| 405 |
-
# )
|
| 406 |
-
|
| 407 |
-
# self.router.add_api_route(
|
| 408 |
-
# "/proposal-rejections",
|
| 409 |
-
# self.get_proposal_rejections,
|
| 410 |
-
# methods=["GET"],
|
| 411 |
-
# tags=["Proposal Rejections"],
|
| 412 |
-
# response_model=ProposalRejectionReasonResponse,
|
| 413 |
-
# )
|
| 414 |
-
# self.router.add_api_route(
|
| 415 |
-
# "/proposal-rejections/{id}",
|
| 416 |
-
# self.get_proposal_rejections_by_id,
|
| 417 |
-
# methods=["GET"],
|
| 418 |
-
# tags=["Proposal Rejections by ID"],
|
| 419 |
-
# response_model=ProposalRejectionReasonResponse,
|
| 420 |
-
# )
|
| 421 |
-
self.router.add_api_route(
|
| 422 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/rejections",
|
| 423 |
-
self.get_proposal_rejections_by_proposal_id,
|
| 424 |
-
methods=["GET"],
|
| 425 |
-
tags=["Proposal Rejections by Proposal ID"],
|
| 426 |
-
response_model=ProposalAllRejectionReasonsResponse,
|
| 427 |
-
)
|
| 428 |
-
# self.router.add_api_route(
|
| 429 |
-
# "/rfps/{rfp_id}/proposals/{proposal_id}/rejections/{id}",
|
| 430 |
-
# self.get_proposal_rejections_by_proposal_and_id,
|
| 431 |
-
# methods=["GET"],
|
| 432 |
-
# tags=["Proposal Rejections by Proposal and ID"],
|
| 433 |
-
# response_model=ProposalRejectionReasonResponse,
|
| 434 |
-
# )
|
| 435 |
-
# self.router.add_api_route(
|
| 436 |
-
# "/proposal-rejections",
|
| 437 |
-
# self.create_proposal_rejections,
|
| 438 |
-
# methods=["POST"],
|
| 439 |
-
# tags=["Proposal Rejections"],
|
| 440 |
-
# response_model=ProposalRejectionReasonResponse,
|
| 441 |
-
# )
|
| 442 |
-
self.router.add_api_route(
|
| 443 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/rejections",
|
| 444 |
-
self.create_all_proposal_rejections,
|
| 445 |
-
methods=["POST"],
|
| 446 |
-
tags=["Proposal Rejections by Proposal ID"],
|
| 447 |
-
response_model=ProposalAllRejectionReasonsResponse,
|
| 448 |
-
)
|
| 449 |
-
# self.router.add_api_route(
|
| 450 |
-
# "/proposal-rejections/{id}",
|
| 451 |
-
# self.update_proposal_rejections,
|
| 452 |
-
# methods=["PUT"],
|
| 453 |
-
# tags=["Proposal Rejections by ID"],
|
| 454 |
-
# response_model=ProposalRejectionReasonResponse,
|
| 455 |
-
# )
|
| 456 |
-
self.router.add_api_route(
|
| 457 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/rejections",
|
| 458 |
-
self.update_all_proposal_rejections,
|
| 459 |
-
methods=["PUT"],
|
| 460 |
-
tags=["Proposal Rejections by Proposal ID"],
|
| 461 |
-
response_model=ProposalAllRejectionReasonsResponse,
|
| 462 |
-
)
|
| 463 |
-
# self.router.add_api_route(
|
| 464 |
-
# "/proposal-rejections/{id}",
|
| 465 |
-
# self.delete_proposal_rejections,
|
| 466 |
-
# methods=["DELETE"],
|
| 467 |
-
# tags=["Proposal Rejections by ID"],
|
| 468 |
-
# response_model=DeleteResponse,
|
| 469 |
-
# )
|
| 470 |
-
self.router.add_api_route(
|
| 471 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/rejections",
|
| 472 |
-
self.delete_all_proposal_rejections,
|
| 473 |
-
methods=["DELETE"],
|
| 474 |
-
tags=["Proposal Rejections by Proposal ID"],
|
| 475 |
-
response_model=DeleteResponse,
|
| 476 |
-
)
|
| 477 |
-
self.router.add_api_route(
|
| 478 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/rejections/{category}",
|
| 479 |
-
self.delete_proposal_rejections_by_proposal_and_category,
|
| 480 |
-
methods=["DELETE"],
|
| 481 |
-
tags=["Proposal Rejections by Proposal and Category"],
|
| 482 |
-
response_model=DeleteResponse,
|
| 483 |
-
)
|
| 484 |
-
# self.router.add_api_route(
|
| 485 |
-
# "/rfps/{rfp_id}/proposals/{proposal_id}/proposal-rejections/{id}",
|
| 486 |
-
# self.delete_proposal_rejections_by_proposal_and_id,
|
| 487 |
-
# methods=["DELETE"],
|
| 488 |
-
# tags=["Proposal Rejections by Proposal and ID"],
|
| 489 |
-
# response_model=DeleteResponse,
|
| 490 |
-
# )
|
| 491 |
-
|
| 492 |
-
# self.router.add_api_route(
|
| 493 |
-
# "/proposal-improvements",
|
| 494 |
-
# self.get_proposal_improvements,
|
| 495 |
-
# methods=["GET"],
|
| 496 |
-
# tags=["Proposal Improvements"],
|
| 497 |
-
# response_model=ProposalImprovementResponse,
|
| 498 |
-
# )
|
| 499 |
-
# self.router.add_api_route(
|
| 500 |
-
# "/proposal-improvements/{id}",
|
| 501 |
-
# self.get_proposal_improvements_by_id,
|
| 502 |
-
# methods=["GET"],
|
| 503 |
-
# tags=["Proposal Improvements by ID"],
|
| 504 |
-
# response_model=ProposalImprovementResponse,
|
| 505 |
-
# )
|
| 506 |
-
self.router.add_api_route(
|
| 507 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/improvements",
|
| 508 |
-
self.get_proposal_improvements_by_proposal_id,
|
| 509 |
-
methods=["GET"],
|
| 510 |
-
tags=["Proposal Improvements by Proposal ID"],
|
| 511 |
-
response_model=ProposalAllImprovementsResponse,
|
| 512 |
-
)
|
| 513 |
-
# self.router.add_api_route(
|
| 514 |
-
# "/proposals/{proposal_id}/proposal-improvements/{id}",
|
| 515 |
-
# self.get_proposal_improvements_by_proposal_and_id,
|
| 516 |
-
# methods=["GET"],
|
| 517 |
-
# tags=["Proposal Improvements by Proposal and ID"],
|
| 518 |
-
# response_model=ProposalImprovementResponse,
|
| 519 |
-
# )
|
| 520 |
-
# self.router.add_api_route(
|
| 521 |
-
# "/proposal-improvements",
|
| 522 |
-
# self.create_proposal_improvements,
|
| 523 |
-
# methods=["POST"],
|
| 524 |
-
# tags=["Proposal Improvements"],
|
| 525 |
-
# response_model=ProposalImprovementResponse,
|
| 526 |
-
# )
|
| 527 |
-
self.router.add_api_route(
|
| 528 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/improvements",
|
| 529 |
-
self.create_all_proposal_improvements,
|
| 530 |
-
methods=["POST"],
|
| 531 |
-
tags=["Proposal Improvements by Proposal ID"],
|
| 532 |
-
response_model=ProposalAllImprovementsResponse,
|
| 533 |
-
)
|
| 534 |
-
# self.router.add_api_route(
|
| 535 |
-
# "/proposal-improvements/{id}",
|
| 536 |
-
# self.update_proposal_improvements,
|
| 537 |
-
# methods=["PUT"],
|
| 538 |
-
# tags=["Proposal Improvements by ID"],
|
| 539 |
-
# response_model=ProposalImprovementResponse,
|
| 540 |
-
# )
|
| 541 |
-
self.router.add_api_route(
|
| 542 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/improvements",
|
| 543 |
-
self.update_all_proposal_improvements,
|
| 544 |
-
methods=["PUT"],
|
| 545 |
-
tags=["Proposal Improvements by Proposal ID"],
|
| 546 |
-
response_model=ProposalAllImprovementsResponse,
|
| 547 |
-
)
|
| 548 |
-
# self.router.add_api_route(
|
| 549 |
-
# "/proposal-improvements/{id}",
|
| 550 |
-
# self.delete_proposal_improvements,
|
| 551 |
-
# methods=["DELETE"],
|
| 552 |
-
# tags=["Proposal Improvements by ID"],
|
| 553 |
-
# response_model=DeleteResponse,
|
| 554 |
-
# )
|
| 555 |
-
self.router.add_api_route(
|
| 556 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/improvements",
|
| 557 |
-
self.delete_all_proposal_improvements,
|
| 558 |
-
methods=["DELETE"],
|
| 559 |
-
tags=["Proposal Improvements by Proposal ID"],
|
| 560 |
-
response_model=DeleteResponse,
|
| 561 |
-
)
|
| 562 |
-
self.router.add_api_route(
|
| 563 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/improvements/{category}",
|
| 564 |
-
self.delete_proposal_improvements_by_proposal_and_category,
|
| 565 |
-
methods=["DELETE"],
|
| 566 |
-
tags=["Proposal Improvements by Proposal and Category"],
|
| 567 |
-
response_model=DeleteResponse,
|
| 568 |
-
)
|
| 569 |
-
# self.router.add_api_route(
|
| 570 |
-
# "/proposals/{proposal_id}/proposal-improvements/{id}",
|
| 571 |
-
# self.delete_proposal_improvements_by_proposal_and_id,
|
| 572 |
-
# methods=["DELETE"],
|
| 573 |
-
# tags=["Proposal Improvements by Proposal and ID"],
|
| 574 |
-
# response_model=DeleteResponse,
|
| 575 |
-
# )
|
| 576 |
-
|
| 577 |
-
# self.router.add_api_route(
|
| 578 |
-
# "/proposal-ai-score",
|
| 579 |
-
# self.get_proposal_ai_score,
|
| 580 |
-
# methods=["GET"],
|
| 581 |
-
# tags=["Proposal AI Score"],
|
| 582 |
-
# response_model=ProposalAIAnalysisResponse,
|
| 583 |
-
# )
|
| 584 |
-
# self.router.add_api_route(
|
| 585 |
-
# "/proposal-ai-score/{id}",
|
| 586 |
-
# self.get_proposal_ai_score_by_id,
|
| 587 |
-
# methods=["GET"],
|
| 588 |
-
# tags=["Proposal AI Score by ID"],
|
| 589 |
-
# response_model=ProposalAIAnalysisResponse,
|
| 590 |
-
# )
|
| 591 |
-
self.router.add_api_route(
|
| 592 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/ai-score",
|
| 593 |
-
self.get_proposal_ai_score_by_proposal_id,
|
| 594 |
-
methods=["GET"],
|
| 595 |
-
tags=["Proposal AI Score by Proposal ID"],
|
| 596 |
-
response_model=ProposalAIAnalysisResponse,
|
| 597 |
-
)
|
| 598 |
-
# self.router.add_api_route(
|
| 599 |
-
# "/proposals/{proposal_id}/proposal-ai-score/{id}",
|
| 600 |
-
# self.get_proposal_ai_score_by_proposal_and_id,
|
| 601 |
-
# methods=["GET"],
|
| 602 |
-
# tags=["Proposal AI Score by Proposal and ID"],
|
| 603 |
-
# response_model=ProposalAIAnalysisResponse,
|
| 604 |
-
# )
|
| 605 |
-
# self.router.add_api_route(
|
| 606 |
-
# "/proposal-ai-score",
|
| 607 |
-
# self.create_proposal_ai_score,
|
| 608 |
-
# methods=["POST"],
|
| 609 |
-
# tags=["Proposal AI Score"],
|
| 610 |
-
# response_model=ProposalAIAnalysisResponse,
|
| 611 |
-
# )
|
| 612 |
-
self.router.add_api_route(
|
| 613 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/ai-score",
|
| 614 |
-
self.create_proposal_ai_score_by_proposal_id,
|
| 615 |
-
methods=["POST"],
|
| 616 |
-
tags=["Proposal AI Score by Proposal ID"],
|
| 617 |
-
response_model=ProposalAIAnalysisResponse,
|
| 618 |
-
)
|
| 619 |
-
# self.router.add_api_route(
|
| 620 |
-
# "/proposal-ai-score/{id}",
|
| 621 |
-
# self.update_proposal_ai_score,
|
| 622 |
-
# methods=["PUT"],
|
| 623 |
-
# tags=["Proposal AI Score by ID"],
|
| 624 |
-
# response_model=ProposalAIAnalysisResponse,
|
| 625 |
-
# )
|
| 626 |
-
self.router.add_api_route(
|
| 627 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/ai-score",
|
| 628 |
-
self.update_proposal_ai_score_by_proposal_id,
|
| 629 |
-
methods=["PUT"],
|
| 630 |
-
tags=["Proposal AI Score by Proposal ID"],
|
| 631 |
-
response_model=ProposalAIAnalysisResponse,
|
| 632 |
-
)
|
| 633 |
-
# self.router.add_api_route(
|
| 634 |
-
# "/proposal-ai-score/{id}",
|
| 635 |
-
# self.delete_proposal_ai_score,
|
| 636 |
-
# methods=["DELETE"],
|
| 637 |
-
# tags=["Proposal AI Score by ID"],
|
| 638 |
-
# response_model=DeleteResponse,
|
| 639 |
-
# )
|
| 640 |
-
self.router.add_api_route(
|
| 641 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/ai-score",
|
| 642 |
-
self.delete_proposal_ai_score_by_proposal_id,
|
| 643 |
-
methods=["DELETE"],
|
| 644 |
-
tags=["Proposal AI Score by Proposal ID"],
|
| 645 |
-
response_model=DeleteResponse,
|
| 646 |
-
)
|
| 647 |
-
# self.router.add_api_route(
|
| 648 |
-
# "/proposals/{proposal_id}/proposal-ai-score/{id}",
|
| 649 |
-
# self.delete_proposal_ai_score_by_proposal_and_id,
|
| 650 |
-
# methods=["DELETE"],
|
| 651 |
-
# tags=["Proposal AI Score by Proposal and ID"],
|
| 652 |
-
# response_model=DeleteResponse,
|
| 653 |
-
# )
|
| 654 |
-
|
| 655 |
-
self.router.add_api_route(
|
| 656 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/analysis",
|
| 657 |
-
self.get_all_proposal_analysis,
|
| 658 |
-
methods=["GET"],
|
| 659 |
-
tags=["Proposal Complete Analysis"],
|
| 660 |
-
response_model=ProposalCompleteAnalysisResponse,
|
| 661 |
-
)
|
| 662 |
-
self.router.add_api_route(
|
| 663 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/analysis",
|
| 664 |
-
self.create_proposal_analysis,
|
| 665 |
-
methods=["POST"],
|
| 666 |
-
tags=["Proposal Complete Analysis"],
|
| 667 |
-
response_model=ProposalCompleteAnalysisResponse,
|
| 668 |
-
)
|
| 669 |
-
self.router.add_api_route(
|
| 670 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/analysis",
|
| 671 |
-
self.update_proposal_analysis,
|
| 672 |
-
methods=["PUT"],
|
| 673 |
-
tags=["Proposal Complete Analysis"],
|
| 674 |
-
response_model=ProposalCompleteAnalysisResponse,
|
| 675 |
-
)
|
| 676 |
-
self.router.add_api_route(
|
| 677 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/analysis",
|
| 678 |
-
self.delete_proposal_analysis,
|
| 679 |
-
methods=["DELETE"],
|
| 680 |
-
tags=["Proposal Complete Analysis"],
|
| 681 |
-
response_model=DeleteResponse,
|
| 682 |
-
)
|
| 683 |
-
|
| 684 |
-
async def get_proposal_strengths(
|
| 685 |
-
self
|
| 686 |
-
):
|
| 687 |
-
async with self.__service() as service:
|
| 688 |
-
try:
|
| 689 |
-
strengths = await service.get_all_data(
|
| 690 |
-
query_type=QueryType.STRENGTHS
|
| 691 |
-
)
|
| 692 |
-
return ProposalStrengthResponse(
|
| 693 |
-
status="success", data=[ProposalStrength(**s) for s in strengths]
|
| 694 |
-
)
|
| 695 |
-
except Exception as e:
|
| 696 |
-
logger.exception(e)
|
| 697 |
-
raise HTTPException(
|
| 698 |
-
status_code=500, detail="Failed to retrieve strengths."
|
| 699 |
-
)
|
| 700 |
-
|
| 701 |
-
async def get_proposal_strengths_by_id(self, id: UUID = Path(...)):
|
| 702 |
-
async with self.__service() as service:
|
| 703 |
-
try:
|
| 704 |
-
strength = await service.get_all_data(
|
| 705 |
-
query_type=QueryType.STRENGTHS, id=id
|
| 706 |
-
)
|
| 707 |
-
return ProposalStrengthResponse(
|
| 708 |
-
status="success", data=[ProposalStrength(**s) for s in strength]
|
| 709 |
-
)
|
| 710 |
-
except Exception as e:
|
| 711 |
-
logger.exception(e)
|
| 712 |
-
raise HTTPException(
|
| 713 |
-
status_code=500, detail="Failed to retrieve strength."
|
| 714 |
-
)
|
| 715 |
-
|
| 716 |
-
async def get_proposal_strengths_by_proposal_id(self, rfp_id: UUID = Path(...), proposal_id: UUID = Path(...)):
|
| 717 |
-
async with self.__service() as service:
|
| 718 |
-
try:
|
| 719 |
-
strength = await service.get_all_data(
|
| 720 |
-
query_type=QueryType.STRENGTHS, proposal_id=proposal_id
|
| 721 |
-
)
|
| 722 |
-
return ProposalAllStrengthsResponse(
|
| 723 |
-
status="success", data=[s["strength_point"] for s in strength]
|
| 724 |
-
)
|
| 725 |
-
except Exception as e:
|
| 726 |
-
logger.exception(e)
|
| 727 |
-
raise HTTPException(
|
| 728 |
-
status_code=500, detail="Failed to retrieve strength."
|
| 729 |
-
)
|
| 730 |
-
|
| 731 |
-
async def get_proposal_strengths_by_proposal_and_category(self, rfp_id: UUID = Path(...), proposal_id: UUID = Path(...), category: Category = Path(...)):
|
| 732 |
-
async with self.__service() as service:
|
| 733 |
-
try:
|
| 734 |
-
strength = await service.get_all_data(
|
| 735 |
-
query_type=QueryType.STRENGTHS, proposal_id=proposal_id, category=category
|
| 736 |
-
)
|
| 737 |
-
return ProposalAllStrengthsResponse(
|
| 738 |
-
status="success", data=[s["strength_point"] for s in strength]
|
| 739 |
-
)
|
| 740 |
-
except Exception as e:
|
| 741 |
-
logger.exception(e)
|
| 742 |
-
raise HTTPException(
|
| 743 |
-
status_code=500, detail="Failed to retrieve strength."
|
| 744 |
-
)
|
| 745 |
-
|
| 746 |
-
async def get_proposal_strengths_by_proposal_and_id(self, rfp_id: UUID = Path(...), proposal_id: UUID = Path(...), id: UUID = Path(...)):
|
| 747 |
-
async with self.__service() as service:
|
| 748 |
-
try:
|
| 749 |
-
strength = await service.get_all_data(
|
| 750 |
-
query_type=QueryType.STRENGTHS, proposal_id=proposal_id, id=id
|
| 751 |
-
)
|
| 752 |
-
return ProposalStrengthResponse(
|
| 753 |
-
status="success", data=[ProposalStrength(**s) for s in strength]
|
| 754 |
-
)
|
| 755 |
-
except Exception as e:
|
| 756 |
-
logger.exception(e)
|
| 757 |
-
raise HTTPException(
|
| 758 |
-
status_code=500, detail="Failed to retrieve strength."
|
| 759 |
-
)
|
| 760 |
-
|
| 761 |
-
async def create_proposal_strengths(self, strength: ProposalStrengthRequest):
|
| 762 |
-
async with self.__service() as service:
|
| 763 |
-
try:
|
| 764 |
-
created = await service.create_data(
|
| 765 |
-
query_type=QueryType.STRENGTHS,
|
| 766 |
-
create_or_update_type=CreateOrUpdateType.ID,
|
| 767 |
-
data=strength.model_dump(mode="json"),
|
| 768 |
-
)
|
| 769 |
-
return ProposalStrengthResponse(
|
| 770 |
-
status="success", data=[ProposalStrength(**s) for s in created]
|
| 771 |
-
)
|
| 772 |
-
except Exception as e:
|
| 773 |
-
logger.exception(e)
|
| 774 |
-
raise HTTPException(
|
| 775 |
-
status_code=500, detail="Failed to create strength."
|
| 776 |
-
)
|
| 777 |
-
|
| 778 |
-
async def create_all_proposal_strengths(
|
| 779 |
-
self, strengths: CreateAllProposalStrengthsRequest, proposal_id: UUID = Path(...), rfp_id: UUID = Path(...)
|
| 780 |
-
):
|
| 781 |
-
async with self.__service() as service:
|
| 782 |
-
try:
|
| 783 |
-
strengths_request = strengths.model_dump(mode="json", exclude_unset=True)
|
| 784 |
-
strengths_request["proposal_id"] = proposal_id
|
| 785 |
-
created = await service.create_data(
|
| 786 |
-
query_type=QueryType.STRENGTHS,
|
| 787 |
-
create_or_update_type=CreateOrUpdateType.ALL,
|
| 788 |
-
data=strengths_request,
|
| 789 |
-
)
|
| 790 |
-
return ProposalAllStrengthsResponse(
|
| 791 |
-
status="success", data=[s["strength_point"] for s in created]
|
| 792 |
-
)
|
| 793 |
-
except Exception as e:
|
| 794 |
-
logger.exception(e)
|
| 795 |
-
raise HTTPException(
|
| 796 |
-
status_code=500, detail="Failed to create strengths."
|
| 797 |
-
)
|
| 798 |
-
|
| 799 |
-
async def update_proposal_strengths(self, strength: UpdateProposalStrengthRequest, id: UUID = Path(...)):
|
| 800 |
-
async with self.__service() as service:
|
| 801 |
-
try:
|
| 802 |
-
strength_request = strength.model_dump(mode="json", exclude_unset=True)
|
| 803 |
-
strength_request["id"] = id
|
| 804 |
-
updated = await service.update_data(
|
| 805 |
-
query_type=QueryType.STRENGTHS,
|
| 806 |
-
create_or_update_type=CreateOrUpdateType.ID,
|
| 807 |
-
data=strength_request,
|
| 808 |
-
)
|
| 809 |
-
return ProposalStrengthResponse(
|
| 810 |
-
status="success", data=[ProposalStrength(**s) for s in updated]
|
| 811 |
-
)
|
| 812 |
-
except Exception as e:
|
| 813 |
-
logger.exception(e)
|
| 814 |
-
raise HTTPException(
|
| 815 |
-
status_code=500, detail="Failed to update strength."
|
| 816 |
-
)
|
| 817 |
-
|
| 818 |
-
async def update_all_proposal_strengths(
|
| 819 |
-
self, strengths: UpdateAllProposalStrengthsRequest, proposal_id: UUID = Path(...), rfp_id: UUID = Path(...), category: Category = Path(...)
|
| 820 |
-
):
|
| 821 |
-
async with self.__service() as service:
|
| 822 |
-
try:
|
| 823 |
-
strengths_request = strengths.model_dump(mode="json", exclude_unset=True)
|
| 824 |
-
strengths_request["proposal_id"] = proposal_id
|
| 825 |
-
strengths_request["category"] = category
|
| 826 |
-
updated = await service.update_data(
|
| 827 |
-
query_type=QueryType.STRENGTHS,
|
| 828 |
-
create_or_update_type=CreateOrUpdateType.ALL,
|
| 829 |
-
data=strengths_request,
|
| 830 |
-
)
|
| 831 |
-
return ProposalAllStrengthsResponse(
|
| 832 |
-
status="success", data=[s["strength_point"] for s in updated]
|
| 833 |
-
)
|
| 834 |
-
except Exception as e:
|
| 835 |
-
logger.exception(e)
|
| 836 |
-
raise HTTPException(
|
| 837 |
-
status_code=500, detail="Failed to update strengths."
|
| 838 |
-
)
|
| 839 |
-
|
| 840 |
-
async def delete_proposal_strength(
|
| 841 |
-
self, id: UUID = Path(...)
|
| 842 |
-
):
|
| 843 |
-
async with self.__service() as service:
|
| 844 |
-
try:
|
| 845 |
-
result = await service.delete_data(
|
| 846 |
-
query_type=QueryType.STRENGTHS, id=id
|
| 847 |
-
)
|
| 848 |
-
return DeleteResponse(status="success")
|
| 849 |
-
except Exception as e:
|
| 850 |
-
logger.exception(e)
|
| 851 |
-
raise HTTPException(
|
| 852 |
-
status_code=500, detail="Failed to delete strength."
|
| 853 |
-
)
|
| 854 |
-
|
| 855 |
-
async def delete_all_proposal_strengths(self, proposal_id: UUID = Path(...), rfp_id: UUID = Path(...)):
|
| 856 |
-
async with self.__service() as service:
|
| 857 |
-
try:
|
| 858 |
-
result = await service.delete_data(
|
| 859 |
-
query_type=QueryType.STRENGTHS, proposal_id=proposal_id
|
| 860 |
-
)
|
| 861 |
-
return DeleteResponse(status="success")
|
| 862 |
-
except Exception as e:
|
| 863 |
-
logger.exception(e)
|
| 864 |
-
raise HTTPException(
|
| 865 |
-
status_code=500, detail="Failed to delete strengths."
|
| 866 |
-
)
|
| 867 |
-
|
| 868 |
-
async def delete_proposal_strengths_by_proposal_and_category(self, proposal_id: UUID = Path(...), category: Category = Path(...), rfp_id: UUID = Path(...)):
|
| 869 |
-
async with self.__service() as service:
|
| 870 |
-
try:
|
| 871 |
-
result = await service.delete_data(
|
| 872 |
-
query_type=QueryType.STRENGTHS, proposal_id=proposal_id, category=category
|
| 873 |
-
)
|
| 874 |
-
return DeleteResponse(status="success")
|
| 875 |
-
except Exception as e:
|
| 876 |
-
logger.exception(e)
|
| 877 |
-
raise HTTPException(
|
| 878 |
-
status_code=500, detail="Failed to delete strength."
|
| 879 |
-
)
|
| 880 |
-
|
| 881 |
-
async def delete_proposal_strengths_by_proposal_and_id(self, proposal_id: UUID = Path(...), id: UUID = Path(...), rfp_id: UUID = Path(...)):
|
| 882 |
-
async with self.__service() as service:
|
| 883 |
-
try:
|
| 884 |
-
result = await service.delete_data(
|
| 885 |
-
query_type=QueryType.STRENGTHS, proposal_id=proposal_id, id=id
|
| 886 |
-
)
|
| 887 |
-
return DeleteResponse(status="success")
|
| 888 |
-
except Exception as e:
|
| 889 |
-
logger.exception(e)
|
| 890 |
-
raise HTTPException(
|
| 891 |
-
status_code=500, detail="Failed to delete strength."
|
| 892 |
-
)
|
| 893 |
-
|
| 894 |
-
async def get_proposal_weakness(
|
| 895 |
-
self
|
| 896 |
-
):
|
| 897 |
-
async with self.__service() as service:
|
| 898 |
-
try:
|
| 899 |
-
weaknesses = await service.get_all_data(
|
| 900 |
-
query_type=QueryType.WEAKNESSES
|
| 901 |
-
)
|
| 902 |
-
return ProposalWeaknessResponse(
|
| 903 |
-
status="success", data=[ProposalWeakness(**w) for w in weaknesses]
|
| 904 |
-
)
|
| 905 |
-
except Exception as e:
|
| 906 |
-
logger.exception(e)
|
| 907 |
-
raise HTTPException(
|
| 908 |
-
status_code=500, detail="Failed to retrieve weaknesses."
|
| 909 |
-
)
|
| 910 |
-
|
| 911 |
-
async def get_proposal_weakness_by_id(self, id: UUID = Path(...)):
|
| 912 |
-
async with self.__service() as service:
|
| 913 |
-
try:
|
| 914 |
-
weakness = await service.get_all_data(
|
| 915 |
-
query_type=QueryType.WEAKNESSES, id=id
|
| 916 |
-
)
|
| 917 |
-
return ProposalWeaknessResponse(
|
| 918 |
-
status="success", data=[ProposalWeakness(**w) for w in weakness]
|
| 919 |
-
)
|
| 920 |
-
except Exception as e:
|
| 921 |
-
logger.exception(e)
|
| 922 |
-
raise HTTPException(
|
| 923 |
-
status_code=500, detail="Failed to retrieve weakness."
|
| 924 |
-
)
|
| 925 |
-
|
| 926 |
-
async def get_proposal_weakness_by_proposal_id(self, proposal_id: UUID = Path(...), rfp_id: UUID = Path(...), category: Category = Query(None)):
|
| 927 |
-
async with self.__service() as service:
|
| 928 |
-
try:
|
| 929 |
-
weakness = await service.get_all_data(
|
| 930 |
-
query_type=QueryType.WEAKNESSES, proposal_id=proposal_id, category=category
|
| 931 |
-
)
|
| 932 |
-
return ProposalAllWeaknessesResponse(
|
| 933 |
-
status="success", data=[w["weakness_point"] for w in weakness]
|
| 934 |
-
)
|
| 935 |
-
except Exception as e:
|
| 936 |
-
logger.exception(e)
|
| 937 |
-
raise HTTPException(
|
| 938 |
-
status_code=500, detail="Failed to retrieve weakness."
|
| 939 |
-
)
|
| 940 |
-
|
| 941 |
-
async def get_proposal_weakness_by_proposal_and_id(self, proposal_id: UUID = Path(...), id: UUID = Path(...), rfp_id: UUID = Path(...)):
|
| 942 |
-
async with self.__service() as service:
|
| 943 |
-
try:
|
| 944 |
-
weakness = await service.get_all_data(
|
| 945 |
-
query_type=QueryType.WEAKNESSES, proposal_id=proposal_id, id=id
|
| 946 |
-
)
|
| 947 |
-
return ProposalWeaknessResponse(
|
| 948 |
-
status="success", data=[ProposalWeakness(**w) for w in weakness]
|
| 949 |
-
)
|
| 950 |
-
except Exception as e:
|
| 951 |
-
logger.exception(e)
|
| 952 |
-
raise HTTPException(
|
| 953 |
-
status_code=500, detail="Failed to retrieve weakness."
|
| 954 |
-
)
|
| 955 |
-
|
| 956 |
-
async def create_proposal_weakness(self, weakness: ProposalWeaknessRequest):
|
| 957 |
-
async with self.__service() as service:
|
| 958 |
-
try:
|
| 959 |
-
created = await service.create_data(
|
| 960 |
-
query_type=QueryType.WEAKNESSES,
|
| 961 |
-
create_or_update_type=CreateOrUpdateType.ID,
|
| 962 |
-
data=weakness.model_dump(mode="json"),
|
| 963 |
-
)
|
| 964 |
-
return ProposalWeaknessResponse(
|
| 965 |
-
status="success", data=[ProposalWeakness(**w) for w in created]
|
| 966 |
-
)
|
| 967 |
-
except Exception as e:
|
| 968 |
-
logger.exception(e)
|
| 969 |
-
raise HTTPException(
|
| 970 |
-
status_code=500, detail="Failed to create weakness."
|
| 971 |
-
)
|
| 972 |
-
|
| 973 |
-
async def create_all_proposal_weakness(
|
| 974 |
-
self, weaknesses: CreateAllProposalWeaknessRequest, proposal_id: UUID
|
| 975 |
-
):
|
| 976 |
-
async with self.__service() as service:
|
| 977 |
-
try:
|
| 978 |
-
weakness_request = weaknesses.model_dump(mode="json", exclude_unset=True)
|
| 979 |
-
weakness_request["proposal_id"] = proposal_id
|
| 980 |
-
created = await service.create_data(
|
| 981 |
-
query_type=QueryType.WEAKNESSES,
|
| 982 |
-
create_or_update_type=CreateOrUpdateType.ALL,
|
| 983 |
-
data=weakness_request,
|
| 984 |
-
)
|
| 985 |
-
return ProposalAllWeaknessesResponse(
|
| 986 |
-
status="success", data=[w["weakness_point"] for w in created]
|
| 987 |
-
)
|
| 988 |
-
except Exception as e:
|
| 989 |
-
logger.exception(e)
|
| 990 |
-
raise HTTPException(
|
| 991 |
-
status_code=500, detail="Failed to create weaknesses."
|
| 992 |
-
)
|
| 993 |
-
|
| 994 |
-
async def update_proposal_weakness(self, weakness: UpdateProposalWeaknessRequest, id: UUID = Path(...)):
|
| 995 |
-
async with self.__service() as service:
|
| 996 |
-
try:
|
| 997 |
-
weakness_request = weakness.model_dump(mode="json", exclude_unset=True)
|
| 998 |
-
weakness_request["id"] = id
|
| 999 |
-
updated = await service.update_data(
|
| 1000 |
-
query_type=QueryType.WEAKNESSES,
|
| 1001 |
-
create_or_update_type=CreateOrUpdateType.ID,
|
| 1002 |
-
data=weakness_request,
|
| 1003 |
-
)
|
| 1004 |
-
return ProposalWeaknessResponse(
|
| 1005 |
-
status="success", data=[ProposalWeakness(**w) for w in updated]
|
| 1006 |
-
)
|
| 1007 |
-
except Exception as e:
|
| 1008 |
-
logger.exception(e)
|
| 1009 |
-
raise HTTPException(
|
| 1010 |
-
status_code=500, detail="Failed to update weakness."
|
| 1011 |
-
)
|
| 1012 |
-
|
| 1013 |
-
async def update_all_proposal_weakness(
|
| 1014 |
-
self, weaknesses: CreateAllProposalWeaknessRequest, proposal_id: UUID = Path(...)
|
| 1015 |
-
):
|
| 1016 |
-
async with self.__service() as service:
|
| 1017 |
-
try:
|
| 1018 |
-
weakness_request = weaknesses.model_dump(mode="json", exclude_unset=True)
|
| 1019 |
-
weakness_request["proposal_id"] = proposal_id
|
| 1020 |
-
updated = await service.update_data(
|
| 1021 |
-
query_type=QueryType.WEAKNESSES,
|
| 1022 |
-
create_or_update_type=CreateOrUpdateType.ALL,
|
| 1023 |
-
data=weakness_request,
|
| 1024 |
-
)
|
| 1025 |
-
return ProposalAllWeaknessesResponse(
|
| 1026 |
-
status="success", data=[w["weakness_point"] for w in updated]
|
| 1027 |
-
)
|
| 1028 |
-
except Exception as e:
|
| 1029 |
-
logger.exception(e)
|
| 1030 |
-
raise HTTPException(
|
| 1031 |
-
status_code=500, detail="Failed to update weaknesses."
|
| 1032 |
-
)
|
| 1033 |
-
|
| 1034 |
-
async def delete_proposal_weakness(
|
| 1035 |
-
self, id: UUID = Path(...)
|
| 1036 |
-
):
|
| 1037 |
-
async with self.__service() as service:
|
| 1038 |
-
try:
|
| 1039 |
-
result = await service.delete_data(
|
| 1040 |
-
query_type=QueryType.WEAKNESSES, id=id
|
| 1041 |
-
)
|
| 1042 |
-
return DeleteResponse(status="success")
|
| 1043 |
-
except Exception as e:
|
| 1044 |
-
logger.exception(e)
|
| 1045 |
-
raise HTTPException(
|
| 1046 |
-
status_code=500, detail="Failed to delete weakness."
|
| 1047 |
-
)
|
| 1048 |
-
|
| 1049 |
-
async def delete_all_proposal_weakness(
|
| 1050 |
-
self, proposal_id: UUID = Path(...), rfp_id: UUID = Path(...)
|
| 1051 |
-
):
|
| 1052 |
-
async with self.__service() as service:
|
| 1053 |
-
try:
|
| 1054 |
-
result = await service.delete_data(
|
| 1055 |
-
query_type=QueryType.WEAKNESSES, proposal_id=proposal_id
|
| 1056 |
-
)
|
| 1057 |
-
return DeleteResponse(status="success")
|
| 1058 |
-
except Exception as e:
|
| 1059 |
-
logger.exception(e)
|
| 1060 |
-
raise HTTPException(
|
| 1061 |
-
status_code=500, detail="Failed to delete weaknesses."
|
| 1062 |
-
)
|
| 1063 |
-
|
| 1064 |
-
async def delete_proposal_weakness_by_proposal_and_category(self, proposal_id: UUID = Path(...), category: Category = Path(...), rfp_id: UUID = Path(...)):
|
| 1065 |
-
async with self.__service() as service:
|
| 1066 |
-
try:
|
| 1067 |
-
result = await service.delete_data(
|
| 1068 |
-
query_type=QueryType.WEAKNESSES, proposal_id=proposal_id, category=category
|
| 1069 |
-
)
|
| 1070 |
-
return DeleteResponse(status="success")
|
| 1071 |
-
except Exception as e:
|
| 1072 |
-
logger.exception(e)
|
| 1073 |
-
raise HTTPException(
|
| 1074 |
-
status_code=500, detail="Failed to delete weakness."
|
| 1075 |
-
)
|
| 1076 |
-
|
| 1077 |
-
async def delete_proposal_weakness_by_proposal_and_id(self, proposal_id: UUID = Path(...), id: UUID = Path(...), rfp_id: UUID = Path(...)):
|
| 1078 |
-
async with self.__service() as service:
|
| 1079 |
-
try:
|
| 1080 |
-
result = await service.delete_data(
|
| 1081 |
-
query_type=QueryType.WEAKNESSES, proposal_id=proposal_id, id=id
|
| 1082 |
-
)
|
| 1083 |
-
return DeleteResponse(status="success")
|
| 1084 |
-
except Exception as e:
|
| 1085 |
-
logger.exception(e)
|
| 1086 |
-
raise HTTPException(
|
| 1087 |
-
status_code=500, detail="Failed to delete weakness."
|
| 1088 |
-
)
|
| 1089 |
-
|
| 1090 |
-
async def get_proposal_rejections(
|
| 1091 |
-
self
|
| 1092 |
-
):
|
| 1093 |
-
async with self.__service() as service:
|
| 1094 |
-
try:
|
| 1095 |
-
rejection_reasons = await service.get_all_data(
|
| 1096 |
-
query_type=QueryType.REJECTION_REASONS
|
| 1097 |
-
)
|
| 1098 |
-
return ProposalRejectionReasonResponse(
|
| 1099 |
-
status="success",
|
| 1100 |
-
data=[ProposalRejectionReason(**r) for r in rejection_reasons],
|
| 1101 |
-
)
|
| 1102 |
-
except Exception as e:
|
| 1103 |
-
logger.exception(e)
|
| 1104 |
-
raise HTTPException(
|
| 1105 |
-
status_code=500, detail="Failed to retrieve rejection reasons."
|
| 1106 |
-
)
|
| 1107 |
-
|
| 1108 |
-
async def get_proposal_rejections_by_id(self, id: UUID = Path(...)):
|
| 1109 |
-
async with self.__service() as service:
|
| 1110 |
-
try:
|
| 1111 |
-
rejection_reason = await service.get_all_data(
|
| 1112 |
-
query_type=QueryType.REJECTION_REASONS, id=id
|
| 1113 |
-
)
|
| 1114 |
-
return ProposalRejectionReasonResponse(
|
| 1115 |
-
status="success", data=[ProposalRejectionReason(**r) for r in rejection_reason]
|
| 1116 |
-
)
|
| 1117 |
-
except Exception as e:
|
| 1118 |
-
logger.exception(e)
|
| 1119 |
-
raise HTTPException(
|
| 1120 |
-
status_code=500, detail="Failed to retrieve rejection reason."
|
| 1121 |
-
)
|
| 1122 |
-
|
| 1123 |
-
async def get_proposal_rejections_by_proposal_id(self, proposal_id: UUID = Path(...), rfp_id: UUID = Path(...), category: Category = Query(None)):
|
| 1124 |
-
async with self.__service() as service:
|
| 1125 |
-
try:
|
| 1126 |
-
rejection_reason = await service.get_all_data(
|
| 1127 |
-
query_type=QueryType.REJECTION_REASONS, proposal_id=proposal_id, category=category
|
| 1128 |
-
)
|
| 1129 |
-
return ProposalAllRejectionReasonsResponse(
|
| 1130 |
-
status="success", data=[r["reason"] for r in rejection_reason]
|
| 1131 |
-
)
|
| 1132 |
-
except Exception as e:
|
| 1133 |
-
logger.exception(e)
|
| 1134 |
-
raise HTTPException(
|
| 1135 |
-
status_code=500, detail="Failed to retrieve rejection reason."
|
| 1136 |
-
)
|
| 1137 |
-
|
| 1138 |
-
async def get_proposal_rejections_by_proposal_and_id(self, proposal_id: UUID = Path(...), id: UUID = Path(...), rfp_id: UUID = Path(...)):
|
| 1139 |
-
async with self.__service() as service:
|
| 1140 |
-
try:
|
| 1141 |
-
rejection_reason = await service.get_all_data(
|
| 1142 |
-
query_type=QueryType.REJECTION_REASONS, proposal_id=proposal_id, id=id
|
| 1143 |
-
)
|
| 1144 |
-
return ProposalRejectionReasonResponse(
|
| 1145 |
-
status="success", data=[ProposalRejectionReason(**r) for r in rejection_reason]
|
| 1146 |
-
)
|
| 1147 |
-
except Exception as e:
|
| 1148 |
-
logger.exception(e)
|
| 1149 |
-
raise HTTPException(
|
| 1150 |
-
status_code=500, detail="Failed to retrieve rejection reason."
|
| 1151 |
-
)
|
| 1152 |
-
|
| 1153 |
-
async def create_proposal_rejections(
|
| 1154 |
-
self, rejection_reason: ProposalRejectionReasonRequest
|
| 1155 |
-
):
|
| 1156 |
-
async with self.__service() as service:
|
| 1157 |
-
try:
|
| 1158 |
-
created = await service.create_data(
|
| 1159 |
-
query_type=QueryType.REJECTION_REASONS,
|
| 1160 |
-
create_or_update_type=CreateOrUpdateType.ID,
|
| 1161 |
-
data=rejection_reason.model_dump(mode="json"),
|
| 1162 |
-
)
|
| 1163 |
-
return ProposalRejectionReasonResponse(
|
| 1164 |
-
status="success",
|
| 1165 |
-
data=[ProposalRejectionReason(**r) for r in created],
|
| 1166 |
-
)
|
| 1167 |
-
except Exception as e:
|
| 1168 |
-
logger.exception(e)
|
| 1169 |
-
raise HTTPException(
|
| 1170 |
-
status_code=500, detail="Failed to create rejection reason."
|
| 1171 |
-
)
|
| 1172 |
-
|
| 1173 |
-
async def create_all_proposal_rejections(
|
| 1174 |
-
self, rejection_reasons: CreateAllProposalRejectionReasonsRequest, proposal_id: UUID = Path(...), rfp_id: UUID = Path(...)
|
| 1175 |
-
):
|
| 1176 |
-
async with self.__service() as service:
|
| 1177 |
-
try:
|
| 1178 |
-
rejection_reasons_data = rejection_reasons.model_dump(mode="json", exclude_unset=True)
|
| 1179 |
-
rejection_reasons_data["proposal_id"] = proposal_id
|
| 1180 |
-
created = await service.create_data(
|
| 1181 |
-
query_type=QueryType.REJECTION_REASONS,
|
| 1182 |
-
create_or_update_type=CreateOrUpdateType.ALL,
|
| 1183 |
-
data=rejection_reasons_data,
|
| 1184 |
-
)
|
| 1185 |
-
return ProposalAllRejectionReasonsResponse(
|
| 1186 |
-
status="success",
|
| 1187 |
-
data=[r["reason"] for r in created],
|
| 1188 |
-
)
|
| 1189 |
-
except Exception as e:
|
| 1190 |
-
logger.exception(e)
|
| 1191 |
-
raise HTTPException(
|
| 1192 |
-
status_code=500, detail="Failed to create rejection reasons."
|
| 1193 |
-
)
|
| 1194 |
-
|
| 1195 |
-
async def update_proposal_rejections(
|
| 1196 |
-
self, rejection_reason: UpdateProposalRejectionReasonRequest, id: UUID = Path(...)
|
| 1197 |
-
):
|
| 1198 |
-
async with self.__service() as service:
|
| 1199 |
-
try:
|
| 1200 |
-
rejection_reason_data = rejection_reason.model_dump(mode="json", exclude_unset=True)
|
| 1201 |
-
rejection_reason_data["id"] = id
|
| 1202 |
-
updated = await service.update_data(
|
| 1203 |
-
query_type=QueryType.REJECTION_REASONS,
|
| 1204 |
-
create_or_update_type=CreateOrUpdateType.ID,
|
| 1205 |
-
data=rejection_reason_data,
|
| 1206 |
-
)
|
| 1207 |
-
return ProposalRejectionReasonResponse(
|
| 1208 |
-
status="success",
|
| 1209 |
-
data=[ProposalRejectionReason(**r) for r in updated],
|
| 1210 |
-
)
|
| 1211 |
-
except Exception as e:
|
| 1212 |
-
logger.exception(e)
|
| 1213 |
-
raise HTTPException(
|
| 1214 |
-
status_code=500, detail="Failed to update rejection reason."
|
| 1215 |
-
)
|
| 1216 |
-
|
| 1217 |
-
async def update_all_proposal_rejections(
|
| 1218 |
-
self, rejection_reasons: CreateAllProposalRejectionReasonsRequest, proposal_id: UUID = Path(...), rfp_id: UUID = Path(...)
|
| 1219 |
-
):
|
| 1220 |
-
async with self.__service() as service:
|
| 1221 |
-
try:
|
| 1222 |
-
rejection_reasons_data = rejection_reasons.model_dump(mode="json", exclude_unset=True)
|
| 1223 |
-
rejection_reasons_data["proposal_id"] = proposal_id
|
| 1224 |
-
updated = await service.update_data(
|
| 1225 |
-
query_type=QueryType.REJECTION_REASONS,
|
| 1226 |
-
create_or_update_type=CreateOrUpdateType.ALL,
|
| 1227 |
-
data=rejection_reasons_data,
|
| 1228 |
-
)
|
| 1229 |
-
return ProposalAllRejectionReasonsResponse(
|
| 1230 |
-
status="success",
|
| 1231 |
-
data=[r["reason"] for r in updated],
|
| 1232 |
-
)
|
| 1233 |
-
except Exception as e:
|
| 1234 |
-
logger.exception(e)
|
| 1235 |
-
raise HTTPException(
|
| 1236 |
-
status_code=500, detail="Failed to update rejection reasons."
|
| 1237 |
-
)
|
| 1238 |
-
|
| 1239 |
-
async def delete_proposal_rejections(
|
| 1240 |
-
self, id: UUID = Path(...)
|
| 1241 |
-
):
|
| 1242 |
-
async with self.__service() as service:
|
| 1243 |
-
try:
|
| 1244 |
-
result = await service.delete_data(
|
| 1245 |
-
query_type=QueryType.REJECTION_REASONS,
|
| 1246 |
-
id=id
|
| 1247 |
-
)
|
| 1248 |
-
return DeleteResponse(status="success")
|
| 1249 |
-
except Exception as e:
|
| 1250 |
-
logger.exception(e)
|
| 1251 |
-
raise HTTPException(
|
| 1252 |
-
status_code=500, detail="Failed to delete rejection reason."
|
| 1253 |
-
)
|
| 1254 |
-
|
| 1255 |
-
async def delete_all_proposal_rejections(
|
| 1256 |
-
self, proposal_id: UUID = Path(...), rfp_id: UUID = Path(...)
|
| 1257 |
-
):
|
| 1258 |
-
async with self.__service() as service:
|
| 1259 |
-
try:
|
| 1260 |
-
result = await service.delete_data(
|
| 1261 |
-
query_type=QueryType.REJECTION_REASONS,
|
| 1262 |
-
proposal_id=proposal_id
|
| 1263 |
-
)
|
| 1264 |
-
return DeleteResponse(status="success")
|
| 1265 |
-
except Exception as e:
|
| 1266 |
-
logger.exception(e)
|
| 1267 |
-
raise HTTPException(
|
| 1268 |
-
status_code=500, detail="Failed to delete rejection reasons."
|
| 1269 |
-
)
|
| 1270 |
-
|
| 1271 |
-
async def delete_proposal_rejections_by_proposal_and_category(
|
| 1272 |
-
self, proposal_id: UUID = Path(...), category: str = Path(...), rfp_id: UUID = Path(...)
|
| 1273 |
-
):
|
| 1274 |
-
async with self.__service() as service:
|
| 1275 |
-
try:
|
| 1276 |
-
result = await service.delete_data(
|
| 1277 |
-
query_type=QueryType.REJECTION_REASONS,
|
| 1278 |
-
category=category,
|
| 1279 |
-
proposal_id=proposal_id
|
| 1280 |
-
)
|
| 1281 |
-
return DeleteResponse(status="success")
|
| 1282 |
-
except Exception as e:
|
| 1283 |
-
logger.exception(e)
|
| 1284 |
-
raise HTTPException(
|
| 1285 |
-
status_code=500, detail="Failed to delete rejection reason."
|
| 1286 |
-
)
|
| 1287 |
-
|
| 1288 |
-
async def delete_proposal_rejections_by_proposal_and_id(
|
| 1289 |
-
self, proposal_id: UUID = Path(...), id: UUID = Path(...)
|
| 1290 |
-
):
|
| 1291 |
-
async with self.__service() as service:
|
| 1292 |
-
try:
|
| 1293 |
-
result = await service.delete_data(
|
| 1294 |
-
query_type=QueryType.REJECTION_REASONS,
|
| 1295 |
-
id=id,
|
| 1296 |
-
proposal_id=proposal_id
|
| 1297 |
-
)
|
| 1298 |
-
return DeleteResponse(status="success")
|
| 1299 |
-
except Exception as e:
|
| 1300 |
-
logger.exception(e)
|
| 1301 |
-
raise HTTPException(
|
| 1302 |
-
status_code=500, detail="Failed to delete rejection reason."
|
| 1303 |
-
)
|
| 1304 |
-
|
| 1305 |
-
async def get_proposal_improvements(
|
| 1306 |
-
self
|
| 1307 |
-
):
|
| 1308 |
-
async with self.__service() as service:
|
| 1309 |
-
try:
|
| 1310 |
-
improvements = await service.get_all_data(
|
| 1311 |
-
query_type=QueryType.IMPROVEMENTS
|
| 1312 |
-
)
|
| 1313 |
-
return ProposalImprovementResponse(
|
| 1314 |
-
status="success",
|
| 1315 |
-
data=[ProposalImprovement(**r) for r in improvements],
|
| 1316 |
-
)
|
| 1317 |
-
except Exception as e:
|
| 1318 |
-
logger.exception(e)
|
| 1319 |
-
raise HTTPException(
|
| 1320 |
-
status_code=500, detail="Failed to retrieve improvements."
|
| 1321 |
-
)
|
| 1322 |
-
|
| 1323 |
-
async def get_proposal_improvements_by_id(
|
| 1324 |
-
self, id: UUID = Path(...)
|
| 1325 |
-
):
|
| 1326 |
-
async with self.__service() as service:
|
| 1327 |
-
try:
|
| 1328 |
-
improvements = await service.get_all_data(
|
| 1329 |
-
query_type=QueryType.IMPROVEMENTS,
|
| 1330 |
-
id=id
|
| 1331 |
-
)
|
| 1332 |
-
return ProposalImprovementResponse(
|
| 1333 |
-
status="success",
|
| 1334 |
-
data=[ProposalImprovement(**r) for r in improvements],
|
| 1335 |
-
)
|
| 1336 |
-
except Exception as e:
|
| 1337 |
-
logger.exception(e)
|
| 1338 |
-
raise HTTPException(
|
| 1339 |
-
status_code=500, detail="Failed to retrieve improvements."
|
| 1340 |
-
)
|
| 1341 |
-
|
| 1342 |
-
async def get_proposal_improvements_by_proposal_id(
|
| 1343 |
-
self, proposal_id: UUID = Path(...), rfp_id: UUID = Path(...), category: Category = Query(None)
|
| 1344 |
-
):
|
| 1345 |
-
async with self.__service() as service:
|
| 1346 |
-
try:
|
| 1347 |
-
improvements = await service.get_all_data(
|
| 1348 |
-
query_type=QueryType.IMPROVEMENTS,
|
| 1349 |
-
proposal_id=proposal_id,
|
| 1350 |
-
category=category
|
| 1351 |
-
)
|
| 1352 |
-
return ProposalAllImprovementsResponse(
|
| 1353 |
-
status="success",
|
| 1354 |
-
data=[r["improvement_point"] for r in improvements],
|
| 1355 |
-
)
|
| 1356 |
-
except Exception as e:
|
| 1357 |
-
logger.exception(e)
|
| 1358 |
-
raise HTTPException(
|
| 1359 |
-
status_code=500, detail="Failed to retrieve improvements."
|
| 1360 |
-
)
|
| 1361 |
-
|
| 1362 |
-
async def get_proposal_improvements_by_proposal_and_id(
|
| 1363 |
-
self, proposal_id: UUID = Path(...), id: UUID = Path(...)
|
| 1364 |
-
):
|
| 1365 |
-
async with self.__service() as service:
|
| 1366 |
-
try:
|
| 1367 |
-
improvements = await service.get_all_data(
|
| 1368 |
-
query_type=QueryType.IMPROVEMENTS,
|
| 1369 |
-
id=id,
|
| 1370 |
-
proposal_id=proposal_id
|
| 1371 |
-
)
|
| 1372 |
-
return ProposalImprovementResponse(
|
| 1373 |
-
status="success",
|
| 1374 |
-
data=[ProposalImprovement(**r) for r in improvements],
|
| 1375 |
-
)
|
| 1376 |
-
except Exception as e:
|
| 1377 |
-
logger.exception(e)
|
| 1378 |
-
raise HTTPException(
|
| 1379 |
-
status_code=500, detail="Failed to retrieve improvements."
|
| 1380 |
-
)
|
| 1381 |
-
|
| 1382 |
-
async def create_proposal_improvements(
|
| 1383 |
-
self, improvement: ProposalImprovementRequest
|
| 1384 |
-
):
|
| 1385 |
-
async with self.__service() as service:
|
| 1386 |
-
try:
|
| 1387 |
-
created = await service.create_data(
|
| 1388 |
-
query_type=QueryType.IMPROVEMENTS,
|
| 1389 |
-
create_or_update_type=CreateOrUpdateType.ID,
|
| 1390 |
-
data=improvement.model_dump(mode="json"),
|
| 1391 |
-
)
|
| 1392 |
-
return ProposalImprovementResponse(
|
| 1393 |
-
status="success", data=[ProposalImprovement(**r) for r in created]
|
| 1394 |
-
)
|
| 1395 |
-
except Exception as e:
|
| 1396 |
-
logger.exception(e)
|
| 1397 |
-
raise HTTPException(
|
| 1398 |
-
status_code=500, detail="Failed to create improvement."
|
| 1399 |
-
)
|
| 1400 |
-
|
| 1401 |
-
async def create_all_proposal_improvements(
|
| 1402 |
-
self, improvements: CreateAllProposalImprovementsRequest, proposal_id: UUID = Path(...), rfp_id: UUID = Path(...)
|
| 1403 |
-
):
|
| 1404 |
-
async with self.__service() as service:
|
| 1405 |
-
try:
|
| 1406 |
-
data = improvements.model_dump(mode="json", exclude_unset=True)
|
| 1407 |
-
data["proposal_id"] = proposal_id
|
| 1408 |
-
created = await service.create_data(
|
| 1409 |
-
query_type=QueryType.IMPROVEMENTS,
|
| 1410 |
-
create_or_update_type=CreateOrUpdateType.ALL,
|
| 1411 |
-
data=data,
|
| 1412 |
-
)
|
| 1413 |
-
return ProposalAllImprovementsResponse(
|
| 1414 |
-
status="success", data=[r["improvement_point"] for r in created]
|
| 1415 |
-
)
|
| 1416 |
-
except Exception as e:
|
| 1417 |
-
logger.exception(e)
|
| 1418 |
-
raise HTTPException(
|
| 1419 |
-
status_code=500, detail="Failed to create improvements."
|
| 1420 |
-
)
|
| 1421 |
-
|
| 1422 |
-
async def update_proposal_improvements(
|
| 1423 |
-
self, improvement: UpdateProposalImprovementRequest, id: UUID = Path(...)
|
| 1424 |
-
):
|
| 1425 |
-
async with self.__service() as service:
|
| 1426 |
-
try:
|
| 1427 |
-
data = improvement.model_dump(mode="json", exclude_unset=True)
|
| 1428 |
-
data["id"] = id
|
| 1429 |
-
updated = await service.update_data(
|
| 1430 |
-
query_type=QueryType.IMPROVEMENTS,
|
| 1431 |
-
create_or_update_type=CreateOrUpdateType.ID,
|
| 1432 |
-
data=data,
|
| 1433 |
-
)
|
| 1434 |
-
return ProposalImprovementResponse(
|
| 1435 |
-
status="success", data=[ProposalImprovement(**r) for r in updated]
|
| 1436 |
-
)
|
| 1437 |
-
except Exception as e:
|
| 1438 |
-
logger.exception(e)
|
| 1439 |
-
raise HTTPException(
|
| 1440 |
-
status_code=500, detail="Failed to update improvement."
|
| 1441 |
-
)
|
| 1442 |
-
|
| 1443 |
-
async def update_all_proposal_improvements(
|
| 1444 |
-
self, improvements: CreateAllProposalImprovementsRequest, proposal_id: UUID = Path(...), rfp_id: UUID = Path(...)
|
| 1445 |
-
):
|
| 1446 |
-
async with self.__service() as service:
|
| 1447 |
-
try:
|
| 1448 |
-
data = improvements.model_dump(mode="json", exclude_unset=True)
|
| 1449 |
-
data["proposal_id"] = proposal_id
|
| 1450 |
-
updated = await service.update_data(
|
| 1451 |
-
query_type=QueryType.IMPROVEMENTS,
|
| 1452 |
-
create_or_update_type=CreateOrUpdateType.ALL,
|
| 1453 |
-
data=data,
|
| 1454 |
-
)
|
| 1455 |
-
return ProposalAllImprovementsResponse(
|
| 1456 |
-
status="success", data=[r["improvement_point"] for r in updated]
|
| 1457 |
-
)
|
| 1458 |
-
except Exception as e:
|
| 1459 |
-
logger.exception(e)
|
| 1460 |
-
raise HTTPException(
|
| 1461 |
-
status_code=500, detail="Failed to update improvements."
|
| 1462 |
-
)
|
| 1463 |
-
|
| 1464 |
-
async def delete_proposal_improvements(
|
| 1465 |
-
self, id: UUID = Path(...)
|
| 1466 |
-
):
|
| 1467 |
-
async with self.__service() as service:
|
| 1468 |
-
try:
|
| 1469 |
-
result = await service.delete_data(
|
| 1470 |
-
query_type=QueryType.IMPROVEMENTS, id=id
|
| 1471 |
-
)
|
| 1472 |
-
return DeleteResponse(status="success")
|
| 1473 |
-
except Exception as e:
|
| 1474 |
-
logger.exception(e)
|
| 1475 |
-
raise HTTPException(
|
| 1476 |
-
status_code=500, detail="Failed to delete improvement."
|
| 1477 |
-
)
|
| 1478 |
-
|
| 1479 |
-
async def delete_all_proposal_improvements(
|
| 1480 |
-
self, proposal_id: UUID = Path(...), rfp_id: UUID = Path(...)
|
| 1481 |
-
):
|
| 1482 |
-
async with self.__service() as service:
|
| 1483 |
-
try:
|
| 1484 |
-
result = await service.delete_data(
|
| 1485 |
-
query_type=QueryType.IMPROVEMENTS, proposal_id=proposal_id
|
| 1486 |
-
)
|
| 1487 |
-
return DeleteResponse(status="success")
|
| 1488 |
-
except Exception as e:
|
| 1489 |
-
logger.exception(e)
|
| 1490 |
-
raise HTTPException(
|
| 1491 |
-
status_code=500, detail="Failed to delete improvements."
|
| 1492 |
-
)
|
| 1493 |
-
|
| 1494 |
-
async def delete_proposal_improvements_by_proposal_and_category(
|
| 1495 |
-
self, category: str = Path(...), proposal_id: UUID = Path(...), rfp_id: UUID = Path(...)
|
| 1496 |
-
):
|
| 1497 |
-
async with self.__service() as service:
|
| 1498 |
-
try:
|
| 1499 |
-
result = await service.delete_data(
|
| 1500 |
-
query_type=QueryType.IMPROVEMENTS, category=category, proposal_id=proposal_id
|
| 1501 |
-
)
|
| 1502 |
-
return DeleteResponse(status="success")
|
| 1503 |
-
except Exception as e:
|
| 1504 |
-
logger.exception(e)
|
| 1505 |
-
raise HTTPException(
|
| 1506 |
-
status_code=500, detail="Failed to delete improvement."
|
| 1507 |
-
)
|
| 1508 |
-
|
| 1509 |
-
async def delete_proposal_improvements_by_proposal_and_id(
|
| 1510 |
-
self, id: UUID = Path(...), proposal_id: UUID = Path(...)
|
| 1511 |
-
):
|
| 1512 |
-
async with self.__service() as service:
|
| 1513 |
-
try:
|
| 1514 |
-
result = await service.delete_data(
|
| 1515 |
-
query_type=QueryType.IMPROVEMENTS, id=id, proposal_id=proposal_id
|
| 1516 |
-
)
|
| 1517 |
-
return DeleteResponse(status="success")
|
| 1518 |
-
except Exception as e:
|
| 1519 |
-
logger.exception(e)
|
| 1520 |
-
raise HTTPException(
|
| 1521 |
-
status_code=500, detail="Failed to delete improvement."
|
| 1522 |
-
)
|
| 1523 |
-
|
| 1524 |
-
async def get_proposal_ai_score(
|
| 1525 |
-
self
|
| 1526 |
-
):
|
| 1527 |
-
async with self.__service() as service:
|
| 1528 |
-
try:
|
| 1529 |
-
scores = await service.get_all_data(
|
| 1530 |
-
query_type=QueryType.AI_SCORE
|
| 1531 |
-
)
|
| 1532 |
-
return ProposalAIAnalysisResponse(
|
| 1533 |
-
status="success", data=[ProposalAIAnalysis(**r) for r in scores]
|
| 1534 |
-
)
|
| 1535 |
-
except Exception as e:
|
| 1536 |
-
logger.exception(e)
|
| 1537 |
-
raise HTTPException(
|
| 1538 |
-
status_code=500, detail="Failed to retrieve AI scores."
|
| 1539 |
-
)
|
| 1540 |
-
|
| 1541 |
-
async def get_proposal_ai_score_by_id(
|
| 1542 |
-
self, id: UUID = Path(...)
|
| 1543 |
-
):
|
| 1544 |
-
async with self.__service() as service:
|
| 1545 |
-
try:
|
| 1546 |
-
scores = await service.get_all_data(
|
| 1547 |
-
query_type=QueryType.AI_SCORE, id=id
|
| 1548 |
-
)
|
| 1549 |
-
return ProposalAIAnalysisResponse(
|
| 1550 |
-
status="success", data=[ProposalAIAnalysis(**r) for r in scores]
|
| 1551 |
-
)
|
| 1552 |
-
except Exception as e:
|
| 1553 |
-
logger.exception(e)
|
| 1554 |
-
raise HTTPException(
|
| 1555 |
-
status_code=500, detail="Failed to retrieve AI scores."
|
| 1556 |
-
)
|
| 1557 |
-
|
| 1558 |
-
async def get_proposal_ai_score_by_proposal_id(
|
| 1559 |
-
self, proposal_id: UUID = Path(...), rfp_id: UUID = Path(...)
|
| 1560 |
-
):
|
| 1561 |
-
async with self.__service() as service:
|
| 1562 |
-
try:
|
| 1563 |
-
scores = await service.get_all_data(
|
| 1564 |
-
query_type=QueryType.AI_SCORE, proposal_id=proposal_id
|
| 1565 |
-
)
|
| 1566 |
-
return ProposalAIAnalysisResponse(
|
| 1567 |
-
status="success", data=[ProposalAIAnalysis(**r) for r in scores]
|
| 1568 |
-
)
|
| 1569 |
-
except Exception as e:
|
| 1570 |
-
logger.exception(e)
|
| 1571 |
-
raise HTTPException(
|
| 1572 |
-
status_code=500, detail="Failed to retrieve AI scores."
|
| 1573 |
-
)
|
| 1574 |
-
|
| 1575 |
-
async def get_proposal_ai_score_by_proposal_and_id(
|
| 1576 |
-
self, id: UUID = Path(...), proposal_id: UUID = Path(...)
|
| 1577 |
-
):
|
| 1578 |
-
async with self.__service() as service:
|
| 1579 |
-
try:
|
| 1580 |
-
scores = await service.get_all_data(
|
| 1581 |
-
query_type=QueryType.AI_SCORE, id=id, proposal_id=proposal_id
|
| 1582 |
-
)
|
| 1583 |
-
return ProposalAIAnalysisResponse(
|
| 1584 |
-
status="success", data=[ProposalAIAnalysis(**r) for r in scores]
|
| 1585 |
-
)
|
| 1586 |
-
except Exception as e:
|
| 1587 |
-
logger.exception(e)
|
| 1588 |
-
raise HTTPException(
|
| 1589 |
-
status_code=500, detail="Failed to retrieve AI scores."
|
| 1590 |
-
)
|
| 1591 |
-
|
| 1592 |
-
async def create_proposal_ai_score(self, score: CreateAiAnalysisRequest):
|
| 1593 |
-
async with self.__service() as service:
|
| 1594 |
-
try:
|
| 1595 |
-
created = await service.create_data(
|
| 1596 |
-
query_type=QueryType.AI_SCORE,
|
| 1597 |
-
create_or_update_type=CreateOrUpdateType.ID,
|
| 1598 |
-
data=score.model_dump(mode="json"),
|
| 1599 |
-
)
|
| 1600 |
-
return ProposalAIAnalysisResponse(
|
| 1601 |
-
status="success", data=[ProposalAIAnalysis(**r) for r in created]
|
| 1602 |
-
)
|
| 1603 |
-
except Exception as e:
|
| 1604 |
-
logger.exception(e)
|
| 1605 |
-
raise HTTPException(
|
| 1606 |
-
status_code=500, detail="Failed to create AI score."
|
| 1607 |
-
)
|
| 1608 |
-
|
| 1609 |
-
async def create_proposal_ai_score_by_proposal_id(self, score: CreateAIAnalysisRequestByProposalId, proposal_id: UUID = Path(...), rfp_id: UUID = Path(...)):
|
| 1610 |
-
async with self.__service() as service:
|
| 1611 |
-
try:
|
| 1612 |
-
data = score.model_dump(mode="json", exclude_unset=True)
|
| 1613 |
-
data["proposal_id"] = proposal_id
|
| 1614 |
-
created = await service.create_data(
|
| 1615 |
-
query_type=QueryType.AI_SCORE,
|
| 1616 |
-
create_or_update_type=CreateOrUpdateType.ID,
|
| 1617 |
-
data=data,
|
| 1618 |
-
)
|
| 1619 |
-
return ProposalAIAnalysisResponse(
|
| 1620 |
-
status="success", data=[ProposalAIAnalysis(**r) for r in created]
|
| 1621 |
-
)
|
| 1622 |
-
except Exception as e:
|
| 1623 |
-
logger.exception(e)
|
| 1624 |
-
raise HTTPException(
|
| 1625 |
-
status_code=500, detail="Failed to create AI score."
|
| 1626 |
-
)
|
| 1627 |
-
|
| 1628 |
-
async def update_proposal_ai_score(self, score: UpdateAiAnalysisRequest, id: UUID = Path(...)):
|
| 1629 |
-
async with self.__service() as service:
|
| 1630 |
-
try:
|
| 1631 |
-
if id:
|
| 1632 |
-
data = score.model_dump(mode="json", exclude_unset=True)
|
| 1633 |
-
data["id"] = id
|
| 1634 |
-
updated = await service.update_data(
|
| 1635 |
-
query_type=QueryType.AI_SCORE,
|
| 1636 |
-
create_or_update_type=CreateOrUpdateType.ID,
|
| 1637 |
-
data=data,
|
| 1638 |
-
)
|
| 1639 |
-
return ProposalAIAnalysisResponse(
|
| 1640 |
-
status="success", data=[ProposalAIAnalysis(**r) for r in updated]
|
| 1641 |
-
)
|
| 1642 |
-
except Exception as e:
|
| 1643 |
-
logger.exception(e)
|
| 1644 |
-
raise HTTPException(
|
| 1645 |
-
status_code=500, detail="Failed to update AI score."
|
| 1646 |
-
)
|
| 1647 |
-
|
| 1648 |
-
async def update_proposal_ai_score_by_proposal_id(self, score: UpdateAIAnalysisRequestByProposalId, proposal_id: UUID = Path(...), rfp_id: UUID = Path(...)):
|
| 1649 |
-
async with self.__service() as service:
|
| 1650 |
-
try:
|
| 1651 |
-
data = score.model_dump(mode="json", exclude_unset=True)
|
| 1652 |
-
data["proposal_id"] = proposal_id
|
| 1653 |
-
updated = await service.update_data(
|
| 1654 |
-
query_type=QueryType.AI_SCORE,
|
| 1655 |
-
create_or_update_type=CreateOrUpdateType.ALL,
|
| 1656 |
-
data=data,
|
| 1657 |
-
)
|
| 1658 |
-
return ProposalAIAnalysisResponse(
|
| 1659 |
-
status="success", data=[ProposalAIAnalysis(**r) for r in updated]
|
| 1660 |
-
)
|
| 1661 |
-
except Exception as e:
|
| 1662 |
-
logger.exception(e)
|
| 1663 |
-
raise HTTPException(
|
| 1664 |
-
status_code=500, detail="Failed to update AI score."
|
| 1665 |
-
)
|
| 1666 |
-
|
| 1667 |
-
async def delete_proposal_ai_score(
|
| 1668 |
-
self, id: UUID = Path(...)
|
| 1669 |
-
):
|
| 1670 |
-
async with self.__service() as service:
|
| 1671 |
-
try:
|
| 1672 |
-
result = await service.delete_data(
|
| 1673 |
-
query_type=QueryType.AI_SCORE, id=id
|
| 1674 |
-
)
|
| 1675 |
-
return DeleteResponse(status="success")
|
| 1676 |
-
except Exception as e:
|
| 1677 |
-
logger.exception(e)
|
| 1678 |
-
raise HTTPException(
|
| 1679 |
-
status_code=500, detail="Failed to delete AI score."
|
| 1680 |
-
)
|
| 1681 |
-
|
| 1682 |
-
async def delete_proposal_ai_score_by_proposal_id(
|
| 1683 |
-
self, proposal_id: UUID = Path(...), rfp_id: UUID = Path(...)
|
| 1684 |
-
):
|
| 1685 |
-
async with self.__service() as service:
|
| 1686 |
-
try:
|
| 1687 |
-
result = await service.delete_data(
|
| 1688 |
-
query_type=QueryType.AI_SCORE, proposal_id=proposal_id
|
| 1689 |
-
)
|
| 1690 |
-
return DeleteResponse(status="success")
|
| 1691 |
-
except Exception as e:
|
| 1692 |
-
logger.exception(e)
|
| 1693 |
-
raise HTTPException(
|
| 1694 |
-
status_code=500, detail="Failed to delete AI score."
|
| 1695 |
-
)
|
| 1696 |
-
|
| 1697 |
-
async def delete_proposal_ai_score_by_proposal_and_id(
|
| 1698 |
-
self, id: UUID = Path(...), proposal_id: UUID = Path(...)
|
| 1699 |
-
):
|
| 1700 |
-
async with self.__service() as service:
|
| 1701 |
-
try:
|
| 1702 |
-
result = await service.delete_data(
|
| 1703 |
-
query_type=QueryType.AI_SCORE, id=id, proposal_id=proposal_id
|
| 1704 |
-
)
|
| 1705 |
-
return DeleteResponse(status="success")
|
| 1706 |
-
except Exception as e:
|
| 1707 |
-
logger.exception(e)
|
| 1708 |
-
raise HTTPException(
|
| 1709 |
-
status_code=500, detail="Failed to delete AI score."
|
| 1710 |
-
)
|
| 1711 |
-
|
| 1712 |
-
async def get_all_proposal_analysis(self, proposal_id: UUID = Path(...), rfp_id: UUID = Path(...)):
|
| 1713 |
-
async with self.__service() as service:
|
| 1714 |
-
try:
|
| 1715 |
-
analysis = await service.get_all_data(
|
| 1716 |
-
query_type=QueryType.ALL, proposal_id=proposal_id
|
| 1717 |
-
)
|
| 1718 |
-
return ProposalCompleteAnalysisResponse(
|
| 1719 |
-
status="success",
|
| 1720 |
-
data=[ProposalCompleteAnalysis(**analysis)],
|
| 1721 |
-
)
|
| 1722 |
-
except Exception as e:
|
| 1723 |
-
logger.exception(e)
|
| 1724 |
-
raise HTTPException(
|
| 1725 |
-
status_code=500, detail="Failed to retrieve analysis."
|
| 1726 |
-
)
|
| 1727 |
-
|
| 1728 |
-
async def create_proposal_analysis(
|
| 1729 |
-
self, analysis: CreateAllProposalAnalysisRequest, proposal_id: UUID = Path(...), rfp_id: UUID = Path(...)
|
| 1730 |
-
):
|
| 1731 |
-
async with self.__service() as service:
|
| 1732 |
-
try:
|
| 1733 |
-
data = analysis.model_dump(mode="json", exclude_unset=True)
|
| 1734 |
-
data["proposal_id"] = proposal_id
|
| 1735 |
-
created = await service.create_data(
|
| 1736 |
-
query_type=QueryType.ALL,
|
| 1737 |
-
create_or_update_type=CreateOrUpdateType.ALL,
|
| 1738 |
-
data=data,
|
| 1739 |
-
)
|
| 1740 |
-
return ProposalCompleteAnalysisResponse(
|
| 1741 |
-
status="success",
|
| 1742 |
-
data=[ProposalCompleteAnalysis(**created)],
|
| 1743 |
-
)
|
| 1744 |
-
except Exception as e:
|
| 1745 |
-
logger.exception(e)
|
| 1746 |
-
raise HTTPException(
|
| 1747 |
-
status_code=500, detail="Failed to create analysis."
|
| 1748 |
-
)
|
| 1749 |
-
|
| 1750 |
-
async def update_proposal_analysis(
|
| 1751 |
-
self, analysis: UpdateAllProposalAnalysisRequest, proposal_id: UUID = Path(...), rfp_id: UUID = Path(...)
|
| 1752 |
-
):
|
| 1753 |
-
async with self.__service() as service:
|
| 1754 |
-
try:
|
| 1755 |
-
data = analysis.model_dump(mode="json", exclude_unset=True)
|
| 1756 |
-
data["proposal_id"] = proposal_id
|
| 1757 |
-
updated = await service.update_data(
|
| 1758 |
-
query_type=QueryType.ALL,
|
| 1759 |
-
create_or_update_type=CreateOrUpdateType.ALL,
|
| 1760 |
-
data=data,
|
| 1761 |
-
)
|
| 1762 |
-
return ProposalCompleteAnalysisResponse(
|
| 1763 |
-
status="success",
|
| 1764 |
-
data=[ProposalCompleteAnalysis(**updated)],
|
| 1765 |
-
)
|
| 1766 |
-
except Exception as e:
|
| 1767 |
-
logger.exception(e)
|
| 1768 |
-
raise HTTPException(
|
| 1769 |
-
status_code=500, detail="Failed to update analysis."
|
| 1770 |
-
)
|
| 1771 |
-
|
| 1772 |
-
async def delete_proposal_analysis(self, proposal_id: UUID = Path(...), rfp_id: UUID = Path(...)):
|
| 1773 |
-
async with self.__service() as service:
|
| 1774 |
-
try:
|
| 1775 |
-
result = await service.delete_data(
|
| 1776 |
-
query_type=QueryType.ALL, proposal_id=proposal_id
|
| 1777 |
-
)
|
| 1778 |
-
return DeleteResponse(status="success")
|
| 1779 |
-
except Exception as e:
|
| 1780 |
-
logger.exception(e)
|
| 1781 |
-
raise HTTPException(
|
| 1782 |
-
status_code=500, detail="Failed to delete analysis."
|
| 1783 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/controllers/_proposal_controller.py
DELETED
|
@@ -1,338 +0,0 @@
|
|
| 1 |
-
from fastapi import APIRouter, HTTPException, Query, Path
|
| 2 |
-
from pydantic import BaseModel, field_validator
|
| 3 |
-
from typing import List, Optional
|
| 4 |
-
from uuid import UUID
|
| 5 |
-
from datetime import datetime
|
| 6 |
-
|
| 7 |
-
from src.config import logger
|
| 8 |
-
from src.services import ProposalService
|
| 9 |
-
from src.models import ProposalStatus, GateCriteria
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
class Proposal(BaseModel):
|
| 13 |
-
id: UUID
|
| 14 |
-
rfp_id: UUID
|
| 15 |
-
name: str
|
| 16 |
-
tep: str
|
| 17 |
-
gate_criteria: GateCriteria
|
| 18 |
-
status: ProposalStatus
|
| 19 |
-
ai_score: Optional[float] = None
|
| 20 |
-
final_score: Optional[float] = None
|
| 21 |
-
created_at: datetime
|
| 22 |
-
updated_at: datetime
|
| 23 |
-
|
| 24 |
-
@field_validator("ai_score", "final_score")
|
| 25 |
-
@classmethod
|
| 26 |
-
def round_to_two_decimals(cls, v):
|
| 27 |
-
if v is not None:
|
| 28 |
-
return round(v, 2)
|
| 29 |
-
return v
|
| 30 |
-
|
| 31 |
-
class ProposalRequest(BaseModel):
|
| 32 |
-
rfp_id: UUID
|
| 33 |
-
name: str
|
| 34 |
-
tep: str
|
| 35 |
-
gate_criteria: GateCriteria
|
| 36 |
-
status: ProposalStatus
|
| 37 |
-
ai_score: Optional[float] = None
|
| 38 |
-
final_score: Optional[float] = None
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
class CreateProposalRequest(BaseModel):
|
| 42 |
-
name: str
|
| 43 |
-
tep: str
|
| 44 |
-
gate_criteria: GateCriteria
|
| 45 |
-
status: ProposalStatus
|
| 46 |
-
ai_score: Optional[float] = None
|
| 47 |
-
final_score: Optional[float] = None
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
class ProposalUpdateRequest(BaseModel):
|
| 51 |
-
rfp_id: Optional[UUID] = None
|
| 52 |
-
name: Optional[str] = None
|
| 53 |
-
tep: Optional[str] = None
|
| 54 |
-
gate_criteria: Optional[GateCriteria] = None
|
| 55 |
-
status: Optional[ProposalStatus] = None
|
| 56 |
-
ai_score: Optional[float] = None
|
| 57 |
-
final_score: Optional[float] = None
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
class UpdateProposalRequest(BaseModel):
|
| 61 |
-
name: Optional[str] = None
|
| 62 |
-
tep: Optional[str] = None
|
| 63 |
-
gate_criteria: Optional[GateCriteria] = None
|
| 64 |
-
status: Optional[ProposalStatus] = None
|
| 65 |
-
ai_score: Optional[float] = None
|
| 66 |
-
final_score: Optional[float] = None
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
class ProposalDeleteResponse(BaseModel):
|
| 70 |
-
status: str
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
class ResponseProposal(BaseModel):
|
| 74 |
-
status: str
|
| 75 |
-
data: Optional[List[Proposal]]
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
class ProposalController:
|
| 79 |
-
def __init__(self):
|
| 80 |
-
self.__proposal_service = ProposalService
|
| 81 |
-
self.router = APIRouter()
|
| 82 |
-
# self.router.add_api_route(
|
| 83 |
-
# "/proposals",
|
| 84 |
-
# self.get_proposals,
|
| 85 |
-
# methods=["GET"],
|
| 86 |
-
# response_model=ResponseProposal,
|
| 87 |
-
# tags=["Proposals"],
|
| 88 |
-
# )
|
| 89 |
-
# self.router.add_api_route(
|
| 90 |
-
# "/proposals/{proposal_id}",
|
| 91 |
-
# self.get_proposals_by_id,
|
| 92 |
-
# methods=["GET"],
|
| 93 |
-
# response_model=ResponseProposal,
|
| 94 |
-
# tags=["Proposals by ID"],
|
| 95 |
-
# )
|
| 96 |
-
self.router.add_api_route(
|
| 97 |
-
"/rfps/{rfp_id}/proposals",
|
| 98 |
-
self.get_proposals_by_rfp_id,
|
| 99 |
-
methods=["GET"],
|
| 100 |
-
response_model=ResponseProposal,
|
| 101 |
-
tags=["Proposals by RFP ID"],
|
| 102 |
-
)
|
| 103 |
-
self.router.add_api_route(
|
| 104 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}",
|
| 105 |
-
self.get_proposals_by_proposal_and_rfp_id,
|
| 106 |
-
methods=["GET"],
|
| 107 |
-
response_model=ResponseProposal,
|
| 108 |
-
tags=["Proposals by Proposal and RFP ID"],
|
| 109 |
-
)
|
| 110 |
-
# self.router.add_api_route(
|
| 111 |
-
# "/proposals",
|
| 112 |
-
# self.create_proposal,
|
| 113 |
-
# methods=["POST"],
|
| 114 |
-
# response_model=ResponseProposal,
|
| 115 |
-
# tags=["Proposals"],
|
| 116 |
-
# )
|
| 117 |
-
self.router.add_api_route(
|
| 118 |
-
"/rfps/{rfp_id}/proposals",
|
| 119 |
-
self.create_proposal_by_rfp_id,
|
| 120 |
-
methods=["POST"],
|
| 121 |
-
response_model=ResponseProposal,
|
| 122 |
-
tags=["Proposals by RFP ID"],
|
| 123 |
-
)
|
| 124 |
-
# self.router.add_api_route(
|
| 125 |
-
# "/proposals/{proposal_id}",
|
| 126 |
-
# self.update_proposal,
|
| 127 |
-
# methods=["PUT"],
|
| 128 |
-
# response_model=ResponseProposal,
|
| 129 |
-
# tags=["Proposals by ID"],
|
| 130 |
-
# )
|
| 131 |
-
self.router.add_api_route(
|
| 132 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}",
|
| 133 |
-
self.update_proposal_by_proposal_and_rfp_id,
|
| 134 |
-
methods=["PUT"],
|
| 135 |
-
response_model=ResponseProposal,
|
| 136 |
-
tags=["Proposals by Proposal and RFP ID"],
|
| 137 |
-
)
|
| 138 |
-
# self.router.add_api_route(
|
| 139 |
-
# "/proposals/{proposal_id}",
|
| 140 |
-
# self.delete_proposal,
|
| 141 |
-
# methods=["DELETE"],
|
| 142 |
-
# response_model=ProposalDeleteResponse,
|
| 143 |
-
# tags=["Proposals by ID"],
|
| 144 |
-
# )
|
| 145 |
-
self.router.add_api_route(
|
| 146 |
-
"/rfps/{rfp_id}/proposals",
|
| 147 |
-
self.delete_proposals_by_rfp_id,
|
| 148 |
-
methods=["DeLETE"],
|
| 149 |
-
response_model=ProposalDeleteResponse,
|
| 150 |
-
tags=["Proposals by RFP ID"],
|
| 151 |
-
)
|
| 152 |
-
self.router.add_api_route(
|
| 153 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}",
|
| 154 |
-
self.delete_proposals_by_proposal_and_rfp_id,
|
| 155 |
-
methods=["DeLETE"],
|
| 156 |
-
response_model=ProposalDeleteResponse,
|
| 157 |
-
tags=["Proposals by Proposal and RFP ID"],
|
| 158 |
-
)
|
| 159 |
-
|
| 160 |
-
async def get_proposals(self):
|
| 161 |
-
async with self.__proposal_service() as service:
|
| 162 |
-
try:
|
| 163 |
-
proposals = await service.get_proposals()
|
| 164 |
-
return ResponseProposal(
|
| 165 |
-
status="success", data=[Proposal(**p) for p in proposals]
|
| 166 |
-
)
|
| 167 |
-
except Exception as e:
|
| 168 |
-
logger.exception(e)
|
| 169 |
-
raise HTTPException(
|
| 170 |
-
status_code=500, detail="Failed to retrieve proposals."
|
| 171 |
-
)
|
| 172 |
-
|
| 173 |
-
async def get_proposals_by_id(
|
| 174 |
-
self,
|
| 175 |
-
proposal_id: str = Path(...),
|
| 176 |
-
):
|
| 177 |
-
async with self.__proposal_service() as service:
|
| 178 |
-
try:
|
| 179 |
-
proposals = await service.get_proposals(proposal_id=proposal_id)
|
| 180 |
-
return ResponseProposal(
|
| 181 |
-
status="success", data=[Proposal(**p) for p in proposals]
|
| 182 |
-
)
|
| 183 |
-
except Exception as e:
|
| 184 |
-
logger.exception(e)
|
| 185 |
-
raise HTTPException(
|
| 186 |
-
status_code=500, detail="Failed to retrieve proposals."
|
| 187 |
-
)
|
| 188 |
-
|
| 189 |
-
async def get_proposals_by_rfp_id(
|
| 190 |
-
self,
|
| 191 |
-
rfp_id: str = Path(...),
|
| 192 |
-
):
|
| 193 |
-
async with self.__proposal_service() as service:
|
| 194 |
-
try:
|
| 195 |
-
proposals = await service.get_proposals(rfp_id=rfp_id)
|
| 196 |
-
return ResponseProposal(
|
| 197 |
-
status="success", data=[Proposal(**p) for p in proposals]
|
| 198 |
-
)
|
| 199 |
-
except Exception as e:
|
| 200 |
-
logger.exception(e)
|
| 201 |
-
raise HTTPException(
|
| 202 |
-
status_code=500, detail="Failed to retrieve proposals."
|
| 203 |
-
)
|
| 204 |
-
|
| 205 |
-
async def get_proposals_by_proposal_and_rfp_id(
|
| 206 |
-
self,
|
| 207 |
-
rfp_id: str = Path(...),
|
| 208 |
-
proposal_id: str = Path(...),
|
| 209 |
-
):
|
| 210 |
-
async with self.__proposal_service() as service:
|
| 211 |
-
try:
|
| 212 |
-
proposals = await service.get_proposals(
|
| 213 |
-
rfp_id=rfp_id, proposal_id=proposal_id
|
| 214 |
-
)
|
| 215 |
-
return ResponseProposal(
|
| 216 |
-
status="success", data=[Proposal(**p) for p in proposals]
|
| 217 |
-
)
|
| 218 |
-
except Exception as e:
|
| 219 |
-
logger.exception(e)
|
| 220 |
-
raise HTTPException(
|
| 221 |
-
status_code=500, detail="Failed to retrieve proposals."
|
| 222 |
-
)
|
| 223 |
-
|
| 224 |
-
async def create_proposal(self, proposal: ProposalRequest):
|
| 225 |
-
async with self.__proposal_service() as service:
|
| 226 |
-
try:
|
| 227 |
-
new_proposal = await service.create_proposal(
|
| 228 |
-
proposal.model_dump(mode="json")
|
| 229 |
-
)
|
| 230 |
-
return ResponseProposal(
|
| 231 |
-
status="success", data=[Proposal(**new_proposal)]
|
| 232 |
-
)
|
| 233 |
-
except Exception as e:
|
| 234 |
-
logger.exception(e)
|
| 235 |
-
raise HTTPException(
|
| 236 |
-
status_code=500, detail="Failed to create proposal."
|
| 237 |
-
)
|
| 238 |
-
|
| 239 |
-
async def create_proposal_by_rfp_id(
|
| 240 |
-
self, proposal: CreateProposalRequest, rfp_id: str = Path(...)
|
| 241 |
-
):
|
| 242 |
-
async with self.__proposal_service() as service:
|
| 243 |
-
try:
|
| 244 |
-
data = proposal.model_dump(exclude_unset=True, mode="json")
|
| 245 |
-
data["rfp_id"] = rfp_id
|
| 246 |
-
new_proposal = await service.create_proposal(proposal=data)
|
| 247 |
-
return ResponseProposal(
|
| 248 |
-
status="success", data=[Proposal(**new_proposal)]
|
| 249 |
-
)
|
| 250 |
-
except Exception as e:
|
| 251 |
-
logger.exception(e)
|
| 252 |
-
raise HTTPException(
|
| 253 |
-
status_code=500, detail="Failed to create proposal."
|
| 254 |
-
)
|
| 255 |
-
|
| 256 |
-
async def update_proposal(
|
| 257 |
-
self, proposal: ProposalUpdateRequest, proposal_id: str = Path(...)
|
| 258 |
-
):
|
| 259 |
-
async with self.__proposal_service() as service:
|
| 260 |
-
try:
|
| 261 |
-
updated_proposal = await service.update_proposal(
|
| 262 |
-
proposal_id=proposal_id,
|
| 263 |
-
proposal=proposal.model_dump(exclude_unset=True),
|
| 264 |
-
)
|
| 265 |
-
return ResponseProposal(
|
| 266 |
-
status="success", data=[Proposal(**updated_proposal)]
|
| 267 |
-
)
|
| 268 |
-
except Exception as e:
|
| 269 |
-
logger.exception(e)
|
| 270 |
-
raise HTTPException(
|
| 271 |
-
status_code=500, detail="Failed to update proposal."
|
| 272 |
-
)
|
| 273 |
-
|
| 274 |
-
async def update_proposal_by_proposal_and_rfp_id(
|
| 275 |
-
self,
|
| 276 |
-
proposal: UpdateProposalRequest,
|
| 277 |
-
rfp_id: str = Path(...),
|
| 278 |
-
proposal_id: str = Path(...),
|
| 279 |
-
):
|
| 280 |
-
async with self.__proposal_service() as service:
|
| 281 |
-
try:
|
| 282 |
-
data = proposal.model_dump(exclude_unset=True, mode="json")
|
| 283 |
-
data["rfp_id"] = rfp_id
|
| 284 |
-
updated_proposal = await service.update_proposal(
|
| 285 |
-
proposal_id=proposal_id,
|
| 286 |
-
proposal=proposal.model_dump(exclude_unset=True),
|
| 287 |
-
)
|
| 288 |
-
return ResponseProposal(
|
| 289 |
-
status="success", data=[Proposal(**updated_proposal)]
|
| 290 |
-
)
|
| 291 |
-
except Exception as e:
|
| 292 |
-
logger.exception(e)
|
| 293 |
-
raise HTTPException(
|
| 294 |
-
status_code=500, detail="Failed to update proposal."
|
| 295 |
-
)
|
| 296 |
-
|
| 297 |
-
async def delete_proposal(self, proposal_id: str = Path(...)):
|
| 298 |
-
async with self.__proposal_service() as service:
|
| 299 |
-
try:
|
| 300 |
-
status = await service.delete_proposal(proposal_id=proposal_id)
|
| 301 |
-
if not status:
|
| 302 |
-
raise HTTPException(status_code=404, detail="Proposal not found.")
|
| 303 |
-
return ProposalDeleteResponse(status="success")
|
| 304 |
-
except Exception as e:
|
| 305 |
-
logger.exception(e)
|
| 306 |
-
raise HTTPException(
|
| 307 |
-
status_code=500, detail="Failed to delete proposal."
|
| 308 |
-
)
|
| 309 |
-
|
| 310 |
-
async def delete_proposals_by_rfp_id(self, rfp_id: str = Path(...)):
|
| 311 |
-
async with self.__proposal_service() as service:
|
| 312 |
-
try:
|
| 313 |
-
status = await service.delete_proposal(rfp_id=rfp_id)
|
| 314 |
-
if not status:
|
| 315 |
-
raise HTTPException(status_code=404, detail="Proposal not found.")
|
| 316 |
-
return ProposalDeleteResponse(status="success")
|
| 317 |
-
except Exception as e:
|
| 318 |
-
logger.exception(e)
|
| 319 |
-
raise HTTPException(
|
| 320 |
-
status_code=500, detail="Failed to delete proposal."
|
| 321 |
-
)
|
| 322 |
-
|
| 323 |
-
async def delete_proposals_by_proposal_and_rfp_id(
|
| 324 |
-
self, rfp_id: str = Path(...), proposal_id: str = Path(...)
|
| 325 |
-
):
|
| 326 |
-
async with self.__proposal_service() as service:
|
| 327 |
-
try:
|
| 328 |
-
status = await service.delete_proposal(
|
| 329 |
-
rfp_id=rfp_id, proposal_id=proposal_id
|
| 330 |
-
)
|
| 331 |
-
if not status:
|
| 332 |
-
raise HTTPException(status_code=404, detail="Proposal not found.")
|
| 333 |
-
return ProposalDeleteResponse(status="success")
|
| 334 |
-
except Exception as e:
|
| 335 |
-
logger.exception(e)
|
| 336 |
-
raise HTTPException(
|
| 337 |
-
status_code=500, detail="Failed to delete proposal."
|
| 338 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/controllers/_proposal_detailed_analysis_controller.py
DELETED
|
@@ -1,291 +0,0 @@
|
|
| 1 |
-
from fastapi import APIRouter, HTTPException, Query, Path
|
| 2 |
-
from pydantic import BaseModel
|
| 3 |
-
from typing import List, Optional
|
| 4 |
-
from uuid import UUID
|
| 5 |
-
from datetime import datetime
|
| 6 |
-
|
| 7 |
-
from src.config import logger
|
| 8 |
-
|
| 9 |
-
from src.models import OperationCriteria, ProposalDetailAnalysis
|
| 10 |
-
|
| 11 |
-
from src.services import ProposalDetailedAnalysisService
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
class ProposalDetailedAnalysis(BaseModel):
|
| 15 |
-
id: UUID
|
| 16 |
-
proposal_id: UUID
|
| 17 |
-
ai_assessment_result: str
|
| 18 |
-
confidence: float
|
| 19 |
-
operation_criteria: OperationCriteria
|
| 20 |
-
created_at: datetime
|
| 21 |
-
updated_at: datetime
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
class ProposalDetailedAnalysisResponse(BaseModel):
|
| 25 |
-
status: str
|
| 26 |
-
data: Optional[List[ProposalDetailedAnalysis]]
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
class OperationCriteriaResult(BaseModel):
|
| 30 |
-
confidence: float
|
| 31 |
-
ai_assessment_result: str
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
class ProposalCompleteDetailedAnalysis(BaseModel):
|
| 35 |
-
mission_alignment: Optional[OperationCriteriaResult] = None
|
| 36 |
-
technical_feasibility: Optional[OperationCriteriaResult] = None
|
| 37 |
-
cost_efficiency: Optional[OperationCriteriaResult] = None
|
| 38 |
-
risk_assessment: Optional[OperationCriteriaResult] = None
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
class CreateProposalDetailedAnalysisRequest(BaseModel):
|
| 42 |
-
data: ProposalCompleteDetailedAnalysis
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
class CompleteAnalysisResponse(BaseModel):
|
| 46 |
-
status: str
|
| 47 |
-
data: Optional[ProposalCompleteDetailedAnalysis]
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
class DeleteResponse(BaseModel):
|
| 51 |
-
status: str
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
class CreateDetailedAnalysisRequest(BaseModel):
|
| 55 |
-
proposal_id: UUID
|
| 56 |
-
confidence: float
|
| 57 |
-
ai_assessment_result: str
|
| 58 |
-
operation_criteria: OperationCriteria
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
class UpdateDetailedAnalysisRequest(BaseModel):
|
| 62 |
-
proposal_id: Optional[UUID] = None
|
| 63 |
-
confidence: Optional[float] = None
|
| 64 |
-
ai_assessment_result: Optional[str] = None
|
| 65 |
-
operation_criteria: Optional[OperationCriteria] = None
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
class ProposalDetailedController:
|
| 69 |
-
def __init__(self):
|
| 70 |
-
self.service = ProposalDetailedAnalysisService
|
| 71 |
-
self.router = APIRouter()
|
| 72 |
-
self.router.add_api_route(
|
| 73 |
-
"/proposal-detailed-analysis",
|
| 74 |
-
self.get_proposal_analysis,
|
| 75 |
-
methods=["GET"],
|
| 76 |
-
tags=["Proposal Detailed Analysis"],
|
| 77 |
-
response_model=ProposalDetailedAnalysisResponse,
|
| 78 |
-
)
|
| 79 |
-
self.router.add_api_route(
|
| 80 |
-
"/proposal-detailed-analysis/{id}",
|
| 81 |
-
self.get_proposal_analysis_by_id,
|
| 82 |
-
methods=["GET"],
|
| 83 |
-
tags=["Proposal Detailed Analysis by ID"],
|
| 84 |
-
response_model=ProposalDetailedAnalysisResponse,
|
| 85 |
-
)
|
| 86 |
-
self.router.add_api_route(
|
| 87 |
-
"/proposals/{proposal_id}/proposal-detailed-analysis",
|
| 88 |
-
self.get_proposal_analysis_by_proposal_id,
|
| 89 |
-
methods=["GET"],
|
| 90 |
-
tags=["Proposal Detailed Analysis by Proposal ID"],
|
| 91 |
-
response_model=CompleteAnalysisResponse,
|
| 92 |
-
)
|
| 93 |
-
self.router.add_api_route(
|
| 94 |
-
"/proposal-detailed-analysis",
|
| 95 |
-
self.create_proposal_analysis,
|
| 96 |
-
methods=["POST"],
|
| 97 |
-
tags=["Proposal Detailed Analysis"],
|
| 98 |
-
response_model=ProposalDetailedAnalysisResponse,
|
| 99 |
-
)
|
| 100 |
-
self.router.add_api_route(
|
| 101 |
-
"/proposals/{proposal_id}/proposal-detailed-analysis",
|
| 102 |
-
self.create_complete_proposal_analysis,
|
| 103 |
-
methods=["POST"],
|
| 104 |
-
tags=["Proposal Detailed Analysis by Proposal ID"],
|
| 105 |
-
response_model=CompleteAnalysisResponse,
|
| 106 |
-
)
|
| 107 |
-
self.router.add_api_route(
|
| 108 |
-
"/proposal-detailed-analysis/{id}",
|
| 109 |
-
self.update_proposal_analysis,
|
| 110 |
-
methods=["PUT"],
|
| 111 |
-
tags=["Proposal Detailed Analysis by ID"],
|
| 112 |
-
response_model=ProposalDetailedAnalysisResponse,
|
| 113 |
-
)
|
| 114 |
-
self.router.add_api_route(
|
| 115 |
-
"/proposals/{proposal_id}/proposal-detailed-analysis",
|
| 116 |
-
self.update_complete_proposal_analysis,
|
| 117 |
-
methods=["PUT"],
|
| 118 |
-
tags=["Proposal Detailed Analysis by Proposal ID"],
|
| 119 |
-
response_model=CompleteAnalysisResponse,
|
| 120 |
-
)
|
| 121 |
-
self.router.add_api_route(
|
| 122 |
-
"/proposal-detailed-analysis/{id}",
|
| 123 |
-
self.delete_proposal_analysis,
|
| 124 |
-
methods=["DELETE"],
|
| 125 |
-
tags=["Proposal Detailed Analysis by ID"],
|
| 126 |
-
response_model=DeleteResponse,
|
| 127 |
-
)
|
| 128 |
-
self.router.add_api_route(
|
| 129 |
-
"/proposals/{proposal_id}/proposal-detailed-analysis",
|
| 130 |
-
self.delete_proposal_analysis_by_proposal_id,
|
| 131 |
-
methods=["DELETE"],
|
| 132 |
-
tags=["Proposal Detailed Analysis by Proposal ID"],
|
| 133 |
-
response_model=DeleteResponse,
|
| 134 |
-
)
|
| 135 |
-
self.router.add_api_route(
|
| 136 |
-
"/proposals/{proposal_id}/proposal-detailed-analysis/{id}",
|
| 137 |
-
self.delete_proposal_analysis_by_proposal_and_id,
|
| 138 |
-
methods=["DELETE"],
|
| 139 |
-
tags=["Proposal Detailed Analysis by Proposal and ID"],
|
| 140 |
-
response_model=DeleteResponse,
|
| 141 |
-
)
|
| 142 |
-
|
| 143 |
-
async def get_proposal_analysis(self):
|
| 144 |
-
async with self.service() as service:
|
| 145 |
-
try:
|
| 146 |
-
result = await service.get_proposal_analysis()
|
| 147 |
-
if not result:
|
| 148 |
-
return ProposalDetailedAnalysisResponse(status="success", data=[])
|
| 149 |
-
return ProposalDetailedAnalysisResponse(
|
| 150 |
-
status="success",
|
| 151 |
-
data=[ProposalDetailedAnalysis(**r) for r in result],
|
| 152 |
-
)
|
| 153 |
-
except Exception as e:
|
| 154 |
-
logger.exception(e)
|
| 155 |
-
raise HTTPException(
|
| 156 |
-
status_code=500, detail="Failed to get proposal analysis."
|
| 157 |
-
)
|
| 158 |
-
|
| 159 |
-
async def get_proposal_analysis_by_id(self, id: str = Path(...)):
|
| 160 |
-
async with self.service() as service:
|
| 161 |
-
try:
|
| 162 |
-
result = await service.get_proposal_analysis(id=id)
|
| 163 |
-
return ProposalDetailedAnalysisResponse(
|
| 164 |
-
status="success",
|
| 165 |
-
data=[ProposalDetailedAnalysis(**r) for r in result],
|
| 166 |
-
)
|
| 167 |
-
except Exception as e:
|
| 168 |
-
logger.exception(e)
|
| 169 |
-
raise HTTPException(
|
| 170 |
-
status_code=500, detail="Failed to get proposal analysis."
|
| 171 |
-
)
|
| 172 |
-
|
| 173 |
-
async def get_proposal_analysis_by_proposal_id(self, proposal_id: str = Path(...)):
|
| 174 |
-
async with self.service() as service:
|
| 175 |
-
try:
|
| 176 |
-
result = await service.get_proposal_analysis_by_proposal_id(
|
| 177 |
-
proposal_id=proposal_id
|
| 178 |
-
)
|
| 179 |
-
return CompleteAnalysisResponse(status="success", data=result)
|
| 180 |
-
except Exception as e:
|
| 181 |
-
logger.exception(e)
|
| 182 |
-
raise HTTPException(
|
| 183 |
-
status_code=500, detail="Failed to get proposal analysis."
|
| 184 |
-
)
|
| 185 |
-
|
| 186 |
-
async def create_proposal_analysis(self, analysis: CreateDetailedAnalysisRequest):
|
| 187 |
-
async with self.service() as service:
|
| 188 |
-
try:
|
| 189 |
-
result = await service.create_proposal_analysis(
|
| 190 |
-
data=analysis.model_dump(mode="json", exclude_unset=True)
|
| 191 |
-
)
|
| 192 |
-
return ProposalDetailedAnalysisResponse(
|
| 193 |
-
status="success",
|
| 194 |
-
data=[ProposalDetailedAnalysis(**r) for r in result],
|
| 195 |
-
)
|
| 196 |
-
except Exception as e:
|
| 197 |
-
logger.exception(e)
|
| 198 |
-
raise HTTPException(
|
| 199 |
-
status_code=500, detail="Failed to create proposal analysis."
|
| 200 |
-
)
|
| 201 |
-
|
| 202 |
-
async def create_complete_proposal_analysis(
|
| 203 |
-
self,
|
| 204 |
-
analysis: CreateProposalDetailedAnalysisRequest,
|
| 205 |
-
proposal_id: UUID = Path(...),
|
| 206 |
-
):
|
| 207 |
-
async with self.service() as service:
|
| 208 |
-
try:
|
| 209 |
-
data = analysis.model_dump(mode="json", exclude_unset=True)
|
| 210 |
-
data["proposal_id"] = proposal_id
|
| 211 |
-
result = await service.create_complete_proposal_analysis(data=data)
|
| 212 |
-
return CompleteAnalysisResponse(status="success", data=result)
|
| 213 |
-
except Exception as e:
|
| 214 |
-
logger.exception(e)
|
| 215 |
-
raise HTTPException(
|
| 216 |
-
status_code=500, detail="Failed to create proposal analysis."
|
| 217 |
-
)
|
| 218 |
-
|
| 219 |
-
async def update_proposal_analysis(
|
| 220 |
-
self, analysis: UpdateDetailedAnalysisRequest, id: UUID = Path(...)
|
| 221 |
-
):
|
| 222 |
-
async with self.service() as service:
|
| 223 |
-
try:
|
| 224 |
-
data = analysis.model_dump(mode="json", exclude_unset=True)
|
| 225 |
-
data["id"] = id
|
| 226 |
-
result = await service.update_proposal_analysis(data=data)
|
| 227 |
-
return ProposalDetailedAnalysisResponse(
|
| 228 |
-
status="success",
|
| 229 |
-
data=[ProposalDetailedAnalysis(**r) for r in result],
|
| 230 |
-
)
|
| 231 |
-
except Exception as e:
|
| 232 |
-
logger.exception(e)
|
| 233 |
-
raise HTTPException(
|
| 234 |
-
status_code=500, detail="Failed to update proposal analysis."
|
| 235 |
-
)
|
| 236 |
-
|
| 237 |
-
async def update_complete_proposal_analysis(
|
| 238 |
-
self,
|
| 239 |
-
analysis: CreateProposalDetailedAnalysisRequest,
|
| 240 |
-
proposal_id: UUID = Path(...),
|
| 241 |
-
):
|
| 242 |
-
async with self.service() as service:
|
| 243 |
-
try:
|
| 244 |
-
data = analysis.model_dump(mode="json", exclude_unset=True)
|
| 245 |
-
data["proposal_id"] = proposal_id
|
| 246 |
-
result = await service.update_complete_proposal_analysis(data=data)
|
| 247 |
-
return CompleteAnalysisResponse(status="success", data=result)
|
| 248 |
-
except Exception as e:
|
| 249 |
-
logger.exception(e)
|
| 250 |
-
raise HTTPException(
|
| 251 |
-
status_code=500, detail="Failed to update proposal analysis."
|
| 252 |
-
)
|
| 253 |
-
|
| 254 |
-
async def delete_proposal_analysis(self, id: str = Path(...)):
|
| 255 |
-
async with self.service() as service:
|
| 256 |
-
try:
|
| 257 |
-
result = await service.delete_proposal_analysis(id=id)
|
| 258 |
-
return DeleteResponse(status="success")
|
| 259 |
-
except Exception as e:
|
| 260 |
-
logger.exception(e)
|
| 261 |
-
raise HTTPException(
|
| 262 |
-
status_code=500, detail="Failed to delete proposal analysis."
|
| 263 |
-
)
|
| 264 |
-
|
| 265 |
-
async def delete_proposal_analysis_by_proposal_id(
|
| 266 |
-
self, proposal_id: str = Path(...)
|
| 267 |
-
):
|
| 268 |
-
async with self.service() as service:
|
| 269 |
-
try:
|
| 270 |
-
result = await service.delete_proposal_analysis(proposal_id=proposal_id)
|
| 271 |
-
return DeleteResponse(status="success")
|
| 272 |
-
except Exception as e:
|
| 273 |
-
logger.exception(e)
|
| 274 |
-
raise HTTPException(
|
| 275 |
-
status_code=500, detail="Failed to delete proposal analysis."
|
| 276 |
-
)
|
| 277 |
-
|
| 278 |
-
async def delete_proposal_analysis_by_proposal_and_id(
|
| 279 |
-
self, proposal_id: str = Path(...), id: str = Path(...)
|
| 280 |
-
):
|
| 281 |
-
async with self.service() as service:
|
| 282 |
-
try:
|
| 283 |
-
result = await service.delete_proposal_analysis(
|
| 284 |
-
proposal_id=proposal_id, id=id
|
| 285 |
-
)
|
| 286 |
-
return DeleteResponse(status="success")
|
| 287 |
-
except Exception as e:
|
| 288 |
-
logger.exception(e)
|
| 289 |
-
raise HTTPException(
|
| 290 |
-
status_code=500, detail="Failed to delete proposal analysis."
|
| 291 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/controllers/_proposal_evaluation_controller.py
DELETED
|
@@ -1,209 +0,0 @@
|
|
| 1 |
-
from fastapi import APIRouter, HTTPException, Query, Path
|
| 2 |
-
from pydantic import BaseModel, field_validator
|
| 3 |
-
from typing import List, Optional
|
| 4 |
-
from uuid import UUID
|
| 5 |
-
from datetime import datetime
|
| 6 |
-
|
| 7 |
-
from src.config import logger
|
| 8 |
-
from src.models import EvaluationType
|
| 9 |
-
from src.services import ProposalEvaluationService
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
class Evaluation(BaseModel):
|
| 13 |
-
id: UUID
|
| 14 |
-
proposal_id: UUID
|
| 15 |
-
evaluation_type: EvaluationType
|
| 16 |
-
ai_score: Optional[float] = None
|
| 17 |
-
adjusted_score: Optional[float] = None
|
| 18 |
-
created_at: datetime
|
| 19 |
-
updated_at: datetime
|
| 20 |
-
|
| 21 |
-
@field_validator("ai_score", "adjusted_score")
|
| 22 |
-
@classmethod
|
| 23 |
-
def round_to_two_decimals(cls, v):
|
| 24 |
-
if v is not None:
|
| 25 |
-
return round(v, 2)
|
| 26 |
-
return v
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
class Response(BaseModel):
|
| 30 |
-
status: str
|
| 31 |
-
data: Optional[List[Evaluation]] = None
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
class RequestEvaluation(BaseModel):
|
| 35 |
-
evaluation_type: Optional[EvaluationType] = None
|
| 36 |
-
ai_score: Optional[float] = None
|
| 37 |
-
adjusted_score: Optional[float] = None
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
class DeleteResponse(BaseModel):
|
| 41 |
-
status: str
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
class CreateEvaluation(BaseModel):
|
| 45 |
-
evaluation_type: EvaluationType
|
| 46 |
-
ai_score: Optional[float] = None
|
| 47 |
-
adjusted_score: Optional[float] = None
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
class CreateEvaluationRequest(BaseModel):
|
| 51 |
-
data: List[CreateEvaluation]
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
class UpdateEvaluation(BaseModel):
|
| 55 |
-
evaluation_type: Optional[EvaluationType] = None
|
| 56 |
-
ai_score: Optional[float] = None
|
| 57 |
-
adjusted_score: Optional[float] = None
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
class ProposalEvaluationController:
|
| 61 |
-
def __init__(self):
|
| 62 |
-
self.service = ProposalEvaluationService
|
| 63 |
-
self.router = APIRouter()
|
| 64 |
-
self.router.add_api_route(
|
| 65 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/evaluations",
|
| 66 |
-
self.get_evaluation_by_proposal_and_rfp_id,
|
| 67 |
-
methods=["GET"],
|
| 68 |
-
response_model=Response,
|
| 69 |
-
tags=["Evaluations by Proposal and RFP ID"],
|
| 70 |
-
)
|
| 71 |
-
self.router.add_api_route(
|
| 72 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/evaluations/{id}",
|
| 73 |
-
self.get_evaluation_by_proposal_and_rfp_id_and_id,
|
| 74 |
-
methods=["GET"],
|
| 75 |
-
response_model=Response,
|
| 76 |
-
tags=["Evaluations by Proposal and RFP ID and ID"],
|
| 77 |
-
)
|
| 78 |
-
|
| 79 |
-
self.router.add_api_route(
|
| 80 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/evaluations",
|
| 81 |
-
self.create_evaluation_by_proposal_and_rfp_id,
|
| 82 |
-
methods=["POST"],
|
| 83 |
-
response_model=Response,
|
| 84 |
-
tags=["Evaluations by Proposal and RFP ID"],
|
| 85 |
-
)
|
| 86 |
-
|
| 87 |
-
self.router.add_api_route(
|
| 88 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/evaluations/{id}",
|
| 89 |
-
self.update_evaluation_by_proposal_and_rfp_id_and_id,
|
| 90 |
-
methods=["PUT"],
|
| 91 |
-
response_model=Response,
|
| 92 |
-
tags=["Evaluations by Proposal and RFP ID and ID"],
|
| 93 |
-
)
|
| 94 |
-
|
| 95 |
-
self.router.add_api_route(
|
| 96 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/evaluations",
|
| 97 |
-
self.delete_evaluation_by_proposal_and_rfp_id,
|
| 98 |
-
methods=["DELETE"],
|
| 99 |
-
response_model=DeleteResponse,
|
| 100 |
-
tags=["Evaluations by Proposal and RFP ID"],
|
| 101 |
-
)
|
| 102 |
-
self.router.add_api_route(
|
| 103 |
-
"/rfps/{rfp_id}/proposals/{proposal_id}/evaluations/{id}",
|
| 104 |
-
self.delete_evaluation_by_proposal_and_rfp_id_and_id,
|
| 105 |
-
methods=["DELETE"],
|
| 106 |
-
response_model=DeleteResponse,
|
| 107 |
-
tags=["Evaluations by Proposal and RFP ID and ID"],
|
| 108 |
-
)
|
| 109 |
-
|
| 110 |
-
async def get_evaluation_by_proposal_and_rfp_id(
|
| 111 |
-
self,
|
| 112 |
-
rfp_id: str = Path(...),
|
| 113 |
-
proposal_id: str = Path(...),
|
| 114 |
-
evaluation_type: Optional[EvaluationType] = Query(None),
|
| 115 |
-
):
|
| 116 |
-
try:
|
| 117 |
-
async with self.service() as service:
|
| 118 |
-
results = await service.get_evaluations(
|
| 119 |
-
proposal_id=proposal_id, evaluation_type=evaluation_type
|
| 120 |
-
)
|
| 121 |
-
return Response(
|
| 122 |
-
status="success",
|
| 123 |
-
data=[Evaluation(**result) for result in results],
|
| 124 |
-
)
|
| 125 |
-
except Exception as e:
|
| 126 |
-
logger.exception(e)
|
| 127 |
-
raise HTTPException(status_code=500, detail="Failed to retrieve proposals.")
|
| 128 |
-
|
| 129 |
-
async def get_evaluation_by_proposal_and_rfp_id_and_id(
|
| 130 |
-
self, rfp_id: str = Path(...), proposal_id: str = Path(...), id: str = Path(...)
|
| 131 |
-
):
|
| 132 |
-
try:
|
| 133 |
-
async with self.service() as service:
|
| 134 |
-
result = await service.get_evaluations(proposal_id=proposal_id, id=id)
|
| 135 |
-
return Response(
|
| 136 |
-
status="success",
|
| 137 |
-
data=[Evaluation(**result) for result in result],
|
| 138 |
-
)
|
| 139 |
-
except Exception as e:
|
| 140 |
-
logger.exception(e)
|
| 141 |
-
raise HTTPException(status_code=500, detail="Failed to retrieve proposals.")
|
| 142 |
-
|
| 143 |
-
async def create_evaluation_by_proposal_and_rfp_id(
|
| 144 |
-
self,
|
| 145 |
-
evaluation: CreateEvaluationRequest,
|
| 146 |
-
rfp_id: str = Path(...),
|
| 147 |
-
proposal_id: str = Path(...),
|
| 148 |
-
):
|
| 149 |
-
try:
|
| 150 |
-
async with self.service() as service:
|
| 151 |
-
result = await service.create_evaluations(
|
| 152 |
-
proposal_id=proposal_id,
|
| 153 |
-
evaluations=evaluation.model_dump(mode="json", exclude_unset=True)[
|
| 154 |
-
"data"
|
| 155 |
-
],
|
| 156 |
-
)
|
| 157 |
-
return Response(
|
| 158 |
-
status="success",
|
| 159 |
-
data=[Evaluation(**result) for result in result],
|
| 160 |
-
)
|
| 161 |
-
except Exception as e:
|
| 162 |
-
logger.exception(e)
|
| 163 |
-
raise HTTPException(status_code=500, detail="Failed to retrieve proposals.")
|
| 164 |
-
|
| 165 |
-
async def update_evaluation_by_proposal_and_rfp_id_and_id(
|
| 166 |
-
self,
|
| 167 |
-
evaluation: UpdateEvaluation,
|
| 168 |
-
rfp_id: str = Path(...),
|
| 169 |
-
proposal_id: str = Path(...),
|
| 170 |
-
id: str = Path(...),
|
| 171 |
-
):
|
| 172 |
-
try:
|
| 173 |
-
async with self.service() as service:
|
| 174 |
-
result = await service.update_evaluations(
|
| 175 |
-
proposal_id=proposal_id,
|
| 176 |
-
id=id,
|
| 177 |
-
evaluations=evaluation.model_dump(mode="json", exclude_unset=True),
|
| 178 |
-
)
|
| 179 |
-
return Response(
|
| 180 |
-
status="success",
|
| 181 |
-
data=[Evaluation(**result) for result in result],
|
| 182 |
-
)
|
| 183 |
-
except Exception as e:
|
| 184 |
-
logger.exception(e)
|
| 185 |
-
raise HTTPException(status_code=500, detail="Failed to retrieve proposals.")
|
| 186 |
-
|
| 187 |
-
async def delete_evaluation_by_proposal_and_rfp_id(
|
| 188 |
-
self, rfp_id: str = Path(...), proposal_id: str = Path(...)
|
| 189 |
-
):
|
| 190 |
-
try:
|
| 191 |
-
async with self.service() as service:
|
| 192 |
-
result = await service.delete_evaluations(proposal_id=proposal_id)
|
| 193 |
-
return DeleteResponse(status="success")
|
| 194 |
-
except Exception as e:
|
| 195 |
-
logger.exception(e)
|
| 196 |
-
raise HTTPException(status_code=500, detail="Failed to retrieve proposals.")
|
| 197 |
-
|
| 198 |
-
async def delete_evaluation_by_proposal_and_rfp_id_and_id(
|
| 199 |
-
self, rfp_id: str = Path(...), proposal_id: str = Path(...), id: str = Path(...)
|
| 200 |
-
):
|
| 201 |
-
try:
|
| 202 |
-
async with self.service() as service:
|
| 203 |
-
result = await service.delete_evaluations(
|
| 204 |
-
proposal_id=proposal_id, id=id
|
| 205 |
-
)
|
| 206 |
-
return DeleteResponse(status="success")
|
| 207 |
-
except Exception as e:
|
| 208 |
-
logger.exception(e)
|
| 209 |
-
raise HTTPException(status_code=500, detail="Failed to retrieve proposals.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/controllers/_rfp_controller.py
DELETED
|
@@ -1,170 +0,0 @@
|
|
| 1 |
-
from fastapi import Path
|
| 2 |
-
from fastapi import APIRouter, HTTPException, Query
|
| 3 |
-
from pydantic import BaseModel, Field, field_validator
|
| 4 |
-
from typing import List, Optional
|
| 5 |
-
from uuid import UUID
|
| 6 |
-
|
| 7 |
-
from src.config import logger
|
| 8 |
-
from src.services import RFPService
|
| 9 |
-
from datetime import datetime
|
| 10 |
-
|
| 11 |
-
from src.models import RFPStatus
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
class RFP(BaseModel):
|
| 15 |
-
id: UUID
|
| 16 |
-
rfp_number: str
|
| 17 |
-
name: str
|
| 18 |
-
received_proposals: int
|
| 19 |
-
evaluated_count: int
|
| 20 |
-
awaiting_evaluation: int
|
| 21 |
-
target_award_date: Optional[datetime] = None
|
| 22 |
-
ko_name: Optional[str] = None
|
| 23 |
-
award_type: Optional[str] = None
|
| 24 |
-
google_drive_id: Optional[str] = None
|
| 25 |
-
status: RFPStatus
|
| 26 |
-
comparative_weights_locked: Optional[bool] = False
|
| 27 |
-
created_at: datetime
|
| 28 |
-
updated_at: datetime
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
class ResponseRFP(BaseModel):
|
| 32 |
-
status: str
|
| 33 |
-
data: Optional[List[RFP]]
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
class RFPRequest(BaseModel):
|
| 37 |
-
rfp_number: str
|
| 38 |
-
name: str
|
| 39 |
-
received_proposals: int
|
| 40 |
-
evaluated_count: int
|
| 41 |
-
target_award_date: Optional[datetime] = None
|
| 42 |
-
ko_name: Optional[str] = None
|
| 43 |
-
award_type: Optional[str] = None
|
| 44 |
-
google_drive_id: Optional[str] = None
|
| 45 |
-
status: RFPStatus
|
| 46 |
-
comparative_weights_locked: Optional[bool] = False
|
| 47 |
-
awaiting_evaluation: int
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
class RFPUpdateRequest(BaseModel):
|
| 51 |
-
rfp_number: Optional[str] = None
|
| 52 |
-
name: Optional[str] = None
|
| 53 |
-
received_proposals: Optional[int] = None
|
| 54 |
-
evaluated_count: Optional[int] = None
|
| 55 |
-
target_award_date: Optional[datetime] = None
|
| 56 |
-
ko_name: Optional[str] = None
|
| 57 |
-
award_type: Optional[str] = None
|
| 58 |
-
google_drive_id: Optional[str] = None
|
| 59 |
-
status: Optional[RFPStatus] = None
|
| 60 |
-
comparative_weights_locked: Optional[bool] = None
|
| 61 |
-
awaiting_evaluation: Optional[int] = None
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
class RFPDeleteResponse(BaseModel):
|
| 65 |
-
status: str
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
class RFPController:
|
| 69 |
-
def __init__(self):
|
| 70 |
-
self.__rfp_service = RFPService
|
| 71 |
-
self.router = APIRouter()
|
| 72 |
-
self.router.add_api_route(
|
| 73 |
-
"/rfps",
|
| 74 |
-
self.get_rfps,
|
| 75 |
-
methods=["GET"],
|
| 76 |
-
response_model=ResponseRFP,
|
| 77 |
-
tags=["RFPs"],
|
| 78 |
-
)
|
| 79 |
-
self.router.add_api_route(
|
| 80 |
-
"/rfps/{rfp_id}",
|
| 81 |
-
self.get_rfps_by_id,
|
| 82 |
-
methods=["GET"],
|
| 83 |
-
response_model=ResponseRFP,
|
| 84 |
-
tags=["RFPs by ID"],
|
| 85 |
-
)
|
| 86 |
-
self.router.add_api_route(
|
| 87 |
-
"/rfps",
|
| 88 |
-
self.create_rfp,
|
| 89 |
-
methods=["POST"],
|
| 90 |
-
response_model=ResponseRFP,
|
| 91 |
-
tags=["RFPs"],
|
| 92 |
-
)
|
| 93 |
-
self.router.add_api_route(
|
| 94 |
-
"/rfps/{rfp_id}",
|
| 95 |
-
self.update_rfp,
|
| 96 |
-
methods=["PUT"],
|
| 97 |
-
response_model=ResponseRFP,
|
| 98 |
-
tags=["RFPs by ID"],
|
| 99 |
-
)
|
| 100 |
-
self.router.add_api_route(
|
| 101 |
-
"/rfps/{rfp_id}",
|
| 102 |
-
self.delete_rfp,
|
| 103 |
-
methods=["DELETE"],
|
| 104 |
-
response_model=RFPDeleteResponse,
|
| 105 |
-
tags=["RFPs by ID"],
|
| 106 |
-
)
|
| 107 |
-
|
| 108 |
-
async def get_rfps(self, status: Optional[RFPStatus] = Query(None)):
|
| 109 |
-
async with self.__rfp_service() as service:
|
| 110 |
-
try:
|
| 111 |
-
rfps = await service.get_rfps(status=status)
|
| 112 |
-
return ResponseRFP(status="success", data=[RFP(**rfp) for rfp in rfps])
|
| 113 |
-
except HTTPException as e:
|
| 114 |
-
logger.warning(e)
|
| 115 |
-
raise e
|
| 116 |
-
except Exception as e:
|
| 117 |
-
logger.exception(e)
|
| 118 |
-
raise HTTPException(status_code=500, detail="Failed to retrieve RFPs.")
|
| 119 |
-
|
| 120 |
-
async def get_rfps_by_id(self, rfp_id: str = Path(...)):
|
| 121 |
-
async with self.__rfp_service() as service:
|
| 122 |
-
try:
|
| 123 |
-
rfps = await service.get_rfps(rfp_id=rfp_id)
|
| 124 |
-
return ResponseRFP(status="success", data=[RFP(**rfp) for rfp in rfps])
|
| 125 |
-
except HTTPException as e:
|
| 126 |
-
logger.warning(e)
|
| 127 |
-
raise e
|
| 128 |
-
except Exception as e:
|
| 129 |
-
logger.exception(e)
|
| 130 |
-
raise HTTPException(status_code=500, detail="Failed to retrieve RFPs.")
|
| 131 |
-
|
| 132 |
-
async def create_rfp(self, rfp: RFPRequest):
|
| 133 |
-
async with self.__rfp_service() as service:
|
| 134 |
-
try:
|
| 135 |
-
rfp = await service.create_rfp(rfp.model_dump(exclude_unset=True))
|
| 136 |
-
return ResponseRFP(status="success", data=[RFP(**rfp)])
|
| 137 |
-
except HTTPException as e:
|
| 138 |
-
logger.warning(e)
|
| 139 |
-
raise e
|
| 140 |
-
except Exception as e:
|
| 141 |
-
logger.exception(e)
|
| 142 |
-
raise HTTPException(status_code=500, detail="Failed to create RFP.")
|
| 143 |
-
|
| 144 |
-
async def update_rfp(self, rfp: RFPUpdateRequest, rfp_id: str = Path(...)):
|
| 145 |
-
async with self.__rfp_service() as service:
|
| 146 |
-
try:
|
| 147 |
-
rfp = await service.update_rfp(
|
| 148 |
-
rfp_id=rfp_id, rfp=rfp.model_dump(exclude_unset=True)
|
| 149 |
-
)
|
| 150 |
-
return ResponseRFP(status="success", data=[RFP(**rfp)])
|
| 151 |
-
except HTTPException as e:
|
| 152 |
-
logger.warning(e)
|
| 153 |
-
raise e
|
| 154 |
-
except Exception as e:
|
| 155 |
-
logger.exception(e)
|
| 156 |
-
raise HTTPException(status_code=500, detail="Failed to update RFP.")
|
| 157 |
-
|
| 158 |
-
async def delete_rfp(self, rfp_id: str = Path(...)):
|
| 159 |
-
async with self.__rfp_service() as service:
|
| 160 |
-
try:
|
| 161 |
-
status = await service.delete_rfp(rfp_id)
|
| 162 |
-
if not status:
|
| 163 |
-
raise HTTPException(status_code=404, detail="RFP not found.")
|
| 164 |
-
return RFPDeleteResponse(status="success")
|
| 165 |
-
except HTTPException as e:
|
| 166 |
-
logger.warning(e)
|
| 167 |
-
raise e
|
| 168 |
-
except Exception as e:
|
| 169 |
-
logger.exception(e)
|
| 170 |
-
raise HTTPException(status_code=500, detail="Failed to delete RFP.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/models/__init__.py
DELETED
|
@@ -1,50 +0,0 @@
|
|
| 1 |
-
from ._base import Base
|
| 2 |
-
from ._proposal import Proposal, ProposalStatus, GateCriteria
|
| 3 |
-
from ._rfp import RFP, RFPStatus
|
| 4 |
-
from ._proposal_ai_analysis import (
|
| 5 |
-
ProposalAIAnalysis,
|
| 6 |
-
ProposalStrength,
|
| 7 |
-
ProposalWeakness,
|
| 8 |
-
ProposalImprovement,
|
| 9 |
-
ProposalRejectionReason,
|
| 10 |
-
Category
|
| 11 |
-
)
|
| 12 |
-
from ._proposal_detailed_analysis import ProposalDetailAnalysis, OperationCriteria
|
| 13 |
-
from ._proposal_query_models import QueryType, CreateOrUpdateType
|
| 14 |
-
from ._letters import Letter, LetterType
|
| 15 |
-
from ._evaluation import EvaluationCriteria, EvaluationCriteriaType
|
| 16 |
-
from ._comparitive_weights import ComparativeWeights, Weights
|
| 17 |
-
from ._proposal_evaluation import Evaluations, EvaluationType
|
| 18 |
-
from ._analysis import Analysis, AnalysisType, AnalysisStatus
|
| 19 |
-
|
| 20 |
-
__all__ = [
|
| 21 |
-
"Base",
|
| 22 |
-
"Proposal",
|
| 23 |
-
"RFP",
|
| 24 |
-
"ProposalAIAnalysis",
|
| 25 |
-
"ProposalStrength",
|
| 26 |
-
"ProposalWeakness",
|
| 27 |
-
"ProposalImprovement",
|
| 28 |
-
"ProposalRejectionReason",
|
| 29 |
-
"ProposalDetailAnalysis",
|
| 30 |
-
"ProposalStatus",
|
| 31 |
-
"QueryType",
|
| 32 |
-
"CreateOrUpdateType",
|
| 33 |
-
"OperationCriteria",
|
| 34 |
-
"GateCriteria",
|
| 35 |
-
"Letter",
|
| 36 |
-
"LetterType",
|
| 37 |
-
"EvaluationCriteria",
|
| 38 |
-
"EvaluationCriteriaType",
|
| 39 |
-
"RFPStatus",
|
| 40 |
-
"Category",
|
| 41 |
-
"ComparativeWeights",
|
| 42 |
-
"Weights",
|
| 43 |
-
"Evaluations",
|
| 44 |
-
"EvaluationType",
|
| 45 |
-
"Analysis",
|
| 46 |
-
"AnalysisType",
|
| 47 |
-
"AnalysisStatus",
|
| 48 |
-
]
|
| 49 |
-
__version__ = "0.1.0"
|
| 50 |
-
__author__ = "Aryan Jain"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/models/_analysis.py
DELETED
|
@@ -1,42 +0,0 @@
|
|
| 1 |
-
from enum import Enum as PyEnum
|
| 2 |
-
|
| 3 |
-
from sqlalchemy import (
|
| 4 |
-
Column,
|
| 5 |
-
DateTime,
|
| 6 |
-
Enum,
|
| 7 |
-
Float,
|
| 8 |
-
ForeignKey,
|
| 9 |
-
Integer,
|
| 10 |
-
String,
|
| 11 |
-
func,
|
| 12 |
-
)
|
| 13 |
-
from sqlalchemy.dialects.postgresql import UUID
|
| 14 |
-
from pydantic import BaseModel
|
| 15 |
-
from ._base import Base
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
class AnalysisType(PyEnum):
|
| 19 |
-
STRENGTHS = "STRENGTHS"
|
| 20 |
-
WEAKNESSES = "WEAKNESSES"
|
| 21 |
-
DEFICIENCIES = "DEFICIENCIES"
|
| 22 |
-
|
| 23 |
-
class AnalysisStatus(PyEnum):
|
| 24 |
-
ACCEPTED = "ACCEPTED"
|
| 25 |
-
REJECTED = "REJECTED"
|
| 26 |
-
PENDING = "PENDING"
|
| 27 |
-
|
| 28 |
-
class Analysis(Base):
|
| 29 |
-
__tablename__ = "analysis"
|
| 30 |
-
id = Column(UUID(as_uuid=True), primary_key=True)
|
| 31 |
-
evaluation_id = Column(
|
| 32 |
-
UUID(as_uuid=True),
|
| 33 |
-
ForeignKey("evaluations.id", ondelete="CASCADE"),
|
| 34 |
-
nullable=False,
|
| 35 |
-
)
|
| 36 |
-
insights = Column(String, nullable=False)
|
| 37 |
-
analysis_type = Column(Enum(AnalysisType), nullable=False)
|
| 38 |
-
status = Column(Enum(AnalysisStatus, name="analysisstatus"), nullable=False, default=AnalysisStatus.PENDING)
|
| 39 |
-
created_at = Column(DateTime(), nullable=False, default=func.now())
|
| 40 |
-
updated_at = Column(
|
| 41 |
-
DateTime(), nullable=False, default=func.now(), onupdate=func.now()
|
| 42 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/models/_base.py
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
from sqlalchemy.orm import declarative_base
|
| 2 |
-
|
| 3 |
-
Base = declarative_base()
|
|
|
|
|
|
|
|
|
|
|
|
src/models/_comparitive_weights.py
DELETED
|
@@ -1,47 +0,0 @@
|
|
| 1 |
-
from enum import Enum as PyEnum
|
| 2 |
-
|
| 3 |
-
from sqlalchemy import (
|
| 4 |
-
Boolean,
|
| 5 |
-
Column,
|
| 6 |
-
DateTime,
|
| 7 |
-
Enum,
|
| 8 |
-
Float,
|
| 9 |
-
ForeignKey,
|
| 10 |
-
Integer,
|
| 11 |
-
String,
|
| 12 |
-
func,
|
| 13 |
-
)
|
| 14 |
-
from sqlalchemy.dialects.postgresql import UUID
|
| 15 |
-
from pydantic import BaseModel
|
| 16 |
-
from ._base import Base
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
class Weights(PyEnum):
|
| 20 |
-
NONE = "NONE"
|
| 21 |
-
LOW = "LOW"
|
| 22 |
-
MEDIUM = "MEDIUM"
|
| 23 |
-
HIGH = "HIGH"
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
class ComparativeWeights(Base):
|
| 27 |
-
__tablename__ = "comparative_weights"
|
| 28 |
-
id = Column(UUID(as_uuid=True), primary_key=True, nullable=False)
|
| 29 |
-
rfp_id = Column(
|
| 30 |
-
UUID(as_uuid=True),
|
| 31 |
-
ForeignKey("rfps.id", ondelete="CASCADE"),
|
| 32 |
-
nullable=False,
|
| 33 |
-
unique=True,
|
| 34 |
-
)
|
| 35 |
-
technical_weight = Column(Enum(Weights, name="comparativeweights"), nullable=False)
|
| 36 |
-
management_weight = Column(Enum(Weights, name="comparativeweights"), nullable=False)
|
| 37 |
-
past_performance_weight = Column(
|
| 38 |
-
Enum(Weights, name="comparativeweights"), nullable=False
|
| 39 |
-
)
|
| 40 |
-
price_weight = Column(Enum(Weights, name="comparativeweights"), nullable=False)
|
| 41 |
-
strengths_weight = Column(Float, nullable=False, default=15)
|
| 42 |
-
weaknesses_weight = Column(Float, nullable=False, default=5)
|
| 43 |
-
flag = Column(Boolean(), nullable=True, default=False)
|
| 44 |
-
created_at = Column(DateTime, nullable=False, default=func.now())
|
| 45 |
-
updated_at = Column(
|
| 46 |
-
DateTime, nullable=False, default=func.now(), onupdate=func.now()
|
| 47 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/models/_evaluation.py
DELETED
|
@@ -1,34 +0,0 @@
|
|
| 1 |
-
from enum import Enum as PyEnum
|
| 2 |
-
|
| 3 |
-
from sqlalchemy import (
|
| 4 |
-
Column,
|
| 5 |
-
DateTime,
|
| 6 |
-
Enum,
|
| 7 |
-
Float,
|
| 8 |
-
ForeignKey,
|
| 9 |
-
Integer,
|
| 10 |
-
String,
|
| 11 |
-
func,
|
| 12 |
-
)
|
| 13 |
-
from sqlalchemy.dialects.postgresql import UUID
|
| 14 |
-
from pydantic import BaseModel
|
| 15 |
-
from ._base import Base
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
class EvaluationCriteriaType(PyEnum):
|
| 19 |
-
EVALUATION = "EVALUATION"
|
| 20 |
-
GATE = "GATE"
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
class EvaluationCriteria(Base):
|
| 24 |
-
__tablename__ = "evaluation_criteria_details"
|
| 25 |
-
id = Column(UUID(as_uuid=True), primary_key=True, nullable=False)
|
| 26 |
-
rfp_id = Column(
|
| 27 |
-
UUID(as_uuid=True), ForeignKey("rfps.id", ondelete="CASCADE"), nullable=False
|
| 28 |
-
)
|
| 29 |
-
evaluation_criteria = Column(String, nullable=True)
|
| 30 |
-
evaluation_criteria_type = Column(Enum(EvaluationCriteriaType), nullable=False)
|
| 31 |
-
created_at = Column(DateTime, nullable=False, default=func.now())
|
| 32 |
-
updated_at = Column(
|
| 33 |
-
DateTime, nullable=False, default=func.now(), onupdate=func.now()
|
| 34 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/models/_letters.py
DELETED
|
@@ -1,36 +0,0 @@
|
|
| 1 |
-
from enum import Enum as PyEnum
|
| 2 |
-
|
| 3 |
-
from sqlalchemy import (
|
| 4 |
-
Column,
|
| 5 |
-
DateTime,
|
| 6 |
-
Enum,
|
| 7 |
-
Float,
|
| 8 |
-
ForeignKey,
|
| 9 |
-
Integer,
|
| 10 |
-
String,
|
| 11 |
-
func,
|
| 12 |
-
)
|
| 13 |
-
from sqlalchemy.dialects.postgresql import UUID
|
| 14 |
-
from pydantic import BaseModel
|
| 15 |
-
from ._base import Base
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
class LetterType(PyEnum):
|
| 19 |
-
UO = "UO"
|
| 20 |
-
DEBRIEF = "DEBRIEF"
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
class Letter(Base):
|
| 24 |
-
__tablename__ = "letters"
|
| 25 |
-
id = Column(UUID(as_uuid=True), primary_key=True, nullable=False)
|
| 26 |
-
proposal_id = Column(
|
| 27 |
-
UUID(as_uuid=True),
|
| 28 |
-
ForeignKey("proposals.id", ondelete="CASCADE"),
|
| 29 |
-
nullable=False,
|
| 30 |
-
)
|
| 31 |
-
letter = Column(String, nullable=True)
|
| 32 |
-
letter_type = Column(Enum(LetterType), nullable=False)
|
| 33 |
-
created_at = Column(DateTime, nullable=False, default=func.now())
|
| 34 |
-
updated_at = Column(
|
| 35 |
-
DateTime, nullable=False, default=func.now(), onupdate=func.now()
|
| 36 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/models/_proposal.py
DELETED
|
@@ -1,46 +0,0 @@
|
|
| 1 |
-
from enum import Enum as PyEnum
|
| 2 |
-
|
| 3 |
-
from sqlalchemy import (
|
| 4 |
-
Column,
|
| 5 |
-
DateTime,
|
| 6 |
-
Enum,
|
| 7 |
-
Float,
|
| 8 |
-
ForeignKey,
|
| 9 |
-
Integer,
|
| 10 |
-
String,
|
| 11 |
-
func,
|
| 12 |
-
)
|
| 13 |
-
from sqlalchemy.dialects.postgresql import UUID
|
| 14 |
-
from pydantic import BaseModel
|
| 15 |
-
from ._base import Base
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
class ProposalStatus(PyEnum):
|
| 19 |
-
APPROVED = "APPROVED"
|
| 20 |
-
EVALUATED = "EVALUATED"
|
| 21 |
-
IN_REVIEW = "IN_REVIEW"
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
class GateCriteria(PyEnum):
|
| 25 |
-
PASS = "PASS"
|
| 26 |
-
FAIL = "FAIL"
|
| 27 |
-
IN_REVIEW = "IN_REVIEW"
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
class Proposal(Base):
|
| 31 |
-
__tablename__ = "proposals"
|
| 32 |
-
|
| 33 |
-
id = Column(UUID(as_uuid=True), primary_key=True, nullable=False)
|
| 34 |
-
rfp_id = Column(
|
| 35 |
-
UUID(as_uuid=True), ForeignKey("rfps.id", ondelete="CASCADE"), nullable=False
|
| 36 |
-
)
|
| 37 |
-
name = Column(String, nullable=False)
|
| 38 |
-
tep = Column(String, nullable=False)
|
| 39 |
-
gate_criteria = Column(Enum(GateCriteria), nullable=False)
|
| 40 |
-
status = Column(Enum(ProposalStatus), nullable=False)
|
| 41 |
-
ai_score = Column(Float, nullable=True)
|
| 42 |
-
final_score = Column(Float, nullable=True)
|
| 43 |
-
created_at = Column(DateTime, nullable=False, default=func.now())
|
| 44 |
-
updated_at = Column(
|
| 45 |
-
DateTime, nullable=False, default=func.now(), onupdate=func.now()
|
| 46 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/models/_proposal_ai_analysis.py
DELETED
|
@@ -1,103 +0,0 @@
|
|
| 1 |
-
from enum import Enum as PyEnum
|
| 2 |
-
|
| 3 |
-
from sqlalchemy import (
|
| 4 |
-
Column,
|
| 5 |
-
DateTime,
|
| 6 |
-
Enum,
|
| 7 |
-
Float,
|
| 8 |
-
ForeignKey,
|
| 9 |
-
Integer,
|
| 10 |
-
String,
|
| 11 |
-
func,
|
| 12 |
-
)
|
| 13 |
-
from sqlalchemy.dialects.postgresql import UUID
|
| 14 |
-
from ._base import Base
|
| 15 |
-
|
| 16 |
-
class Category(PyEnum):
|
| 17 |
-
TECHNICAL = "TECHNICAL"
|
| 18 |
-
MANAGEMENT = "MANAGEMENT"
|
| 19 |
-
PAST_PERFORMANCE = "PAST_PERFORMANCE"
|
| 20 |
-
PRICE = "PRICE"
|
| 21 |
-
|
| 22 |
-
class ProposalAIAnalysis(Base):
|
| 23 |
-
__tablename__ = "proposal_ai_analyses"
|
| 24 |
-
|
| 25 |
-
id = Column(UUID(as_uuid=True), primary_key=True, nullable=False)
|
| 26 |
-
proposal_id = Column(
|
| 27 |
-
UUID(as_uuid=True),
|
| 28 |
-
ForeignKey("proposals.id", ondelete="CASCADE"),
|
| 29 |
-
nullable=False,
|
| 30 |
-
)
|
| 31 |
-
ai_score = Column(Float, nullable=False)
|
| 32 |
-
created_at = Column(DateTime, nullable=False, default=func.now())
|
| 33 |
-
updated_at = Column(
|
| 34 |
-
DateTime, nullable=False, default=func.now(), onupdate=func.now()
|
| 35 |
-
)
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
class ProposalStrength(Base):
|
| 39 |
-
__tablename__ = "proposal_strengths"
|
| 40 |
-
|
| 41 |
-
id = Column(UUID(as_uuid=True), primary_key=True, nullable=False)
|
| 42 |
-
proposal_id = Column(
|
| 43 |
-
UUID(as_uuid=True),
|
| 44 |
-
ForeignKey("proposals.id", ondelete="CASCADE"),
|
| 45 |
-
nullable=False,
|
| 46 |
-
)
|
| 47 |
-
strength_point = Column(String, nullable=False)
|
| 48 |
-
category = Column(Enum(Category, name="strengthcategory"), nullable=False)
|
| 49 |
-
created_at = Column(DateTime, nullable=False, default=func.now())
|
| 50 |
-
updated_at = Column(
|
| 51 |
-
DateTime, nullable=False, default=func.now(), onupdate=func.now()
|
| 52 |
-
)
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
class ProposalRejectionReason(Base):
|
| 56 |
-
__tablename__ = "proposal_rejection_reasons"
|
| 57 |
-
|
| 58 |
-
id = Column(UUID(as_uuid=True), primary_key=True, nullable=False)
|
| 59 |
-
proposal_id = Column(
|
| 60 |
-
UUID(as_uuid=True),
|
| 61 |
-
ForeignKey("proposals.id", ondelete="CASCADE"),
|
| 62 |
-
nullable=False,
|
| 63 |
-
)
|
| 64 |
-
reason = Column(String, nullable=False)
|
| 65 |
-
category = Column(Enum(Category, name="rejectioncategory"), nullable=False)
|
| 66 |
-
created_at = Column(DateTime, nullable=False, default=func.now())
|
| 67 |
-
updated_at = Column(
|
| 68 |
-
DateTime, nullable=False, default=func.now(), onupdate=func.now()
|
| 69 |
-
)
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
class ProposalWeakness(Base):
|
| 73 |
-
__tablename__ = "proposal_weaknesses"
|
| 74 |
-
|
| 75 |
-
id = Column(UUID(as_uuid=True), primary_key=True, nullable=False)
|
| 76 |
-
proposal_id = Column(
|
| 77 |
-
UUID(as_uuid=True),
|
| 78 |
-
ForeignKey("proposals.id", ondelete="CASCADE"),
|
| 79 |
-
nullable=False,
|
| 80 |
-
)
|
| 81 |
-
weakness_point = Column(String, nullable=False)
|
| 82 |
-
category = Column(Enum(Category, name="weaknesscategory"), nullable=False)
|
| 83 |
-
created_at = Column(DateTime, nullable=False, default=func.now())
|
| 84 |
-
updated_at = Column(
|
| 85 |
-
DateTime, nullable=False, default=func.now(), onupdate=func.now()
|
| 86 |
-
)
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
class ProposalImprovement(Base):
|
| 90 |
-
__tablename__ = "proposal_improvements"
|
| 91 |
-
|
| 92 |
-
id = Column(UUID(as_uuid=True), primary_key=True, nullable=False)
|
| 93 |
-
proposal_id = Column(
|
| 94 |
-
UUID(as_uuid=True),
|
| 95 |
-
ForeignKey("proposals.id", ondelete="CASCADE"),
|
| 96 |
-
nullable=False,
|
| 97 |
-
)
|
| 98 |
-
improvement_point = Column(String, nullable=False)
|
| 99 |
-
category = Column(Enum(Category, name="improvementcategory"), nullable=False)
|
| 100 |
-
created_at = Column(DateTime, nullable=False, default=func.now())
|
| 101 |
-
updated_at = Column(
|
| 102 |
-
DateTime, nullable=False, default=func.now(), onupdate=func.now()
|
| 103 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/models/_proposal_detailed_analysis.py
DELETED
|
@@ -1,39 +0,0 @@
|
|
| 1 |
-
from enum import Enum as PyEnum
|
| 2 |
-
|
| 3 |
-
from sqlalchemy import (
|
| 4 |
-
Column,
|
| 5 |
-
DateTime,
|
| 6 |
-
Enum,
|
| 7 |
-
Float,
|
| 8 |
-
ForeignKey,
|
| 9 |
-
Integer,
|
| 10 |
-
String,
|
| 11 |
-
func,
|
| 12 |
-
)
|
| 13 |
-
from sqlalchemy.dialects.postgresql import UUID
|
| 14 |
-
from pydantic import BaseModel
|
| 15 |
-
from ._base import Base
|
| 16 |
-
|
| 17 |
-
class OperationCriteria(PyEnum):
|
| 18 |
-
MISSION_ALIGNMENT = "MISSION_ALIGNMENT"
|
| 19 |
-
TECHNICAL_FEASIBILITY = "TECHNICAL_FEASIBILITY"
|
| 20 |
-
COST_EFFICIENCY = "COST_EFFICIENCY"
|
| 21 |
-
RISK_ASSESSMENT = "RISK_ASSESSMENT"
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
class ProposalDetailAnalysis(Base):
|
| 25 |
-
__tablename__ = "proposal_detail_analyses"
|
| 26 |
-
|
| 27 |
-
id = Column(UUID(as_uuid=True), primary_key=True, nullable=False)
|
| 28 |
-
proposal_id = Column(
|
| 29 |
-
UUID(as_uuid=True),
|
| 30 |
-
ForeignKey("proposals.id", ondelete="CASCADE"),
|
| 31 |
-
nullable=False,
|
| 32 |
-
)
|
| 33 |
-
ai_assessment_result = Column(String, nullable=False)
|
| 34 |
-
confidence = Column(Float, nullable=False)
|
| 35 |
-
operation_criteria = Column(Enum(OperationCriteria), nullable=False)
|
| 36 |
-
created_at = Column(DateTime, nullable=False, default=func.now())
|
| 37 |
-
updated_at = Column(
|
| 38 |
-
DateTime, nullable=False, default=func.now(), onupdate=func.now()
|
| 39 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/models/_proposal_evaluation.py
DELETED
|
@@ -1,40 +0,0 @@
|
|
| 1 |
-
from enum import Enum as PyEnum
|
| 2 |
-
|
| 3 |
-
from sqlalchemy import (
|
| 4 |
-
Column,
|
| 5 |
-
DateTime,
|
| 6 |
-
Enum,
|
| 7 |
-
Float,
|
| 8 |
-
ForeignKey,
|
| 9 |
-
Integer,
|
| 10 |
-
String,
|
| 11 |
-
func,
|
| 12 |
-
)
|
| 13 |
-
from sqlalchemy.dialects.postgresql import UUID
|
| 14 |
-
from pydantic import BaseModel
|
| 15 |
-
from ._base import Base
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
class EvaluationType(PyEnum):
|
| 19 |
-
TECHNICAL = "TECHNICAL"
|
| 20 |
-
MANAGEMENT = "MANAGEMENT"
|
| 21 |
-
PAST_PERFORMANCE = "PAST_PERFORMANCE"
|
| 22 |
-
PRICE = "PRICE"
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
class Evaluations(Base):
|
| 26 |
-
__tablename__ = "evaluations"
|
| 27 |
-
|
| 28 |
-
id = Column(UUID(as_uuid=True), primary_key=True)
|
| 29 |
-
proposal_id = Column(
|
| 30 |
-
UUID(as_uuid=True),
|
| 31 |
-
ForeignKey("proposals.id", ondelete="CASCADE"),
|
| 32 |
-
nullable=False,
|
| 33 |
-
)
|
| 34 |
-
evaluation_type = Column(Enum(EvaluationType), nullable=False)
|
| 35 |
-
ai_score = Column(Float(), nullable=True)
|
| 36 |
-
adjusted_score = Column(Float(), nullable=True)
|
| 37 |
-
created_at = Column(DateTime(), nullable=False, default=func.now())
|
| 38 |
-
updated_at = Column(
|
| 39 |
-
DateTime(), nullable=False, default=func.now(), onupdate=func.now()
|
| 40 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/models/_proposal_query_models.py
DELETED
|
@@ -1,13 +0,0 @@
|
|
| 1 |
-
from enum import Enum as PyEnum
|
| 2 |
-
|
| 3 |
-
class QueryType(PyEnum):
|
| 4 |
-
ALL = "ALL"
|
| 5 |
-
AI_SCORE = "AI_SCORE"
|
| 6 |
-
STRENGTHS = "STRENGTHS"
|
| 7 |
-
WEAKNESSES = "WEAKNESSES"
|
| 8 |
-
REJECTION_REASONS = "REJECTION_REASONS"
|
| 9 |
-
IMPROVEMENTS = "IMPROVEMENTS"
|
| 10 |
-
|
| 11 |
-
class CreateOrUpdateType(PyEnum):
|
| 12 |
-
ALL = "ALL"
|
| 13 |
-
ID = "ID"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/models/_rfp.py
DELETED
|
@@ -1,47 +0,0 @@
|
|
| 1 |
-
from enum import Enum as PyEnum
|
| 2 |
-
|
| 3 |
-
from sqlalchemy import (
|
| 4 |
-
Boolean,
|
| 5 |
-
Column,
|
| 6 |
-
DateTime,
|
| 7 |
-
Enum,
|
| 8 |
-
Float,
|
| 9 |
-
ForeignKey,
|
| 10 |
-
Integer,
|
| 11 |
-
String,
|
| 12 |
-
func,
|
| 13 |
-
)
|
| 14 |
-
from sqlalchemy.dialects.postgresql import UUID
|
| 15 |
-
from pydantic import BaseModel
|
| 16 |
-
from ._base import Base
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
class RFPStatus(PyEnum):
|
| 20 |
-
ACTIVE = "ACTIVE"
|
| 21 |
-
IN_PROGRESS = "IN_PROGRESS"
|
| 22 |
-
NOT_YET_STARTED = "NOT_YET_STARTED"
|
| 23 |
-
CUSTOMER_HOLD = "CUSTOMER_HOLD"
|
| 24 |
-
UNSOLVED_ISSUES = "UNSOLVED_ISSUES"
|
| 25 |
-
AWARDED = "AWARDED"
|
| 26 |
-
PROTESTED = "PROTESTED"
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
class RFP(Base):
|
| 30 |
-
__tablename__ = "rfps"
|
| 31 |
-
|
| 32 |
-
id = Column(UUID(as_uuid=True), primary_key=True, nullable=False)
|
| 33 |
-
rfp_number = Column(String, nullable=False)
|
| 34 |
-
name = Column(String, nullable=False)
|
| 35 |
-
received_proposals = Column(Integer, nullable=False)
|
| 36 |
-
evaluated_count = Column(Integer, nullable=False)
|
| 37 |
-
awaiting_evaluation = Column(Integer, nullable=False)
|
| 38 |
-
target_award_date = Column(DateTime, nullable=True)
|
| 39 |
-
ko_name = Column(String, nullable=True)
|
| 40 |
-
award_type = Column(String, nullable=True)
|
| 41 |
-
google_drive_id = Column(String, nullable=True)
|
| 42 |
-
status = Column(Enum(RFPStatus), nullable=False)
|
| 43 |
-
comparative_weights_locked = Column(Boolean(), nullable=True, default=False)
|
| 44 |
-
created_at = Column(DateTime, nullable=False, default=func.now())
|
| 45 |
-
updated_at = Column(
|
| 46 |
-
DateTime, nullable=False, default=func.now(), onupdate=func.now()
|
| 47 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/repositories/__init__.py
DELETED
|
@@ -1,23 +0,0 @@
|
|
| 1 |
-
from ._base_repository import BaseRepository
|
| 2 |
-
from ._rfp_repository import RFPRepository
|
| 3 |
-
from ._proposal_repository import ProposalRepository
|
| 4 |
-
from ._proposal_detailed_analysis_repository import ProposalDetailedAnalysisRepository
|
| 5 |
-
from ._letter_repository import LetterRepository
|
| 6 |
-
from ._evaluation_repository import EvaluationRepository
|
| 7 |
-
from ._comparative_weight_repository import ComparativeWeightRepository
|
| 8 |
-
from ._proposal_evaluation_repository import ProposalEvaluationRepository
|
| 9 |
-
from ._analysis_repository import AnalysisRepository
|
| 10 |
-
|
| 11 |
-
__all__ = [
|
| 12 |
-
"BaseRepository",
|
| 13 |
-
"RFPRepository",
|
| 14 |
-
"ProposalRepository",
|
| 15 |
-
"ProposalDetailedAnalysisRepository",
|
| 16 |
-
"LetterRepository",
|
| 17 |
-
"EvaluationRepository",
|
| 18 |
-
"ComparativeWeightRepository",
|
| 19 |
-
"ProposalEvaluationRepository",
|
| 20 |
-
"AnalysisRepository",
|
| 21 |
-
]
|
| 22 |
-
__version__ = "0.1.0"
|
| 23 |
-
__author__ = "Aryan Jain"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/repositories/_analysis_repository.py
DELETED
|
@@ -1,99 +0,0 @@
|
|
| 1 |
-
import uuid
|
| 2 |
-
from sqlalchemy import select
|
| 3 |
-
from ._base_repository import BaseRepository
|
| 4 |
-
from src.models import AnalysisType, Analysis
|
| 5 |
-
from src.utils import ScoreClient
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
class AnalysisRepository(BaseRepository):
|
| 9 |
-
def __init__(self):
|
| 10 |
-
super().__init__(Analysis)
|
| 11 |
-
self.score_client = ScoreClient
|
| 12 |
-
|
| 13 |
-
async def __aenter__(self):
|
| 14 |
-
return self
|
| 15 |
-
|
| 16 |
-
async def __aexit__(self, exc_type, exc_value, traceback):
|
| 17 |
-
pass
|
| 18 |
-
|
| 19 |
-
async def get_analysis(
|
| 20 |
-
self,
|
| 21 |
-
evaluation_id: str = None,
|
| 22 |
-
analysis_type: AnalysisType = None,
|
| 23 |
-
id: str = None,
|
| 24 |
-
):
|
| 25 |
-
async with self.get_session() as session:
|
| 26 |
-
query = select(Analysis)
|
| 27 |
-
if evaluation_id is not None:
|
| 28 |
-
query = query.filter(Analysis.evaluation_id == evaluation_id)
|
| 29 |
-
if analysis_type is not None:
|
| 30 |
-
query = query.filter(Analysis.analysis_type == analysis_type)
|
| 31 |
-
if id is not None:
|
| 32 |
-
query = query.filter(Analysis.id == id)
|
| 33 |
-
result = await session.execute(query)
|
| 34 |
-
results = result.scalars().all()
|
| 35 |
-
return [
|
| 36 |
-
{k: v for k, v in result.__dict__.items() if not k.startswith("_")}
|
| 37 |
-
for result in results
|
| 38 |
-
]
|
| 39 |
-
|
| 40 |
-
async def create_analysis(
|
| 41 |
-
self,
|
| 42 |
-
evaluation_id: str,
|
| 43 |
-
evaluation_analysis: dict,
|
| 44 |
-
analysis_score_type: str = None,
|
| 45 |
-
):
|
| 46 |
-
async with self.get_session() as session:
|
| 47 |
-
for analysis in evaluation_analysis:
|
| 48 |
-
analysis["evaluation_id"] = evaluation_id
|
| 49 |
-
analysis["id"] = str(uuid.uuid4())
|
| 50 |
-
instance = Analysis(**analysis)
|
| 51 |
-
session.add(instance)
|
| 52 |
-
await session.commit()
|
| 53 |
-
await session.refresh(instance)
|
| 54 |
-
async with self.score_client() as score_client:
|
| 55 |
-
await score_client.update_ai_score(
|
| 56 |
-
evaluation_id=evaluation_id, analysis_score_type=analysis_score_type
|
| 57 |
-
)
|
| 58 |
-
return await self.get_analysis(evaluation_id=evaluation_id)
|
| 59 |
-
|
| 60 |
-
async def update_analysis(
|
| 61 |
-
self, evaluation_id: str = None, id: str = None, proposal_analysis: dict = None
|
| 62 |
-
):
|
| 63 |
-
async with self.get_session() as session:
|
| 64 |
-
if id:
|
| 65 |
-
query = select(Analysis).where(Analysis.id == id)
|
| 66 |
-
result = await session.execute(query)
|
| 67 |
-
instance = result.scalars().one()
|
| 68 |
-
for k, v in proposal_analysis.items():
|
| 69 |
-
setattr(instance, k, v)
|
| 70 |
-
await session.commit()
|
| 71 |
-
await session.refresh(instance)
|
| 72 |
-
async with self.score_client() as score_client:
|
| 73 |
-
await score_client.update_ai_score(evaluation_id=evaluation_id)
|
| 74 |
-
return [
|
| 75 |
-
{
|
| 76 |
-
k: v
|
| 77 |
-
for k, v in instance.__dict__.items()
|
| 78 |
-
if not k.startswith("_")
|
| 79 |
-
}
|
| 80 |
-
]
|
| 81 |
-
else:
|
| 82 |
-
await self.delete_analysis(evaluation_id=evaluation_id)
|
| 83 |
-
return await self.create_analysis(
|
| 84 |
-
evaluation_id=evaluation_id, evaluation_analysis=proposal_analysis
|
| 85 |
-
)
|
| 86 |
-
|
| 87 |
-
async def delete_analysis(self, evaluation_id: str = None, id: str = None):
|
| 88 |
-
async with self.get_session() as session:
|
| 89 |
-
query = select(Analysis)
|
| 90 |
-
if evaluation_id:
|
| 91 |
-
query = query.filter(Analysis.evaluation_id == evaluation_id)
|
| 92 |
-
if id:
|
| 93 |
-
query = query.filter(Analysis.id == id)
|
| 94 |
-
result = await session.execute(query)
|
| 95 |
-
instances = result.scalars().all()
|
| 96 |
-
for instance in instances:
|
| 97 |
-
await session.delete(instance)
|
| 98 |
-
await session.commit()
|
| 99 |
-
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|