desdeo 1.2__py3-none-any.whl → 2.1.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.
- desdeo/__init__.py +8 -8
- desdeo/adm/ADMAfsar.py +551 -0
- desdeo/adm/ADMChen.py +414 -0
- desdeo/adm/BaseADM.py +119 -0
- desdeo/adm/__init__.py +11 -0
- desdeo/api/README.md +73 -0
- desdeo/api/__init__.py +15 -0
- desdeo/api/app.py +50 -0
- desdeo/api/config.py +90 -0
- desdeo/api/config.toml +64 -0
- desdeo/api/db.py +27 -0
- desdeo/api/db_init.py +85 -0
- desdeo/api/db_models.py +164 -0
- desdeo/api/malaga_db_init.py +27 -0
- desdeo/api/models/__init__.py +266 -0
- desdeo/api/models/archive.py +23 -0
- desdeo/api/models/emo.py +128 -0
- desdeo/api/models/enautilus.py +69 -0
- desdeo/api/models/gdm/gdm_aggregate.py +139 -0
- desdeo/api/models/gdm/gdm_base.py +69 -0
- desdeo/api/models/gdm/gdm_score_bands.py +114 -0
- desdeo/api/models/gdm/gnimbus.py +138 -0
- desdeo/api/models/generic.py +104 -0
- desdeo/api/models/generic_states.py +401 -0
- desdeo/api/models/nimbus.py +158 -0
- desdeo/api/models/preference.py +128 -0
- desdeo/api/models/problem.py +717 -0
- desdeo/api/models/reference_point_method.py +18 -0
- desdeo/api/models/session.py +49 -0
- desdeo/api/models/state.py +463 -0
- desdeo/api/models/user.py +52 -0
- desdeo/api/models/utopia.py +25 -0
- desdeo/api/routers/_EMO.backup +309 -0
- desdeo/api/routers/_NAUTILUS.py +245 -0
- desdeo/api/routers/_NAUTILUS_navigator.py +233 -0
- desdeo/api/routers/_NIMBUS.py +765 -0
- desdeo/api/routers/__init__.py +5 -0
- desdeo/api/routers/emo.py +497 -0
- desdeo/api/routers/enautilus.py +237 -0
- desdeo/api/routers/gdm/gdm_aggregate.py +234 -0
- desdeo/api/routers/gdm/gdm_base.py +420 -0
- desdeo/api/routers/gdm/gdm_score_bands/gdm_score_bands_manager.py +398 -0
- desdeo/api/routers/gdm/gdm_score_bands/gdm_score_bands_routers.py +377 -0
- desdeo/api/routers/gdm/gnimbus/gnimbus_manager.py +698 -0
- desdeo/api/routers/gdm/gnimbus/gnimbus_routers.py +591 -0
- desdeo/api/routers/generic.py +233 -0
- desdeo/api/routers/nimbus.py +705 -0
- desdeo/api/routers/problem.py +307 -0
- desdeo/api/routers/reference_point_method.py +93 -0
- desdeo/api/routers/session.py +100 -0
- desdeo/api/routers/test.py +16 -0
- desdeo/api/routers/user_authentication.py +520 -0
- desdeo/api/routers/utils.py +187 -0
- desdeo/api/routers/utopia.py +230 -0
- desdeo/api/schema.py +100 -0
- desdeo/api/tests/__init__.py +0 -0
- desdeo/api/tests/conftest.py +151 -0
- desdeo/api/tests/test_enautilus.py +330 -0
- desdeo/api/tests/test_models.py +1179 -0
- desdeo/api/tests/test_routes.py +1075 -0
- desdeo/api/utils/_database.py +263 -0
- desdeo/api/utils/_logger.py +29 -0
- desdeo/api/utils/database.py +36 -0
- desdeo/api/utils/emo_database.py +40 -0
- desdeo/core.py +34 -0
- desdeo/emo/__init__.py +159 -0
- desdeo/emo/hooks/archivers.py +188 -0
- desdeo/emo/methods/EAs.py +541 -0
- desdeo/emo/methods/__init__.py +0 -0
- desdeo/emo/methods/bases.py +12 -0
- desdeo/emo/methods/templates.py +111 -0
- desdeo/emo/operators/__init__.py +1 -0
- desdeo/emo/operators/crossover.py +1282 -0
- desdeo/emo/operators/evaluator.py +114 -0
- desdeo/emo/operators/generator.py +459 -0
- desdeo/emo/operators/mutation.py +1224 -0
- desdeo/emo/operators/scalar_selection.py +202 -0
- desdeo/emo/operators/selection.py +1778 -0
- desdeo/emo/operators/termination.py +286 -0
- desdeo/emo/options/__init__.py +108 -0
- desdeo/emo/options/algorithms.py +435 -0
- desdeo/emo/options/crossover.py +164 -0
- desdeo/emo/options/generator.py +131 -0
- desdeo/emo/options/mutation.py +260 -0
- desdeo/emo/options/repair.py +61 -0
- desdeo/emo/options/scalar_selection.py +66 -0
- desdeo/emo/options/selection.py +127 -0
- desdeo/emo/options/templates.py +383 -0
- desdeo/emo/options/termination.py +143 -0
- desdeo/explanations/__init__.py +6 -0
- desdeo/explanations/explainer.py +100 -0
- desdeo/explanations/utils.py +90 -0
- desdeo/gdm/__init__.py +22 -0
- desdeo/gdm/gdmtools.py +45 -0
- desdeo/gdm/score_bands.py +114 -0
- desdeo/gdm/voting_rules.py +50 -0
- desdeo/mcdm/__init__.py +41 -0
- desdeo/mcdm/enautilus.py +338 -0
- desdeo/mcdm/gnimbus.py +484 -0
- desdeo/mcdm/nautili.py +345 -0
- desdeo/mcdm/nautilus.py +477 -0
- desdeo/mcdm/nautilus_navigator.py +656 -0
- desdeo/mcdm/nimbus.py +417 -0
- desdeo/mcdm/pareto_navigator.py +269 -0
- desdeo/mcdm/reference_point_method.py +186 -0
- desdeo/problem/__init__.py +83 -0
- desdeo/problem/evaluator.py +561 -0
- desdeo/problem/external/__init__.py +18 -0
- desdeo/problem/external/core.py +356 -0
- desdeo/problem/external/pymoo_provider.py +266 -0
- desdeo/problem/external/runtime.py +44 -0
- desdeo/problem/gurobipy_evaluator.py +562 -0
- desdeo/problem/infix_parser.py +341 -0
- desdeo/problem/json_parser.py +944 -0
- desdeo/problem/pyomo_evaluator.py +487 -0
- desdeo/problem/schema.py +1829 -0
- desdeo/problem/simulator_evaluator.py +348 -0
- desdeo/problem/sympy_evaluator.py +244 -0
- desdeo/problem/testproblems/__init__.py +88 -0
- desdeo/problem/testproblems/benchmarks_server.py +120 -0
- desdeo/problem/testproblems/binh_and_korn_problem.py +88 -0
- desdeo/problem/testproblems/cake_problem.py +185 -0
- desdeo/problem/testproblems/dmitry_forest_problem_discrete.py +71 -0
- desdeo/problem/testproblems/dtlz2_problem.py +102 -0
- desdeo/problem/testproblems/forest_problem.py +283 -0
- desdeo/problem/testproblems/knapsack_problem.py +163 -0
- desdeo/problem/testproblems/mcwb_problem.py +831 -0
- desdeo/problem/testproblems/mixed_variable_dimenrions_problem.py +83 -0
- desdeo/problem/testproblems/momip_problem.py +172 -0
- desdeo/problem/testproblems/multi_valued_constraints.py +119 -0
- desdeo/problem/testproblems/nimbus_problem.py +143 -0
- desdeo/problem/testproblems/pareto_navigator_problem.py +89 -0
- desdeo/problem/testproblems/re_problem.py +492 -0
- desdeo/problem/testproblems/river_pollution_problems.py +440 -0
- desdeo/problem/testproblems/rocket_injector_design_problem.py +140 -0
- desdeo/problem/testproblems/simple_problem.py +351 -0
- desdeo/problem/testproblems/simulator_problem.py +92 -0
- desdeo/problem/testproblems/single_objective.py +289 -0
- desdeo/problem/testproblems/spanish_sustainability_problem.py +945 -0
- desdeo/problem/testproblems/zdt_problem.py +274 -0
- desdeo/problem/utils.py +245 -0
- desdeo/tools/GenerateReferencePoints.py +181 -0
- desdeo/tools/__init__.py +120 -0
- desdeo/tools/desc_gen.py +22 -0
- desdeo/tools/generics.py +165 -0
- desdeo/tools/group_scalarization.py +3090 -0
- desdeo/tools/gurobipy_solver_interfaces.py +258 -0
- desdeo/tools/indicators_binary.py +117 -0
- desdeo/tools/indicators_unary.py +362 -0
- desdeo/tools/interaction_schema.py +38 -0
- desdeo/tools/intersection.py +54 -0
- desdeo/tools/iterative_pareto_representer.py +99 -0
- desdeo/tools/message.py +265 -0
- desdeo/tools/ng_solver_interfaces.py +199 -0
- desdeo/tools/non_dominated_sorting.py +134 -0
- desdeo/tools/patterns.py +283 -0
- desdeo/tools/proximal_solver.py +99 -0
- desdeo/tools/pyomo_solver_interfaces.py +477 -0
- desdeo/tools/reference_vectors.py +229 -0
- desdeo/tools/scalarization.py +2065 -0
- desdeo/tools/scipy_solver_interfaces.py +454 -0
- desdeo/tools/score_bands.py +627 -0
- desdeo/tools/utils.py +388 -0
- desdeo/tools/visualizations.py +67 -0
- desdeo/utopia_stuff/__init__.py +0 -0
- desdeo/utopia_stuff/data/1.json +15 -0
- desdeo/utopia_stuff/data/2.json +13 -0
- desdeo/utopia_stuff/data/3.json +15 -0
- desdeo/utopia_stuff/data/4.json +17 -0
- desdeo/utopia_stuff/data/5.json +15 -0
- desdeo/utopia_stuff/from_json.py +40 -0
- desdeo/utopia_stuff/reinit_user.py +38 -0
- desdeo/utopia_stuff/utopia_db_init.py +212 -0
- desdeo/utopia_stuff/utopia_problem.py +403 -0
- desdeo/utopia_stuff/utopia_problem_old.py +415 -0
- desdeo/utopia_stuff/utopia_reference_solutions.py +79 -0
- desdeo-2.1.0.dist-info/METADATA +186 -0
- desdeo-2.1.0.dist-info/RECORD +180 -0
- {desdeo-1.2.dist-info → desdeo-2.1.0.dist-info}/WHEEL +1 -1
- desdeo-2.1.0.dist-info/licenses/LICENSE +21 -0
- desdeo-1.2.dist-info/METADATA +0 -16
- desdeo-1.2.dist-info/RECORD +0 -4
desdeo/api/config.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Defines dataclasses to store configurations loaded from 'config.toml'."""
|
|
2
|
+
|
|
3
|
+
import tomllib
|
|
4
|
+
import os
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import List
|
|
8
|
+
|
|
9
|
+
from pydantic_settings import BaseSettings
|
|
10
|
+
|
|
11
|
+
# Load the config data once
|
|
12
|
+
config_path = Path(__file__).resolve().parent / "config.toml"
|
|
13
|
+
with config_path.open("rb") as fp:
|
|
14
|
+
config_data = tomllib.load(fp)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class GeneralSettings(BaseSettings):
|
|
18
|
+
"""General settings."""
|
|
19
|
+
|
|
20
|
+
debug: bool = config_data["settings"]["debug"]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
SettingsConfig = GeneralSettings()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ServerDebugConfig(BaseSettings):
|
|
27
|
+
"""Server setup settings (development)."""
|
|
28
|
+
|
|
29
|
+
test_user_analyst_name: str = config_data["server-debug"]["test_user_analyst_name"]
|
|
30
|
+
test_user_analyst_password: str = config_data["server-debug"]["test_user_analyst_password"]
|
|
31
|
+
test_user_dm1_name: str = config_data["server-debug"]["test_user_dm1_name"]
|
|
32
|
+
test_user_dm1_password: str = config_data["server-debug"]["test_user_dm1_password"]
|
|
33
|
+
test_user_dm2_name: str = config_data["server-debug"]["test_user_dm2_name"]
|
|
34
|
+
test_user_dm2_password: str = config_data["server-debug"]["test_user_dm2_password"]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class AuthDebugConfig(BaseSettings):
|
|
38
|
+
"""Authentication settings (debug)."""
|
|
39
|
+
|
|
40
|
+
authjwt_secret_key: str = config_data["auth-debug"]["authjwt_secret_key"]
|
|
41
|
+
authjwt_algorithm: str = config_data["auth-debug"]["authjwt_algorithm"]
|
|
42
|
+
authjwt_access_token_expires: int = config_data["auth-debug"]["authjwt_access_token_expires"]
|
|
43
|
+
authjwt_refresh_token_expires: int = config_data["auth-debug"]["authjwt_refresh_token_expires"]
|
|
44
|
+
cors_origins: List[str] = config_data["auth-debug"]["cors_origins"]
|
|
45
|
+
cookie_domain: str = config_data["auth-debug"]["cookie_domain"]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class DatabaseDebugConfig(BaseSettings):
|
|
49
|
+
"""Database setting (development)."""
|
|
50
|
+
|
|
51
|
+
db_host: str = config_data["database-debug"]["db_host"]
|
|
52
|
+
db_port: str = config_data["database-debug"]["db_port"]
|
|
53
|
+
db_database: str = config_data["database-debug"]["db_database"]
|
|
54
|
+
db_username: str = config_data["database-debug"]["db_username"]
|
|
55
|
+
db_password: str = config_data["database-debug"]["db_password"]
|
|
56
|
+
db_pool_size: int = config_data["database-debug"]["db_pool_size"]
|
|
57
|
+
db_max_overflow: int = config_data["database-debug"]["db_max_overflow"]
|
|
58
|
+
db_pool: bool = config_data["database-debug"]["db_pool"]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class DatabaseDeployConfig(BaseSettings):
|
|
62
|
+
"""Database setting (deployment)."""
|
|
63
|
+
|
|
64
|
+
db_host: str = os.getenv("DB_HOST")
|
|
65
|
+
db_port: str = os.getenv("DB_PORT")
|
|
66
|
+
db_database: str = os.getenv("DB_NAME")
|
|
67
|
+
db_username: str = os.getenv("DB_USER")
|
|
68
|
+
db_password: str = os.getenv("DB_PASSWORD")
|
|
69
|
+
db_pool_size: int = config_data["database-deploy"]["db_pool_size"]
|
|
70
|
+
db_max_overflow: int = config_data["database-deploy"]["db_max_overflow"]
|
|
71
|
+
db_pool: bool = config_data["database-deploy"]["db_pool"]
|
|
72
|
+
db_driver: str = os.getenv("DB_DRIVER", config_data["database-deploy"]["db_driver"])
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class AuthDeployConfig(BaseSettings):
|
|
76
|
+
"""Authentication settings (deployment)."""
|
|
77
|
+
|
|
78
|
+
authjwt_secret_key: str = os.getenv("AUTHJWT_SECRET")
|
|
79
|
+
authjwt_algorithm: str = config_data["auth-deploy"]["authjwt_algorithm"]
|
|
80
|
+
authjwt_access_token_expires: int = config_data["auth-deploy"]["authjwt_access_token_expires"]
|
|
81
|
+
authjwt_refresh_token_expires: int = config_data["auth-deploy"]["authjwt_refresh_token_expires"]
|
|
82
|
+
cors_origins: List[str] = json.loads(os.getenv("CORS_ORIGINS", "[]"))
|
|
83
|
+
cookie_domain: str = os.getenv("COOKIE_DOMAIN", "")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
AuthConfig = AuthDebugConfig() if SettingsConfig.debug else AuthDeployConfig()
|
|
87
|
+
|
|
88
|
+
DatabaseConfig = DatabaseDebugConfig() if SettingsConfig.debug else DatabaseDeployConfig()
|
|
89
|
+
|
|
90
|
+
ServerConfig = ServerDebugConfig() if SettingsConfig.debug else None
|
desdeo/api/config.toml
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
[settings]
|
|
2
|
+
# This is ignored if $DESDEO_PRODUCTION env variable is present
|
|
3
|
+
debug = true
|
|
4
|
+
|
|
5
|
+
# development configs
|
|
6
|
+
[server-debug]
|
|
7
|
+
test_user_analyst_name = "analyst"
|
|
8
|
+
test_user_analyst_password = "analyst"
|
|
9
|
+
test_user_dm1_name = "dm1"
|
|
10
|
+
test_user_dm1_password = "dm1"
|
|
11
|
+
test_user_dm2_name = "dm2"
|
|
12
|
+
test_user_dm2_password = "dm2"
|
|
13
|
+
|
|
14
|
+
# Auth configuration
|
|
15
|
+
[auth-debug]
|
|
16
|
+
authjwt_secret_key = "36b96a23d24cebdeadce6d98fa53356111e6f3e85b8144d7273dcba230b9eb18"
|
|
17
|
+
authjwt_algorithm = "HS256"
|
|
18
|
+
authjwt_access_token_expires = 15 # in minutes
|
|
19
|
+
authjwt_refresh_token_expires = 30 # in minutes
|
|
20
|
+
cors_origins = [
|
|
21
|
+
"http://localhost",
|
|
22
|
+
"http://localhost:8000",
|
|
23
|
+
"http://localhost:5173",
|
|
24
|
+
"http://127.0.0.1",
|
|
25
|
+
"http://127.0.0.1:8000",
|
|
26
|
+
"http://127.0.0.1:5173",
|
|
27
|
+
]
|
|
28
|
+
cookie_domain = "" # Not needed for testing
|
|
29
|
+
|
|
30
|
+
[auth-deploy]
|
|
31
|
+
# secret key should be read from env DO NOT EXPOSE!!!!
|
|
32
|
+
# authjwt_secret_key = $AUTHJWT_SECRET
|
|
33
|
+
authjwt_algorithm = "HS256"
|
|
34
|
+
authjwt_access_token_expires = 5 # in minutes
|
|
35
|
+
authjwt_refresh_token_expires = 180 # in minutes
|
|
36
|
+
# These are not that sensitive info but read from env regardless
|
|
37
|
+
# cors_origins = $CORS_ORIGINS
|
|
38
|
+
# cookie_domain = $COOKIE_DOMAIN
|
|
39
|
+
|
|
40
|
+
# SQLite setup (enabled for local development)
|
|
41
|
+
[database-debug]
|
|
42
|
+
db_host = ""
|
|
43
|
+
db_port = ""
|
|
44
|
+
db_database = "sqlite:///./test.db"
|
|
45
|
+
db_username = ""
|
|
46
|
+
db_password = ""
|
|
47
|
+
db_pool_size = 1
|
|
48
|
+
db_max_overflow = 0
|
|
49
|
+
db_pool = false
|
|
50
|
+
db_driver = "postgresql+asyncpg"
|
|
51
|
+
|
|
52
|
+
# Database configuration (deployment)
|
|
53
|
+
[database-deploy]
|
|
54
|
+
# READ FROM ENV!!!
|
|
55
|
+
# db_host = $DB_HOST
|
|
56
|
+
# db_port = $DB_PORT
|
|
57
|
+
# db_database = $DB_NAME
|
|
58
|
+
# db_username = $DB_USER
|
|
59
|
+
# db_password = $DB_PASSWORD
|
|
60
|
+
db_pool_size = 20
|
|
61
|
+
db_max_overflow = 20
|
|
62
|
+
db_pool = true
|
|
63
|
+
# Can be overridden with $DB_DRIVER env variable
|
|
64
|
+
db_driver = "postgresql+asyncpg"
|
desdeo/api/db.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Database configuration file for the API."""
|
|
2
|
+
|
|
3
|
+
from sqlmodel import Session, create_engine
|
|
4
|
+
|
|
5
|
+
from desdeo.api.config import DatabaseConfig, SettingsConfig
|
|
6
|
+
|
|
7
|
+
if SettingsConfig.debug:
|
|
8
|
+
# debug and development stuff
|
|
9
|
+
|
|
10
|
+
# SQLite setup
|
|
11
|
+
engine = create_engine(DatabaseConfig.db_database, connect_args={"check_same_thread": False})
|
|
12
|
+
|
|
13
|
+
else:
|
|
14
|
+
DB_USER = DatabaseConfig.db_username
|
|
15
|
+
DB_PASSWORD = DatabaseConfig.db_password
|
|
16
|
+
DB_HOST = DatabaseConfig.db_host
|
|
17
|
+
DB_PORT = DatabaseConfig.db_port
|
|
18
|
+
DB_NAME = DatabaseConfig.db_database
|
|
19
|
+
|
|
20
|
+
# Now the use of postgres is hardcoded for deployment, which may be fine
|
|
21
|
+
engine = create_engine(f"postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_session():
|
|
25
|
+
"""Yield the current database session."""
|
|
26
|
+
with Session(engine) as session:
|
|
27
|
+
yield session
|
desdeo/api/db_init.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""This module initializes the database."""
|
|
2
|
+
|
|
3
|
+
import warnings
|
|
4
|
+
|
|
5
|
+
from sqlalchemy_utils import database_exists
|
|
6
|
+
from sqlmodel import Session, SQLModel
|
|
7
|
+
|
|
8
|
+
from desdeo.api.config import ServerConfig, SettingsConfig
|
|
9
|
+
from desdeo.api.db import engine
|
|
10
|
+
from desdeo.api.models import ProblemDB, User, UserRole
|
|
11
|
+
from desdeo.api.routers.user_authentication import get_password_hash
|
|
12
|
+
from desdeo.problem.testproblems import dtlz2, river_pollution_problem, simple_knapsack
|
|
13
|
+
|
|
14
|
+
problems = [dtlz2(10, 3), simple_knapsack(), river_pollution_problem()]
|
|
15
|
+
|
|
16
|
+
if __name__ == "__main__":
|
|
17
|
+
if SettingsConfig.debug:
|
|
18
|
+
# debug stuff
|
|
19
|
+
|
|
20
|
+
print("Creating database tables.")
|
|
21
|
+
if not database_exists(engine.url):
|
|
22
|
+
SQLModel.metadata.create_all(engine)
|
|
23
|
+
else:
|
|
24
|
+
warnings.warn("Database already exists. Clearing it.", stacklevel=1)
|
|
25
|
+
# Drop all tables
|
|
26
|
+
SQLModel.metadata.reflect(bind=engine)
|
|
27
|
+
SQLModel.metadata.drop_all(bind=engine)
|
|
28
|
+
SQLModel.metadata.create_all(engine)
|
|
29
|
+
print("Database tables created.")
|
|
30
|
+
|
|
31
|
+
with Session(engine) as session:
|
|
32
|
+
user_analyst = User(
|
|
33
|
+
username=ServerConfig.test_user_analyst_name,
|
|
34
|
+
password_hash=get_password_hash(ServerConfig.test_user_analyst_password),
|
|
35
|
+
role=UserRole.analyst,
|
|
36
|
+
group="test",
|
|
37
|
+
)
|
|
38
|
+
session.add(user_analyst)
|
|
39
|
+
session.commit()
|
|
40
|
+
session.refresh(user_analyst)
|
|
41
|
+
|
|
42
|
+
for problem in problems:
|
|
43
|
+
problem_db = ProblemDB.from_problem(problem, user_analyst)
|
|
44
|
+
|
|
45
|
+
session.add(problem_db)
|
|
46
|
+
|
|
47
|
+
session.commit()
|
|
48
|
+
session.refresh(problem_db)
|
|
49
|
+
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
db.add(user_analyst)
|
|
53
|
+
db.commit()
|
|
54
|
+
db.refresh(user_analyst)
|
|
55
|
+
|
|
56
|
+
# add first test DM user
|
|
57
|
+
user_dm1 = db_models.User(
|
|
58
|
+
username=ServerDebugConfig.test_user_dm1_name,
|
|
59
|
+
password_hash=get_password_hash(ServerDebugConfig.test_user_dm1_password),
|
|
60
|
+
role=UserRole.DM,
|
|
61
|
+
privileges=[],
|
|
62
|
+
user_group="",
|
|
63
|
+
)
|
|
64
|
+
db.add(user_dm1)
|
|
65
|
+
db.commit()
|
|
66
|
+
db.refresh(user_dm1)
|
|
67
|
+
|
|
68
|
+
# add second test DM user
|
|
69
|
+
user_dm2 = db_models.User(
|
|
70
|
+
username=ServerDebugConfig.test_user_dm2_name,
|
|
71
|
+
password_hash=get_password_hash(ServerDebugConfig.test_user_dm2_password),
|
|
72
|
+
role=UserRole.DM,
|
|
73
|
+
privileges=[],
|
|
74
|
+
user_group="",
|
|
75
|
+
)
|
|
76
|
+
db.add(user_dm2)
|
|
77
|
+
db.commit()
|
|
78
|
+
db.refresh(user_dm2)
|
|
79
|
+
|
|
80
|
+
db.close()
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
else:
|
|
84
|
+
# deployment stuff
|
|
85
|
+
pass
|
desdeo/api/db_models.py
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""All models for the API. I put them all in a single file for simplicity."""
|
|
2
|
+
|
|
3
|
+
# TODO: ADD TIMESTAMP COLUMNS TO ALL TABLES
|
|
4
|
+
|
|
5
|
+
from sqlalchemy import Enum, ForeignKey, Integer, String
|
|
6
|
+
from sqlalchemy.dialects.postgresql import ARRAY, FLOAT, JSON, JSONB
|
|
7
|
+
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
8
|
+
|
|
9
|
+
from desdeo.api import schema
|
|
10
|
+
from desdeo.api.db import Base
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class User(Base):
|
|
14
|
+
"""A user with a password, stored problems, role, and user group."""
|
|
15
|
+
|
|
16
|
+
__tablename__ = "user"
|
|
17
|
+
id: Mapped[int] = mapped_column(primary_key=True, unique=True)
|
|
18
|
+
username: Mapped[str] = mapped_column(unique=True, nullable=False)
|
|
19
|
+
password_hash: Mapped[str] = mapped_column(nullable=False)
|
|
20
|
+
role: Mapped[schema.UserRole] = mapped_column(nullable=False)
|
|
21
|
+
user_group: Mapped[str] = mapped_column(nullable=True)
|
|
22
|
+
# privilages: Mapped[list[schema.UserPrivileges]] = mapped_column(ARRAY(Enum(schema.UserPrivileges)), nullable=False)
|
|
23
|
+
privileges: Mapped[list[schema.UserPrivileges]] = mapped_column(JSON, nullable=False)
|
|
24
|
+
|
|
25
|
+
def __repr__(self):
|
|
26
|
+
"""Return a string representation of the user (username)."""
|
|
27
|
+
return f"User: ('{self.username}')"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Problem(Base):
|
|
31
|
+
"""A model to store a problem and its associated data."""
|
|
32
|
+
|
|
33
|
+
__tablename__ = "problem"
|
|
34
|
+
id: Mapped[int] = mapped_column(primary_key=True, unique=True)
|
|
35
|
+
owner = mapped_column(Integer, ForeignKey("user.id"), nullable=True) # Null if problem is public.
|
|
36
|
+
name: Mapped[str] = mapped_column(nullable=False)
|
|
37
|
+
# kind and obj_kind are also in value, but we need them as columns for querying. Maybe?
|
|
38
|
+
kind: Mapped[schema.ProblemKind] = mapped_column(nullable=False)
|
|
39
|
+
obj_kind: Mapped[schema.ObjectiveKind] = mapped_column(nullable=False)
|
|
40
|
+
# role_permission: Mapped[list[schema.UserRole]] = mapped_column(ARRAY(Enum(schema.UserRole)), nullable=True)
|
|
41
|
+
role_permission: Mapped[list[schema.UserRole]] = mapped_column(JSON, nullable=True)
|
|
42
|
+
# Mapped doesn't work with JSON, so we use JSON directly.
|
|
43
|
+
value = mapped_column(JSON, nullable=False) # desdeo.problem.schema.Problem
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class UserProblemAccess(Base):
|
|
47
|
+
"""A model to store user's access to problems."""
|
|
48
|
+
|
|
49
|
+
__tablename__ = "user_problem_access"
|
|
50
|
+
id: Mapped[int] = mapped_column(primary_key=True, unique=True)
|
|
51
|
+
user_id = mapped_column(Integer, ForeignKey("user.id", ondelete="CASCADE"), nullable=False)
|
|
52
|
+
problem_access: Mapped[int] = mapped_column(Integer, ForeignKey("problem.id"), nullable=False)
|
|
53
|
+
problem = relationship("Problem", foreign_keys=[problem_access], lazy="selectin")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class Method(Base):
|
|
57
|
+
"""A model to store a method and its associated data."""
|
|
58
|
+
|
|
59
|
+
__tablename__ = "method"
|
|
60
|
+
id: Mapped[int] = mapped_column(primary_key=True, unique=True)
|
|
61
|
+
kind: Mapped[schema.Methods] = mapped_column(Enum(schema.Methods), nullable=False)
|
|
62
|
+
# properties: Mapped[list[schema.MethodProperties]] = mapped_column(
|
|
63
|
+
# ARRAY(Enum(schema.MethodProperties)), nullable=False
|
|
64
|
+
# )
|
|
65
|
+
properties: Mapped[list[schema.MethodProperties]] = mapped_column(JSON, nullable=False)
|
|
66
|
+
name: Mapped[str] = mapped_column(nullable=False)
|
|
67
|
+
parameters = mapped_column(JSON, nullable=True)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class Preference(Base):
|
|
71
|
+
"""A model to store user preferences provided by the DM."""
|
|
72
|
+
|
|
73
|
+
__tablename__ = "preference"
|
|
74
|
+
id: Mapped[int] = mapped_column(primary_key=True, unique=True)
|
|
75
|
+
user = mapped_column(Integer, ForeignKey("user.id"), nullable=False)
|
|
76
|
+
problem = mapped_column(Integer, ForeignKey("problem.id"), nullable=False)
|
|
77
|
+
previous_preference = mapped_column(Integer, ForeignKey("preference.id"), nullable=True)
|
|
78
|
+
method = mapped_column(Integer, ForeignKey("method.id"), nullable=False)
|
|
79
|
+
kind: Mapped[str] # Depends on the method
|
|
80
|
+
value = mapped_column(JSON, nullable=False)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class MethodState(Base):
|
|
84
|
+
"""A model to store the state of a method. Contains all the information needed to restore the state of a method."""
|
|
85
|
+
|
|
86
|
+
__tablename__ = "method_state"
|
|
87
|
+
id: Mapped[int] = mapped_column(primary_key=True, unique=True)
|
|
88
|
+
user = mapped_column(Integer, ForeignKey("user.id"), nullable=False)
|
|
89
|
+
problem = mapped_column(Integer, ForeignKey("problem.id"), nullable=False)
|
|
90
|
+
method = mapped_column(Integer, ForeignKey("method.id"), nullable=False) # Honestly, this can just be a string.
|
|
91
|
+
preference = mapped_column(Integer, ForeignKey("preference.id"), nullable=True)
|
|
92
|
+
value = mapped_column(JSON, nullable=False) # Depends on the method.
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class Results(Base):
|
|
96
|
+
"""A model to store the results of a method run.
|
|
97
|
+
|
|
98
|
+
The results can be partial or complete, depending on the method. For example, NAUTILUS can return ranges instead of
|
|
99
|
+
solutions. The overlap between the Results and SolutionArchive tables is intentional. Though if you have a better
|
|
100
|
+
idea, feel free to change it.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
__tablename__ = "results"
|
|
104
|
+
id: Mapped[int] = mapped_column(primary_key=True, unique=True)
|
|
105
|
+
user = mapped_column(Integer, ForeignKey("user.id"), nullable=False)
|
|
106
|
+
problem = mapped_column(Integer, ForeignKey("problem.id"), nullable=False)
|
|
107
|
+
# TODO: The method is temporarily nullable for initial testing. It should be non-nullable.
|
|
108
|
+
method = mapped_column(Integer, ForeignKey("method.id"), nullable=True)
|
|
109
|
+
method_state = mapped_column(Integer, ForeignKey("method_state.id"), nullable=True)
|
|
110
|
+
value = mapped_column(JSON, nullable=False) # Depends on the method
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class SolutionArchive(Base):
|
|
114
|
+
"""A model to store a solution archive.
|
|
115
|
+
|
|
116
|
+
The archive can be used to store the results of a method run. Note that each entry must be a single,
|
|
117
|
+
complete solution. This is different from the Results table, which can store partial results.
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
__tablename__ = "solution_archive"
|
|
121
|
+
id: Mapped[int] = mapped_column(primary_key=True, unique=True)
|
|
122
|
+
user = mapped_column(Integer, ForeignKey("user.id"), nullable=False)
|
|
123
|
+
problem = mapped_column(Integer, ForeignKey("problem.id"), nullable=False)
|
|
124
|
+
method = mapped_column(Integer, ForeignKey("method.id"), nullable=False)
|
|
125
|
+
preference = mapped_column(Integer, ForeignKey("preference.id"), nullable=True)
|
|
126
|
+
# decision_variables = mapped_column(ARRAY(FLOAT), nullable=True)
|
|
127
|
+
decision_variables = mapped_column(JSON, nullable=True)
|
|
128
|
+
# objectives = mapped_column(ARRAY(FLOAT), nullable=False)
|
|
129
|
+
objectives = mapped_column(JSON, nullable=False)
|
|
130
|
+
# constraints = mapped_column(ARRAY(FLOAT), nullable=True)
|
|
131
|
+
constraints = mapped_column(JSON, nullable=True)
|
|
132
|
+
# extra_funcs = mapped_column(ARRAY(FLOAT), nullable=True)
|
|
133
|
+
extra_funcs = mapped_column(JSON, nullable=True)
|
|
134
|
+
other_info = mapped_column(
|
|
135
|
+
JSON,
|
|
136
|
+
nullable=True,
|
|
137
|
+
) # Depends on the method. May include things such as scalarization functions value, etc.
|
|
138
|
+
saved: Mapped[bool] = mapped_column(nullable=False)
|
|
139
|
+
current: Mapped[bool] = mapped_column(nullable=False)
|
|
140
|
+
chosen: Mapped[bool] = mapped_column(nullable=False)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class Log(Base):
|
|
144
|
+
"""A model to store logs of user actions. I have no idea what to put in this table."""
|
|
145
|
+
|
|
146
|
+
__tablename__ = "log"
|
|
147
|
+
id: Mapped[int] = mapped_column(primary_key=True, unique=True)
|
|
148
|
+
user = mapped_column(Integer, ForeignKey("user.id"), nullable=False)
|
|
149
|
+
action: Mapped[str] = mapped_column(nullable=False)
|
|
150
|
+
value = mapped_column(JSON, nullable=False)
|
|
151
|
+
timestamp: Mapped[str] = mapped_column(nullable=False)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class Utopia(Base):
|
|
155
|
+
"""A model to store user specific information relating to Utopia problems."""
|
|
156
|
+
|
|
157
|
+
__tablename__ = "utopia"
|
|
158
|
+
id: Mapped[int] = mapped_column(primary_key=True, unique=True)
|
|
159
|
+
problem: Mapped[int] = mapped_column(Integer, ForeignKey("problem.id"), nullable=False)
|
|
160
|
+
map_json: Mapped[str] = mapped_column(nullable=False)
|
|
161
|
+
schedule_dict = mapped_column(JSONB, nullable=False)
|
|
162
|
+
years: Mapped[list[str]] = mapped_column(ARRAY(String), nullable=False)
|
|
163
|
+
stand_id_field: Mapped[str] = mapped_column(String, nullable=False)
|
|
164
|
+
stand_descriptor = mapped_column(JSONB, nullable=True)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import json # noqa: D100
|
|
2
|
+
|
|
3
|
+
from desdeo.api.db_init import * # noqa: F403
|
|
4
|
+
from desdeo.utopia_stuff.utopia_problem import utopia_problem
|
|
5
|
+
|
|
6
|
+
with open("C:/MyTemp/code/students_and_passwords.json") as file: # noqa: PTH123
|
|
7
|
+
userdict = json.load(file)
|
|
8
|
+
|
|
9
|
+
db = SessionLocal()
|
|
10
|
+
|
|
11
|
+
holding_num = 1
|
|
12
|
+
for name in userdict:
|
|
13
|
+
print(name)
|
|
14
|
+
user = db_models.User(
|
|
15
|
+
username=name,
|
|
16
|
+
password_hash=get_password_hash(userdict[name]),
|
|
17
|
+
role=UserRole.DM,
|
|
18
|
+
privilages=[],
|
|
19
|
+
user_group="",
|
|
20
|
+
)
|
|
21
|
+
db.add(user)
|
|
22
|
+
|
|
23
|
+
db.commit()
|
|
24
|
+
|
|
25
|
+
# Extra problem ends here
|
|
26
|
+
|
|
27
|
+
db.close()
|