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.
- gams/__init__.py +27 -0
- gams/_version.py +1 -0
- gams/connect/__init__.py +28 -0
- gams/connect/agents/__init__.py +24 -0
- gams/connect/agents/_excel/__init__.py +32 -0
- gams/connect/agents/_excel/excelagent.py +312 -0
- gams/connect/agents/_excel/workbook.py +155 -0
- gams/connect/agents/_sqlconnectors/__init__.py +42 -0
- gams/connect/agents/_sqlconnectors/_accesshandler.py +211 -0
- gams/connect/agents/_sqlconnectors/_databasehandler.py +250 -0
- gams/connect/agents/_sqlconnectors/_mysqlhandler.py +168 -0
- gams/connect/agents/_sqlconnectors/_postgreshandler.py +131 -0
- gams/connect/agents/_sqlconnectors/_pyodbchandler.py +112 -0
- gams/connect/agents/_sqlconnectors/_sqlalchemyhandler.py +74 -0
- gams/connect/agents/_sqlconnectors/_sqlitehandler.py +262 -0
- gams/connect/agents/_sqlconnectors/_sqlserverhandler.py +179 -0
- gams/connect/agents/concatenate.py +440 -0
- gams/connect/agents/connectagent.py +743 -0
- gams/connect/agents/csvreader.py +675 -0
- gams/connect/agents/csvwriter.py +151 -0
- gams/connect/agents/domainwriter.py +143 -0
- gams/connect/agents/excelreader.py +756 -0
- gams/connect/agents/excelwriter.py +467 -0
- gams/connect/agents/filter.py +223 -0
- gams/connect/agents/gamsreader.py +112 -0
- gams/connect/agents/gamswriter.py +239 -0
- gams/connect/agents/gdxreader.py +109 -0
- gams/connect/agents/gdxwriter.py +146 -0
- gams/connect/agents/labelmanipulator.py +303 -0
- gams/connect/agents/projection.py +539 -0
- gams/connect/agents/pythoncode.py +71 -0
- gams/connect/agents/rawcsvreader.py +248 -0
- gams/connect/agents/rawexcelreader.py +312 -0
- gams/connect/agents/schema/CSVReader.yaml +92 -0
- gams/connect/agents/schema/CSVWriter.yaml +44 -0
- gams/connect/agents/schema/Concatenate.yaml +52 -0
- gams/connect/agents/schema/DomainWriter.yaml +25 -0
- gams/connect/agents/schema/ExcelReader.yaml +121 -0
- gams/connect/agents/schema/ExcelWriter.yaml +78 -0
- gams/connect/agents/schema/Filter.yaml +74 -0
- gams/connect/agents/schema/GAMSReader.yaml +20 -0
- gams/connect/agents/schema/GAMSWriter.yaml +47 -0
- gams/connect/agents/schema/GDXReader.yaml +23 -0
- gams/connect/agents/schema/GDXWriter.yaml +32 -0
- gams/connect/agents/schema/LabelManipulator.yaml +99 -0
- gams/connect/agents/schema/Projection.yaml +24 -0
- gams/connect/agents/schema/PythonCode.yaml +6 -0
- gams/connect/agents/schema/RawCSVReader.yaml +34 -0
- gams/connect/agents/schema/RawExcelReader.yaml +42 -0
- gams/connect/agents/schema/SQLReader.yaml +75 -0
- gams/connect/agents/schema/SQLWriter.yaml +103 -0
- gams/connect/agents/sqlreader.py +301 -0
- gams/connect/agents/sqlwriter.py +276 -0
- gams/connect/connectdatabase.py +275 -0
- gams/connect/connectvalidator.py +93 -0
- gams/connect/errors.py +34 -0
- gams/control/__init__.py +136 -0
- gams/control/database.py +2231 -0
- gams/control/execution.py +1900 -0
- gams/control/options.py +2792 -0
- gams/control/workspace.py +1198 -0
- gams/core/__init__.py +24 -0
- gams/core/cfg/__init__.py +26 -0
- gams/core/cfg/_cfgmcc.cp312-win_amd64.pyd +0 -0
- gams/core/cfg/cfgmcc.py +519 -0
- gams/core/dct/__init__.py +26 -0
- gams/core/dct/_dctmcc.cp312-win_amd64.pyd +0 -0
- gams/core/dct/dctmcc.py +574 -0
- gams/core/embedded/__init__.py +26 -0
- gams/core/embedded/gamsemb.py +1024 -0
- gams/core/emp/__init__.py +24 -0
- gams/core/emp/emplexer.py +89 -0
- gams/core/emp/empyacc.py +281 -0
- gams/core/gdx/__init__.py +26 -0
- gams/core/gdx/_gdxcc.cp312-win_amd64.pyd +0 -0
- gams/core/gdx/gdxcc.py +866 -0
- gams/core/gev/__init__.py +26 -0
- gams/core/gev/_gevmcc.cp312-win_amd64.pyd +0 -0
- gams/core/gev/gevmcc.py +855 -0
- gams/core/gmd/__init__.py +26 -0
- gams/core/gmd/_gmdcc.cp312-win_amd64.pyd +0 -0
- gams/core/gmd/gmdcc.py +917 -0
- gams/core/gmo/__init__.py +26 -0
- gams/core/gmo/_gmomcc.cp312-win_amd64.pyd +0 -0
- gams/core/gmo/gmomcc.py +2046 -0
- gams/core/idx/__init__.py +26 -0
- gams/core/idx/_idxcc.cp312-win_amd64.pyd +0 -0
- gams/core/idx/idxcc.py +510 -0
- gams/core/numpy/__init__.py +29 -0
- gams/core/numpy/_gams2numpy.cp312-win_amd64.pyd +0 -0
- gams/core/numpy/gams2numpy.py +1048 -0
- gams/core/opt/__init__.py +26 -0
- gams/core/opt/_optcc.cp312-win_amd64.pyd +0 -0
- gams/core/opt/optcc.py +840 -0
- gams/engine/__init__.py +204 -0
- gams/engine/api/__init__.py +13 -0
- gams/engine/api/auth_api.py +7653 -0
- gams/engine/api/cleanup_api.py +751 -0
- gams/engine/api/default_api.py +887 -0
- gams/engine/api/hypercube_api.py +2629 -0
- gams/engine/api/jobs_api.py +5229 -0
- gams/engine/api/licenses_api.py +2220 -0
- gams/engine/api/namespaces_api.py +7783 -0
- gams/engine/api/usage_api.py +5627 -0
- gams/engine/api/users_api.py +5931 -0
- gams/engine/api_client.py +804 -0
- gams/engine/api_response.py +21 -0
- gams/engine/configuration.py +601 -0
- gams/engine/exceptions.py +216 -0
- gams/engine/models/__init__.py +86 -0
- gams/engine/models/bad_input.py +89 -0
- gams/engine/models/cleanable_job_result.py +104 -0
- gams/engine/models/cleanable_job_result_page.py +113 -0
- gams/engine/models/engine_license.py +107 -0
- gams/engine/models/files_not_found.py +93 -0
- gams/engine/models/forwarded_token_response.py +112 -0
- gams/engine/models/generic_key_value_pair.py +89 -0
- gams/engine/models/hypercube.py +160 -0
- gams/engine/models/hypercube_page.py +111 -0
- gams/engine/models/hypercube_summary.py +91 -0
- gams/engine/models/hypercube_token.py +97 -0
- gams/engine/models/identity_provider.py +107 -0
- gams/engine/models/identity_provider_ldap.py +121 -0
- gams/engine/models/identity_provider_oauth2.py +146 -0
- gams/engine/models/identity_provider_oauth2_scope.py +89 -0
- gams/engine/models/identity_provider_oauth2_with_secret.py +152 -0
- gams/engine/models/identity_provider_oidc.py +133 -0
- gams/engine/models/identity_provider_oidc_with_secret.py +143 -0
- gams/engine/models/inex.py +91 -0
- gams/engine/models/invitation.py +136 -0
- gams/engine/models/invitation_quota.py +106 -0
- gams/engine/models/invitation_token.py +87 -0
- gams/engine/models/job.py +165 -0
- gams/engine/models/job_no_text_entry.py +138 -0
- gams/engine/models/job_no_text_entry_page.py +111 -0
- gams/engine/models/license.py +91 -0
- gams/engine/models/log_piece.py +96 -0
- gams/engine/models/message.py +87 -0
- gams/engine/models/message_and_token.py +99 -0
- gams/engine/models/message_with_webhook_id.py +89 -0
- gams/engine/models/model_auth_token.py +87 -0
- gams/engine/models/model_configuration.py +125 -0
- gams/engine/models/model_default_instance.py +99 -0
- gams/engine/models/model_default_user_instance.py +98 -0
- gams/engine/models/model_hypercube_job.py +106 -0
- gams/engine/models/model_hypercube_usage.py +130 -0
- gams/engine/models/model_instance_info.py +116 -0
- gams/engine/models/model_instance_info_full.py +123 -0
- gams/engine/models/model_instance_pool_info.py +112 -0
- gams/engine/models/model_job_labels.py +179 -0
- gams/engine/models/model_job_usage.py +133 -0
- gams/engine/models/model_pool_usage.py +124 -0
- gams/engine/models/model_usage.py +115 -0
- gams/engine/models/model_user.py +96 -0
- gams/engine/models/model_userinstance_info.py +119 -0
- gams/engine/models/model_userinstancepool_info.py +95 -0
- gams/engine/models/model_version.py +91 -0
- gams/engine/models/models.py +120 -0
- gams/engine/models/namespace.py +104 -0
- gams/engine/models/namespace_quota.py +96 -0
- gams/engine/models/namespace_with_permission.py +96 -0
- gams/engine/models/not_found.py +91 -0
- gams/engine/models/password_policy.py +97 -0
- gams/engine/models/perm_and_username.py +89 -0
- gams/engine/models/quota.py +117 -0
- gams/engine/models/quota_exceeded.py +97 -0
- gams/engine/models/status_code_meaning.py +89 -0
- gams/engine/models/stream_entry.py +89 -0
- gams/engine/models/system_wide_license.py +92 -0
- gams/engine/models/text_entries.py +87 -0
- gams/engine/models/text_entry.py +101 -0
- gams/engine/models/time_span.py +95 -0
- gams/engine/models/time_span_pool_worker.py +99 -0
- gams/engine/models/token_forward_error.py +87 -0
- gams/engine/models/user.py +127 -0
- gams/engine/models/user_group_member.py +96 -0
- gams/engine/models/user_groups.py +108 -0
- gams/engine/models/vapid_info.py +87 -0
- gams/engine/models/webhook.py +138 -0
- gams/engine/models/webhook_parameterized_event.py +99 -0
- gams/engine/py.typed +0 -0
- gams/engine/rest.py +258 -0
- gams/magic/__init__.py +32 -0
- gams/magic/gams_magic.py +142 -0
- gams/magic/interactive.py +402 -0
- gams/tools/__init__.py +30 -0
- gams/tools/errors.py +34 -0
- gams/tools/toolcollection/__init__.py +24 -0
- gams/tools/toolcollection/alg/__init__.py +24 -0
- gams/tools/toolcollection/alg/rank.py +51 -0
- gams/tools/toolcollection/data/__init__.py +24 -0
- gams/tools/toolcollection/data/csvread.py +444 -0
- gams/tools/toolcollection/data/csvwrite.py +311 -0
- gams/tools/toolcollection/data/exceldump.py +47 -0
- gams/tools/toolcollection/data/sqlitewrite.py +276 -0
- gams/tools/toolcollection/gdxservice/__init__.py +24 -0
- gams/tools/toolcollection/gdxservice/gdxencoding.py +104 -0
- gams/tools/toolcollection/gdxservice/gdxrename.py +94 -0
- gams/tools/toolcollection/linalg/__init__.py +24 -0
- gams/tools/toolcollection/linalg/cholesky.py +57 -0
- gams/tools/toolcollection/linalg/eigenvalue.py +56 -0
- gams/tools/toolcollection/linalg/eigenvector.py +58 -0
- gams/tools/toolcollection/linalg/invert.py +55 -0
- gams/tools/toolcollection/linalg/ols.py +138 -0
- gams/tools/toolcollection/tooltemplate.py +321 -0
- gams/tools/toolcollection/win32/__init__.py +24 -0
- gams/tools/toolcollection/win32/excelmerge.py +93 -0
- gams/tools/toolcollection/win32/exceltalk.py +76 -0
- gams/tools/toolcollection/win32/msappavail.py +49 -0
- gams/tools/toolcollection/win32/shellexecute.py +54 -0
- gams/tools/tools.py +116 -0
- gams/transfer/__init__.py +35 -0
- gams/transfer/_abcs/__init__.py +37 -0
- gams/transfer/_abcs/container_abcs.py +433 -0
- gams/transfer/_internals/__init__.py +63 -0
- gams/transfer/_internals/algorithms.py +436 -0
- gams/transfer/_internals/casepreservingdict.py +124 -0
- gams/transfer/_internals/constants.py +270 -0
- gams/transfer/_internals/domainviolation.py +103 -0
- gams/transfer/_internals/specialvalues.py +172 -0
- gams/transfer/containers/__init__.py +26 -0
- gams/transfer/containers/_container.py +1794 -0
- gams/transfer/containers/_io/__init__.py +28 -0
- gams/transfer/containers/_io/containers.py +164 -0
- gams/transfer/containers/_io/gdx.py +1029 -0
- gams/transfer/containers/_io/gmd.py +872 -0
- gams/transfer/containers/_mixins/__init__.py +26 -0
- gams/transfer/containers/_mixins/ccc.py +1274 -0
- gams/transfer/syms/__init__.py +33 -0
- gams/transfer/syms/_methods/__init__.py +24 -0
- gams/transfer/syms/_methods/tables.py +120 -0
- gams/transfer/syms/_methods/toDict.py +115 -0
- gams/transfer/syms/_methods/toList.py +83 -0
- gams/transfer/syms/_methods/toValue.py +60 -0
- gams/transfer/syms/_mixins/__init__.py +32 -0
- gams/transfer/syms/_mixins/equals.py +626 -0
- gams/transfer/syms/_mixins/generateRecords.py +499 -0
- gams/transfer/syms/_mixins/pivot.py +313 -0
- gams/transfer/syms/_mixins/pve.py +627 -0
- gams/transfer/syms/_mixins/sa.py +27 -0
- gams/transfer/syms/_mixins/sapve.py +27 -0
- gams/transfer/syms/_mixins/saua.py +27 -0
- gams/transfer/syms/_mixins/sauapve.py +199 -0
- gams/transfer/syms/_mixins/spve.py +1528 -0
- gams/transfer/syms/_mixins/ve.py +936 -0
- gams/transfer/syms/container_syms/__init__.py +31 -0
- gams/transfer/syms/container_syms/_alias.py +984 -0
- gams/transfer/syms/container_syms/_equation.py +333 -0
- gams/transfer/syms/container_syms/_parameter.py +973 -0
- gams/transfer/syms/container_syms/_set.py +604 -0
- gams/transfer/syms/container_syms/_universe_alias.py +461 -0
- gams/transfer/syms/container_syms/_variable.py +321 -0
- gamsapi-52.5.0.dist-info/METADATA +150 -0
- gamsapi-52.5.0.dist-info/RECORD +257 -0
- gamsapi-52.5.0.dist-info/WHEEL +5 -0
- gamsapi-52.5.0.dist-info/licenses/LICENSE +22 -0
- gamsapi-52.5.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,467 @@
|
|
|
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 os
|
|
27
|
+
import warnings
|
|
28
|
+
from gams import transfer as gt
|
|
29
|
+
from gams.connect.agents._excel import ExcelAgent
|
|
30
|
+
from gams.connect.agents._excel import Workbook
|
|
31
|
+
from gams.connect.connectvalidator import ConnectValidator
|
|
32
|
+
import openpyxl
|
|
33
|
+
from openpyxl.styles import Font
|
|
34
|
+
from openpyxl.utils.dataframe import dataframe_to_rows
|
|
35
|
+
import pandas as pd
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ExcelWriter(ExcelAgent):
|
|
39
|
+
_index_parameter_map = {
|
|
40
|
+
"cdim": "columnDimension",
|
|
41
|
+
"columndimension": "columnDimension",
|
|
42
|
+
"mergedcells": "mergedCells",
|
|
43
|
+
"clearsheet": "clearSheet",
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
_EXCEL_ROW_LIMIT = 1048576
|
|
47
|
+
_EXCEL_COL_LIMIT = 16384
|
|
48
|
+
|
|
49
|
+
def __init__(self, cdb, inst, agent_index):
|
|
50
|
+
super().__init__(cdb, inst, agent_index)
|
|
51
|
+
self._parse_options(self._inst)
|
|
52
|
+
if os.path.splitext(self._file)[1] != ".xlsx":
|
|
53
|
+
self._connect_error("The ExcelWriter does support .xlsx files only.")
|
|
54
|
+
|
|
55
|
+
def _check_openpyxl(self):
|
|
56
|
+
try:
|
|
57
|
+
opxl_version = list(map(int, openpyxl.__version__.split(".")))
|
|
58
|
+
if opxl_version[0] < 3 or (
|
|
59
|
+
opxl_version[0] == 3 and opxl_version[1] < 1
|
|
60
|
+
): # check openpyxl<3.1.0
|
|
61
|
+
warnings.warn(
|
|
62
|
+
f"The used openpyxl version is {openpyxl.__version__}. openpyxl<3.1.0 can lead to wrong data being exported. Upgrading to version 3.1.0 or later is recommended.",
|
|
63
|
+
category=Warning,
|
|
64
|
+
)
|
|
65
|
+
except ValueError: # silence errors when converting to int
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
def _parse_options(self, inst):
|
|
69
|
+
inst["file"] = os.path.abspath(inst["file"])
|
|
70
|
+
self._file = inst["file"]
|
|
71
|
+
self._merged_cells = inst["mergedCells"]
|
|
72
|
+
self._cdim = inst["columnDimension"]
|
|
73
|
+
self._value_subs = inst["valueSubstitutions"]
|
|
74
|
+
self._clear_sheet = inst["clearSheet"]
|
|
75
|
+
self._trace = inst["trace"]
|
|
76
|
+
self._symbols = inst["symbols"]
|
|
77
|
+
self._index = inst["index"]
|
|
78
|
+
self._engine = "openpyxl"
|
|
79
|
+
|
|
80
|
+
self._toc = inst["tableOfContents"]
|
|
81
|
+
if self._toc and not isinstance(self._toc, dict):
|
|
82
|
+
v = ConnectValidator(
|
|
83
|
+
{"tableOfContents": self._cdb.load_schema(self)["tableOfContents"]}
|
|
84
|
+
)
|
|
85
|
+
self._toc = {"tableOfContents": {}}
|
|
86
|
+
self._toc = v.normalized(self._toc)
|
|
87
|
+
self._toc = v.normalize_of_rules(self._toc)
|
|
88
|
+
self._toc = self._toc["tableOfContents"]
|
|
89
|
+
inst["tableOfContents"] = self._toc
|
|
90
|
+
|
|
91
|
+
def _open(self):
|
|
92
|
+
if os.path.exists(self._file):
|
|
93
|
+
try:
|
|
94
|
+
self._wb = Workbook(self._file, read_only=False, data_only=False)
|
|
95
|
+
except PermissionError as e:
|
|
96
|
+
self._connect_error(
|
|
97
|
+
str(e)
|
|
98
|
+
+ "\nThe file may already be open and might need to be closed first."
|
|
99
|
+
)
|
|
100
|
+
else:
|
|
101
|
+
if self._index:
|
|
102
|
+
self._connect_error(
|
|
103
|
+
f"Workbook >{self._file}< needs to exist if used with index option."
|
|
104
|
+
)
|
|
105
|
+
self._wb = Workbook()
|
|
106
|
+
|
|
107
|
+
def _write(self, df, rdim, cdim, sheet, nw_row, nw_col, merged_cells):
|
|
108
|
+
row = nw_row + 1
|
|
109
|
+
use_index = True if rdim > 0 else False
|
|
110
|
+
use_header = True if cdim > 0 else False
|
|
111
|
+
last_row_labels = [None] * rdim
|
|
112
|
+
|
|
113
|
+
for idx, r in enumerate(
|
|
114
|
+
dataframe_to_rows(df, index=use_index, header=use_header)
|
|
115
|
+
):
|
|
116
|
+
r2 = list(r)
|
|
117
|
+
if idx < cdim:
|
|
118
|
+
if merged_cells:
|
|
119
|
+
for idx2, x in enumerate(r2[rdim:]):
|
|
120
|
+
if idx2 == 0: # first iteration
|
|
121
|
+
merge_start = merge_end = nw_col + rdim + 1
|
|
122
|
+
elif x is None:
|
|
123
|
+
merge_end += 1
|
|
124
|
+
if idx2 == len(r2) - 1 - rdim: # last iteration
|
|
125
|
+
if merge_start != merge_end:
|
|
126
|
+
sheet.merge_cells(
|
|
127
|
+
start_row=row,
|
|
128
|
+
start_column=merge_start,
|
|
129
|
+
end_row=row,
|
|
130
|
+
end_column=merge_end,
|
|
131
|
+
)
|
|
132
|
+
else:
|
|
133
|
+
if merge_start != merge_end:
|
|
134
|
+
sheet.merge_cells(
|
|
135
|
+
start_row=row,
|
|
136
|
+
start_column=merge_start,
|
|
137
|
+
end_row=row,
|
|
138
|
+
end_column=merge_end,
|
|
139
|
+
)
|
|
140
|
+
merge_start = merge_end = merge_end + 1
|
|
141
|
+
else:
|
|
142
|
+
for idx2, x in enumerate(r2[rdim:]):
|
|
143
|
+
if x is None:
|
|
144
|
+
r2[idx2 + rdim] = r2[idx2 + rdim - 1]
|
|
145
|
+
# remove extra empty row
|
|
146
|
+
elif idx == cdim and rdim > 0:
|
|
147
|
+
continue
|
|
148
|
+
else:
|
|
149
|
+
if merged_cells:
|
|
150
|
+
if idx == cdim + 1: # first iteration
|
|
151
|
+
merge_start = [nw_row + cdim + 1] * rdim
|
|
152
|
+
merge_end = [nw_row + cdim + 1] * rdim
|
|
153
|
+
else:
|
|
154
|
+
for x in range(rdim):
|
|
155
|
+
if r2[x] is None:
|
|
156
|
+
merge_end[x] += 1
|
|
157
|
+
if idx == df.shape[0] + cdim: # last iteration
|
|
158
|
+
sheet.merge_cells(
|
|
159
|
+
start_row=merge_start[x],
|
|
160
|
+
start_column=x + nw_col + 1,
|
|
161
|
+
end_row=merge_end[x],
|
|
162
|
+
end_column=x + nw_col + 1,
|
|
163
|
+
)
|
|
164
|
+
else:
|
|
165
|
+
if merge_start[x] != merge_end[x]:
|
|
166
|
+
sheet.merge_cells(
|
|
167
|
+
start_row=merge_start[x],
|
|
168
|
+
start_column=x + nw_col + 1,
|
|
169
|
+
end_row=merge_end[x],
|
|
170
|
+
end_column=x + nw_col + 1,
|
|
171
|
+
)
|
|
172
|
+
merge_start[x] = merge_end[x] = merge_end[x] + 1
|
|
173
|
+
else:
|
|
174
|
+
for x in range(rdim):
|
|
175
|
+
if r2[x] is None:
|
|
176
|
+
r2[x] = last_row_labels[x]
|
|
177
|
+
last_row_labels = r2[:rdim]
|
|
178
|
+
|
|
179
|
+
for col in range(len(r2)):
|
|
180
|
+
if r2[col] is not None:
|
|
181
|
+
sheet.cell(row, col + nw_col + 1).value = r2[col]
|
|
182
|
+
row += 1
|
|
183
|
+
|
|
184
|
+
def _pivot_cdim_only(self, df, dim, value_text):
|
|
185
|
+
cols = df.columns.values.tolist()
|
|
186
|
+
df = df.sort_values(by=cols[:dim]).reset_index(drop=True)
|
|
187
|
+
df["_first"] = value_text
|
|
188
|
+
df = df.pivot(index=["_first"], columns=cols[:-1], values=[value_text])
|
|
189
|
+
df.columns = df.columns.droplevel(0) # remove column index "values"
|
|
190
|
+
df.rename_axis([None], axis=0, inplace=True) # remove index names
|
|
191
|
+
df.rename_axis([None] * dim, axis=1, inplace=True) # remove column index names
|
|
192
|
+
return df
|
|
193
|
+
|
|
194
|
+
def _pivot_rdim_only(self, df, dim, value_text):
|
|
195
|
+
cols = df.columns.values.tolist()
|
|
196
|
+
df["_first"] = value_text
|
|
197
|
+
df = df.pivot(
|
|
198
|
+
index=cols[:dim], columns=["_first"], values=[value_text]
|
|
199
|
+
).sort_index()
|
|
200
|
+
df.columns = df.columns.droplevel(0) # remove column index "values"
|
|
201
|
+
df.rename_axis([None] * dim, axis=0, inplace=True) # remove index names
|
|
202
|
+
df.rename_axis([None], axis=1, inplace=True) # remove column index names
|
|
203
|
+
return df
|
|
204
|
+
|
|
205
|
+
def _pivot_rdim_cdim(self, df, rdim, cdim, value_text):
|
|
206
|
+
dim = rdim + cdim
|
|
207
|
+
cols = df.columns.values.tolist()
|
|
208
|
+
df = df.sort_values(by=cols[rdim:dim]).reset_index(drop=True)
|
|
209
|
+
df = df.pivot(
|
|
210
|
+
index=cols[:rdim], columns=cols[rdim:-1], values=[value_text]
|
|
211
|
+
).sort_index()
|
|
212
|
+
df.columns = df.columns.droplevel(0) # remove column index "values"
|
|
213
|
+
df.rename_axis([None] * rdim, axis=0, inplace=True) # remove index names
|
|
214
|
+
df.rename_axis([None] * cdim, axis=1, inplace=True) # remove column index names
|
|
215
|
+
return df
|
|
216
|
+
|
|
217
|
+
def _reshape_dataframe(self, df, dim, rdim, cdim, value):
|
|
218
|
+
# turn integer column names into string column names to avoid problems reshaping if rdim=0
|
|
219
|
+
df.columns = df.columns.map(str)
|
|
220
|
+
if dim == 0:
|
|
221
|
+
pass
|
|
222
|
+
elif rdim == 0:
|
|
223
|
+
df = self._pivot_cdim_only(df, dim, value)
|
|
224
|
+
elif cdim == 0:
|
|
225
|
+
df = self._pivot_rdim_only(df, dim, value)
|
|
226
|
+
else:
|
|
227
|
+
df = self._pivot_rdim_cdim(df, rdim, cdim, value)
|
|
228
|
+
return df
|
|
229
|
+
|
|
230
|
+
def _expected_excel_shape(self, df, rdim, cdim):
|
|
231
|
+
nr_rows = df.shape[0] + cdim
|
|
232
|
+
nr_cols = df.shape[1] + rdim
|
|
233
|
+
return (nr_rows, nr_cols)
|
|
234
|
+
|
|
235
|
+
def _validate_range(self, sym_name, df, rdim, cdim, nw_col, nw_row, se_col, se_row):
|
|
236
|
+
required_rows, required_columns = self._expected_excel_shape(df, rdim, cdim)
|
|
237
|
+
|
|
238
|
+
if required_rows + nw_row > self._EXCEL_ROW_LIMIT:
|
|
239
|
+
self._connect_error(
|
|
240
|
+
f"Attempting to write >{required_rows}< rows starting from row >{nw_row}< exceeds Excel's row limit of >{self._EXCEL_ROW_LIMIT}< for symbol >{sym_name}<."
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
if required_columns + nw_col > self._EXCEL_COL_LIMIT:
|
|
244
|
+
self._connect_error(
|
|
245
|
+
f"Attempting to write >{required_columns}< columns starting from column >{nw_col}< exceeds Excel's column limit of >{self._EXCEL_COL_LIMIT}< for symbol >{sym_name}<."
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
if se_row is not None:
|
|
249
|
+
actual_rows = se_row - nw_row # + 1
|
|
250
|
+
if required_rows > actual_rows:
|
|
251
|
+
self._connect_error(f"Data exceeds range for symbol >{sym_name}<.")
|
|
252
|
+
|
|
253
|
+
if se_col is not None:
|
|
254
|
+
actual_columns = se_col - nw_col # + 1
|
|
255
|
+
if required_columns > actual_columns:
|
|
256
|
+
self._connect_error(f"Data exceeds range for symbol >{sym_name}<.")
|
|
257
|
+
|
|
258
|
+
def _write_toc(self):
|
|
259
|
+
if self._toc["sort"]:
|
|
260
|
+
self._toc_symbols.sort(key=lambda x: x[0].name)
|
|
261
|
+
sheet = self.sheet_by_name(self._toc["sheetName"], self._wb, True, True)
|
|
262
|
+
self._wb.move_sheet(
|
|
263
|
+
sheet, -self._wb.index(sheet)
|
|
264
|
+
) # move toc sheet to the beginning
|
|
265
|
+
sheet.cell(1, 1).value = "Name"
|
|
266
|
+
sheet.cell(1, 2).value = "Type"
|
|
267
|
+
sheet.cell(1, 3).value = "Dimension"
|
|
268
|
+
sheet.cell(1, 4).value = "Record Count"
|
|
269
|
+
sheet.cell(1, 5).value = "Explanatory text"
|
|
270
|
+
row = 2
|
|
271
|
+
for sym, rng in self._toc_symbols:
|
|
272
|
+
sheet.cell(row, 1).value = sym.name
|
|
273
|
+
sheet.cell(row, 1).hyperlink = f"#{rng}"
|
|
274
|
+
sheet.cell(row, 1).font = Font(underline="single", color="0563C1")
|
|
275
|
+
sheet.cell(row, 2).value = (
|
|
276
|
+
"Parameter" if isinstance(sym, gt.Parameter) else "Set"
|
|
277
|
+
)
|
|
278
|
+
sheet.cell(row, 3).value = sym.dimension
|
|
279
|
+
sheet.cell(row, 4).value = sym.number_records
|
|
280
|
+
sheet.cell(row, 5).value = sym.description
|
|
281
|
+
row += 1
|
|
282
|
+
|
|
283
|
+
def _write_symbols(self, symbols, validate=False):
|
|
284
|
+
if validate:
|
|
285
|
+
sym_schema = self._cdb.load_schema(self)["symbols"]["oneof"][1]["schema"][
|
|
286
|
+
"schema"
|
|
287
|
+
]
|
|
288
|
+
v = ConnectValidator(sym_schema)
|
|
289
|
+
for i, sym in enumerate(symbols):
|
|
290
|
+
if validate:
|
|
291
|
+
sym = v.validated(sym)
|
|
292
|
+
if sym is None:
|
|
293
|
+
self._connect_error(
|
|
294
|
+
f"Validation of item {i} in index failed: {v.errors}"
|
|
295
|
+
)
|
|
296
|
+
sym = v.normalize_of_rules(sym)
|
|
297
|
+
self._write_symbol(sym)
|
|
298
|
+
|
|
299
|
+
def _create_symbol_instructions(self, rec):
|
|
300
|
+
is_symbol = not None in (rec[1], rec[2])
|
|
301
|
+
inst = {}
|
|
302
|
+
if is_symbol:
|
|
303
|
+
sym_type = str(rec[0]).lower().strip() if rec[0] else ""
|
|
304
|
+
inst["name"] = rec[1].strip()
|
|
305
|
+
inst["range"] = rec[2].strip()
|
|
306
|
+
if self._trace > 1:
|
|
307
|
+
self._cdb.print_log(
|
|
308
|
+
f"Parsing symbol >{inst['name']}< with >type: {sym_type}< and >range: {inst['range']}<."
|
|
309
|
+
)
|
|
310
|
+
if len(sym_type) > 0:
|
|
311
|
+
sym_name = inst["name"]
|
|
312
|
+
self._symbols_exist_cdb(sym_name, should_exist=True)
|
|
313
|
+
symbol = self._cdb.container[sym_name]
|
|
314
|
+
if sym_type == "par":
|
|
315
|
+
if not isinstance(symbol, gt.Parameter):
|
|
316
|
+
self._connect_error(
|
|
317
|
+
f"Type mismatch: Symbol type was specified as >{sym_type}< but the symbol is of type >{type(symbol).__name__}<"
|
|
318
|
+
)
|
|
319
|
+
elif sym_type == "set":
|
|
320
|
+
if not isinstance(symbol, gt.Set):
|
|
321
|
+
self._connect_error(
|
|
322
|
+
f"Type mismatch: Symbol type was specified as >{sym_type}< but the symbol is of type >{type(symbol).__name__}<"
|
|
323
|
+
)
|
|
324
|
+
else:
|
|
325
|
+
self._connect_error(
|
|
326
|
+
f"Unknown symbol type >{sym_type}<. Valid symbol types are >par< and >set<."
|
|
327
|
+
)
|
|
328
|
+
return inst
|
|
329
|
+
|
|
330
|
+
def _write_from_index(self):
|
|
331
|
+
symbols = self.parse_index(self._index, self._wb, self._index_parameter_map)
|
|
332
|
+
self._write_symbols(symbols, True)
|
|
333
|
+
|
|
334
|
+
def _write_symbol(self, sym):
|
|
335
|
+
sym_raw = sym.copy()
|
|
336
|
+
self._update_sym_inst(sym, self._inst)
|
|
337
|
+
|
|
338
|
+
sym["range"] = self._dict_get(sym, "range", sym["name"] + "!A1")
|
|
339
|
+
sym["range"] = self.normalize_range(sym["range"])
|
|
340
|
+
|
|
341
|
+
sym_name = sym["name"]
|
|
342
|
+
self._symbols_exist_cdb(sym_name, should_exist=True)
|
|
343
|
+
gt_sym = self._cdb.container[sym_name]
|
|
344
|
+
dim = gt_sym.dimension
|
|
345
|
+
if sym["columnDimension"] == "infer":
|
|
346
|
+
sym["columnDimension"] = 1 if dim > 0 else 0
|
|
347
|
+
|
|
348
|
+
sym_range = sym["range"]
|
|
349
|
+
merged_cells = sym["mergedCells"]
|
|
350
|
+
cdim = sym["columnDimension"]
|
|
351
|
+
value_subs = sym["valueSubstitutions"]
|
|
352
|
+
clear_sheet = sym["clearSheet"]
|
|
353
|
+
|
|
354
|
+
if self._trace > 0:
|
|
355
|
+
self._log_instructions(
|
|
356
|
+
sym, sym_raw, description=f"Write symbol >{sym['name']}<:"
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
if self._trace > 2:
|
|
360
|
+
self._cdb.print_log(
|
|
361
|
+
f"Connect Container symbol >{sym_name}<:\n {gt_sym.records}\n"
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
if not isinstance(gt_sym, (gt.Set, gt.Parameter)):
|
|
365
|
+
self._connect_error(
|
|
366
|
+
f"Symbol type >{type(gt_sym)}< of symbol >{sym_name}< is not supported. Supported symbol types are set and parameter."
|
|
367
|
+
)
|
|
368
|
+
sym_type = "par" if isinstance(gt_sym, gt.Parameter) else "set"
|
|
369
|
+
|
|
370
|
+
df = gt_sym.records.copy(deep=True)
|
|
371
|
+
|
|
372
|
+
if isinstance(gt_sym, gt.Set):
|
|
373
|
+
value = "element_text"
|
|
374
|
+
elif isinstance(gt_sym, gt.Parameter):
|
|
375
|
+
value = "value"
|
|
376
|
+
|
|
377
|
+
if cdim > dim:
|
|
378
|
+
self._connect_error(
|
|
379
|
+
f"columnDimension >{cdim}< exceeds dimension of symbol >{sym_name}<."
|
|
380
|
+
)
|
|
381
|
+
rdim = dim - cdim
|
|
382
|
+
|
|
383
|
+
df = self._apply_value_substitutions(
|
|
384
|
+
df,
|
|
385
|
+
value_subs,
|
|
386
|
+
sym_type,
|
|
387
|
+
sv_eps="EPS",
|
|
388
|
+
sv_na="NA",
|
|
389
|
+
sv_undef="UNDEF",
|
|
390
|
+
sv_posinf="INF",
|
|
391
|
+
sv_neginf="-INF",
|
|
392
|
+
)
|
|
393
|
+
if self._trace > 2:
|
|
394
|
+
self._cdb.print_log(
|
|
395
|
+
f"DataFrame after valueSubstitutions ({sym_name}):\n{df}\n"
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
if (
|
|
399
|
+
value == "element_text" and rdim * cdim > 0
|
|
400
|
+
): # replace empty element_text by Y when exporting a true table
|
|
401
|
+
df.loc[df[value] == "", value] = "Y"
|
|
402
|
+
|
|
403
|
+
sheet, nw_col, nw_row, se_col, se_row, toc_range = self.parse_range(
|
|
404
|
+
sym_range, self._wb, clear_sheet, True
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
df = self._reshape_dataframe(df, dim, rdim, cdim, value)
|
|
408
|
+
if self._trace > 2:
|
|
409
|
+
self._cdb.print_log(f"DataFrame after reshaping ({sym_name}):\n{df}\n")
|
|
410
|
+
|
|
411
|
+
self._validate_range(sym_name, df, rdim, cdim, nw_col, nw_row, se_col, se_row)
|
|
412
|
+
|
|
413
|
+
self._toc_symbols.append((gt_sym, toc_range))
|
|
414
|
+
|
|
415
|
+
if gt_sym.records is None or gt_sym.number_records == 0:
|
|
416
|
+
return
|
|
417
|
+
|
|
418
|
+
self._write(
|
|
419
|
+
df,
|
|
420
|
+
rdim,
|
|
421
|
+
cdim,
|
|
422
|
+
sheet,
|
|
423
|
+
nw_row,
|
|
424
|
+
nw_col,
|
|
425
|
+
merged_cells,
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
def execute(self):
|
|
429
|
+
if self._trace > 0:
|
|
430
|
+
self._log_instructions(self._inst, self._inst_raw)
|
|
431
|
+
self._describe_container(self._cdb.container, "Connect Container:")
|
|
432
|
+
|
|
433
|
+
toc = self._inst_raw.get("tableOfContents")
|
|
434
|
+
|
|
435
|
+
self._open()
|
|
436
|
+
|
|
437
|
+
self._check_openpyxl()
|
|
438
|
+
self._toc_symbols = []
|
|
439
|
+
try:
|
|
440
|
+
if self._index:
|
|
441
|
+
self._write_from_index()
|
|
442
|
+
elif self._symbols == "all":
|
|
443
|
+
self._symbols = [
|
|
444
|
+
{"name": s[0]}
|
|
445
|
+
for s in self._cdb.container
|
|
446
|
+
if isinstance(s[1], (gt.Parameter, gt.Set))
|
|
447
|
+
]
|
|
448
|
+
self._write_symbols(self._symbols, True)
|
|
449
|
+
else:
|
|
450
|
+
self._write_symbols(self._symbols)
|
|
451
|
+
if self._toc:
|
|
452
|
+
self._write_toc()
|
|
453
|
+
finally:
|
|
454
|
+
self._wb.close()
|
|
455
|
+
self._close()
|
|
456
|
+
|
|
457
|
+
def _close(self):
|
|
458
|
+
if len(self._wb.sheetnames) == 0:
|
|
459
|
+
self._cdb.print_log(f"No sheets in Excel file >{self._file}<. Skipping.")
|
|
460
|
+
else:
|
|
461
|
+
try:
|
|
462
|
+
self._wb.save(self._file)
|
|
463
|
+
except PermissionError as e:
|
|
464
|
+
self._connect_error(
|
|
465
|
+
str(e)
|
|
466
|
+
+ "\nThe file may already be open and might need to be closed first."
|
|
467
|
+
)
|
|
@@ -0,0 +1,223 @@
|
|
|
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 re
|
|
27
|
+
import pandas as pd
|
|
28
|
+
from gams import transfer as gt
|
|
29
|
+
from gams.connect.agents.connectagent import ConnectAgent
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Filter(ConnectAgent):
|
|
33
|
+
|
|
34
|
+
def __init__(self, cdb, inst, agent_index):
|
|
35
|
+
super().__init__(cdb, inst, agent_index)
|
|
36
|
+
self._parse_options(self._inst)
|
|
37
|
+
|
|
38
|
+
def _parse_options(self, inst):
|
|
39
|
+
self._name = inst["name"]
|
|
40
|
+
self._new_name = inst["newName"]
|
|
41
|
+
self._value_filters = self._dict_get(inst, "valueFilters", [])
|
|
42
|
+
self._label_filters = self._dict_get(inst, "labelFilters", [])
|
|
43
|
+
self._trace = inst["trace"]
|
|
44
|
+
self._label_filters_dict = {}
|
|
45
|
+
for f in self._label_filters:
|
|
46
|
+
c = f["dimension"]
|
|
47
|
+
if c != "all":
|
|
48
|
+
c = c - 1
|
|
49
|
+
if c in self._label_filters_dict:
|
|
50
|
+
self._connect_error(f"More than one label filter for dimension {c+1}.")
|
|
51
|
+
self._label_filters_dict[c] = f
|
|
52
|
+
|
|
53
|
+
def _filter_labels(self, df, f, c):
|
|
54
|
+
if c == "all":
|
|
55
|
+
for c in range(0, self._sym.dimension):
|
|
56
|
+
df = self._filter_labels(df, f, c)
|
|
57
|
+
else:
|
|
58
|
+
if c >= self._sym.dimension:
|
|
59
|
+
self._connect_error(
|
|
60
|
+
f"Invalid dimension {c+1} for symbol with {self._sym.dimension} dimensions. Hint: dimension is 1-indexed."
|
|
61
|
+
)
|
|
62
|
+
if f.get("keep") is not None:
|
|
63
|
+
df = df.loc[df.iloc[:, c].isin(f["keep"])]
|
|
64
|
+
elif f.get("reject") is not None:
|
|
65
|
+
df = df.loc[~df.iloc[:, c].isin(f["reject"])]
|
|
66
|
+
elif f.get("regex") is not None:
|
|
67
|
+
regex = re.compile(f["regex"])
|
|
68
|
+
df = df.loc[df.iloc[:, c].str.fullmatch(regex)]
|
|
69
|
+
return df
|
|
70
|
+
|
|
71
|
+
def _filter_values(self, df, f, c, skip_trace=False):
|
|
72
|
+
if isinstance(self._sym, gt.Set):
|
|
73
|
+
self._connect_error(
|
|
74
|
+
"Value filters are not supported for symbols of type set."
|
|
75
|
+
)
|
|
76
|
+
rule_identifier = f["ruleIdentifier"]
|
|
77
|
+
rule = self._dict_get(f, "rule", "")
|
|
78
|
+
rule = (
|
|
79
|
+
"(" + rule.replace(rule_identifier, f'df["{c}"]') + ")"
|
|
80
|
+
if rule
|
|
81
|
+
else "[True]*len(df)"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
include_sv = ""
|
|
85
|
+
exclude_sv = ""
|
|
86
|
+
reject_sv = self._dict_get(f, "rejectSpecialValues", [])
|
|
87
|
+
if isinstance(reject_sv, str):
|
|
88
|
+
reject_sv = [reject_sv]
|
|
89
|
+
|
|
90
|
+
if "EPS" in reject_sv:
|
|
91
|
+
exclude_sv += f' & (~gt.SpecialValues.isEps(df["{c}"]))'
|
|
92
|
+
else:
|
|
93
|
+
include_sv += f' | (gt.SpecialValues.isEps(df["{c}"]))'
|
|
94
|
+
if "INF" in reject_sv:
|
|
95
|
+
exclude_sv += f' & (~gt.SpecialValues.isPosInf(df["{c}"]))'
|
|
96
|
+
else:
|
|
97
|
+
include_sv += f' | (gt.SpecialValues.isPosInf(df["{c}"]))'
|
|
98
|
+
if "-INF" in reject_sv:
|
|
99
|
+
exclude_sv += f' & (~gt.SpecialValues.isNegInf(df["{c}"]))'
|
|
100
|
+
else:
|
|
101
|
+
include_sv += f' | (gt.SpecialValues.isNegInf(df["{c}"]))'
|
|
102
|
+
if "UNDEF" in reject_sv:
|
|
103
|
+
exclude_sv += f' & (~gt.SpecialValues.isUndef(df["{c}"]))'
|
|
104
|
+
else:
|
|
105
|
+
include_sv += f' | (gt.SpecialValues.isUndef(df["{c}"]))'
|
|
106
|
+
if "NA" in reject_sv:
|
|
107
|
+
exclude_sv += f' & (~gt.SpecialValues.isNA(df["{c}"]))'
|
|
108
|
+
else:
|
|
109
|
+
include_sv += f' | (gt.SpecialValues.isNA(df["{c}"]))'
|
|
110
|
+
rule += exclude_sv + include_sv
|
|
111
|
+
|
|
112
|
+
if self._trace > 1 and not skip_trace:
|
|
113
|
+
self._cdb.print_log(f'Applying rule for attribute "{c}": {rule}')
|
|
114
|
+
if c == "all":
|
|
115
|
+
if isinstance(self._sym, gt.Parameter):
|
|
116
|
+
value_columns = ["value"]
|
|
117
|
+
elif isinstance(self._sym, (gt.Variable, gt.Equation)):
|
|
118
|
+
value_columns = [
|
|
119
|
+
"level",
|
|
120
|
+
"marginal",
|
|
121
|
+
"lower",
|
|
122
|
+
"upper",
|
|
123
|
+
"scale",
|
|
124
|
+
]
|
|
125
|
+
for c in value_columns:
|
|
126
|
+
df = self._filter_values(df, f, c, True)
|
|
127
|
+
else:
|
|
128
|
+
if not df.empty:
|
|
129
|
+
df = eval(f"df.loc[({rule})]", {"df": df, "gt": gt})
|
|
130
|
+
return df
|
|
131
|
+
|
|
132
|
+
def execute(self):
|
|
133
|
+
if self._trace > 0:
|
|
134
|
+
self._log_instructions(self._inst, self._inst_raw)
|
|
135
|
+
self._describe_container(self._cdb.container, "Connect Container (before):")
|
|
136
|
+
|
|
137
|
+
self._symbols_exist_cdb(self._name, should_exist=True)
|
|
138
|
+
self._sym = self._cdb.container[self._name]
|
|
139
|
+
|
|
140
|
+
if self._new_name.casefold() == self._name.casefold():
|
|
141
|
+
self._connect_error(
|
|
142
|
+
f"newName >{self._new_name}< must be different from name >{self._name}<. Hint: The names are case-insensitive."
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
self._symbols_exist_cdb(self._new_name)
|
|
146
|
+
|
|
147
|
+
if isinstance(self._sym, gt.Set):
|
|
148
|
+
tsym = gt.Set(self._cdb.container, self._new_name, self._sym.domain)
|
|
149
|
+
elif isinstance(self._sym, gt.Parameter):
|
|
150
|
+
tsym = gt.Parameter(self._cdb.container, self._new_name, self._sym.domain)
|
|
151
|
+
elif isinstance(self._sym, gt.Variable):
|
|
152
|
+
tsym = gt.Variable(
|
|
153
|
+
self._cdb.container,
|
|
154
|
+
self._new_name,
|
|
155
|
+
self._sym.type,
|
|
156
|
+
self._sym.domain,
|
|
157
|
+
)
|
|
158
|
+
elif isinstance(self._sym, gt.Equation):
|
|
159
|
+
tsym = gt.Equation(
|
|
160
|
+
self._cdb.container,
|
|
161
|
+
self._new_name,
|
|
162
|
+
self._sym.type,
|
|
163
|
+
self._sym.domain,
|
|
164
|
+
)
|
|
165
|
+
else:
|
|
166
|
+
self._connect_error("Symbol type not supported.")
|
|
167
|
+
|
|
168
|
+
df = self._sym_records_no_none(self._sym).copy(deep=True)
|
|
169
|
+
|
|
170
|
+
if self._trace > 2:
|
|
171
|
+
self._cdb.print_log(f"Original DataFrame:\n{df}")
|
|
172
|
+
for c in self._label_filters_dict:
|
|
173
|
+
f = self._label_filters_dict[c]
|
|
174
|
+
if self._trace > 0:
|
|
175
|
+
self._log_instructions(f, description="Applying label filter:")
|
|
176
|
+
if c == "all":
|
|
177
|
+
df = self._filter_labels(df, f, c)
|
|
178
|
+
if self._trace > 2:
|
|
179
|
+
self._cdb.print_log(
|
|
180
|
+
f"DataFrame after label filter for dimension 'all':\n{df}"
|
|
181
|
+
)
|
|
182
|
+
else:
|
|
183
|
+
df = self._filter_labels(df, f, c)
|
|
184
|
+
if self._trace > 2:
|
|
185
|
+
self._cdb.print_log(
|
|
186
|
+
f"DataFrame after label filter for dimension {c+1}:\n{df}"
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
val_cols = [f["attribute"] for f in self._value_filters]
|
|
190
|
+
if len(val_cols) != len(set(val_cols)):
|
|
191
|
+
for c in set(val_cols):
|
|
192
|
+
val_cols.remove(c)
|
|
193
|
+
self._connect_error(
|
|
194
|
+
f"More than one value filter for attribute {set(val_cols)}."
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
for f in self._value_filters:
|
|
198
|
+
c = f["attribute"]
|
|
199
|
+
if self._trace > 0:
|
|
200
|
+
self._log_instructions(f, description="Applying value filter:")
|
|
201
|
+
if isinstance(self._sym, gt.Parameter) and c not in ["all", "value"]:
|
|
202
|
+
self._connect_error(
|
|
203
|
+
f"Invalid attribute >{c}< for symbol type parameter."
|
|
204
|
+
)
|
|
205
|
+
if (
|
|
206
|
+
isinstance(self._sym, gt.Variable) or isinstance(self._sym, gt.Equation)
|
|
207
|
+
) and c not in ["all", "level", "marginal", "upper", "lower", "scale"]:
|
|
208
|
+
self._connect_error(
|
|
209
|
+
f"Invalid attribute >{c}< for symbol type variable/equation."
|
|
210
|
+
)
|
|
211
|
+
df = self._filter_values(df, f, c)
|
|
212
|
+
if self._trace > 2:
|
|
213
|
+
self._cdb.print_log(
|
|
214
|
+
f'DataFrame after value filter for attribute "{c}":\n{df}'
|
|
215
|
+
)
|
|
216
|
+
tsym.setRecords(df)
|
|
217
|
+
|
|
218
|
+
if self._trace > 2:
|
|
219
|
+
self._cdb.print_log(
|
|
220
|
+
f"Connect Container symbol={self._new_name}:\n {tsym.records}\n"
|
|
221
|
+
)
|
|
222
|
+
if self._trace > 0:
|
|
223
|
+
self._describe_container(self._cdb.container, "Connect Container (after):")
|