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.
Files changed (182) hide show
  1. desdeo/__init__.py +8 -8
  2. desdeo/adm/ADMAfsar.py +551 -0
  3. desdeo/adm/ADMChen.py +414 -0
  4. desdeo/adm/BaseADM.py +119 -0
  5. desdeo/adm/__init__.py +11 -0
  6. desdeo/api/README.md +73 -0
  7. desdeo/api/__init__.py +15 -0
  8. desdeo/api/app.py +50 -0
  9. desdeo/api/config.py +90 -0
  10. desdeo/api/config.toml +64 -0
  11. desdeo/api/db.py +27 -0
  12. desdeo/api/db_init.py +85 -0
  13. desdeo/api/db_models.py +164 -0
  14. desdeo/api/malaga_db_init.py +27 -0
  15. desdeo/api/models/__init__.py +266 -0
  16. desdeo/api/models/archive.py +23 -0
  17. desdeo/api/models/emo.py +128 -0
  18. desdeo/api/models/enautilus.py +69 -0
  19. desdeo/api/models/gdm/gdm_aggregate.py +139 -0
  20. desdeo/api/models/gdm/gdm_base.py +69 -0
  21. desdeo/api/models/gdm/gdm_score_bands.py +114 -0
  22. desdeo/api/models/gdm/gnimbus.py +138 -0
  23. desdeo/api/models/generic.py +104 -0
  24. desdeo/api/models/generic_states.py +401 -0
  25. desdeo/api/models/nimbus.py +158 -0
  26. desdeo/api/models/preference.py +128 -0
  27. desdeo/api/models/problem.py +717 -0
  28. desdeo/api/models/reference_point_method.py +18 -0
  29. desdeo/api/models/session.py +49 -0
  30. desdeo/api/models/state.py +463 -0
  31. desdeo/api/models/user.py +52 -0
  32. desdeo/api/models/utopia.py +25 -0
  33. desdeo/api/routers/_EMO.backup +309 -0
  34. desdeo/api/routers/_NAUTILUS.py +245 -0
  35. desdeo/api/routers/_NAUTILUS_navigator.py +233 -0
  36. desdeo/api/routers/_NIMBUS.py +765 -0
  37. desdeo/api/routers/__init__.py +5 -0
  38. desdeo/api/routers/emo.py +497 -0
  39. desdeo/api/routers/enautilus.py +237 -0
  40. desdeo/api/routers/gdm/gdm_aggregate.py +234 -0
  41. desdeo/api/routers/gdm/gdm_base.py +420 -0
  42. desdeo/api/routers/gdm/gdm_score_bands/gdm_score_bands_manager.py +398 -0
  43. desdeo/api/routers/gdm/gdm_score_bands/gdm_score_bands_routers.py +377 -0
  44. desdeo/api/routers/gdm/gnimbus/gnimbus_manager.py +698 -0
  45. desdeo/api/routers/gdm/gnimbus/gnimbus_routers.py +591 -0
  46. desdeo/api/routers/generic.py +233 -0
  47. desdeo/api/routers/nimbus.py +705 -0
  48. desdeo/api/routers/problem.py +307 -0
  49. desdeo/api/routers/reference_point_method.py +93 -0
  50. desdeo/api/routers/session.py +100 -0
  51. desdeo/api/routers/test.py +16 -0
  52. desdeo/api/routers/user_authentication.py +520 -0
  53. desdeo/api/routers/utils.py +187 -0
  54. desdeo/api/routers/utopia.py +230 -0
  55. desdeo/api/schema.py +100 -0
  56. desdeo/api/tests/__init__.py +0 -0
  57. desdeo/api/tests/conftest.py +151 -0
  58. desdeo/api/tests/test_enautilus.py +330 -0
  59. desdeo/api/tests/test_models.py +1179 -0
  60. desdeo/api/tests/test_routes.py +1075 -0
  61. desdeo/api/utils/_database.py +263 -0
  62. desdeo/api/utils/_logger.py +29 -0
  63. desdeo/api/utils/database.py +36 -0
  64. desdeo/api/utils/emo_database.py +40 -0
  65. desdeo/core.py +34 -0
  66. desdeo/emo/__init__.py +159 -0
  67. desdeo/emo/hooks/archivers.py +188 -0
  68. desdeo/emo/methods/EAs.py +541 -0
  69. desdeo/emo/methods/__init__.py +0 -0
  70. desdeo/emo/methods/bases.py +12 -0
  71. desdeo/emo/methods/templates.py +111 -0
  72. desdeo/emo/operators/__init__.py +1 -0
  73. desdeo/emo/operators/crossover.py +1282 -0
  74. desdeo/emo/operators/evaluator.py +114 -0
  75. desdeo/emo/operators/generator.py +459 -0
  76. desdeo/emo/operators/mutation.py +1224 -0
  77. desdeo/emo/operators/scalar_selection.py +202 -0
  78. desdeo/emo/operators/selection.py +1778 -0
  79. desdeo/emo/operators/termination.py +286 -0
  80. desdeo/emo/options/__init__.py +108 -0
  81. desdeo/emo/options/algorithms.py +435 -0
  82. desdeo/emo/options/crossover.py +164 -0
  83. desdeo/emo/options/generator.py +131 -0
  84. desdeo/emo/options/mutation.py +260 -0
  85. desdeo/emo/options/repair.py +61 -0
  86. desdeo/emo/options/scalar_selection.py +66 -0
  87. desdeo/emo/options/selection.py +127 -0
  88. desdeo/emo/options/templates.py +383 -0
  89. desdeo/emo/options/termination.py +143 -0
  90. desdeo/explanations/__init__.py +6 -0
  91. desdeo/explanations/explainer.py +100 -0
  92. desdeo/explanations/utils.py +90 -0
  93. desdeo/gdm/__init__.py +22 -0
  94. desdeo/gdm/gdmtools.py +45 -0
  95. desdeo/gdm/score_bands.py +114 -0
  96. desdeo/gdm/voting_rules.py +50 -0
  97. desdeo/mcdm/__init__.py +41 -0
  98. desdeo/mcdm/enautilus.py +338 -0
  99. desdeo/mcdm/gnimbus.py +484 -0
  100. desdeo/mcdm/nautili.py +345 -0
  101. desdeo/mcdm/nautilus.py +477 -0
  102. desdeo/mcdm/nautilus_navigator.py +656 -0
  103. desdeo/mcdm/nimbus.py +417 -0
  104. desdeo/mcdm/pareto_navigator.py +269 -0
  105. desdeo/mcdm/reference_point_method.py +186 -0
  106. desdeo/problem/__init__.py +83 -0
  107. desdeo/problem/evaluator.py +561 -0
  108. desdeo/problem/external/__init__.py +18 -0
  109. desdeo/problem/external/core.py +356 -0
  110. desdeo/problem/external/pymoo_provider.py +266 -0
  111. desdeo/problem/external/runtime.py +44 -0
  112. desdeo/problem/gurobipy_evaluator.py +562 -0
  113. desdeo/problem/infix_parser.py +341 -0
  114. desdeo/problem/json_parser.py +944 -0
  115. desdeo/problem/pyomo_evaluator.py +487 -0
  116. desdeo/problem/schema.py +1829 -0
  117. desdeo/problem/simulator_evaluator.py +348 -0
  118. desdeo/problem/sympy_evaluator.py +244 -0
  119. desdeo/problem/testproblems/__init__.py +88 -0
  120. desdeo/problem/testproblems/benchmarks_server.py +120 -0
  121. desdeo/problem/testproblems/binh_and_korn_problem.py +88 -0
  122. desdeo/problem/testproblems/cake_problem.py +185 -0
  123. desdeo/problem/testproblems/dmitry_forest_problem_discrete.py +71 -0
  124. desdeo/problem/testproblems/dtlz2_problem.py +102 -0
  125. desdeo/problem/testproblems/forest_problem.py +283 -0
  126. desdeo/problem/testproblems/knapsack_problem.py +163 -0
  127. desdeo/problem/testproblems/mcwb_problem.py +831 -0
  128. desdeo/problem/testproblems/mixed_variable_dimenrions_problem.py +83 -0
  129. desdeo/problem/testproblems/momip_problem.py +172 -0
  130. desdeo/problem/testproblems/multi_valued_constraints.py +119 -0
  131. desdeo/problem/testproblems/nimbus_problem.py +143 -0
  132. desdeo/problem/testproblems/pareto_navigator_problem.py +89 -0
  133. desdeo/problem/testproblems/re_problem.py +492 -0
  134. desdeo/problem/testproblems/river_pollution_problems.py +440 -0
  135. desdeo/problem/testproblems/rocket_injector_design_problem.py +140 -0
  136. desdeo/problem/testproblems/simple_problem.py +351 -0
  137. desdeo/problem/testproblems/simulator_problem.py +92 -0
  138. desdeo/problem/testproblems/single_objective.py +289 -0
  139. desdeo/problem/testproblems/spanish_sustainability_problem.py +945 -0
  140. desdeo/problem/testproblems/zdt_problem.py +274 -0
  141. desdeo/problem/utils.py +245 -0
  142. desdeo/tools/GenerateReferencePoints.py +181 -0
  143. desdeo/tools/__init__.py +120 -0
  144. desdeo/tools/desc_gen.py +22 -0
  145. desdeo/tools/generics.py +165 -0
  146. desdeo/tools/group_scalarization.py +3090 -0
  147. desdeo/tools/gurobipy_solver_interfaces.py +258 -0
  148. desdeo/tools/indicators_binary.py +117 -0
  149. desdeo/tools/indicators_unary.py +362 -0
  150. desdeo/tools/interaction_schema.py +38 -0
  151. desdeo/tools/intersection.py +54 -0
  152. desdeo/tools/iterative_pareto_representer.py +99 -0
  153. desdeo/tools/message.py +265 -0
  154. desdeo/tools/ng_solver_interfaces.py +199 -0
  155. desdeo/tools/non_dominated_sorting.py +134 -0
  156. desdeo/tools/patterns.py +283 -0
  157. desdeo/tools/proximal_solver.py +99 -0
  158. desdeo/tools/pyomo_solver_interfaces.py +477 -0
  159. desdeo/tools/reference_vectors.py +229 -0
  160. desdeo/tools/scalarization.py +2065 -0
  161. desdeo/tools/scipy_solver_interfaces.py +454 -0
  162. desdeo/tools/score_bands.py +627 -0
  163. desdeo/tools/utils.py +388 -0
  164. desdeo/tools/visualizations.py +67 -0
  165. desdeo/utopia_stuff/__init__.py +0 -0
  166. desdeo/utopia_stuff/data/1.json +15 -0
  167. desdeo/utopia_stuff/data/2.json +13 -0
  168. desdeo/utopia_stuff/data/3.json +15 -0
  169. desdeo/utopia_stuff/data/4.json +17 -0
  170. desdeo/utopia_stuff/data/5.json +15 -0
  171. desdeo/utopia_stuff/from_json.py +40 -0
  172. desdeo/utopia_stuff/reinit_user.py +38 -0
  173. desdeo/utopia_stuff/utopia_db_init.py +212 -0
  174. desdeo/utopia_stuff/utopia_problem.py +403 -0
  175. desdeo/utopia_stuff/utopia_problem_old.py +415 -0
  176. desdeo/utopia_stuff/utopia_reference_solutions.py +79 -0
  177. desdeo-2.1.0.dist-info/METADATA +186 -0
  178. desdeo-2.1.0.dist-info/RECORD +180 -0
  179. {desdeo-1.2.dist-info → desdeo-2.1.0.dist-info}/WHEEL +1 -1
  180. desdeo-2.1.0.dist-info/licenses/LICENSE +21 -0
  181. desdeo-1.2.dist-info/METADATA +0 -16
  182. 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
@@ -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()