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,248 @@
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 math
27
+ import os
28
+ from gams.connect.agents.connectagent import ConnectAgent
29
+ from gams.core.gmd import *
30
+ import gams.transfer as gt
31
+ import pandas as pd
32
+
33
+
34
+ class RawCSVReader(ConnectAgent):
35
+
36
+ def __init__(self, cdb, inst, agent_index):
37
+ super().__init__(cdb, inst, agent_index)
38
+ self._parse_options(self._inst)
39
+
40
+ def _parse_options(self, inst):
41
+ inst["file"] = os.path.abspath(inst["file"])
42
+ self._csv_file = inst["file"]
43
+ self._trace = inst["trace"]
44
+ self._rowLabel = inst["rowLabel"]
45
+ self._colLabel = inst["columnLabel"]
46
+ self._read_as_string = inst["readAsString"]
47
+ self._r_name = inst["rName"]
48
+ self._c_name = inst["cName"]
49
+ self._vs_name = inst["vsName"]
50
+ self._vu_name = inst["vuName"]
51
+ self._vf_name = inst["vfName"]
52
+ self._read_csv_arguments = self._dict_get(inst, "readCSVArguments", {})
53
+
54
+ def _utf8_lead_byte(self, b):
55
+ # A UTF-8 intermediate byte starts with the bits 10xxxxxx.
56
+ return (b & 0xC0) != 0x80
57
+
58
+ def _utf8_byte_truncate(self, text, max_bytes):
59
+ # If text[max_bytes] is not a lead byte, back up until a lead byte is
60
+ # found and truncate before that character.
61
+ utf8 = text.encode("utf8")
62
+ if len(utf8) <= max_bytes:
63
+ return text
64
+ i = max_bytes
65
+ while i > 0 and not self._utf8_lead_byte(utf8[i]):
66
+ i -= 1
67
+ return utf8[:i].decode("utf8")
68
+
69
+ def _addUel(self, uel, keys):
70
+ nuel = self._trunc_org.get(uel, -1)
71
+ if nuel != -1:
72
+ keys[2] = self._uel_list[nuel - 1]
73
+ self._vu.append((*keys, uel))
74
+ return True
75
+ nuel = self._trunc_issued.get(uel, -1)
76
+ if nuel == -1 and len(uel.encode("utf8")) < GLOBAL_UEL_IDENT_SIZE:
77
+ if uel.find("'") != -1 and uel.find('"') != -1:
78
+ return False
79
+ if len(uel) and min(uel) < " ":
80
+ return False
81
+ self._uel_list.append(uel)
82
+ nuel = len(self._uel_list)
83
+ keys[2] = uel
84
+ self._vu.append((*keys, uel))
85
+ self._trunc_org[uel] = nuel
86
+ self._trunc_issued[uel] = nuel
87
+ return True
88
+ # We need a new name for uel because it is too long or has already been issued as a trunc name
89
+ next_trunc_nr = 0
90
+ while True:
91
+ next_trunc_nr += 1
92
+ xs = "~" + str(next_trunc_nr)
93
+ newname = (
94
+ self._utf8_byte_truncate(uel, GLOBAL_UEL_IDENT_SIZE - len(xs) - 1) + xs
95
+ )
96
+ if (self._trunc_issued.get(newname, -1) == -1) and (
97
+ self._trunc_org.get(newname, -1) == -1
98
+ ):
99
+ break
100
+
101
+ if newname.find("'") != -1 and newname.find('"') != -1:
102
+ return False
103
+ if len(newname) and min(newname) < " ":
104
+ return False
105
+ self._uel_list.append(newname)
106
+ nuel = len(self._uel_list)
107
+ keys[2] = newname
108
+ self._vu.append((*keys, self._utf8_byte_truncate(uel, GMS_SSSIZE - 2)))
109
+ self._trunc_org[uel] = nuel
110
+ self._trunc_issued[newname] = nuel
111
+ return True
112
+
113
+ def execute(self):
114
+ if self._trace > 0:
115
+ self._log_instructions(self._inst, self._inst_raw)
116
+ self._describe_container(self._cdb.container, "Connect Container (before):")
117
+
118
+ self._symbols_exist_cdb(self._r_name)
119
+ self._R = self._cdb._container.addSet(self._r_name, "*", description="Rows")
120
+
121
+ self._symbols_exist_cdb(self._c_name)
122
+ self._C = self._cdb._container.addSet(self._c_name, "*", description="Columns")
123
+
124
+ self._symbols_exist_cdb(self._vs_name)
125
+ self._VS = self._cdb._container.addSet(
126
+ self._vs_name,
127
+ [self._R, self._C],
128
+ description="Cells with explanatory text",
129
+ )
130
+
131
+ self._symbols_exist_cdb(self._vu_name)
132
+ self._VU = self._cdb._container.addSet(
133
+ self._vu_name,
134
+ [self._R, self._C, "*"],
135
+ description="Cells with potential GAMS label",
136
+ )
137
+
138
+ self._symbols_exist_cdb(self._vf_name)
139
+ self._VF = self._cdb._container.addParameter(
140
+ self._vf_name,
141
+ [self._R, self._C],
142
+ description="Cells with numerical value",
143
+ )
144
+ self._read_csv_args = {
145
+ "skipinitialspace": True,
146
+ "keep_default_na": False,
147
+ "na_values": "",
148
+ }
149
+ self._read_csv_args.update(self._read_csv_arguments)
150
+ self._read_csv_args.update({"header": None})
151
+ if self._read_as_string:
152
+ self._read_csv_args.update({"dtype": str})
153
+
154
+ self._trunc_org = {}
155
+ self._trunc_issued = {}
156
+ self._uel_list = []
157
+ self._vu = []
158
+ if self._trace > 1:
159
+ self._cdb.print_log(
160
+ f"Arguments for reading the CSV file:\n{self._read_csv_args}"
161
+ )
162
+ df = pd.read_csv(self._csv_file, **self._read_csv_args)
163
+ if self._trace > 2:
164
+ self._cdb.print_log(
165
+ f"Raw DataFrame directly after reading the CSV file:\n{df}"
166
+ )
167
+
168
+ if self._trace > 1:
169
+ self._cdb.print_log(f"DataFrame.shape: {df.shape}")
170
+ self._R.setRecords([f"{self._rowLabel}{r+1}" for r in range(df.shape[0])])
171
+ self._C.setRecords([f"{self._colLabel}{c+1}" for c in range(df.shape[1])])
172
+
173
+ # Now go over cells
174
+ vf = []
175
+ vs = []
176
+ skeys = [None] * 3
177
+ for c in enumerate(df):
178
+ skeys[1] = f"{self._colLabel}{c[0]+1}"
179
+ for r in df[c[1]].items():
180
+ cell_value = r[1]
181
+ if isinstance(cell_value, float) and pd.isna(cell_value):
182
+ continue
183
+ if isinstance(cell_value, str) and len(cell_value) == 0:
184
+ continue
185
+ skeys[0] = f"{self._rowLabel}{r[0]+1}"
186
+ if self._trace > 2:
187
+ self._cdb.print_log(
188
+ f"Cell {skeys[0]} {skeys[1]} {cell_value} type={type(cell_value)}"
189
+ )
190
+
191
+ try:
192
+ dval = float(cell_value)
193
+ float_converted = not math.isfinite(dval)
194
+ if self._trace > 2:
195
+ self._cdb.print_log(f" Float cell: {dval}")
196
+ except:
197
+ dval = None
198
+ if isinstance(cell_value, str):
199
+ CELL_VALUE = cell_value.upper()
200
+ if CELL_VALUE == "EPS":
201
+ dval = gt.SpecialValues.EPS
202
+ elif CELL_VALUE == "NA":
203
+ dval = gt.SpecialValues.NA
204
+ elif CELL_VALUE == "UNDEF":
205
+ dval = gt.SpecialValues.UNDEF
206
+ elif CELL_VALUE == "TRUE":
207
+ dval = 1.0
208
+ elif CELL_VALUE == "FALSE":
209
+ dval = 0.0
210
+ if self._trace > 2 and isinstance(dval, float):
211
+ self._cdb.print_log(f" Converted float: {dval}")
212
+ float_converted = True
213
+ if isinstance(dval, float):
214
+ vf.append((*skeys[:-1], dval))
215
+ if not isinstance(dval, float) or float_converted:
216
+ try:
217
+ sval = str(cell_value)
218
+ if self._trace > 2:
219
+ self._cdb.print_log(f" Str cell: >{sval}<")
220
+ vs.append(
221
+ (
222
+ *skeys[:-1],
223
+ self._utf8_byte_truncate(sval, GMS_SSSIZE - 2),
224
+ )
225
+ )
226
+ except:
227
+ pass
228
+
229
+ try:
230
+ sval = str(cell_value)
231
+ self._addUel(sval, skeys)
232
+ except:
233
+ pass
234
+
235
+ if len(vf):
236
+ self._VF.setRecords(vf)
237
+ if len(vs):
238
+ self._VS.setRecords(vs)
239
+ if len(self._vu):
240
+ self._VU.setRecords(self._vu)
241
+
242
+ # For symbols with None records, empty df is assigned
243
+ self._transform_sym_none_to_empty(self._VF)
244
+ self._transform_sym_none_to_empty(self._VS)
245
+ self._transform_sym_none_to_empty(self._VU)
246
+
247
+ if self._trace > 0:
248
+ self._describe_container(self._cdb.container, "Connect Container (after):")
@@ -0,0 +1,312 @@
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 datetime
27
+ import os
28
+ from gams.connect.agents.connectagent import ConnectAgent
29
+ from gams.core.gmd import *
30
+ import gams.transfer as gt
31
+ import openpyxl
32
+ import openpyxl.cell.cell
33
+ import openpyxl.cell.read_only
34
+ import openpyxl.utils.datetime
35
+ import pandas as pd
36
+
37
+
38
+ class RawExcelReader(ConnectAgent):
39
+
40
+ def __init__(self, cdb, inst, agent_index):
41
+ super().__init__(cdb, inst, agent_index)
42
+ self._parse_options(self._inst)
43
+
44
+ def _parse_options(self, inst):
45
+ inst["file"] = os.path.abspath(inst["file"])
46
+ self._xlsx_file = inst["file"]
47
+ self._read_only = not inst["mergedCells"]
48
+ self._s_name = inst["sName"]
49
+ self._w_name = inst["wName"]
50
+ self._ws_name = inst["wsName"]
51
+ self._r_name = inst["rName"]
52
+ self._c_name = inst["cName"]
53
+ self._vs_name = inst["vsName"]
54
+ self._vu_name = inst["vuName"]
55
+ self._vf_name = inst["vfName"]
56
+ self._rowLabel = inst["rowLabel"]
57
+ self._colLabel = inst["columnLabel"]
58
+ self._trace = inst["trace"]
59
+ self._sheetLabel = inst["sheetLabel"]
60
+
61
+ def _utf8_lead_byte(self, b):
62
+ # A UTF-8 intermediate byte starts with the bits 10xxxxxx.
63
+ return (b & 0xC0) != 0x80
64
+
65
+ def _utf8_byte_truncate(self, text, max_bytes):
66
+ # If text[max_bytes] is not a lead byte, back up until a lead byte is
67
+ # found and truncate before that character.
68
+ utf8 = text.encode("utf8")
69
+ if len(utf8) <= max_bytes:
70
+ return text
71
+ i = max_bytes
72
+ while i > 0 and not self._utf8_lead_byte(utf8[i]):
73
+ i -= 1
74
+ return utf8[:i].decode("utf8")
75
+
76
+ def _get_cell_value(self, sheet, cell):
77
+ if not isinstance(cell, openpyxl.cell.cell.MergedCell):
78
+ return cell.value
79
+
80
+ # "Oh no, the cell is merged!"
81
+ for rng in sheet.merged_cells.ranges:
82
+ if cell.coordinate in rng:
83
+ return rng.start_cell.value
84
+
85
+ self._connect_error("Merged cell is not in any merge range!")
86
+
87
+ def _addUel(self, uel, keys):
88
+ nuel = self._trunc_org.get(uel, -1)
89
+ if nuel != -1:
90
+ keys[3] = self._uel_list[nuel - 1]
91
+ self._vu.append((*keys, uel))
92
+ return True
93
+ nuel = self._trunc_issued.get(uel, -1)
94
+ if nuel == -1 and len(uel.encode("utf8")) < GLOBAL_UEL_IDENT_SIZE:
95
+ if uel.find("'") != -1 and uel.find('"') != -1:
96
+ return False
97
+ if len(uel) and min(uel) < " ":
98
+ return False
99
+ self._uel_list.append(uel)
100
+ nuel = len(self._uel_list)
101
+ keys[3] = uel
102
+ self._vu.append((*keys, uel))
103
+ self._trunc_org[uel] = nuel
104
+ self._trunc_issued[uel] = nuel
105
+ return True
106
+ # We need a new name for uel because it is too long or has already been issued as a trunc name
107
+ next_trunc_nr = 0
108
+ while True:
109
+ next_trunc_nr += 1
110
+ xs = "~" + str(next_trunc_nr)
111
+ newname = (
112
+ self._utf8_byte_truncate(uel, GLOBAL_UEL_IDENT_SIZE - len(xs) - 1) + xs
113
+ )
114
+ if (self._trunc_issued.get(newname, -1) == -1) and (
115
+ self._trunc_org.get(newname, -1) == -1
116
+ ):
117
+ break
118
+
119
+ if newname.find("'") != -1 and newname.find('"') != -1:
120
+ return False
121
+ if len(newname) and min(newname) < " ":
122
+ return False
123
+ self._uel_list.append(newname)
124
+ nuel = len(self._uel_list)
125
+ keys[3] = newname
126
+ self._vu.append((*keys, self._utf8_byte_truncate(uel, GMS_SSSIZE - 2)))
127
+ self._trunc_org[uel] = nuel
128
+ self._trunc_issued[newname] = nuel
129
+ return True
130
+
131
+ def execute(self):
132
+ if self._trace > 0:
133
+ self._log_instructions(self._inst, self._inst_raw)
134
+ self._describe_container(self._cdb.container, "Connect Container (before):")
135
+
136
+ self._symbols_exist_cdb(self._s_name)
137
+ self._S = self._cdb._container.addSet(
138
+ self._s_name, "*", description="Workbook sheets"
139
+ )
140
+
141
+ self._symbols_exist_cdb(self._w_name)
142
+ self._W = self._cdb._container.addSet(
143
+ self._w_name, "*", description="Workbook sheets by name"
144
+ )
145
+
146
+ self._symbols_exist_cdb(self._ws_name)
147
+ self._WS = self._cdb._container.addSet(
148
+ self._ws_name,
149
+ [self._S, self._W],
150
+ description="Workbook map",
151
+ )
152
+
153
+ self._symbols_exist_cdb(self._r_name)
154
+ self._R = self._cdb._container.addSet(self._r_name, "*", description="Rows")
155
+
156
+ self._symbols_exist_cdb(self._c_name)
157
+ self._C = self._cdb._container.addSet(self._c_name, "*", description="Columns")
158
+
159
+ self._symbols_exist_cdb(self._vs_name)
160
+ self._VS = self._cdb._container.addSet(
161
+ self._vs_name,
162
+ [self._S, self._R, self._C],
163
+ description="Cells with explanatory text",
164
+ )
165
+
166
+ self._symbols_exist_cdb(self._vu_name)
167
+ self._VU = self._cdb._container.addSet(
168
+ self._vu_name,
169
+ [self._S, self._R, self._C, "*"],
170
+ description="Cells with potential GAMS label",
171
+ )
172
+
173
+ self._symbols_exist_cdb(self._vf_name)
174
+ self._VF = self._cdb._container.addParameter(
175
+ self._vf_name,
176
+ [self._S, self._R, self._C],
177
+ description="Cells with numerical value",
178
+ )
179
+
180
+ self._trunc_org = {}
181
+ self._trunc_issued = {}
182
+ self._uel_list = []
183
+ self._next_trunc_nr = 1
184
+ self._vu = []
185
+
186
+ wb = openpyxl.load_workbook(
187
+ filename=self._xlsx_file, read_only=self._read_only, data_only=True
188
+ )
189
+
190
+ if self._trace > 1:
191
+ self._cdb.print_log(f"Number of sheets: {len(wb.sheetnames)}")
192
+
193
+ self._S.setRecords(
194
+ [(f"{self._sheetLabel}{s[0]+1}", s[1]) for s in enumerate(wb.sheetnames)]
195
+ )
196
+ try:
197
+ max_row = max([s.max_row for s in wb.worksheets])
198
+ except:
199
+ for s in wb.worksheets:
200
+ s.calculate_dimension(force=True)
201
+ max_row = max([s.max_row for s in wb.worksheets])
202
+
203
+ if self._trace > 1:
204
+ self._cdb.print_log(f"Max row: {max_row}")
205
+ self._R.setRecords([f"{self._rowLabel}{r+1}" for r in range(max_row)])
206
+ max_col = max([s.max_column for s in wb.worksheets])
207
+ if self._trace > 1:
208
+ self._cdb.print_log(f"Max column: {max_col}")
209
+ self._C.setRecords([f"{self._colLabel}{c+1}" for c in range(max_col)])
210
+ self._W.setRecords(wb.sheetnames)
211
+ self._WS.setRecords(
212
+ [(f"{self._sheetLabel}{s[0]+1}", s[1]) for s in enumerate(wb.sheetnames)]
213
+ )
214
+
215
+ # Now go over the cells
216
+ skeys = [None] * 4
217
+ vf = []
218
+ vs = []
219
+
220
+ for s in enumerate(wb.worksheets):
221
+ skeys[0] = f"{self._sheetLabel}{s[0]+1}"
222
+ for r in s[1].iter_rows():
223
+ if not len(r):
224
+ continue
225
+ skeys[1] = None
226
+ for c in r:
227
+ if isinstance(c, openpyxl.cell.read_only.EmptyCell):
228
+ continue
229
+ if not self._read_only: # potentially merged cells
230
+ cell_value = self._get_cell_value(s[1], c)
231
+ else:
232
+ cell_value = c.value
233
+ if cell_value == None:
234
+ continue
235
+
236
+ if skeys[1] == None:
237
+ skeys[1] = f"{self._rowLabel}{c.row}"
238
+ skeys[2] = f"{self._colLabel}{c.column}"
239
+
240
+ if self._trace > 2:
241
+ self._cdb.print_log(
242
+ f"Cell {self._sheetLabel}{s[0]+1}!{self._rowLabel}={c.row} {self._colLabel}={c.column}"
243
+ )
244
+ if isinstance(cell_value, datetime.datetime):
245
+ if self._trace > 2:
246
+ self._cdb.print_log(
247
+ f" Date cell: {openpyxl.utils.datetime.to_excel(cell_value)}"
248
+ )
249
+ vf.append(
250
+ (
251
+ *skeys[:-1],
252
+ openpyxl.utils.datetime.to_excel(cell_value),
253
+ )
254
+ )
255
+ self._addUel(str(cell_value), skeys)
256
+ continue
257
+
258
+ if isinstance(cell_value, str):
259
+ if self._trace > 2:
260
+ self._cdb.print_log(f" Str cell: >{cell_value}<")
261
+ vs.append(
262
+ (
263
+ *skeys[:-1],
264
+ self._utf8_byte_truncate(cell_value, GMS_SSSIZE - 2),
265
+ )
266
+ )
267
+
268
+ try:
269
+ dval = float(cell_value)
270
+ if pd.isna(dval):
271
+ dval = gt.SpecialValues.NA
272
+ if self._trace > 2:
273
+ self._cdb.print_log(f" Float cell: {dval}")
274
+ except:
275
+ dval = None
276
+ if isinstance(cell_value, str):
277
+ CELL_VALUE = cell_value.upper()
278
+ if CELL_VALUE == "EPS":
279
+ dval = gt.SpecialValues.EPS
280
+ elif CELL_VALUE == "NA":
281
+ dval = gt.SpecialValues.NA
282
+ elif CELL_VALUE == "UNDEF":
283
+ dval = gt.SpecialValues.UNDEF
284
+ if self._trace > 2 and isinstance(dval, float):
285
+ self._cdb.print_log(f" Converted float: {dval}")
286
+ if isinstance(dval, float):
287
+ vf.append((*skeys[:-1], dval))
288
+
289
+ try:
290
+ sval = str(cell_value)
291
+ if self._trace > 2 and isinstance(dval, float):
292
+ self._cdb.print_log(f" Converted string: >{sval}<")
293
+ except:
294
+ continue
295
+ self._addUel(sval, skeys)
296
+
297
+ if len(vf):
298
+ self._VF.setRecords(vf)
299
+ if len(vs):
300
+ self._VS.setRecords(vs)
301
+ if len(self._vu):
302
+ self._VU.setRecords(self._vu)
303
+
304
+ # For symbols with None records, empty df is assigned
305
+ self._transform_sym_none_to_empty(self._VF)
306
+ self._transform_sym_none_to_empty(self._VS)
307
+ self._transform_sym_none_to_empty(self._VU)
308
+
309
+ wb.close()
310
+
311
+ if self._trace > 0:
312
+ self._describe_container(self._cdb.container, "Connect Container (after):")
@@ -0,0 +1,92 @@
1
+ file:
2
+ type: string
3
+ required: true
4
+ name:
5
+ type: string
6
+ required: true
7
+ type:
8
+ type: string
9
+ default: par
10
+ allowed: [par, set]
11
+ indexColumns:
12
+ default: null
13
+ nullable: true
14
+ oneof:
15
+ - type: [integer, string]
16
+ - type: list
17
+ schema:
18
+ type: [integer, string]
19
+ indexSubstitutions:
20
+ type: dict
21
+ default: null
22
+ nullable: true
23
+ valueColumns:
24
+ default: null
25
+ nullable: true
26
+ oneof:
27
+ - type: [integer, string]
28
+ - type: list
29
+ schema:
30
+ type: [integer, string]
31
+ valueSubstitutions:
32
+ type: dict
33
+ default: null
34
+ nullable: true
35
+ autoColumn:
36
+ type: string
37
+ default: null
38
+ nullable: true
39
+ autoRow:
40
+ type: string
41
+ default: null
42
+ nullable: true
43
+ decimalSeparator:
44
+ type: string
45
+ default: "."
46
+ fieldSeparator:
47
+ type: string
48
+ default: ","
49
+ thousandsSeparator:
50
+ type: string
51
+ default: null
52
+ nullable: true
53
+ header:
54
+ default: infer
55
+ oneof:
56
+ - type: string
57
+ allowed: [infer]
58
+ - type: boolean
59
+ - type: list
60
+ schema:
61
+ type: integer
62
+ names:
63
+ type: list
64
+ default: null
65
+ nullable: true
66
+ schema:
67
+ type: string
68
+ quoting:
69
+ type: integer
70
+ default: 0
71
+ allowed: [0, 1, 2, 3]
72
+ readCSVArguments:
73
+ type: dict
74
+ default: null
75
+ nullable: true
76
+ skipRows:
77
+ default: null
78
+ nullable: true
79
+ oneof:
80
+ - type: integer
81
+ - type: list
82
+ schema:
83
+ type: integer
84
+ stack:
85
+ default: infer
86
+ oneof:
87
+ - type: string
88
+ allowed: [infer]
89
+ - type: boolean
90
+ trace:
91
+ type: integer
92
+ default: 0
@@ -0,0 +1,44 @@
1
+ file:
2
+ type: string
3
+ required: true
4
+ name:
5
+ type: string
6
+ required: true
7
+ decimalSeparator:
8
+ type: string
9
+ default: "."
10
+ fieldSeparator:
11
+ type: string
12
+ default: ","
13
+ header:
14
+ type: boolean
15
+ default: true
16
+ quoting:
17
+ type: integer
18
+ default: 0
19
+ allowed: [0, 1, 2, 3] # QUOTE_MINIMAL (0), QUOTE_ALL (1), QUOTE_NONNUMERIC (2) or QUOTE_NONE (3)
20
+ setHeader:
21
+ type: string
22
+ default: null
23
+ nullable: true
24
+ skipText:
25
+ type: boolean
26
+ default: false
27
+ toCSVArguments:
28
+ type: dict
29
+ default: null
30
+ nullable: true
31
+ unstack:
32
+ default: false
33
+ oneof:
34
+ - type: boolean
35
+ - type: list
36
+ schema:
37
+ type: integer
38
+ valueSubstitutions:
39
+ type: dict
40
+ default: null
41
+ nullable: true
42
+ trace:
43
+ type: integer
44
+ default: 0