diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3679386..bce9338 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -105,10 +105,10 @@ jobs: run: uv sync --dev - name: Ruff lint - run: uv run ruff check src/ + run: uv run ruff check foresight/ - name: Ruff format check - run: uv run ruff format --check src/ + run: uv run ruff format --check foresight/ test: name: Tests diff --git a/README.md b/README.md index b993acb..87308a1 100644 --- a/README.md +++ b/README.md @@ -75,19 +75,21 @@ Commit messages must follow [Conventional Commits](https://www.conventionalcommi ``` foresight/ ├── backend/ -│ ├── src/ -│ │ └── foresight/ # Installable package (see pyproject.toml) -│ │ ├── main.py # FastAPI app entrypoint (foresight.main:app) -│ │ ├── config.py # Settings (pydantic-settings) -│ │ ├── routes/ # API route handlers -│ │ ├── schemas/ # Pydantic request/response models -│ │ ├── services/ # Business logic -│ │ ├── repository/ # Data-access layer -│ │ └── database/ # SQLAlchemy + Alembic -│ │ ├── base.py # Declarative base -│ │ ├── session.py # Async session factory -│ │ ├── models/ # SQLAlchemy models -│ │ └── migrations/ # Alembic migrations +│ ├── foresight/ # Installable package (see pyproject.toml) +│ │ ├── main.py # FastAPI app entrypoint (foresight.main:app) +│ │ ├── config.py # Settings (pydantic-settings) +│ │ ├── routes/ # API route handlers +│ │ ├── schemas/ # Pydantic request/response models +│ │ ├── services/ # Business logic +│ │ │ ├── ml/ # ML models (regression, bayesian, ...) +│ │ │ └── parsers/ # Client data ingestion +│ │ ├── repository/ # Data-access layer +│ │ └── database/ # SQLAlchemy + Alembic +│ │ ├── base.py # Declarative base +│ │ ├── session.py # Async session factory +│ │ ├── models/ # SQLAlchemy models +│ │ └── migrations/ # Alembic migrations +│ ├── scripts/ # Dev tooling (mock data, model comparison) │ ├── tests/ # Pytest suite │ ├── alembic.ini │ ├── pyproject.toml diff --git a/backend/Dockerfile b/backend/Dockerfile index 2bd3882..77def63 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -30,7 +30,7 @@ EXPOSE 8000 # ---- dev ------------------------------------------------------------------- # Adds dev dependencies (pytest, ruff, ...) and runs uvicorn with --reload. -# docker-compose.yml bind-mounts ./backend/src over /app/src so edits are +# docker-compose.yml bind-mounts ./backend/foresight over /app/foresight so edits are # picked up live. Selected via `build.target: dev` in docker-compose.yml. FROM base AS dev diff --git a/backend/alembic.ini b/backend/alembic.ini index 894430d..ce5fe9f 100644 --- a/backend/alembic.ini +++ b/backend/alembic.ini @@ -1,5 +1,5 @@ [alembic] -script_location = src/foresight/database/migrations +script_location = foresight/database/migrations # The `foresight` package is installed via `uv sync`, so no sys.path # manipulation is needed for `from foresight...` imports in env.py to resolve. diff --git a/backend/src/foresight/__init__.py b/backend/foresight/__init__.py similarity index 100% rename from backend/src/foresight/__init__.py rename to backend/foresight/__init__.py diff --git a/backend/src/foresight/config.py b/backend/foresight/config.py similarity index 100% rename from backend/src/foresight/config.py rename to backend/foresight/config.py diff --git a/backend/src/foresight/database/__init__.py b/backend/foresight/database/__init__.py similarity index 100% rename from backend/src/foresight/database/__init__.py rename to backend/foresight/database/__init__.py diff --git a/backend/src/foresight/database/base.py b/backend/foresight/database/base.py similarity index 100% rename from backend/src/foresight/database/base.py rename to backend/foresight/database/base.py diff --git a/backend/src/foresight/database/migrations/env.py b/backend/foresight/database/migrations/env.py similarity index 100% rename from backend/src/foresight/database/migrations/env.py rename to backend/foresight/database/migrations/env.py diff --git a/backend/src/foresight/database/migrations/script.py.mako b/backend/foresight/database/migrations/script.py.mako similarity index 100% rename from backend/src/foresight/database/migrations/script.py.mako rename to backend/foresight/database/migrations/script.py.mako diff --git a/backend/src/foresight/database/migrations/versions/.gitkeep b/backend/foresight/database/migrations/versions/.gitkeep similarity index 100% rename from backend/src/foresight/database/migrations/versions/.gitkeep rename to backend/foresight/database/migrations/versions/.gitkeep diff --git a/backend/src/foresight/database/migrations/versions/72e850f4fa7b_hotel_and_external_schema.py b/backend/foresight/database/migrations/versions/72e850f4fa7b_hotel_and_external_schema.py similarity index 100% rename from backend/src/foresight/database/migrations/versions/72e850f4fa7b_hotel_and_external_schema.py rename to backend/foresight/database/migrations/versions/72e850f4fa7b_hotel_and_external_schema.py diff --git a/backend/src/foresight/database/models/__init__.py b/backend/foresight/database/models/__init__.py similarity index 100% rename from backend/src/foresight/database/models/__init__.py rename to backend/foresight/database/models/__init__.py diff --git a/backend/src/foresight/database/models/external.py b/backend/foresight/database/models/external.py similarity index 100% rename from backend/src/foresight/database/models/external.py rename to backend/foresight/database/models/external.py diff --git a/backend/src/foresight/database/models/hotel.py b/backend/foresight/database/models/hotel.py similarity index 100% rename from backend/src/foresight/database/models/hotel.py rename to backend/foresight/database/models/hotel.py diff --git a/backend/src/foresight/database/session.py b/backend/foresight/database/session.py similarity index 100% rename from backend/src/foresight/database/session.py rename to backend/foresight/database/session.py diff --git a/backend/src/foresight/main.py b/backend/foresight/main.py similarity index 100% rename from backend/src/foresight/main.py rename to backend/foresight/main.py diff --git a/backend/src/foresight/repository/__init__.py b/backend/foresight/repository/__init__.py similarity index 100% rename from backend/src/foresight/repository/__init__.py rename to backend/foresight/repository/__init__.py diff --git a/backend/src/foresight/routes/__init__.py b/backend/foresight/routes/__init__.py similarity index 100% rename from backend/src/foresight/routes/__init__.py rename to backend/foresight/routes/__init__.py diff --git a/backend/src/foresight/routes/health.py b/backend/foresight/routes/health.py similarity index 100% rename from backend/src/foresight/routes/health.py rename to backend/foresight/routes/health.py diff --git a/backend/src/foresight/schemas/__init__.py b/backend/foresight/schemas/__init__.py similarity index 100% rename from backend/src/foresight/schemas/__init__.py rename to backend/foresight/schemas/__init__.py diff --git a/backend/src/foresight/schemas/hotel.py b/backend/foresight/schemas/hotel.py similarity index 100% rename from backend/src/foresight/schemas/hotel.py rename to backend/foresight/schemas/hotel.py diff --git a/backend/src/foresight/services/__init__.py b/backend/foresight/services/__init__.py similarity index 100% rename from backend/src/foresight/services/__init__.py rename to backend/foresight/services/__init__.py diff --git a/backend/src/services/ml/__init__.py b/backend/foresight/services/ml/__init__.py similarity index 100% rename from backend/src/services/ml/__init__.py rename to backend/foresight/services/ml/__init__.py diff --git a/backend/src/services/ml/base.py b/backend/foresight/services/ml/base.py similarity index 100% rename from backend/src/services/ml/base.py rename to backend/foresight/services/ml/base.py diff --git a/backend/src/services/ml/bayesian.py b/backend/foresight/services/ml/bayesian.py similarity index 97% rename from backend/src/services/ml/bayesian.py rename to backend/foresight/services/ml/bayesian.py index 2ec5d96..af04f80 100644 --- a/backend/src/services/ml/bayesian.py +++ b/backend/foresight/services/ml/bayesian.py @@ -3,7 +3,7 @@ from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler -from services.ml.base import Model +from foresight.services.ml.base import Model class BayesianModel(Model): diff --git a/backend/src/services/ml/neural_net.py b/backend/foresight/services/ml/neural_net.py similarity index 96% rename from backend/src/services/ml/neural_net.py rename to backend/foresight/services/ml/neural_net.py index 3b08f3e..c46bc0a 100644 --- a/backend/src/services/ml/neural_net.py +++ b/backend/foresight/services/ml/neural_net.py @@ -2,7 +2,7 @@ from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler -from services.ml.base import Model +from foresight.services.ml.base import Model DEFAULTS = { "hidden_layer_sizes": (32, 16), diff --git a/backend/src/services/ml/regression.py b/backend/foresight/services/ml/regression.py similarity index 98% rename from backend/src/services/ml/regression.py rename to backend/foresight/services/ml/regression.py index 0f379e2..b5abec6 100644 --- a/backend/src/services/ml/regression.py +++ b/backend/foresight/services/ml/regression.py @@ -1,7 +1,7 @@ import numpy as np from sklearn.ensemble import HistGradientBoostingRegressor -from services.ml.base import Model +from foresight.services.ml.base import Model class GBMModel(Model): diff --git a/backend/src/services/ml/timeseries.py b/backend/foresight/services/ml/timeseries.py similarity index 98% rename from backend/src/services/ml/timeseries.py rename to backend/foresight/services/ml/timeseries.py index eb0e24d..8eb9004 100644 --- a/backend/src/services/ml/timeseries.py +++ b/backend/foresight/services/ml/timeseries.py @@ -1,6 +1,6 @@ from statsmodels.tsa.statespace.sarimax import SARIMAX -from services.ml.base import Model +from foresight.services.ml.base import Model class TimeSeriesModel(Model): diff --git a/backend/src/parsers/__init__.py b/backend/foresight/services/parsers/__init__.py similarity index 100% rename from backend/src/parsers/__init__.py rename to backend/foresight/services/parsers/__init__.py diff --git a/backend/src/parsers/hotel_data_parser.py b/backend/foresight/services/parsers/hotel_data_parser.py similarity index 100% rename from backend/src/parsers/hotel_data_parser.py rename to backend/foresight/services/parsers/hotel_data_parser.py diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 3c6ed78..3412379 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -42,12 +42,12 @@ ml = [ ] [tool.hatch.build.targets.wheel] -packages = ["src/foresight"] +packages = ["foresight"] [tool.ruff] target-version = "py312" -src = ["src"] -exclude = ["src/foresight/database/migrations"] +src = ["."] +exclude = ["foresight/database/migrations"] line-length = 88 [tool.ruff.lint] @@ -69,4 +69,4 @@ known-first-party = ["foresight"] [tool.pytest.ini_options] asyncio_mode = "auto" testpaths = ["tests"] -pythonpath = ["src", "scripts"] +pythonpath = ["scripts"] diff --git a/backend/scripts/compare_models.py b/backend/scripts/compare_models.py index 9f3af18..ace9671 100644 --- a/backend/scripts/compare_models.py +++ b/backend/scripts/compare_models.py @@ -3,19 +3,14 @@ Run from backend/: uv run python scripts/compare_models.py """ -import sys -from pathlib import Path - import numpy as np import pandas as pd - from generate_mock_data import write_csv -sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src")) -from services.ml.bayesian import BayesianModel -from services.ml.neural_net import NNModel -from services.ml.regression import GBMModel -from services.ml.timeseries import TimeSeriesModel +from foresight.services.ml.bayesian import BayesianModel +from foresight.services.ml.neural_net import NNModel +from foresight.services.ml.regression import GBMModel +from foresight.services.ml.timeseries import TimeSeriesModel # Known only after the night, so never used to predict price. POST_STAY_COLUMNS = ["rooms_sold", "occupancy", "room_revenue"] diff --git a/backend/tests/test_ml_models.py b/backend/tests/test_ml_models.py index f6bca88..d604fcc 100644 --- a/backend/tests/test_ml_models.py +++ b/backend/tests/test_ml_models.py @@ -4,11 +4,11 @@ import pandas as pd import pytest -from services.ml.base import Model -from services.ml.bayesian import BayesianModel -from services.ml.neural_net import NNModel -from services.ml.regression import GBMModel -from services.ml.timeseries import TimeSeriesModel +from foresight.services.ml.base import Model +from foresight.services.ml.bayesian import BayesianModel +from foresight.services.ml.neural_net import NNModel +from foresight.services.ml.regression import GBMModel +from foresight.services.ml.timeseries import TimeSeriesModel MODELS = [BayesianModel, NNModel, GBMModel, TimeSeriesModel] @@ -65,6 +65,7 @@ def test_nn_has_no_interval(price_split): model.predict_interval(x_test) assert model.evaluate(x_test, y_test)["coverage"] is None + class FixedModel(Model): """Returns preset predictions and a +/-5 band, recording the alpha asked for.""" diff --git a/backend/uv.lock b/backend/uv.lock index 3a7670b..d3769fd 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -331,6 +331,8 @@ dependencies = [ { name = "alembic" }, { name = "asyncpg" }, { name = "fastapi" }, + { name = "pandas" }, + { name = "psycopg2-binary" }, { name = "pydantic-settings" }, { name = "python-dotenv" }, { name = "sqlalchemy", extra = ["asyncio"] }, @@ -359,6 +361,8 @@ requires-dist = [ { name = "alembic", specifier = ">=1.15" }, { name = "asyncpg", specifier = ">=0.30" }, { name = "fastapi", specifier = ">=0.115" }, + { name = "pandas", specifier = ">=2.2" }, + { name = "psycopg2-binary", specifier = ">=2.9" }, { name = "pydantic-settings", specifier = ">=2.9" }, { name = "python-dotenv", specifier = ">=1.1" }, { name = "sqlalchemy", extras = ["asyncio"], specifier = ">=2.0" }, @@ -1114,6 +1118,58 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/45/e2/bbb7129c9e7999a6b8ee9cca3b66486c25c423ab5a75f34071798b74ce94/pre_commit-4.6.2-py2.py3-none-any.whl", hash = "sha256:e2dde9a75d3bce11bd3831c26d134df00a2803c1d818be6a0383c3dcda25dc4e", size = 226202, upload-time = "2026-08-10T22:07:16.942Z" }, ] +[[package]] +name = "psycopg2-binary" +version = "2.9.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/76/7b4383014be0fcc6c1c0e24292845a14e1672cf17fca62ca0a2bd5f4563d/psycopg2_binary-2.9.13.tar.gz", hash = "sha256:e324ecf60f952d21dd11413b8bbed0951bbd99579a06fd06f28bfc37737cd373", size = 378112, upload-time = "2026-09-10T00:06:12.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/d1/d0125c56b865e3bc9f318d84930b2df71a729229dbb0ce12de748a82a6d7/psycopg2_binary-2.9.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2bf9f97a6df69a5d89d054b8cf5257a0916096c479800715fbfe7974dbcb3a26", size = 3725735, upload-time = "2026-09-09T23:54:51.182Z" }, + { url = "https://files.pythonhosted.org/packages/54/a5/b5a73d0910555e38ee12c49c1740855f8a1e9776e87d65f0c51e1bab762a/psycopg2_binary-2.9.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:07b7bd9f410650c34c3532162cc329f112368d78a3fc8668cb1ea9df61bc11bf", size = 3818000, upload-time = "2026-09-09T23:54:53.229Z" }, + { url = "https://files.pythonhosted.org/packages/3d/43/3e4783f62ae3f4fc19a5acf8d1c394df54458f1336febe188f317556d2a7/psycopg2_binary-2.9.13-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:0463c00f946517f3e69192a59e6601e023ff9de45ad0a875eda3d6b1bebeb7ce", size = 4585760, upload-time = "2026-09-09T23:54:55.313Z" }, + { url = "https://files.pythonhosted.org/packages/8d/c4/a9a67ae65ad3d567eb0fc9cdf9a5a2783b779aecdcdc8945f1807b13d99e/psycopg2_binary-2.9.13-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e3861eba31f8ea8663fd876166b032fd89179e42aa63764d6feb281f13f9eb60", size = 4282380, upload-time = "2026-09-09T23:54:57.362Z" }, + { url = "https://files.pythonhosted.org/packages/b3/db/9d459d3da12e0b841cf1596579455aaa27e9e593e3e9a5a4ded5a55a7c15/psycopg2_binary-2.9.13-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3dc3372b3731b3ef23407fe06b94f640ef87a2bda242fa386033d5589c87514a", size = 5902039, upload-time = "2026-09-09T23:55:01.955Z" }, + { url = "https://files.pythonhosted.org/packages/d6/53/21079c10a581c50b6817498eda7c3481c1b3cdb41482bd08ebfccd3664c4/psycopg2_binary-2.9.13-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0405dd4d97720e7ab177aa02e493f524907c4cb3c445ac173e2627948d3d0528", size = 4119840, upload-time = "2026-09-09T23:55:04.336Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ce/71e8d9e1b4f3e78157b49a5abdff50d915e95f2812550f6c9b4f2e4d5e94/psycopg2_binary-2.9.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b6ae51708201f501a171b02419d0c30878a743c369c9054eb1289f0f8d5979e2", size = 3661077, upload-time = "2026-09-09T23:55:06.118Z" }, + { url = "https://files.pythonhosted.org/packages/d9/54/b17616472f09a0fae96f8852692948b7eaa7c971d7629696da0e5932d996/psycopg2_binary-2.9.13-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:81682c227cc1849c4a6adf7b85274229073bb4c9d6ad5697222c695dcea5a8a7", size = 3307202, upload-time = "2026-09-09T23:55:08.061Z" }, + { url = "https://files.pythonhosted.org/packages/77/c7/d9737e222a377dac67a0ce0a2c73e7231a57f5cf18bb35a65d5c8d45d5d2/psycopg2_binary-2.9.13-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:13d955f6054a705a19554364fe9888d0a6e8b0746dc7ebc08a447c7b4fd4145c", size = 3052893, upload-time = "2026-09-09T23:55:10.209Z" }, + { url = "https://files.pythonhosted.org/packages/7d/3d/c406c9f698f518c264381192c2bdf8952ee84e469ffa9f82db1411f57385/psycopg2_binary-2.9.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7e2405196a8cfe6cd3e54172a54452dcf85c241eaf2e9dde7190d7469f7f5ef7", size = 3355210, upload-time = "2026-09-09T23:55:11.883Z" }, + { url = "https://files.pythonhosted.org/packages/27/64/6e3a96699770af2d0d49a2002f722c69b656fc27623ff89281cf2b109644/psycopg2_binary-2.9.13-cp312-cp312-win_amd64.whl", hash = "sha256:376ebf7d8aee4b7386b2bac31fdc27911e7e57cd0a88f1e038b8b149398ac008", size = 2767784, upload-time = "2026-09-09T23:55:13.823Z" }, + { url = "https://files.pythonhosted.org/packages/82/0a/795f2869788373cf7d08410341a444196e8ccebbac07a70a8f9a1f60e72f/psycopg2_binary-2.9.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4d66bfd44a46eb88cff0287929a4193fb45166b6c1f84bb1b233cc17ece0813c", size = 3725723, upload-time = "2026-09-09T23:55:15.887Z" }, + { url = "https://files.pythonhosted.org/packages/b5/63/5a9633f4563a73beba69b20a846ddd14c1c6ac072f5e8aab0da97ffabc2a/psycopg2_binary-2.9.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f818161d2302b3b3e9c75d5a1d0a5c5679e92e45cfec6432b9d5432dde5ff1f1", size = 3817976, upload-time = "2026-09-09T23:55:18.025Z" }, + { url = "https://files.pythonhosted.org/packages/6c/e2/b2e3b3a4331dc8b58e328cda30f3d0cc43a94b7aaf0c8383efd53dd10e95/psycopg2_binary-2.9.13-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:31db6cba66df5231dfd91d9f69188bec3fe6c8baae384e93a0ce792067ee2d98", size = 4585813, upload-time = "2026-09-09T23:55:20.112Z" }, + { url = "https://files.pythonhosted.org/packages/56/5c/87daea77c4132114d1a5da3a4928dd59446c3b3cc73d288cae08cf0b91a6/psycopg2_binary-2.9.13-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f04ada42bcd537adbaf8b7f3140237a204e452a88d0c1831cfce69f7d2e59f4e", size = 4282438, upload-time = "2026-09-09T23:55:22.329Z" }, + { url = "https://files.pythonhosted.org/packages/91/e5/56f9efdc9337acbd1a75798d97163183b63a1babc17602f7163009506c96/psycopg2_binary-2.9.13-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa37089795bd9701576edc2eb5849ce77a439eda9dfdfa47857449332cfa5292", size = 5902064, upload-time = "2026-09-09T23:55:24.37Z" }, + { url = "https://files.pythonhosted.org/packages/e4/15/f7ed0b90b47b73a9087306b42267eccfd919f92c0fb057e46bd2fa2efa4d/psycopg2_binary-2.9.13-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:41c2eb569ebd0e1b02d30d361a46932923b193fe1b5e641fb4d547c75e218955", size = 4119848, upload-time = "2026-09-09T23:55:26.433Z" }, + { url = "https://files.pythonhosted.org/packages/42/08/3091347b9fc5766e979aba6b0756ad14ce867a6bb245f3d69ac71fb768c6/psycopg2_binary-2.9.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3f699a5225094a5c61402984e2fc1eca20e940223e76767c88189efb0c313f69", size = 3661129, upload-time = "2026-09-09T23:55:28.449Z" }, + { url = "https://files.pythonhosted.org/packages/34/c4/4f9a84d55484c9794b364548eb6e1fe10a57f123afd19729e5a1cc8ad7fc/psycopg2_binary-2.9.13-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5f04ae99c9fbb94c3197ec88599ed7db921f6adcddfe83687a74c7ead4037c22", size = 3307262, upload-time = "2026-09-09T23:55:30.384Z" }, + { url = "https://files.pythonhosted.org/packages/83/42/6eba8306a61dc890805ae475a9e71790a1c5461ccacbd4f0a1f3f57b40f0/psycopg2_binary-2.9.13-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:81404c37e0344ebcf10aac127d33d35137e5dbab1daf9f3deee46188fd5879c2", size = 3052898, upload-time = "2026-09-09T23:55:32.961Z" }, + { url = "https://files.pythonhosted.org/packages/b3/5d/42a8935ab280e8dcd7c07a655c0c3d25d62e9e242be1961ac14630f1294a/psycopg2_binary-2.9.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:feb7b1856f6ca805cc0e08739858f6cdfed8ce903390126af30343c62899a389", size = 3355265, upload-time = "2026-09-09T23:55:35.071Z" }, + { url = "https://files.pythonhosted.org/packages/87/c2/0e0ffb4caeb651631cbc6c8ead83e2a16457750b1d2eb7f5ef111c1f4d36/psycopg2_binary-2.9.13-cp313-cp313-win_amd64.whl", hash = "sha256:691da68ae5dd7c3ac77514357d35ece7b1ba8b5f3e6c92735198aa6159c355c8", size = 2767914, upload-time = "2026-09-09T23:55:37.14Z" }, + { url = "https://files.pythonhosted.org/packages/5f/32/897c074cb99fbdda7d34b0a2546097a59162bb3d04c0d546ae4ec82345e3/psycopg2_binary-2.9.13-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2ca263643ae37998ae04d18e431df34d0d61f12b47640dab585f14b6dbe00798", size = 3725788, upload-time = "2026-09-09T23:55:39.04Z" }, + { url = "https://files.pythonhosted.org/packages/0f/f4/e3a789de34c9ac25d20b25c2be583da16394a2ba0926da1c863653831f41/psycopg2_binary-2.9.13-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c0214c7da18a28d108aa7108c8a3cca8035c7911ec97ef9ec0827569c9a2720", size = 3818111, upload-time = "2026-09-09T23:55:40.979Z" }, + { url = "https://files.pythonhosted.org/packages/72/29/647724c43ac510dbc59b80e20e85d439deb94f5d5a024153c32330fa041d/psycopg2_binary-2.9.13-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5d89e064bb12b40cad696cf4975e6da86f8c60f14cd06cb6c1bc0a7f5d01761f", size = 4586235, upload-time = "2026-09-09T23:55:43.012Z" }, + { url = "https://files.pythonhosted.org/packages/91/ad/7f52f92cc65c23778daff7eec4ee2099236694a0a4723a5f180d0708b607/psycopg2_binary-2.9.13-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:190c18b97d9ef72f2e88c451b6588af90d6bd7bf54cb94b963280dc86a2c7076", size = 4282424, upload-time = "2026-09-09T23:55:44.843Z" }, + { url = "https://files.pythonhosted.org/packages/3d/2a/1a472059b198942d99651656e2bc610575584478bfe68d297ecabbd4887f/psycopg2_binary-2.9.13-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c00ebe9a2f31151aade0db233dc1446513a95e92c39ce055ee097af0ae86be1c", size = 5902365, upload-time = "2026-09-09T23:55:46.619Z" }, + { url = "https://files.pythonhosted.org/packages/91/1a/171ea5dac7b3a0fa57b3cb59c2ad6d7b8bc60732368fecfd2ed1f1288392/psycopg2_binary-2.9.13-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5085f7ff7b1e890f279577cedeb8c628957869a340fa34a39f7f406500b3c916", size = 4119938, upload-time = "2026-09-09T23:55:49.381Z" }, + { url = "https://files.pythonhosted.org/packages/41/ce/3c6d4ad71853a59eee6a575fe36df4bb40752a9735a27bd62af66b454ed5/psycopg2_binary-2.9.13-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4e55357d1943673d491bbabb171c891704fc6a22441fea539e05a5c27a79ea3c", size = 3661557, upload-time = "2026-09-09T23:55:51.269Z" }, + { url = "https://files.pythonhosted.org/packages/10/a3/1819a01bf951eab2afb5ca2a3d11f50500bf536fecff088154372a8d1985/psycopg2_binary-2.9.13-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:3e60b06ec7f9dc3e5f1106d12706514b6d6b92c3dc438fcdf4e43e65cc660d1b", size = 3307770, upload-time = "2026-09-09T23:55:53.196Z" }, + { url = "https://files.pythonhosted.org/packages/4e/df/22f4aec952cd5b2dd02f438399583ed69f7d04b90e7c31659d9571bbe188/psycopg2_binary-2.9.13-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:dde942b46ce20f6c4464cdf551f3293207f803f4e4354454eb1f5599c3eb1fa1", size = 3052935, upload-time = "2026-09-09T23:55:55.117Z" }, + { url = "https://files.pythonhosted.org/packages/95/42/aab651bc22bafa961806ca3b21027bb0739a2730b0e6f7f0778baeb95e67/psycopg2_binary-2.9.13-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:215777c62ce81c3b487cefdb6a41969944eb982309f91349ff3ca0323d6f17ed", size = 3355387, upload-time = "2026-09-09T23:55:57.366Z" }, + { url = "https://files.pythonhosted.org/packages/bc/af/3b8220633eaf955e95ea7be67d76e81a0d1cd3c76362ea504b91ffa079db/psycopg2_binary-2.9.13-cp314-cp314-win_amd64.whl", hash = "sha256:f3088eb80f58ed933c62d87128741d31e786edc862e23266d3c286763d646de0", size = 2863890, upload-time = "2026-09-09T23:55:59.056Z" }, + { url = "https://files.pythonhosted.org/packages/6e/f1/377d17fc8425220d17552691cd2b97aa232da92173f5dead71278b83f8ab/psycopg2_binary-2.9.13-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:38397def2d794ffde9db80f63d6820253e61b17483112652a318355f51a56f50", size = 3725913, upload-time = "2026-09-09T23:56:00.736Z" }, + { url = "https://files.pythonhosted.org/packages/67/64/27208e67cd6e663f69bf7bf905cf69db066a015c90ac9ca948a56a8e9d78/psycopg2_binary-2.9.13-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:dff5c70ed9789ccb0d97ff4a7da51dc523a255c4ec95df188fa5d44adcae4ea8", size = 3818303, upload-time = "2026-09-09T23:56:02.551Z" }, + { url = "https://files.pythonhosted.org/packages/6b/98/67d2f34a1d18367b5f655bdd101759f8474286c74ffe701b7d6e3abd7fda/psycopg2_binary-2.9.13-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:08d3b81a6a91775c937abf97d4c58fc9142e8e35fb91c387d24f81d15c98e6cf", size = 4586508, upload-time = "2026-09-09T23:56:04.706Z" }, + { url = "https://files.pythonhosted.org/packages/bb/47/46c227deaf322dceafa0b7b321b4e5de9cc797014b7a353349b2e09b1118/psycopg2_binary-2.9.13-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:541a487a9ccd72b5e38f37f27b0ce78cb7eb3e336e7b5277d45463010c03a7a8", size = 4282383, upload-time = "2026-09-09T23:56:06.678Z" }, + { url = "https://files.pythonhosted.org/packages/f4/3c/e8705ffa381160d842eaf06a8446e8416f1a2497dd70a7e62277f3be6e7a/psycopg2_binary-2.9.13-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:562fe2a43b30e781848dce63d9080c15414c777c96df348c4342558338cc7bf3", size = 5902765, upload-time = "2026-09-09T23:56:08.634Z" }, + { url = "https://files.pythonhosted.org/packages/53/cc/359821c18317228b8032456a3740c98045b719ed003a594b9ebac9330b86/psycopg2_binary-2.9.13-cp315-cp315-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dddfe650e7dda464d676c27fbedb5061f1ad05e1604627f54c770d7f799d36e9", size = 4122849, upload-time = "2026-09-09T23:56:10.671Z" }, + { url = "https://files.pythonhosted.org/packages/17/e5/4d935acb6d3258c7a767b3d527e54c0b537649101b55002a5dbcfe747e2a/psycopg2_binary-2.9.13-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:4ff0f575cbb14f30445858dcfdd751e043486f5290915df78a9818bc74042eff", size = 3662084, upload-time = "2026-09-09T23:56:12.316Z" }, + { url = "https://files.pythonhosted.org/packages/89/56/9e9bbc7c773c5de7bb25dd35d7f041c2a6f0fcfa9207a1ceaf01a1bc687c/psycopg2_binary-2.9.13-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:d79530b4c1af657d5620a1d21b8e39f2996aa06821d5564d05b22d6b8cd413d0", size = 3308077, upload-time = "2026-09-09T23:56:15.262Z" }, + { url = "https://files.pythonhosted.org/packages/36/fa/ed742cd4e5dbddcb44702f9c4a97f7f5b62d97e3d9d00907ecc8ac750ef4/psycopg2_binary-2.9.13-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:6ede8595767e19d30a7e8a84a7d47bfde6176d45d194fed08dbb68d1584a780b", size = 3055143, upload-time = "2026-09-09T23:56:17.168Z" }, + { url = "https://files.pythonhosted.org/packages/d5/3a/5c2cb71a844ee236be2ce91b286d797e34a21489909357c7cfba0f5c0197/psycopg2_binary-2.9.13-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:0ebcf3c4266a695df9d0ef51296155f60c86ac51cf82f0d0dd2e827255a891c5", size = 3355374, upload-time = "2026-09-09T23:56:18.793Z" }, + { url = "https://files.pythonhosted.org/packages/e8/30/3991c9fdcca90a5a1e55435292f4d74d176da2be15f3998f6858da3658cc/psycopg2_binary-2.9.13-cp315-cp315-win_amd64.whl", hash = "sha256:1752b9821f1377404d65ac43af03d59a1eccc57fb2c1eb8305f9a3fe8eb7a8ba", size = 2867246, upload-time = "2026-09-09T23:56:20.501Z" }, +] + [[package]] name = "pydantic" version = "2.13.5" diff --git a/docker-compose.yml b/docker-compose.yml index 56a4a7a..356073f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -27,7 +27,7 @@ services: environment: DATABASE_URL: "postgresql+asyncpg://foresight:foresight@db:5432/foresight_db" volumes: - - ./backend/src:/app/src + - ./backend/foresight:/app/foresight - ./backend/alembic.ini:/app/alembic.ini depends_on: db: @@ -49,9 +49,9 @@ services: DATABASE_URL: "postgresql+asyncpg://foresight:foresight@db:5432/foresight_db" volumes: # Mount source code for hot reload. The `foresight` package is installed - # editable in the image, so edits under ./backend/src/foresight are + # editable in the image, so edits under ./backend/foresight are # picked up live by uvicorn --reload. - - ./backend/src:/app/src + - ./backend/foresight:/app/foresight - ./backend/alembic.ini:/app/alembic.ini depends_on: db: diff --git a/justfile b/justfile index 0014558..9103d3f 100644 --- a/justfile +++ b/justfile @@ -69,16 +69,16 @@ test: # Run ruff linter lint: - cd backend && uv run ruff check src/ + cd backend && uv run ruff check foresight/ # Run ruff formatter format: - cd backend && uv run ruff format src/ + cd backend && uv run ruff format foresight/ # Run ruff checks and fix fix: - cd backend && uv run ruff check --fix src/ - cd backend && uv run ruff format src/ + cd backend && uv run ruff check --fix foresight/ + cd backend && uv run ruff format foresight/ # --- Pre-commit ---