gamsapi 52.5.0__cp312-cp312-win_amd64.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 (257) hide show
  1. gams/__init__.py +27 -0
  2. gams/_version.py +1 -0
  3. gams/connect/__init__.py +28 -0
  4. gams/connect/agents/__init__.py +24 -0
  5. gams/connect/agents/_excel/__init__.py +32 -0
  6. gams/connect/agents/_excel/excelagent.py +312 -0
  7. gams/connect/agents/_excel/workbook.py +155 -0
  8. gams/connect/agents/_sqlconnectors/__init__.py +42 -0
  9. gams/connect/agents/_sqlconnectors/_accesshandler.py +211 -0
  10. gams/connect/agents/_sqlconnectors/_databasehandler.py +250 -0
  11. gams/connect/agents/_sqlconnectors/_mysqlhandler.py +168 -0
  12. gams/connect/agents/_sqlconnectors/_postgreshandler.py +131 -0
  13. gams/connect/agents/_sqlconnectors/_pyodbchandler.py +112 -0
  14. gams/connect/agents/_sqlconnectors/_sqlalchemyhandler.py +74 -0
  15. gams/connect/agents/_sqlconnectors/_sqlitehandler.py +262 -0
  16. gams/connect/agents/_sqlconnectors/_sqlserverhandler.py +179 -0
  17. gams/connect/agents/concatenate.py +440 -0
  18. gams/connect/agents/connectagent.py +743 -0
  19. gams/connect/agents/csvreader.py +675 -0
  20. gams/connect/agents/csvwriter.py +151 -0
  21. gams/connect/agents/domainwriter.py +143 -0
  22. gams/connect/agents/excelreader.py +756 -0
  23. gams/connect/agents/excelwriter.py +467 -0
  24. gams/connect/agents/filter.py +223 -0
  25. gams/connect/agents/gamsreader.py +112 -0
  26. gams/connect/agents/gamswriter.py +239 -0
  27. gams/connect/agents/gdxreader.py +109 -0
  28. gams/connect/agents/gdxwriter.py +146 -0
  29. gams/connect/agents/labelmanipulator.py +303 -0
  30. gams/connect/agents/projection.py +539 -0
  31. gams/connect/agents/pythoncode.py +71 -0
  32. gams/connect/agents/rawcsvreader.py +248 -0
  33. gams/connect/agents/rawexcelreader.py +312 -0
  34. gams/connect/agents/schema/CSVReader.yaml +92 -0
  35. gams/connect/agents/schema/CSVWriter.yaml +44 -0
  36. gams/connect/agents/schema/Concatenate.yaml +52 -0
  37. gams/connect/agents/schema/DomainWriter.yaml +25 -0
  38. gams/connect/agents/schema/ExcelReader.yaml +121 -0
  39. gams/connect/agents/schema/ExcelWriter.yaml +78 -0
  40. gams/connect/agents/schema/Filter.yaml +74 -0
  41. gams/connect/agents/schema/GAMSReader.yaml +20 -0
  42. gams/connect/agents/schema/GAMSWriter.yaml +47 -0
  43. gams/connect/agents/schema/GDXReader.yaml +23 -0
  44. gams/connect/agents/schema/GDXWriter.yaml +32 -0
  45. gams/connect/agents/schema/LabelManipulator.yaml +99 -0
  46. gams/connect/agents/schema/Projection.yaml +24 -0
  47. gams/connect/agents/schema/PythonCode.yaml +6 -0
  48. gams/connect/agents/schema/RawCSVReader.yaml +34 -0
  49. gams/connect/agents/schema/RawExcelReader.yaml +42 -0
  50. gams/connect/agents/schema/SQLReader.yaml +75 -0
  51. gams/connect/agents/schema/SQLWriter.yaml +103 -0
  52. gams/connect/agents/sqlreader.py +301 -0
  53. gams/connect/agents/sqlwriter.py +276 -0
  54. gams/connect/connectdatabase.py +275 -0
  55. gams/connect/connectvalidator.py +93 -0
  56. gams/connect/errors.py +34 -0
  57. gams/control/__init__.py +136 -0
  58. gams/control/database.py +2231 -0
  59. gams/control/execution.py +1900 -0
  60. gams/control/options.py +2792 -0
  61. gams/control/workspace.py +1198 -0
  62. gams/core/__init__.py +24 -0
  63. gams/core/cfg/__init__.py +26 -0
  64. gams/core/cfg/_cfgmcc.cp312-win_amd64.pyd +0 -0
  65. gams/core/cfg/cfgmcc.py +519 -0
  66. gams/core/dct/__init__.py +26 -0
  67. gams/core/dct/_dctmcc.cp312-win_amd64.pyd +0 -0
  68. gams/core/dct/dctmcc.py +574 -0
  69. gams/core/embedded/__init__.py +26 -0
  70. gams/core/embedded/gamsemb.py +1024 -0
  71. gams/core/emp/__init__.py +24 -0
  72. gams/core/emp/emplexer.py +89 -0
  73. gams/core/emp/empyacc.py +281 -0
  74. gams/core/gdx/__init__.py +26 -0
  75. gams/core/gdx/_gdxcc.cp312-win_amd64.pyd +0 -0
  76. gams/core/gdx/gdxcc.py +866 -0
  77. gams/core/gev/__init__.py +26 -0
  78. gams/core/gev/_gevmcc.cp312-win_amd64.pyd +0 -0
  79. gams/core/gev/gevmcc.py +855 -0
  80. gams/core/gmd/__init__.py +26 -0
  81. gams/core/gmd/_gmdcc.cp312-win_amd64.pyd +0 -0
  82. gams/core/gmd/gmdcc.py +917 -0
  83. gams/core/gmo/__init__.py +26 -0
  84. gams/core/gmo/_gmomcc.cp312-win_amd64.pyd +0 -0
  85. gams/core/gmo/gmomcc.py +2046 -0
  86. gams/core/idx/__init__.py +26 -0
  87. gams/core/idx/_idxcc.cp312-win_amd64.pyd +0 -0
  88. gams/core/idx/idxcc.py +510 -0
  89. gams/core/numpy/__init__.py +29 -0
  90. gams/core/numpy/_gams2numpy.cp312-win_amd64.pyd +0 -0
  91. gams/core/numpy/gams2numpy.py +1048 -0
  92. gams/core/opt/__init__.py +26 -0
  93. gams/core/opt/_optcc.cp312-win_amd64.pyd +0 -0
  94. gams/core/opt/optcc.py +840 -0
  95. gams/engine/__init__.py +204 -0
  96. gams/engine/api/__init__.py +13 -0
  97. gams/engine/api/auth_api.py +7653 -0
  98. gams/engine/api/cleanup_api.py +751 -0
  99. gams/engine/api/default_api.py +887 -0
  100. gams/engine/api/hypercube_api.py +2629 -0
  101. gams/engine/api/jobs_api.py +5229 -0
  102. gams/engine/api/licenses_api.py +2220 -0
  103. gams/engine/api/namespaces_api.py +7783 -0
  104. gams/engine/api/usage_api.py +5627 -0
  105. gams/engine/api/users_api.py +5931 -0
  106. gams/engine/api_client.py +804 -0
  107. gams/engine/api_response.py +21 -0
  108. gams/engine/configuration.py +601 -0
  109. gams/engine/exceptions.py +216 -0
  110. gams/engine/models/__init__.py +86 -0
  111. gams/engine/models/bad_input.py +89 -0
  112. gams/engine/models/cleanable_job_result.py +104 -0
  113. gams/engine/models/cleanable_job_result_page.py +113 -0
  114. gams/engine/models/engine_license.py +107 -0
  115. gams/engine/models/files_not_found.py +93 -0
  116. gams/engine/models/forwarded_token_response.py +112 -0
  117. gams/engine/models/generic_key_value_pair.py +89 -0
  118. gams/engine/models/hypercube.py +160 -0
  119. gams/engine/models/hypercube_page.py +111 -0
  120. gams/engine/models/hypercube_summary.py +91 -0
  121. gams/engine/models/hypercube_token.py +97 -0
  122. gams/engine/models/identity_provider.py +107 -0
  123. gams/engine/models/identity_provider_ldap.py +121 -0
  124. gams/engine/models/identity_provider_oauth2.py +146 -0
  125. gams/engine/models/identity_provider_oauth2_scope.py +89 -0
  126. gams/engine/models/identity_provider_oauth2_with_secret.py +152 -0
  127. gams/engine/models/identity_provider_oidc.py +133 -0
  128. gams/engine/models/identity_provider_oidc_with_secret.py +143 -0
  129. gams/engine/models/inex.py +91 -0
  130. gams/engine/models/invitation.py +136 -0
  131. gams/engine/models/invitation_quota.py +106 -0
  132. gams/engine/models/invitation_token.py +87 -0
  133. gams/engine/models/job.py +165 -0
  134. gams/engine/models/job_no_text_entry.py +138 -0
  135. gams/engine/models/job_no_text_entry_page.py +111 -0
  136. gams/engine/models/license.py +91 -0
  137. gams/engine/models/log_piece.py +96 -0
  138. gams/engine/models/message.py +87 -0
  139. gams/engine/models/message_and_token.py +99 -0
  140. gams/engine/models/message_with_webhook_id.py +89 -0
  141. gams/engine/models/model_auth_token.py +87 -0
  142. gams/engine/models/model_configuration.py +125 -0
  143. gams/engine/models/model_default_instance.py +99 -0
  144. gams/engine/models/model_default_user_instance.py +98 -0
  145. gams/engine/models/model_hypercube_job.py +106 -0
  146. gams/engine/models/model_hypercube_usage.py +130 -0
  147. gams/engine/models/model_instance_info.py +116 -0
  148. gams/engine/models/model_instance_info_full.py +123 -0
  149. gams/engine/models/model_instance_pool_info.py +112 -0
  150. gams/engine/models/model_job_labels.py +179 -0
  151. gams/engine/models/model_job_usage.py +133 -0
  152. gams/engine/models/model_pool_usage.py +124 -0
  153. gams/engine/models/model_usage.py +115 -0
  154. gams/engine/models/model_user.py +96 -0
  155. gams/engine/models/model_userinstance_info.py +119 -0
  156. gams/engine/models/model_userinstancepool_info.py +95 -0
  157. gams/engine/models/model_version.py +91 -0
  158. gams/engine/models/models.py +120 -0
  159. gams/engine/models/namespace.py +104 -0
  160. gams/engine/models/namespace_quota.py +96 -0
  161. gams/engine/models/namespace_with_permission.py +96 -0
  162. gams/engine/models/not_found.py +91 -0
  163. gams/engine/models/password_policy.py +97 -0
  164. gams/engine/models/perm_and_username.py +89 -0
  165. gams/engine/models/quota.py +117 -0
  166. gams/engine/models/quota_exceeded.py +97 -0
  167. gams/engine/models/status_code_meaning.py +89 -0
  168. gams/engine/models/stream_entry.py +89 -0
  169. gams/engine/models/system_wide_license.py +92 -0
  170. gams/engine/models/text_entries.py +87 -0
  171. gams/engine/models/text_entry.py +101 -0
  172. gams/engine/models/time_span.py +95 -0
  173. gams/engine/models/time_span_pool_worker.py +99 -0
  174. gams/engine/models/token_forward_error.py +87 -0
  175. gams/engine/models/user.py +127 -0
  176. gams/engine/models/user_group_member.py +96 -0
  177. gams/engine/models/user_groups.py +108 -0
  178. gams/engine/models/vapid_info.py +87 -0
  179. gams/engine/models/webhook.py +138 -0
  180. gams/engine/models/webhook_parameterized_event.py +99 -0
  181. gams/engine/py.typed +0 -0
  182. gams/engine/rest.py +258 -0
  183. gams/magic/__init__.py +32 -0
  184. gams/magic/gams_magic.py +142 -0
  185. gams/magic/interactive.py +402 -0
  186. gams/tools/__init__.py +30 -0
  187. gams/tools/errors.py +34 -0
  188. gams/tools/toolcollection/__init__.py +24 -0
  189. gams/tools/toolcollection/alg/__init__.py +24 -0
  190. gams/tools/toolcollection/alg/rank.py +51 -0
  191. gams/tools/toolcollection/data/__init__.py +24 -0
  192. gams/tools/toolcollection/data/csvread.py +444 -0
  193. gams/tools/toolcollection/data/csvwrite.py +311 -0
  194. gams/tools/toolcollection/data/exceldump.py +47 -0
  195. gams/tools/toolcollection/data/sqlitewrite.py +276 -0
  196. gams/tools/toolcollection/gdxservice/__init__.py +24 -0
  197. gams/tools/toolcollection/gdxservice/gdxencoding.py +104 -0
  198. gams/tools/toolcollection/gdxservice/gdxrename.py +94 -0
  199. gams/tools/toolcollection/linalg/__init__.py +24 -0
  200. gams/tools/toolcollection/linalg/cholesky.py +57 -0
  201. gams/tools/toolcollection/linalg/eigenvalue.py +56 -0
  202. gams/tools/toolcollection/linalg/eigenvector.py +58 -0
  203. gams/tools/toolcollection/linalg/invert.py +55 -0
  204. gams/tools/toolcollection/linalg/ols.py +138 -0
  205. gams/tools/toolcollection/tooltemplate.py +321 -0
  206. gams/tools/toolcollection/win32/__init__.py +24 -0
  207. gams/tools/toolcollection/win32/excelmerge.py +93 -0
  208. gams/tools/toolcollection/win32/exceltalk.py +76 -0
  209. gams/tools/toolcollection/win32/msappavail.py +49 -0
  210. gams/tools/toolcollection/win32/shellexecute.py +54 -0
  211. gams/tools/tools.py +116 -0
  212. gams/transfer/__init__.py +35 -0
  213. gams/transfer/_abcs/__init__.py +37 -0
  214. gams/transfer/_abcs/container_abcs.py +433 -0
  215. gams/transfer/_internals/__init__.py +63 -0
  216. gams/transfer/_internals/algorithms.py +436 -0
  217. gams/transfer/_internals/casepreservingdict.py +124 -0
  218. gams/transfer/_internals/constants.py +270 -0
  219. gams/transfer/_internals/domainviolation.py +103 -0
  220. gams/transfer/_internals/specialvalues.py +172 -0
  221. gams/transfer/containers/__init__.py +26 -0
  222. gams/transfer/containers/_container.py +1794 -0
  223. gams/transfer/containers/_io/__init__.py +28 -0
  224. gams/transfer/containers/_io/containers.py +164 -0
  225. gams/transfer/containers/_io/gdx.py +1029 -0
  226. gams/transfer/containers/_io/gmd.py +872 -0
  227. gams/transfer/containers/_mixins/__init__.py +26 -0
  228. gams/transfer/containers/_mixins/ccc.py +1274 -0
  229. gams/transfer/syms/__init__.py +33 -0
  230. gams/transfer/syms/_methods/__init__.py +24 -0
  231. gams/transfer/syms/_methods/tables.py +120 -0
  232. gams/transfer/syms/_methods/toDict.py +115 -0
  233. gams/transfer/syms/_methods/toList.py +83 -0
  234. gams/transfer/syms/_methods/toValue.py +60 -0
  235. gams/transfer/syms/_mixins/__init__.py +32 -0
  236. gams/transfer/syms/_mixins/equals.py +626 -0
  237. gams/transfer/syms/_mixins/generateRecords.py +499 -0
  238. gams/transfer/syms/_mixins/pivot.py +313 -0
  239. gams/transfer/syms/_mixins/pve.py +627 -0
  240. gams/transfer/syms/_mixins/sa.py +27 -0
  241. gams/transfer/syms/_mixins/sapve.py +27 -0
  242. gams/transfer/syms/_mixins/saua.py +27 -0
  243. gams/transfer/syms/_mixins/sauapve.py +199 -0
  244. gams/transfer/syms/_mixins/spve.py +1528 -0
  245. gams/transfer/syms/_mixins/ve.py +936 -0
  246. gams/transfer/syms/container_syms/__init__.py +31 -0
  247. gams/transfer/syms/container_syms/_alias.py +984 -0
  248. gams/transfer/syms/container_syms/_equation.py +333 -0
  249. gams/transfer/syms/container_syms/_parameter.py +973 -0
  250. gams/transfer/syms/container_syms/_set.py +604 -0
  251. gams/transfer/syms/container_syms/_universe_alias.py +461 -0
  252. gams/transfer/syms/container_syms/_variable.py +321 -0
  253. gamsapi-52.5.0.dist-info/METADATA +150 -0
  254. gamsapi-52.5.0.dist-info/RECORD +257 -0
  255. gamsapi-52.5.0.dist-info/WHEEL +5 -0
  256. gamsapi-52.5.0.dist-info/licenses/LICENSE +22 -0
  257. gamsapi-52.5.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,276 @@
1
+ #
2
+ # GAMS - General Algebraic Modeling System Python API
3
+ #
4
+ # Copyright (c) 2017-2026 GAMS Development Corp. <support@gams.com>
5
+ # Copyright (c) 2017-2026 GAMS Software GmbH <support@gams.com>
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+ #
25
+
26
+ import gams.transfer as gt
27
+ from gams.connect.agents.connectagent import ConnectAgent
28
+ from gams.connect.agents._sqlconnectors import (
29
+ AccessConnector,
30
+ MySQLConnector,
31
+ PostgresConnector,
32
+ PyodbcConnector,
33
+ SQLAlchemyConnector,
34
+ SQLiteConnector,
35
+ SQLServerConnector,
36
+ )
37
+ from gams.connect.agents._sqlconnectors._databasehandler import ConnectionType
38
+ from gams.connect.connectvalidator import ConnectValidator
39
+
40
+
41
+ class SQLWriter(ConnectAgent):
42
+ def __init__(self, cdb, inst, agent_index):
43
+ super().__init__(cdb, inst, agent_index)
44
+ self._parse_options(self._inst)
45
+
46
+ def _parse_options(self, inst):
47
+ # global options
48
+ self._input_cnctn = inst["connection"] # input db credentials/path thru user.
49
+ self._cnctn_type = inst["connectionType"]
50
+ self._connection_args = self._dict_get(inst, "connectionArguments", {})
51
+ self._ifExists = inst["ifExists"]
52
+ self._schema_name = inst["schemaName"]
53
+ self._to_sql_args = inst["toSQLArguments"]
54
+ self._trace = inst["trace"]
55
+ self._insertMethod = inst["insertMethod"]
56
+ self._unstack = inst["unstack"]
57
+ self._value_sub = inst["valueSubstitutions"]
58
+ self._dtype_map = inst["dTypeMap"]
59
+ self._col_encloser = inst["columnEncloser"]
60
+ self._skip_text = inst["skipText"]
61
+ self._fast = (
62
+ False if self._cnctn_type != ConnectionType.SQLITE.value else inst["fast"]
63
+ ) # NO effect for rest of the connectionTypes
64
+ self._small = (
65
+ False if self._cnctn_type != ConnectionType.SQLITE.value else inst["small"]
66
+ ) # NO effect for rest of the connectionTypes
67
+ self._symbols = inst["symbols"] # symbol option
68
+ self._write_all = self._symbols == "all"
69
+ self._sqlitewrite_globalCommit = self._connection_args.pop(
70
+ "__globalCommit__", False
71
+ )
72
+ self._handler = self._get_handler(self._cnctn_type)
73
+
74
+ def _get_handler(
75
+ self, cnctn_type
76
+ ) -> (
77
+ SQLiteConnector
78
+ | SQLAlchemyConnector
79
+ | PyodbcConnector
80
+ | MySQLConnector
81
+ | PostgresConnector
82
+ | SQLServerConnector
83
+ | AccessConnector
84
+ ):
85
+ handlers = {
86
+ ConnectionType.SQLITE.value: SQLiteConnector,
87
+ ConnectionType.SQLALCHEMY.value: SQLAlchemyConnector,
88
+ ConnectionType.PYODBC.value: PyodbcConnector,
89
+ ConnectionType.MYSQL.value: MySQLConnector,
90
+ ConnectionType.POSTGRES.value: PostgresConnector,
91
+ ConnectionType.SQLSERVER.value: SQLServerConnector,
92
+ ConnectionType.ACCESS.value: AccessConnector,
93
+ }
94
+ handler_class = handlers[cnctn_type]
95
+
96
+ return handler_class(
97
+ error_callback=self._connect_error,
98
+ printLog_callback=self._cdb.print_log,
99
+ trace=self._trace,
100
+ )
101
+
102
+ def _open(self):
103
+ options = {
104
+ "_sqlitewrite_globalCommit": self._sqlitewrite_globalCommit,
105
+ "small": self._small,
106
+ "fast": self._fast,
107
+ }
108
+ self._handler.connect(
109
+ connection_details=self._input_cnctn,
110
+ connection_args=self._connection_args,
111
+ **options,
112
+ )
113
+
114
+ def execute(self):
115
+ if self._trace > 0:
116
+ self._log_instructions(self._inst, self._inst_raw)
117
+ self._describe_container(self._cdb.container, "Connect Container:")
118
+
119
+ self._handler.validate_insert_method(method=self._insertMethod)
120
+ self._open()
121
+
122
+ try:
123
+ self._handler.create_transaction()
124
+
125
+ if self._write_all:
126
+ self._symbols = []
127
+ sym_schema = self._cdb.load_schema(self)["symbols"]["oneof"][1][
128
+ "schema"
129
+ ]["schema"]
130
+ v = ConnectValidator(sym_schema)
131
+ for name, sym in self._cdb.container.data.items():
132
+ if type(sym) in [gt.Set, gt.Parameter]:
133
+ sym_inst = v.validated({"name": name, "tableName": name}) # type: ignore
134
+ if sym_inst is None:
135
+ self._connect_error(
136
+ f"Validation for symbol >{name}< failed: {v.errors}" # type: ignore
137
+ )
138
+ sym_inst = v.normalize_of_rules(sym_inst)
139
+ self._symbols.append(sym_inst)
140
+
141
+ symbols_raw = self._symbols.copy()
142
+ sym_list = []
143
+ for s in self._symbols:
144
+ sym_name = s["name"]
145
+ self._symbols_exist_cdb(sym_name, should_exist=True)
146
+ self._update_sym_inst(s, self._inst)
147
+ sym_list.append(s["name"])
148
+
149
+ pre_write_args = {
150
+ # only implemented for sqlite, pre_write_procedures must return the container
151
+ "container": self._cdb.container,
152
+ "directory": self._system_directory,
153
+ "sym_list": sym_list,
154
+ }
155
+ write_container = self._handler.pre_write_procedures(**pre_write_args)
156
+
157
+ for sym, sym_raw in zip(self._symbols, symbols_raw):
158
+ if self._trace > 0:
159
+ self._log_instructions(
160
+ sym, sym_raw, description=f"Write symbol >{sym['name']}<:"
161
+ )
162
+
163
+ sym_name = sym["name"]
164
+ table_name = sym["tableName"]
165
+ schema = sym["schemaName"]
166
+ exists = sym["ifExists"]
167
+ unstack = sym["unstack"]
168
+ value_sub = sym["valueSubstitutions"]
169
+ dtype_map = self._dict_get(sym, "dTypeMap", {})
170
+ insertMethod = sym["insertMethod"]
171
+ skip_text = sym["skipText"]
172
+ self._handler.validate_insert_method(method=insertMethod)
173
+
174
+ if self._small and table_name == "UEL$":
175
+ self._connect_error(
176
+ "tableName >UEL$< is not allowed. >UEL$< is a preserved tableName with small set to >True<."
177
+ )
178
+
179
+ gt_sym = write_container[sym_name]
180
+
181
+ if self._trace > 2:
182
+ self._cdb.print_log(
183
+ f"Connect Container symbol={sym_name}:\n {gt_sym.records}\n" # type: ignore
184
+ )
185
+
186
+ if not isinstance(gt_sym, gt.Set) and not isinstance(
187
+ gt_sym, gt.Parameter
188
+ ):
189
+ self._connect_error(
190
+ f"Symbol type >{type(gt_sym)}< of symbol >{sym_name}< is not supported. Supported symbol types are set and parameter."
191
+ )
192
+
193
+ dim = gt_sym.dimension
194
+ df = self._sym_records_no_none(gt_sym).copy(deep=True)
195
+ sym_type = "par" if isinstance(gt_sym, gt.Parameter) else "set"
196
+ value = "value" if sym_type == "par" else "element_text"
197
+
198
+ if value_sub:
199
+ df = self._apply_value_substitutions(df, value_sub, sym_type)
200
+ if self._trace > 2:
201
+ self._cdb.print_log(f"After value substitution:\n{df}")
202
+
203
+ if unstack and dim > 0:
204
+ if (
205
+ sym_type == "set" and skip_text
206
+ ): # replace all element_text by Y when exporting a true table
207
+ df.loc[:, value] = "Y"
208
+ elif (
209
+ sym_type == "set"
210
+ ): # replace empty element_text by Y when exporting a true table
211
+ df.loc[df[value] == "", value] = "Y"
212
+ cols = list(df.columns)
213
+ if dim > 1:
214
+ df = df.pivot(index=cols[0:-2], columns=cols[-2], values=value)
215
+ df.reset_index(inplace=True, drop=False)
216
+ elif len(df) > 0:
217
+ df = df.set_index(cols[0]).T.reset_index(drop=True)
218
+ else:
219
+ self._connect_error(
220
+ f"unstack: >{unstack}< on 1-dimensional symbol with empty DataFrame not allowed."
221
+ )
222
+ df.rename_axis(
223
+ [None], axis=1, inplace=True
224
+ ) # remove column index names
225
+ if self._trace > 2:
226
+ self._cdb.print_log(f"DataFrame after unstack:\n{df}")
227
+
228
+ elif dim > 0:
229
+ df.sort_values(df.columns[:-1].tolist(), inplace=True)
230
+ if self._trace > 2:
231
+ self._cdb.print_log(f"DataFrame after sort:\n{df}")
232
+ if sym_type == "set" and skip_text:
233
+ df = df.drop(columns="element_text")
234
+
235
+ writeFunction_args = {
236
+ "name": table_name,
237
+ "schema": schema,
238
+ "if_exists": exists,
239
+ "insertMethod": insertMethod,
240
+ "columnEncloser": self._col_encloser,
241
+ "dtype_map": dtype_map,
242
+ "toSQLArguments": self._dict_get(sym, "toSQLArguments", {}),
243
+ "index": False,
244
+ }
245
+
246
+ dim_after_unstack = None
247
+ if self._small and dim > 0:
248
+ dim_after_unstack = dim - 1 if unstack else dim
249
+ col = df.columns[:dim_after_unstack]
250
+ df[col] = df[col].astype("int64")
251
+ writeFunction_args.update({"name": f"[{table_name}$]"})
252
+
253
+ self._handler.write_dataframe(df, writeFunction_args)
254
+
255
+ post_write_args = {
256
+ "tableName": table_name,
257
+ "dim_after_unstack": dim_after_unstack,
258
+ "colList": df.columns.tolist(),
259
+ "dim": dim,
260
+ }
261
+ self._handler.post_write_procedures(
262
+ **post_write_args
263
+ ) # only implemented for sqlite
264
+
265
+ if not self._sqlitewrite_globalCommit:
266
+ self._handler.commit()
267
+
268
+ if self._sqlitewrite_globalCommit:
269
+ self._handler.commit()
270
+
271
+ except Exception:
272
+ self._handler.rollback()
273
+ raise
274
+
275
+ finally:
276
+ self._handler.close()
@@ -0,0 +1,275 @@
1
+ #
2
+ # GAMS - General Algebraic Modeling System Python API
3
+ #
4
+ # Copyright (c) 2017-2026 GAMS Development Corp. <support@gams.com>
5
+ # Copyright (c) 2017-2026 GAMS Software GmbH <support@gams.com>
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+ #
25
+
26
+ import importlib
27
+ import sys
28
+ import weakref
29
+ import yaml
30
+ from gams.connect.agents.connectagent import ConnectAgent
31
+ from gams.connect.connectvalidator import ConnectValidator
32
+ from gams.connect.errors import GamsConnectException
33
+ from gams.control import GamsWorkspace
34
+ from gams.transfer import Container
35
+
36
+
37
+ class ConnectDatabase(object):
38
+ """
39
+ A ConnectDatabase contains data in the form of a gams.transfer.Container instance.
40
+ Running the execute() method instantiates Connect agents that read, write, or modify
41
+ symbol data.
42
+
43
+ Parameters
44
+ ----------
45
+ system_directory : str
46
+ GAMS system directory to be used.
47
+ container : gams.transfer.Container, optional
48
+ A Container to be used by the ConnectDatabase, by default None. If omitted, the ConnectDatabase will instantiate a new and empty container.
49
+ ecdb : gams.core.embedded.ECGAMSDatabase, optional
50
+ When running in a GAMS context (e.g. embedded code), this can be used to allow connection to the embedded code GAMS database, by default None.
51
+ """
52
+
53
+ def __init__(self, system_directory, container=None, ecdb=None):
54
+ self._system_directory = system_directory
55
+ self._ecdb = ecdb
56
+ self._schema_cache = {}
57
+ self._ws = GamsWorkspace(system_directory=self._system_directory)
58
+ if container is None:
59
+ self._container = Container(system_directory=self._system_directory)
60
+ else:
61
+ self._container = container
62
+ if self._ecdb:
63
+ ecdb._cdb = self
64
+
65
+ def __del__(self):
66
+ pass
67
+
68
+ def print_log(self, msg, end="\n"):
69
+ """
70
+ Print msg to the GAMS log if avaiable, uses print() otherwise.
71
+
72
+ Parameters
73
+ ----------
74
+ msg : str
75
+ The message to be printed.
76
+ end : str, optional
77
+ String to be put after the message, by default "\n".
78
+ """
79
+ if self._ecdb:
80
+ self._ecdb.printLog(msg, end)
81
+ else:
82
+ print(msg, end=end)
83
+ sys.stdout.flush()
84
+
85
+ def _get_agent_class(self, agent_name):
86
+ # Try to load the agent module from 'gams.connect.agents'
87
+ try:
88
+ mod = importlib.import_module("gams.connect.agents." + agent_name.lower())
89
+ err = None
90
+ except ModuleNotFoundError as e:
91
+ err = e
92
+ if err:
93
+ try:
94
+ if (
95
+ err.name != "gams.connect.agents." + agent_name.lower()
96
+ ): # the connect agent module itself was found but an import in the source itself did fail
97
+ raise GamsConnectException(str(err), traceback=True)
98
+ mod = importlib.import_module(agent_name.lower())
99
+ err = None
100
+ except ModuleNotFoundError as e:
101
+ err = e
102
+
103
+ # Try to retrieve the agent class from the found module
104
+ if not err:
105
+ try:
106
+ agent_class = vars(mod)[agent_name]
107
+ except KeyError as e:
108
+ err = e
109
+
110
+ # Compile error in case of previous errors
111
+ if err:
112
+ msg = f"Connect agent '{agent_name}' not found. The following agents are available: {', '.join(self._get_available_agents())}."
113
+ raise GamsConnectException(msg)
114
+
115
+ return agent_class
116
+
117
+ def _get_idx_str(self, agent_name, idx_list):
118
+ idx_str = f"{agent_name}"
119
+ if idx_list:
120
+ idx_str += f"({idx_list[0]})"
121
+ for i in idx_list[1:]:
122
+ idx_str = f"PythonCode({i})" + "->" + idx_str
123
+ return idx_str
124
+
125
+ def _execute(self, instructions, idx_list=[]):
126
+ if isinstance(instructions, list):
127
+ if all(isinstance(inst, dict) for inst in instructions):
128
+ inst_list = instructions
129
+ else:
130
+ raise GamsConnectException(
131
+ "Invalid data type for instructions argument. Needs to be 'list of dict'."
132
+ )
133
+ elif isinstance(instructions, dict):
134
+ inst_list = [instructions]
135
+ else:
136
+ raise GamsConnectException(
137
+ f"Invalid data type for instructions argument. Needs to be 'dict' or 'list', but was '{type(instructions).__name__}'."
138
+ )
139
+
140
+ agent_instances = []
141
+ for idx, inst in enumerate(inst_list, start=1):
142
+ root = list(inst.keys())
143
+ if len(root) != 1:
144
+ raise GamsConnectException(
145
+ f"Invalid agent definition with {len(root)} agent names instead of 1."
146
+ )
147
+ agent_name = root[0]
148
+ if not isinstance(agent_name, str):
149
+ raise GamsConnectException(
150
+ f"Invalid data type for agent name. Needs to be 'str', but was '{type(agent_name).__name__}'."
151
+ )
152
+
153
+ inst = inst[agent_name]
154
+
155
+ agent_class = self._get_agent_class(agent_name)
156
+ if not issubclass(agent_class, ConnectAgent):
157
+ raise GamsConnectException(
158
+ f"Agent class '{agent_name}' has to be derived from gams.connect.agents.connectagent.ConnectAgent",
159
+ traceback=True,
160
+ )
161
+
162
+ agent_schema = self.load_schema(agent_class.__name__)
163
+ v = ConnectValidator(agent_schema)
164
+ if not v.validate(inst):
165
+ raise GamsConnectException(
166
+ f"Validation of instructions for agent {self._get_idx_str(agent_name, [idx] + idx_list)} failed: {v.errors}"
167
+ )
168
+ agent_instance = agent_class(self, inst, [idx] + idx_list)
169
+ agent_instances.append(agent_instance)
170
+
171
+ for idx, agent in enumerate(agent_instances, start=1):
172
+ agent.setup_log()
173
+ try:
174
+ execute_return = agent.execute()
175
+ except GamsConnectException:
176
+ raise
177
+ except Exception as e:
178
+ e.add_note(self._get_idx_str(agent._agent_name, agent._agent_index) + " failed")
179
+ raise
180
+ agent.restore_log()
181
+
182
+ if (
183
+ type(agent).__name__ == "PythonCode" and execute_return
184
+ ): # PythonCode generated instructions
185
+ self._execute(execute_return, [idx] + idx_list)
186
+
187
+ def execute(self, instructions):
188
+ """
189
+ Instantiates and executes one or multiple Connect agents.
190
+
191
+ Parameters
192
+ ----------
193
+ instructions : list, dict
194
+ The instructions to be used for instantiating and executing the agents. Use list for executnig multiple agents and dict for a single one.
195
+
196
+ Raises
197
+ ----------
198
+ GamsConnectException
199
+ If the instructions are invalid or if the specified agent could not be loaded.
200
+ """
201
+ self._execute(instructions)
202
+
203
+ @property
204
+ def container(self):
205
+ return self._container
206
+
207
+ @property
208
+ def ecdb(self):
209
+ return self._ecdb
210
+
211
+ @property
212
+ def system_directory(self):
213
+ return self._system_directory
214
+
215
+ def _get_available_agents(self):
216
+ try:
217
+ agents = []
218
+ with importlib.resources.as_file(
219
+ importlib.resources.files("gams.connect.agents") / "schema"
220
+ ) as schema_path:
221
+ for f in schema_path.rglob('*.yaml'):
222
+ if f.is_file():
223
+ agents.append(f.name.split(".")[0])
224
+ return agents
225
+ except:
226
+ return []
227
+
228
+ #def _get_close_agent(self, agent_name):
229
+ # import difflib
230
+ # agents = self._get_available_agents()
231
+ # agents = {a.lower(): a for a in agents} # use case-insensitive comparison since otherwise "csvwriter" -> "ExcelWriter" instead of "CSVWriter
232
+ # m = difflib.get_close_matches(agent_name.lower(), agents.keys(), n=1, cutoff=0.7)
233
+ # return agents[m[0]] if m else None
234
+
235
+ def load_schema(self, agent):
236
+ """
237
+ Returns the cerberus schema for a specific agent as Python data structure.
238
+
239
+ Parameters
240
+ ----------
241
+ agent : str, ConnectAgent
242
+ Either the name of the Connect agent or the ConnectAgent instance itself.
243
+
244
+ Returns
245
+ -------
246
+ dict
247
+ Python data structure to be used for validation to ensure correct format of instructions given to a Connect agent.
248
+ """
249
+
250
+ if isinstance(agent, str):
251
+ agent_name = agent
252
+ elif isinstance(agent, ConnectAgent):
253
+ agent_name = agent.__class__.__name__
254
+ else:
255
+ raise GamsConnectException(
256
+ f"Parameter 'agent' needs to be of type str or ConnectAgent, but was {type(agent)}."
257
+ )
258
+ with importlib.resources.as_file(
259
+ importlib.resources.files("gams.connect.agents") / "schema"
260
+ ) as schema_path:
261
+ schema_file = schema_path / (agent_name + ".yaml")
262
+ fstat = schema_file.stat()
263
+ mtime = fstat.st_mtime
264
+ size = fstat.st_size
265
+ if (
266
+ agent_name in self._schema_cache
267
+ and mtime == self._schema_cache[agent_name]["mtime"]
268
+ and size == self._schema_cache[agent_name]["size"]
269
+ ):
270
+ schema = self._schema_cache[agent_name]["schema"]
271
+ else:
272
+ schema = schema_file.read_text()
273
+ schema = yaml.safe_load(schema)
274
+ self._schema_cache[agent_name] = {"schema": schema, "mtime": mtime, "size": size}
275
+ return schema
@@ -0,0 +1,93 @@
1
+ #
2
+ # GAMS - General Algebraic Modeling System Python API
3
+ #
4
+ # Copyright (c) 2017-2026 GAMS Development Corp. <support@gams.com>
5
+ # Copyright (c) 2017-2026 GAMS Software GmbH <support@gams.com>
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+ #
25
+
26
+ from cerberus import Validator
27
+ from cerberus.platform import Hashable
28
+
29
+
30
+ class ConnectValidator(Validator):
31
+
32
+ def __init__(self, *args, **kwargs):
33
+ super(ConnectValidator, self).__init__(*args, **kwargs)
34
+
35
+ # Custom validator that does not consider null values when validating excludes rule
36
+ def _validate_excludes(self, excluded_fields, field, value):
37
+ """{'type': ('hashable', 'list'), 'schema': {'type': 'hashable'}}"""
38
+ if isinstance(excluded_fields, Hashable):
39
+ excluded_fields = [excluded_fields]
40
+
41
+ # get all fields from excluded_fields which values are not None
42
+ not_none_fields = [
43
+ ef
44
+ for ef in excluded_fields
45
+ # if self.document.get(ef) != self.schema[ef]["default"]
46
+ if self.document.get(ef) is not None
47
+ ]
48
+ # if value == self.schema[field]["default"]:
49
+ if value is None: # None value is considered to be the same as missing
50
+ if (
51
+ not_none_fields
52
+ ): # if all other fields are None or missing as well, we can skip validation
53
+ return
54
+
55
+ # if not_none_fields has values, then we want to get the default behavior for those fields only
56
+ super()._validate_excludes(not_none_fields, field, value)
57
+
58
+ def normalize_of_rules(self, document):
59
+ """
60
+ Perform normalization of 'anyof' and 'oneof' on the first
61
+ level of the document only. The implementation looks for 'anyof' and 'oneof'
62
+ in the schema and finds the first sub schema that matches. This needs to be
63
+ considered especially for 'anyof' since multiple sub schemas are allowed to
64
+ match. The determined sub schema will be used to perform the normalization.
65
+ :param document: the document to be normalized
66
+ :return: the normalized document
67
+ """
68
+
69
+ document_copy = dict(document)
70
+ for k, v in self.schema.items():
71
+ if document[k] is not None:
72
+ is_list = v.get("type") == "list"
73
+ of_rule = v
74
+ if is_list:
75
+ of_rule = of_rule.get("schema", {})
76
+ of_rule = of_rule.get("anyof", of_rule.get("oneof"))
77
+ if of_rule is not None:
78
+ document_copy[k] = []
79
+ if is_list:
80
+ doc_list = [{k: x} for x in document[k]]
81
+ else:
82
+ doc_list = [{k: document[k]}]
83
+ for sub_doc in doc_list:
84
+ for sub_schema in of_rule:
85
+ new_schema = {k: sub_schema}
86
+ v = ConnectValidator(new_schema)
87
+ sub_doc_normalized = v.validated(sub_doc)
88
+ if sub_doc_normalized is not None:
89
+ if is_list:
90
+ document_copy[k].append(sub_doc_normalized[k])
91
+ else:
92
+ document_copy[k] = sub_doc_normalized[k]
93
+ return document_copy
gams/connect/errors.py ADDED
@@ -0,0 +1,34 @@
1
+ #
2
+ # GAMS - General Algebraic Modeling System Python API
3
+ #
4
+ # Copyright (c) 2017-2026 GAMS Development Corp. <support@gams.com>
5
+ # Copyright (c) 2017-2026 GAMS Software GmbH <support@gams.com>
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+ #
25
+
26
+ import sys
27
+
28
+
29
+ class GamsConnectException(Exception):
30
+ def __init__(self, message, traceback=False):
31
+ super().__init__(message)
32
+ self.traceback = traceback
33
+ if not self.traceback:
34
+ sys.tracebacklimit = 0