lecrapaud 0.4.1__py3-none-any.whl → 0.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of lecrapaud might be problematic. Click here for more details.

Files changed (37) hide show
  1. lecrapaud/config.py +15 -12
  2. lecrapaud/db/alembic/env.py +7 -2
  3. lecrapaud/db/alembic/versions/2025_06_20_1924-1edada319fd7_initial_setup.py +214 -0
  4. lecrapaud/db/alembic.ini +1 -1
  5. lecrapaud/db/models/base.py +28 -0
  6. lecrapaud/db/models/dataset.py +5 -6
  7. lecrapaud/db/models/feature.py +4 -3
  8. lecrapaud/db/models/feature_selection.py +11 -8
  9. lecrapaud/db/models/feature_selection_rank.py +4 -3
  10. lecrapaud/db/models/model.py +0 -1
  11. lecrapaud/db/models/model_selection.py +9 -4
  12. lecrapaud/db/models/model_training.py +2 -3
  13. lecrapaud/db/models/score.py +5 -13
  14. lecrapaud/db/models/target.py +2 -3
  15. lecrapaud/db/session.py +40 -18
  16. lecrapaud-0.5.0.dist-info/METADATA +263 -0
  17. lecrapaud-0.5.0.dist-info/RECORD +46 -0
  18. lecrapaud/db/alembic/versions/2025_04_06_1738-7390745388e4_initial_setup.py +0 -295
  19. lecrapaud/db/alembic/versions/2025_04_06_1755-40cd8d3e798e_unique_constraint_for_data.py +0 -30
  20. lecrapaud/db/alembic/versions/2025_05_23_1724-2360941fa0bd_longer_string.py +0 -52
  21. lecrapaud/db/alembic/versions/2025_05_27_1159-b96396dcfaff_add_env_to_trading_tables.py +0 -34
  22. lecrapaud/db/alembic/versions/2025_05_27_1337-40cbfc215f7c_fix_nb_character_on_portfolio.py +0 -39
  23. lecrapaud/db/alembic/versions/2025_05_27_1526-3de994115317_to_datetime.py +0 -36
  24. lecrapaud/db/alembic/versions/2025_05_27_2003-25c227c684f8_add_fees_to_transactions.py +0 -30
  25. lecrapaud/db/alembic/versions/2025_05_27_2047-6b6f2d38e9bc_double_instead_of_float.py +0 -132
  26. lecrapaud/db/alembic/versions/2025_05_31_1111-c175e4a36d68_generalise_stock_to_group.py +0 -36
  27. lecrapaud/db/alembic/versions/2025_05_31_1256-5681095bfc27_create_investment_run_and_portfolio_.py +0 -62
  28. lecrapaud/db/alembic/versions/2025_05_31_1806-339927587383_add_investment_run_id.py +0 -107
  29. lecrapaud/db/alembic/versions/2025_05_31_1834-52b809a34371_make_nullablee.py +0 -50
  30. lecrapaud/db/alembic/versions/2025_05_31_1849-3b8550297e8e_change_date_to_datetime.py +0 -44
  31. lecrapaud/db/alembic/versions/2025_05_31_1852-e6b8c95d8243_add_date_to_portfolio_history.py +0 -30
  32. lecrapaud/db/alembic/versions/2025_06_10_1136-db8cdd83563a_addnewsandoptiontodata.py +0 -32
  33. lecrapaud/db/alembic/versions/2025_06_17_1652-c45f5e49fa2c_make_fields_nullable.py +0 -89
  34. lecrapaud-0.4.1.dist-info/METADATA +0 -171
  35. lecrapaud-0.4.1.dist-info/RECORD +0 -61
  36. {lecrapaud-0.4.1.dist-info → lecrapaud-0.5.0.dist-info}/LICENSE +0 -0
  37. {lecrapaud-0.4.1.dist-info → lecrapaud-0.5.0.dist-info}/WHEEL +0 -0
lecrapaud/config.py CHANGED
@@ -5,22 +5,25 @@ load_dotenv(override=False)
5
5
 
6
6
  PYTHON_ENV = os.getenv("PYTHON_ENV")
7
7
  REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379")
8
- EMAIL = os.getenv("EMAIL")
9
8
  DATASET_ID = os.getenv("DATASET_ID")
10
- RECEIVER_EMAIL = os.getenv("RECEIVER_EMAIL")
11
- USERNAME = os.getenv("USERNAME")
12
- FRAISE = os.getenv("FRAISE")
13
- FA2 = os.getenv("2FA")
14
- INT = os.getenv("INT")
15
9
  LOGGING_LEVEL = os.getenv("LOGGING_LEVEL", "INFO")
16
- ALPHA_VENTAGE_API_KEY = os.getenv("ALPHA_VENTAGE_API_KEY")
17
10
 
18
- DB_USER = os.getenv("TEST_DB_USER") if PYTHON_ENV == "Test" else os.getenv("DB_USER")
11
+ DB_USER = (
12
+ os.getenv("TEST_DB_USER") if PYTHON_ENV == "Test" else os.getenv("DB_USER", None)
13
+ )
19
14
  DB_PASSWORD = (
20
- os.getenv("TEST_DB_PASSWORD") if PYTHON_ENV == "Test" else os.getenv("DB_PASSWORD")
15
+ os.getenv("TEST_DB_PASSWORD")
16
+ if PYTHON_ENV == "Test"
17
+ else os.getenv("DB_PASSWORD", None)
18
+ )
19
+ DB_HOST = (
20
+ os.getenv("TEST_DB_HOST") if PYTHON_ENV == "Test" else os.getenv("DB_HOST", None)
21
+ )
22
+ DB_PORT = (
23
+ os.getenv("TEST_DB_PORT") if PYTHON_ENV == "Test" else os.getenv("DB_PORT", None)
24
+ )
25
+ DB_NAME = (
26
+ os.getenv("TEST_DB_NAME") if PYTHON_ENV == "Test" else os.getenv("DB_NAME", None)
21
27
  )
22
- DB_HOST = os.getenv("TEST_DB_HOST") if PYTHON_ENV == "Test" else os.getenv("DB_HOST")
23
- DB_PORT = os.getenv("TEST_DB_PORT") if PYTHON_ENV == "Test" else os.getenv("DB_PORT")
24
- DB_NAME = os.getenv("TEST_DB_NAME") if PYTHON_ENV == "Test" else os.getenv("DB_NAME")
25
28
  DB_URI = os.getenv("DB_URI", None)
26
29
  OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
@@ -5,6 +5,7 @@ from sqlalchemy import pool
5
5
 
6
6
  from alembic import context
7
7
  from lecrapaud.db.session import DATABASE_URL
8
+ from lecrapaud.db.models.base import Base
8
9
 
9
10
  # this is the Alembic Config object, which provides
10
11
  # access to the values within the .ini file in use.
@@ -18,7 +19,6 @@ if config.config_file_name is not None:
18
19
 
19
20
  # add your model's MetaData object here
20
21
  # for 'autogenerate' support
21
- from lecrapaud.db.models.base import Base
22
22
 
23
23
  target_metadata = Base.metadata
24
24
 
@@ -46,6 +46,7 @@ def run_migrations_offline() -> None:
46
46
  target_metadata=target_metadata,
47
47
  literal_binds=True,
48
48
  dialect_opts={"paramstyle": "named"},
49
+ version_table="lecrapaud_alembic_version",
49
50
  )
50
51
 
51
52
  with context.begin_transaction():
@@ -66,7 +67,11 @@ def run_migrations_online() -> None:
66
67
  )
67
68
 
68
69
  with connectable.connect() as connection:
69
- context.configure(connection=connection, target_metadata=target_metadata)
70
+ context.configure(
71
+ connection=connection,
72
+ target_metadata=target_metadata,
73
+ version_table="lecrapaud_alembic_version",
74
+ )
70
75
 
71
76
  with context.begin_transaction():
72
77
  context.run_migrations()
@@ -0,0 +1,214 @@
1
+ """initial_setup
2
+
3
+ Revision ID: 1edada319fd7
4
+ Revises:
5
+ Create Date: 2025-06-20 19:24:25.033055
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 = '1edada319fd7'
16
+ down_revision: Union[str, None] = None
17
+ branch_labels: Union[str, Sequence[str], None] = None
18
+ depends_on: Union[str, Sequence[str], None] = None
19
+
20
+
21
+ def upgrade() -> None:
22
+ # ### commands auto generated by Alembic - please adjust! ###
23
+ op.create_table('lecrapaud_datasets',
24
+ sa.Column('id', sa.BigInteger(), autoincrement=True, nullable=False),
25
+ sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
26
+ sa.Column('updated_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
27
+ sa.Column('name', sa.String(length=50), nullable=False),
28
+ sa.Column('path', sa.String(length=255), nullable=True),
29
+ sa.Column('type', sa.String(length=50), nullable=False),
30
+ sa.Column('size', sa.Integer(), nullable=False),
31
+ sa.Column('train_size', sa.Integer(), nullable=True),
32
+ sa.Column('val_size', sa.Integer(), nullable=True),
33
+ sa.Column('test_size', sa.Integer(), nullable=True),
34
+ sa.Column('corr_threshold', sa.Float(), nullable=False),
35
+ sa.Column('max_features', sa.Integer(), nullable=False),
36
+ sa.Column('percentile', sa.Float(), nullable=False),
37
+ sa.Column('number_of_groups', sa.Integer(), nullable=True),
38
+ sa.Column('list_of_groups', sa.JSON(), nullable=True),
39
+ sa.Column('start_date', sa.DateTime(), nullable=True),
40
+ sa.Column('end_date', sa.DateTime(), nullable=True),
41
+ sa.Column('train_start_date', sa.DateTime(), nullable=True),
42
+ sa.Column('train_end_date', sa.DateTime(), nullable=True),
43
+ sa.Column('val_start_date', sa.DateTime(), nullable=True),
44
+ sa.Column('val_end_date', sa.DateTime(), nullable=True),
45
+ sa.Column('test_start_date', sa.DateTime(), nullable=True),
46
+ sa.Column('test_end_date', sa.DateTime(), nullable=True),
47
+ sa.PrimaryKeyConstraint('id'),
48
+ sa.UniqueConstraint('name', name='uq_datasets_composite')
49
+ )
50
+ op.create_index(op.f('ix_lecrapaud_datasets_id'), 'lecrapaud_datasets', ['id'], unique=False)
51
+ op.create_table('lecrapaud_features',
52
+ sa.Column('id', sa.BigInteger(), autoincrement=True, nullable=False),
53
+ sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
54
+ sa.Column('updated_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
55
+ sa.Column('name', sa.String(length=50), nullable=False),
56
+ sa.Column('type', sa.String(length=50), nullable=True),
57
+ sa.PrimaryKeyConstraint('id'),
58
+ sa.UniqueConstraint('name')
59
+ )
60
+ op.create_index(op.f('ix_lecrapaud_features_id'), 'lecrapaud_features', ['id'], unique=False)
61
+ op.create_table('lecrapaud_models',
62
+ sa.Column('id', sa.BigInteger(), autoincrement=True, nullable=False),
63
+ sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
64
+ sa.Column('updated_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
65
+ sa.Column('name', sa.String(length=50), nullable=False),
66
+ sa.Column('type', sa.String(length=50), nullable=False),
67
+ sa.PrimaryKeyConstraint('id'),
68
+ sa.UniqueConstraint('name', 'type', name='uq_model_composite')
69
+ )
70
+ op.create_index(op.f('ix_lecrapaud_models_id'), 'lecrapaud_models', ['id'], unique=False)
71
+ op.create_table('lecrapaud_targets',
72
+ sa.Column('id', sa.BigInteger(), autoincrement=True, nullable=False),
73
+ sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
74
+ sa.Column('updated_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
75
+ sa.Column('name', sa.String(length=50), nullable=False),
76
+ sa.Column('type', sa.String(length=50), nullable=False),
77
+ sa.Column('description', sa.String(length=255), nullable=True),
78
+ sa.PrimaryKeyConstraint('id'),
79
+ sa.UniqueConstraint('name', 'type', name='uq_target_composite')
80
+ )
81
+ op.create_index(op.f('ix_lecrapaud_targets_id'), 'lecrapaud_targets', ['id'], unique=False)
82
+ op.create_table('lecrapaud_dataset_target_association',
83
+ sa.Column('dataset_id', sa.BigInteger(), nullable=False),
84
+ sa.Column('target_id', sa.BigInteger(), nullable=False),
85
+ sa.ForeignKeyConstraint(['dataset_id'], ['lecrapaud_datasets.id'], ondelete='CASCADE'),
86
+ sa.ForeignKeyConstraint(['target_id'], ['lecrapaud_targets.id'], ondelete='CASCADE'),
87
+ sa.PrimaryKeyConstraint('dataset_id', 'target_id')
88
+ )
89
+ op.create_table('lecrapaud_feature_selections',
90
+ sa.Column('id', sa.BigInteger(), autoincrement=True, nullable=False),
91
+ sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
92
+ sa.Column('updated_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
93
+ sa.Column('training_time', sa.Integer(), nullable=True),
94
+ sa.Column('best_features_path', sa.String(length=255), nullable=True),
95
+ sa.Column('dataset_id', sa.BigInteger(), nullable=False),
96
+ sa.Column('target_id', sa.BigInteger(), nullable=False),
97
+ sa.ForeignKeyConstraint(['dataset_id'], ['lecrapaud_datasets.id'], ondelete='CASCADE'),
98
+ sa.ForeignKeyConstraint(['target_id'], ['lecrapaud_targets.id'], ondelete='CASCADE'),
99
+ sa.PrimaryKeyConstraint('id'),
100
+ sa.UniqueConstraint('dataset_id', 'target_id', name='uq_feature_selection_composite')
101
+ )
102
+ op.create_index(op.f('ix_lecrapaud_feature_selections_id'), 'lecrapaud_feature_selections', ['id'], unique=False)
103
+ op.create_table('lecrapaud_model_selections',
104
+ sa.Column('id', sa.BigInteger(), autoincrement=True, nullable=False),
105
+ sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
106
+ sa.Column('updated_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
107
+ sa.Column('best_model_params', sa.JSON(), nullable=True),
108
+ sa.Column('best_model_path', sa.String(length=255), nullable=True),
109
+ sa.Column('best_model_id', sa.BigInteger(), nullable=True),
110
+ sa.Column('target_id', sa.BigInteger(), nullable=False),
111
+ sa.Column('dataset_id', sa.BigInteger(), nullable=False),
112
+ sa.ForeignKeyConstraint(['best_model_id'], ['lecrapaud_models.id'], ondelete='CASCADE'),
113
+ sa.ForeignKeyConstraint(['dataset_id'], ['lecrapaud_datasets.id'], ondelete='CASCADE'),
114
+ sa.ForeignKeyConstraint(['target_id'], ['lecrapaud_targets.id'], ondelete='CASCADE'),
115
+ sa.PrimaryKeyConstraint('id'),
116
+ sa.UniqueConstraint('target_id', 'dataset_id', name='uq_model_selection_composite')
117
+ )
118
+ op.create_index(op.f('ix_lecrapaud_model_selections_id'), 'lecrapaud_model_selections', ['id'], unique=False)
119
+ op.create_table('lecrapaud_feature_selection_association',
120
+ sa.Column('feature_selection_id', sa.BigInteger(), nullable=False),
121
+ sa.Column('feature_id', sa.BigInteger(), nullable=False),
122
+ sa.ForeignKeyConstraint(['feature_id'], ['lecrapaud_features.id'], ondelete='CASCADE'),
123
+ sa.ForeignKeyConstraint(['feature_selection_id'], ['lecrapaud_feature_selections.id'], ondelete='CASCADE'),
124
+ sa.PrimaryKeyConstraint('feature_selection_id', 'feature_id')
125
+ )
126
+ op.create_table('lecrapaud_feature_selection_ranks',
127
+ sa.Column('id', sa.BigInteger(), autoincrement=True, nullable=False),
128
+ sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
129
+ sa.Column('updated_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
130
+ sa.Column('score', sa.Float(), nullable=True),
131
+ sa.Column('pvalue', sa.Float(), nullable=True),
132
+ sa.Column('support', sa.Integer(), nullable=True),
133
+ sa.Column('rank', sa.Integer(), nullable=True),
134
+ sa.Column('method', sa.String(length=50), nullable=True),
135
+ sa.Column('training_time', sa.Integer(), nullable=True),
136
+ sa.Column('feature_id', sa.BigInteger(), nullable=True),
137
+ sa.Column('feature_selection_id', sa.BigInteger(), nullable=True),
138
+ sa.ForeignKeyConstraint(['feature_id'], ['lecrapaud_features.id'], ondelete='CASCADE'),
139
+ sa.ForeignKeyConstraint(['feature_selection_id'], ['lecrapaud_feature_selections.id'], ondelete='CASCADE'),
140
+ sa.PrimaryKeyConstraint('id'),
141
+ sa.UniqueConstraint('feature_id', 'feature_selection_id', 'method', name='uq_feature_selection_rank_composite')
142
+ )
143
+ op.create_index(op.f('ix_lecrapaud_feature_selection_ranks_id'), 'lecrapaud_feature_selection_ranks', ['id'], unique=False)
144
+ op.create_table('lecrapaud_model_trainings',
145
+ sa.Column('id', sa.BigInteger(), autoincrement=True, nullable=False),
146
+ sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
147
+ sa.Column('updated_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
148
+ sa.Column('best_params', sa.JSON(), nullable=True),
149
+ sa.Column('model_path', sa.String(length=255), nullable=True),
150
+ sa.Column('training_time', sa.Integer(), nullable=True),
151
+ sa.Column('model_id', sa.BigInteger(), nullable=False),
152
+ sa.Column('model_selection_id', sa.BigInteger(), nullable=False),
153
+ sa.ForeignKeyConstraint(['model_id'], ['lecrapaud_models.id'], ),
154
+ sa.ForeignKeyConstraint(['model_selection_id'], ['lecrapaud_model_selections.id'], ondelete='CASCADE'),
155
+ sa.PrimaryKeyConstraint('id'),
156
+ sa.UniqueConstraint('model_id', 'model_selection_id', name='uq_model_training_composite')
157
+ )
158
+ op.create_index(op.f('ix_lecrapaud_model_trainings_id'), 'lecrapaud_model_trainings', ['id'], unique=False)
159
+ op.create_table('lecrapaud_scores',
160
+ sa.Column('id', sa.BigInteger(), autoincrement=True, nullable=False),
161
+ sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
162
+ sa.Column('updated_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
163
+ sa.Column('type', sa.String(length=50), nullable=False),
164
+ sa.Column('training_time', sa.Integer(), nullable=True),
165
+ sa.Column('eval_data_std', sa.Float(), nullable=True),
166
+ sa.Column('rmse', sa.Float(), nullable=True),
167
+ sa.Column('rmse_std_ratio', sa.Float(), nullable=True),
168
+ sa.Column('mae', sa.Float(), nullable=True),
169
+ sa.Column('mape', sa.Float(), nullable=True),
170
+ sa.Column('mam', sa.Float(), nullable=True),
171
+ sa.Column('mad', sa.Float(), nullable=True),
172
+ sa.Column('mae_mam_ratio', sa.Float(), nullable=True),
173
+ sa.Column('mae_mad_ratio', sa.Float(), nullable=True),
174
+ sa.Column('r2', sa.Float(), nullable=True),
175
+ sa.Column('logloss', sa.Float(), nullable=True),
176
+ sa.Column('accuracy', sa.Float(), nullable=True),
177
+ sa.Column('precision', sa.Float(), nullable=True),
178
+ sa.Column('recall', sa.Float(), nullable=True),
179
+ sa.Column('f1', sa.Float(), nullable=True),
180
+ sa.Column('roc_auc', sa.Float(), nullable=True),
181
+ sa.Column('threshold', sa.Float(), nullable=True),
182
+ sa.Column('precision_at_threshold', sa.Float(), nullable=True),
183
+ sa.Column('recall_at_threshold', sa.Float(), nullable=True),
184
+ sa.Column('model_training_id', sa.BigInteger(), nullable=False),
185
+ sa.ForeignKeyConstraint(['model_training_id'], ['lecrapaud_model_trainings.id'], ondelete='CASCADE'),
186
+ sa.PrimaryKeyConstraint('id')
187
+ )
188
+ op.create_index(op.f('ix_lecrapaud_scores_id'), 'lecrapaud_scores', ['id'], unique=False)
189
+ # ### end Alembic commands ###
190
+
191
+
192
+ def downgrade() -> None:
193
+ # ### commands auto generated by Alembic - please adjust! ###
194
+ op.drop_index(op.f('ix_lecrapaud_scores_id'), table_name='lecrapaud_scores')
195
+ op.drop_table('lecrapaud_scores')
196
+ op.drop_index(op.f('ix_lecrapaud_model_trainings_id'), table_name='lecrapaud_model_trainings')
197
+ op.drop_table('lecrapaud_model_trainings')
198
+ op.drop_index(op.f('ix_lecrapaud_feature_selection_ranks_id'), table_name='lecrapaud_feature_selection_ranks')
199
+ op.drop_table('lecrapaud_feature_selection_ranks')
200
+ op.drop_table('lecrapaud_feature_selection_association')
201
+ op.drop_index(op.f('ix_lecrapaud_model_selections_id'), table_name='lecrapaud_model_selections')
202
+ op.drop_table('lecrapaud_model_selections')
203
+ op.drop_index(op.f('ix_lecrapaud_feature_selections_id'), table_name='lecrapaud_feature_selections')
204
+ op.drop_table('lecrapaud_feature_selections')
205
+ op.drop_table('lecrapaud_dataset_target_association')
206
+ op.drop_index(op.f('ix_lecrapaud_targets_id'), table_name='lecrapaud_targets')
207
+ op.drop_table('lecrapaud_targets')
208
+ op.drop_index(op.f('ix_lecrapaud_models_id'), table_name='lecrapaud_models')
209
+ op.drop_table('lecrapaud_models')
210
+ op.drop_index(op.f('ix_lecrapaud_features_id'), table_name='lecrapaud_features')
211
+ op.drop_table('lecrapaud_features')
212
+ op.drop_index(op.f('ix_lecrapaud_datasets_id'), table_name='lecrapaud_datasets')
213
+ op.drop_table('lecrapaud_datasets')
214
+ # ### end Alembic commands ###
lecrapaud/db/alembic.ini CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [alembic]
4
4
  # path to migration scripts
5
- script_location = alembic
5
+ script_location = lecrapaud/db/alembic
6
6
 
7
7
  # template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
8
8
  # Uncomment the line below if you want the files to be prepended with date and time
@@ -1,12 +1,14 @@
1
1
  """Base SQLAlchemy model with CRUD operations."""
2
2
 
3
3
  from functools import wraps
4
+ import re
4
5
 
5
6
  from sqlalchemy.orm import DeclarativeBase
6
7
  from sqlalchemy import desc, asc, and_, delete
7
8
  from sqlalchemy.inspection import inspect
8
9
  from sqlalchemy.orm.attributes import InstrumentedAttribute
9
10
  from lecrapaud.db.session import get_db
11
+ from sqlalchemy.ext.declarative import declared_attr
10
12
 
11
13
 
12
14
  def with_db(func):
@@ -23,8 +25,34 @@ def with_db(func):
23
25
  return wrapper
24
26
 
25
27
 
28
+ # Utility functions
29
+
30
+
31
+ def camel_to_snake(name):
32
+ s1 = re.sub(r"(.)([A-Z][a-z]+)", r"\1_\2", name)
33
+ s2 = re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", s1)
34
+ return s2.lower()
35
+
36
+
37
+ def pluralize(name):
38
+ return name if name.endswith("s") else name + "s"
39
+
40
+
26
41
  # declarative base class
27
42
  class Base(DeclarativeBase):
43
+ @declared_attr
44
+ def __tablename__(cls):
45
+ # If the model sets __tablename__, use it (with prefix if not present)
46
+ if "__tablename__" in cls.__dict__:
47
+ base_name = cls.__dict__["__tablename__"]
48
+ if not base_name.startswith("lecrapaud_"):
49
+ return f"lecrapaud_{base_name}"
50
+ return base_name
51
+ # Otherwise, generate from class name
52
+ snake = camel_to_snake(cls.__name__)
53
+ plural = pluralize(snake)
54
+ return f"lecrapaud_{plural}"
55
+
28
56
  @classmethod
29
57
  @with_db
30
58
  def create(cls, db, **kwargs):
@@ -21,26 +21,25 @@ from lecrapaud.db.session import get_db
21
21
  from lecrapaud.db.models.base import Base
22
22
 
23
23
  # jointures
24
- dataset_target_association = Table(
25
- "dataset_target_association",
24
+ lecrapaud_dataset_target_association = Table(
25
+ "lecrapaud_dataset_target_association",
26
26
  Base.metadata,
27
27
  Column(
28
28
  "dataset_id",
29
29
  BigInteger,
30
- ForeignKey("datasets.id", ondelete="CASCADE"),
30
+ ForeignKey("lecrapaud_datasets.id", ondelete="CASCADE"),
31
31
  primary_key=True,
32
32
  ),
33
33
  Column(
34
34
  "target_id",
35
35
  BigInteger,
36
- ForeignKey("targets.id", ondelete="CASCADE"),
36
+ ForeignKey("lecrapaud_targets.id", ondelete="CASCADE"),
37
37
  primary_key=True,
38
38
  ),
39
39
  )
40
40
 
41
41
 
42
42
  class Dataset(Base):
43
- __tablename__ = "datasets"
44
43
 
45
44
  id = Column(BigInteger, primary_key=True, index=True, autoincrement=True)
46
45
  created_at = Column(
@@ -87,7 +86,7 @@ class Dataset(Base):
87
86
  )
88
87
  targets = relationship(
89
88
  "Target",
90
- secondary=dataset_target_association,
89
+ secondary=lecrapaud_dataset_target_association,
91
90
  back_populates="datasets",
92
91
  lazy="selectin",
93
92
  )
@@ -18,11 +18,12 @@ from sqlalchemy.orm import relationship, Mapped, mapped_column, DeclarativeBase
18
18
 
19
19
  from lecrapaud.db.session import get_db
20
20
  from lecrapaud.db.models.base import Base
21
- from lecrapaud.db.models.feature_selection import feature_selection_association
21
+ from lecrapaud.db.models.feature_selection import (
22
+ lecrapaud_feature_selection_association,
23
+ )
22
24
 
23
25
 
24
26
  class Feature(Base):
25
- __tablename__ = "features"
26
27
 
27
28
  id = Column(BigInteger, primary_key=True, index=True, autoincrement=True)
28
29
  created_at = Column(
@@ -39,7 +40,7 @@ class Feature(Base):
39
40
 
40
41
  feature_selections = relationship(
41
42
  "FeatureSelection",
42
- secondary=feature_selection_association,
43
+ secondary=lecrapaud_feature_selection_association,
43
44
  back_populates="features",
44
45
  lazy="selectin",
45
46
  )
@@ -28,26 +28,25 @@ from lecrapaud.db.session import get_db
28
28
  from lecrapaud.db.models.base import Base, with_db
29
29
 
30
30
  # jointures
31
- feature_selection_association = Table(
32
- "feature_selection_association",
31
+ lecrapaud_feature_selection_association = Table(
32
+ "lecrapaud_feature_selection_association",
33
33
  Base.metadata,
34
34
  Column(
35
35
  "feature_selection_id",
36
36
  BigInteger,
37
- ForeignKey("feature_selections.id", ondelete="CASCADE"),
37
+ ForeignKey("lecrapaud_feature_selections.id", ondelete="CASCADE"),
38
38
  primary_key=True,
39
39
  ),
40
40
  Column(
41
41
  "feature_id",
42
42
  BigInteger,
43
- ForeignKey("features.id", ondelete="CASCADE"),
43
+ ForeignKey("lecrapaud_features.id", ondelete="CASCADE"),
44
44
  primary_key=True,
45
45
  ),
46
46
  )
47
47
 
48
48
 
49
49
  class FeatureSelection(Base):
50
- __tablename__ = "feature_selections"
51
50
 
52
51
  id = Column(BigInteger, primary_key=True, index=True, autoincrement=True)
53
52
  created_at = Column(
@@ -62,10 +61,14 @@ class FeatureSelection(Base):
62
61
  training_time = Column(Integer)
63
62
  best_features_path = Column(String(255))
64
63
  dataset_id = Column(
65
- BigInteger, ForeignKey("datasets.id", ondelete="CASCADE"), nullable=False
64
+ BigInteger,
65
+ ForeignKey("lecrapaud_datasets.id", ondelete="CASCADE"),
66
+ nullable=False,
66
67
  )
67
68
  target_id = Column(
68
- BigInteger, ForeignKey("targets.id", ondelete="CASCADE"), nullable=False
69
+ BigInteger,
70
+ ForeignKey("lecrapaud_targets.id", ondelete="CASCADE"),
71
+ nullable=False,
69
72
  )
70
73
 
71
74
  dataset = relationship(
@@ -82,7 +85,7 @@ class FeatureSelection(Base):
82
85
  )
83
86
  features = relationship(
84
87
  "Feature",
85
- secondary=feature_selection_association,
88
+ secondary=lecrapaud_feature_selection_association,
86
89
  back_populates="feature_selections",
87
90
  lazy="selectin",
88
91
  )
@@ -23,7 +23,6 @@ from lecrapaud.db.models.base import Base, with_db
23
23
 
24
24
 
25
25
  class FeatureSelectionRank(Base):
26
- __tablename__ = "feature_selection_ranks"
27
26
 
28
27
  id = Column(BigInteger, primary_key=True, index=True, autoincrement=True)
29
28
  created_at = Column(
@@ -41,9 +40,11 @@ class FeatureSelectionRank(Base):
41
40
  rank = Column(Integer)
42
41
  method = Column(String(50))
43
42
  training_time = Column(Integer)
44
- feature_id = Column(BigInteger, ForeignKey("features.id", ondelete="CASCADE"))
43
+ feature_id = Column(
44
+ BigInteger, ForeignKey("lecrapaud_features.id", ondelete="CASCADE")
45
+ )
45
46
  feature_selection_id = Column(
46
- BigInteger, ForeignKey("feature_selections.id", ondelete="CASCADE")
47
+ BigInteger, ForeignKey("lecrapaud_feature_selections.id", ondelete="CASCADE")
47
48
  )
48
49
 
49
50
  feature = relationship("Feature", lazy="selectin")
@@ -22,7 +22,6 @@ from lecrapaud.db.models.base import Base
22
22
 
23
23
 
24
24
  class Model(Base):
25
- __tablename__ = "models"
26
25
 
27
26
  id = Column(BigInteger, primary_key=True, index=True, autoincrement=True)
28
27
  created_at = Column(
@@ -22,7 +22,6 @@ from lecrapaud.db.models.base import Base
22
22
 
23
23
 
24
24
  class ModelSelection(Base):
25
- __tablename__ = "model_selections"
26
25
 
27
26
  id = Column(BigInteger, primary_key=True, index=True, autoincrement=True)
28
27
  created_at = Column(
@@ -36,12 +35,18 @@ class ModelSelection(Base):
36
35
  )
37
36
  best_model_params = Column(JSON)
38
37
  best_model_path = Column(String(255))
39
- best_model_id = Column(BigInteger, ForeignKey("models.id", ondelete="CASCADE"))
38
+ best_model_id = Column(
39
+ BigInteger, ForeignKey("lecrapaud_models.id", ondelete="CASCADE")
40
+ )
40
41
  target_id = Column(
41
- BigInteger, ForeignKey("targets.id", ondelete="CASCADE"), nullable=False
42
+ BigInteger,
43
+ ForeignKey("lecrapaud_targets.id", ondelete="CASCADE"),
44
+ nullable=False,
42
45
  )
43
46
  dataset_id = Column(
44
- BigInteger, ForeignKey("datasets.id", ondelete="CASCADE"), nullable=False
47
+ BigInteger,
48
+ ForeignKey("lecrapaud_datasets.id", ondelete="CASCADE"),
49
+ nullable=False,
45
50
  )
46
51
 
47
52
  best_model = relationship("Model", lazy="selectin")
@@ -22,7 +22,6 @@ from lecrapaud.db.models.base import Base
22
22
 
23
23
 
24
24
  class ModelTraining(Base):
25
- __tablename__ = "model_trainings"
26
25
 
27
26
  id = Column(BigInteger, primary_key=True, index=True, autoincrement=True)
28
27
  created_at = Column(
@@ -37,10 +36,10 @@ class ModelTraining(Base):
37
36
  best_params = Column(JSON)
38
37
  model_path = Column(String(255))
39
38
  training_time = Column(Integer)
40
- model_id = Column(BigInteger, ForeignKey("models.id"), nullable=False)
39
+ model_id = Column(BigInteger, ForeignKey("lecrapaud_models.id"), nullable=False)
41
40
  model_selection_id = Column(
42
41
  BigInteger,
43
- ForeignKey("model_selections.id", ondelete="CASCADE"),
42
+ ForeignKey("lecrapaud_model_selections.id", ondelete="CASCADE"),
44
43
  nullable=False,
45
44
  )
46
45
 
@@ -2,27 +2,17 @@ from sqlalchemy import (
2
2
  Column,
3
3
  Integer,
4
4
  String,
5
- DateTime,
6
- Date,
7
5
  Float,
8
- JSON,
9
- Table,
10
6
  ForeignKey,
11
7
  BigInteger,
12
- Index,
13
8
  TIMESTAMP,
14
9
  )
15
- from sqlalchemy import desc, asc, cast, text, func
16
-
17
- from sqlalchemy.orm import relationship, Mapped, mapped_column, DeclarativeBase
18
-
19
- from lecrapaud.db.session import get_db
10
+ from sqlalchemy import func
11
+ from sqlalchemy.orm import relationship
20
12
  from lecrapaud.db.models.base import Base
21
13
 
22
14
 
23
15
  class Score(Base):
24
- __tablename__ = "scores"
25
-
26
16
  id = Column(BigInteger, primary_key=True, index=True, autoincrement=True)
27
17
  created_at = Column(
28
18
  TIMESTAMP(timezone=True), server_default=func.now(), nullable=False
@@ -57,7 +47,9 @@ class Score(Base):
57
47
  precision_at_threshold = Column(Float)
58
48
  recall_at_threshold = Column(Float)
59
49
  model_training_id = Column(
60
- BigInteger, ForeignKey("model_trainings.id", ondelete="CASCADE"), nullable=False
50
+ BigInteger,
51
+ ForeignKey("lecrapaud_model_trainings.id", ondelete="CASCADE"),
52
+ nullable=False,
61
53
  )
62
54
 
63
55
  model_trainings = relationship(
@@ -19,11 +19,10 @@ from sqlalchemy.orm import relationship, Mapped, mapped_column, DeclarativeBase
19
19
 
20
20
  from lecrapaud.db.session import get_db
21
21
  from lecrapaud.db.models.base import Base
22
- from lecrapaud.db.models.dataset import dataset_target_association
22
+ from lecrapaud.db.models.dataset import lecrapaud_dataset_target_association
23
23
 
24
24
 
25
25
  class Target(Base):
26
- __tablename__ = "targets"
27
26
 
28
27
  id = Column(BigInteger, primary_key=True, index=True, autoincrement=True)
29
28
  created_at = Column(
@@ -41,7 +40,7 @@ class Target(Base):
41
40
 
42
41
  datasets = relationship(
43
42
  "Dataset",
44
- secondary=dataset_target_association,
43
+ secondary=lecrapaud_dataset_target_association,
45
44
  back_populates="targets",
46
45
  lazy="selectin",
47
46
  )