floodmodeller-api 0.4.2.post1__py3-none-any.whl → 0.4.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. floodmodeller_api/__init__.py +8 -9
  2. floodmodeller_api/_base.py +184 -176
  3. floodmodeller_api/backup.py +273 -273
  4. floodmodeller_api/dat.py +909 -831
  5. floodmodeller_api/diff.py +136 -119
  6. floodmodeller_api/ied.py +307 -306
  7. floodmodeller_api/ief.py +647 -637
  8. floodmodeller_api/ief_flags.py +253 -253
  9. floodmodeller_api/inp.py +266 -266
  10. floodmodeller_api/libs/libifcoremd.dll +0 -0
  11. floodmodeller_api/libs/libifcoremt.so.5 +0 -0
  12. floodmodeller_api/libs/libifport.so.5 +0 -0
  13. floodmodeller_api/{libmmd.dll → libs/libimf.so} +0 -0
  14. floodmodeller_api/libs/libintlc.so.5 +0 -0
  15. floodmodeller_api/libs/libmmd.dll +0 -0
  16. floodmodeller_api/libs/libsvml.so +0 -0
  17. floodmodeller_api/libs/libzzn_read.so +0 -0
  18. floodmodeller_api/libs/zzn_read.dll +0 -0
  19. floodmodeller_api/logs/__init__.py +2 -2
  20. floodmodeller_api/logs/lf.py +320 -312
  21. floodmodeller_api/logs/lf_helpers.py +354 -352
  22. floodmodeller_api/logs/lf_params.py +643 -529
  23. floodmodeller_api/mapping.py +84 -0
  24. floodmodeller_api/test/__init__.py +4 -4
  25. floodmodeller_api/test/conftest.py +9 -8
  26. floodmodeller_api/test/test_backup.py +117 -117
  27. floodmodeller_api/test/test_dat.py +221 -92
  28. floodmodeller_api/test/test_data/All Units 4_6.DAT +1081 -1081
  29. floodmodeller_api/test/test_data/All Units 4_6.feb +1081 -1081
  30. floodmodeller_api/test/test_data/BRIDGE.DAT +926 -926
  31. floodmodeller_api/test/test_data/Culvert_Inlet_Outlet.dat +36 -36
  32. floodmodeller_api/test/test_data/Culvert_Inlet_Outlet.feb +36 -36
  33. floodmodeller_api/test/test_data/DamBreakADI.xml +52 -52
  34. floodmodeller_api/test/test_data/DamBreakFAST.xml +58 -58
  35. floodmodeller_api/test/test_data/DamBreakFAST_dy.xml +53 -53
  36. floodmodeller_api/test/test_data/DamBreakTVD.xml +55 -55
  37. floodmodeller_api/test/test_data/DefenceBreach.xml +53 -53
  38. floodmodeller_api/test/test_data/DefenceBreachFAST.xml +60 -60
  39. floodmodeller_api/test/test_data/DefenceBreachFAST_dy.xml +55 -55
  40. floodmodeller_api/test/test_data/Domain1+2_QH.xml +76 -76
  41. floodmodeller_api/test/test_data/Domain1_H.xml +41 -41
  42. floodmodeller_api/test/test_data/Domain1_Q.xml +41 -41
  43. floodmodeller_api/test/test_data/Domain1_Q_FAST.xml +48 -48
  44. floodmodeller_api/test/test_data/Domain1_Q_FAST_dy.xml +48 -48
  45. floodmodeller_api/test/test_data/Domain1_Q_xml_expected.json +263 -0
  46. floodmodeller_api/test/test_data/Domain1_W.xml +41 -41
  47. floodmodeller_api/test/test_data/EX1.DAT +321 -321
  48. floodmodeller_api/test/test_data/EX1.ext +107 -107
  49. floodmodeller_api/test/test_data/EX1.feb +320 -320
  50. floodmodeller_api/test/test_data/EX1.gxy +107 -107
  51. floodmodeller_api/test/test_data/EX17.DAT +421 -422
  52. floodmodeller_api/test/test_data/EX17.ext +213 -213
  53. floodmodeller_api/test/test_data/EX17.feb +422 -422
  54. floodmodeller_api/test/test_data/EX18.DAT +375 -375
  55. floodmodeller_api/test/test_data/EX18_DAT_expected.json +3876 -0
  56. floodmodeller_api/test/test_data/EX2.DAT +302 -302
  57. floodmodeller_api/test/test_data/EX3.DAT +926 -926
  58. floodmodeller_api/test/test_data/EX3_DAT_expected.json +16235 -0
  59. floodmodeller_api/test/test_data/EX3_IEF_expected.json +61 -0
  60. floodmodeller_api/test/test_data/EX6.DAT +2084 -2084
  61. floodmodeller_api/test/test_data/EX6.ext +532 -532
  62. floodmodeller_api/test/test_data/EX6.feb +2084 -2084
  63. floodmodeller_api/test/test_data/EX6_DAT_expected.json +31647 -0
  64. floodmodeller_api/test/test_data/Event Data Example.DAT +336 -336
  65. floodmodeller_api/test/test_data/Event Data Example.ext +107 -107
  66. floodmodeller_api/test/test_data/Event Data Example.feb +336 -336
  67. floodmodeller_api/test/test_data/Linked1D2D.xml +52 -52
  68. floodmodeller_api/test/test_data/Linked1D2DFAST.xml +53 -53
  69. floodmodeller_api/test/test_data/Linked1D2DFAST_dy.xml +48 -48
  70. floodmodeller_api/test/test_data/Linked1D2D_xml_expected.json +313 -0
  71. floodmodeller_api/test/test_data/blockage.dat +50 -50
  72. floodmodeller_api/test/test_data/blockage.ext +45 -45
  73. floodmodeller_api/test/test_data/blockage.feb +9 -9
  74. floodmodeller_api/test/test_data/blockage.gxy +71 -71
  75. floodmodeller_api/test/test_data/defaultUnits.dat +127 -127
  76. floodmodeller_api/test/test_data/defaultUnits.ext +45 -45
  77. floodmodeller_api/test/test_data/defaultUnits.feb +9 -9
  78. floodmodeller_api/test/test_data/defaultUnits.fmpx +58 -58
  79. floodmodeller_api/test/test_data/defaultUnits.gxy +85 -85
  80. floodmodeller_api/test/test_data/ex3.ief +20 -20
  81. floodmodeller_api/test/test_data/ex3.lf1 +2800 -2800
  82. floodmodeller_api/test/test_data/ex4.DAT +1374 -1374
  83. floodmodeller_api/test/test_data/ex4_changed.DAT +1374 -1374
  84. floodmodeller_api/test/test_data/example1.inp +329 -329
  85. floodmodeller_api/test/test_data/example2.inp +158 -158
  86. floodmodeller_api/test/test_data/example3.inp +297 -297
  87. floodmodeller_api/test/test_data/example4.inp +388 -388
  88. floodmodeller_api/test/test_data/example5.inp +147 -147
  89. floodmodeller_api/test/test_data/example6.inp +154 -154
  90. floodmodeller_api/test/test_data/jump.dat +176 -176
  91. floodmodeller_api/test/test_data/network.dat +1374 -1374
  92. floodmodeller_api/test/test_data/network.ext +45 -45
  93. floodmodeller_api/test/test_data/network.exy +1 -1
  94. floodmodeller_api/test/test_data/network.feb +45 -45
  95. floodmodeller_api/test/test_data/network.ied +45 -45
  96. floodmodeller_api/test/test_data/network.ief +20 -20
  97. floodmodeller_api/test/test_data/network.inp +147 -147
  98. floodmodeller_api/test/test_data/network.pxy +57 -57
  99. floodmodeller_api/test/test_data/network.zzd +122 -122
  100. floodmodeller_api/test/test_data/network_dat_expected.json +21837 -0
  101. floodmodeller_api/test/test_data/network_from_tabularCSV.csv +87 -87
  102. floodmodeller_api/test/test_data/network_ied_expected.json +287 -0
  103. floodmodeller_api/test/test_data/rnweir.dat +9 -9
  104. floodmodeller_api/test/test_data/rnweir.ext +45 -45
  105. floodmodeller_api/test/test_data/rnweir.feb +9 -9
  106. floodmodeller_api/test/test_data/rnweir.gxy +45 -45
  107. floodmodeller_api/test/test_data/rnweir_default.dat +74 -74
  108. floodmodeller_api/test/test_data/rnweir_default.ext +45 -45
  109. floodmodeller_api/test/test_data/rnweir_default.feb +9 -9
  110. floodmodeller_api/test/test_data/rnweir_default.fmpx +58 -58
  111. floodmodeller_api/test/test_data/rnweir_default.gxy +53 -53
  112. floodmodeller_api/test/test_data/unit checks.dat +16 -16
  113. floodmodeller_api/test/test_ied.py +29 -29
  114. floodmodeller_api/test/test_ief.py +125 -24
  115. floodmodeller_api/test/test_inp.py +47 -48
  116. floodmodeller_api/test/test_json.py +114 -0
  117. floodmodeller_api/test/test_logs_lf.py +48 -51
  118. floodmodeller_api/test/test_tool.py +165 -152
  119. floodmodeller_api/test/test_toolbox_structure_log.py +234 -239
  120. floodmodeller_api/test/test_xml2d.py +151 -156
  121. floodmodeller_api/test/test_zzn.py +36 -34
  122. floodmodeller_api/to_from_json.py +218 -0
  123. floodmodeller_api/tool.py +332 -329
  124. floodmodeller_api/toolbox/__init__.py +5 -5
  125. floodmodeller_api/toolbox/example_tool.py +45 -45
  126. floodmodeller_api/toolbox/model_build/__init__.py +2 -2
  127. floodmodeller_api/toolbox/model_build/add_siltation_definition.py +100 -98
  128. floodmodeller_api/toolbox/model_build/structure_log/__init__.py +1 -1
  129. floodmodeller_api/toolbox/model_build/structure_log/structure_log.py +287 -289
  130. floodmodeller_api/toolbox/model_build/structure_log_definition.py +76 -76
  131. floodmodeller_api/units/__init__.py +10 -10
  132. floodmodeller_api/units/_base.py +214 -212
  133. floodmodeller_api/units/boundaries.py +467 -467
  134. floodmodeller_api/units/comment.py +52 -55
  135. floodmodeller_api/units/conduits.py +382 -402
  136. floodmodeller_api/units/helpers.py +123 -131
  137. floodmodeller_api/units/iic.py +107 -101
  138. floodmodeller_api/units/losses.py +305 -306
  139. floodmodeller_api/units/sections.py +444 -446
  140. floodmodeller_api/units/structures.py +1690 -1683
  141. floodmodeller_api/units/units.py +93 -104
  142. floodmodeller_api/units/unsupported.py +44 -44
  143. floodmodeller_api/units/variables.py +87 -89
  144. floodmodeller_api/urban1d/__init__.py +11 -11
  145. floodmodeller_api/urban1d/_base.py +188 -179
  146. floodmodeller_api/urban1d/conduits.py +93 -85
  147. floodmodeller_api/urban1d/general_parameters.py +58 -58
  148. floodmodeller_api/urban1d/junctions.py +81 -79
  149. floodmodeller_api/urban1d/losses.py +81 -74
  150. floodmodeller_api/urban1d/outfalls.py +114 -110
  151. floodmodeller_api/urban1d/raingauges.py +111 -111
  152. floodmodeller_api/urban1d/subsections.py +92 -98
  153. floodmodeller_api/urban1d/xsections.py +147 -144
  154. floodmodeller_api/util.py +77 -21
  155. floodmodeller_api/validation/parameters.py +660 -660
  156. floodmodeller_api/validation/urban_parameters.py +388 -404
  157. floodmodeller_api/validation/validation.py +110 -108
  158. floodmodeller_api/version.py +1 -1
  159. floodmodeller_api/xml2d.py +688 -673
  160. floodmodeller_api/xml2d_template.py +37 -37
  161. floodmodeller_api/zzn.py +387 -363
  162. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.3.dist-info}/LICENSE.txt +13 -13
  163. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.3.dist-info}/METADATA +82 -82
  164. floodmodeller_api-0.4.3.dist-info/RECORD +179 -0
  165. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.3.dist-info}/WHEEL +1 -1
  166. floodmodeller_api/libifcoremd.dll +0 -0
  167. floodmodeller_api/test/test_data/EX3.bmp +0 -0
  168. floodmodeller_api/test/test_data/test_output.csv +0 -87
  169. floodmodeller_api/zzn_read.dll +0 -0
  170. floodmodeller_api-0.4.2.post1.dist-info/RECORD +0 -164
  171. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.3.dist-info}/entry_points.txt +0 -0
  172. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.3.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,8 @@
1
- from .dat import DAT
2
- from .ied import IED
3
- from .ief import IEF
4
- from .inp import INP
5
- from .logs import LF1, LF2
6
- from .util import open_docs
7
- from .version import __version__
8
- from .xml2d import XML2D
9
- from .zzn import ZZN
1
+ from .dat import DAT
2
+ from .ied import IED
3
+ from .ief import IEF
4
+ from .inp import INP
5
+ from .logs import LF1, LF2
6
+ from .version import __version__
7
+ from .xml2d import XML2D
8
+ from .zzn import ZZN
@@ -1,176 +1,184 @@
1
- """
2
- Flood Modeller Python API
3
- Copyright (C) 2023 Jacobs U.K. Limited
4
-
5
- This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
6
- as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
7
-
8
- This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
9
- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
10
-
11
- You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.
12
-
13
- If you have any query about this program or this License, please contact us at support@floodmodeller.com or write to the following
14
- address: Jacobs UK Limited, Flood Modeller, Cottons Centre, Cottons Lane, London, SE1 2QG, United Kingdom.
15
- """
16
-
17
- """ Holds the base file class for API file classes """
18
-
19
- from pathlib import Path
20
- from typing import NoReturn, Optional, Union
21
-
22
- from .backup import File
23
- from .diff import check_item_with_dataframe_equal
24
- from .units._base import Unit
25
- from .units.iic import IIC
26
- from .urban1d._base import UrbanSubsection, UrbanUnit
27
- from .version import __version__
28
-
29
-
30
- class FMFile:
31
- """Base class for all Flood Modeller File types"""
32
-
33
- _filetype: Optional[str] = None
34
- _suffix: Optional[str] = None
35
-
36
- def __init__(self, filepath: Optional[Union[str, Path]]):
37
- if filepath is not None:
38
- self._filepath = Path(filepath).resolve() # save filepath to class
39
- # Check if filepath valid
40
- # * Add check or fix for path lengths greater than DOS standard length of 260 characters
41
-
42
- if not self._filepath.suffix.lower() == self._suffix:
43
- raise TypeError(
44
- f"Given filepath does not point to a {self._filetype} file. Please point to the full path for a {self._filetype} file"
45
- )
46
- if not self._filepath.exists():
47
- raise FileNotFoundError(
48
- f"{self._filetype} file does not exist! If you are wanting to create a new {self._filetype}, initiate the class without a given "
49
- f"filepath to create a new blank {self._filetype} or point the filepath of an existing {self._filetype} to use as a template, "
50
- f"then use the .save() method to save to a new filepath"
51
- )
52
- # If the file is not a ZZN file, then perform a backup
53
- # This performs a conditional back up, only copying the file if an equivalent copy doesn't already exist
54
- if not self._filetype == "ZZN":
55
- file = File(path=self._filepath)
56
- file.backup()
57
- # Add the file object as a property to expose the restore method
58
- self.file = file
59
-
60
- def __repr__(self):
61
- return f"<floodmodeller_api Class: {self._filetype}(filepath={self._filepath})>"
62
-
63
- def _write(self):
64
- raise NotImplementedError
65
-
66
- def _read(self):
67
- raise NotImplementedError
68
-
69
- def _update(self):
70
- f"""Updates the existing {self._filetype} based on any altered attributes"""
71
- if self._filepath is None:
72
- raise UserWarning(
73
- f"{self._filetype} must be saved to a specific filepath before update() can be called."
74
- )
75
-
76
- string = self._write()
77
- with open(self._filepath, "w") as _file:
78
- _file.write(string)
79
- print(f"{self._filetype} File Updated!")
80
-
81
- def _save(self, filepath):
82
- filepath = Path(filepath).absolute()
83
- if not filepath.suffix.lower() == self._suffix:
84
- raise TypeError(
85
- f'Given filepath does not point to a filepath suffixed "{self._suffix}". Please point to the full path to save the {self._filetype} file'
86
- )
87
-
88
- if not filepath.parent.exists():
89
- Path.mkdir(filepath.parent)
90
-
91
- string = self._write()
92
- with open(filepath, "w") as _file:
93
- _file.write(string)
94
- self._filepath = filepath # Updates the filepath attribute to the given path
95
-
96
- print(f"{self._filetype} File Saved to: {filepath}")
97
-
98
- def _diff(self, other, force_print=False):
99
- try:
100
- if self._filetype != other._filetype:
101
- raise TypeError("Cannot compare objects of different filetypes")
102
- diff = self._get_diff(other)
103
- if diff[0]:
104
- print("No difference, files are equivalent")
105
- else:
106
- print(f"Files not equivalent, {len(diff[1])} difference(s) found:")
107
- if len(diff[1]) > 25 and not force_print:
108
- print("[Showing first 25 differences...] ")
109
- print("\n".join([f" {name}: {reason}" for name, reason in diff[1][:25]]))
110
- print("\n...To see full list of all differences add force_print=True")
111
- else:
112
- print("\n".join([f" {name}: {reason}" for name, reason in diff[1]]))
113
- except Exception as e:
114
- self._handle_exception(e, when="compare")
115
-
116
- def _get_diff(self, other):
117
- return self.__eq__(other, return_diff=True) # pylint: disable=unnecessary-dunder-call
118
-
119
- def _handle_exception(self, err, when) -> NoReturn:
120
- tb = err.__traceback__
121
- while tb.tb_next is not None:
122
- tb = tb.tb_next
123
- line_no = tb.tb_lineno
124
- tb_path = Path(tb.tb_frame.f_code.co_filename)
125
- fname = "/".join(tb_path.parts[-2:])
126
-
127
- raise Exception(
128
- "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
129
- f"\nAPI Error: Problem encountered when trying to {when} {self._filetype} file {self._filepath}."
130
- f"\n\nDetails: {__version__}-{fname}-{line_no}"
131
- f"\nMsg: {err}"
132
- "\n\nFor additional support, go to: https://github.com/People-Places-Solutions/floodmodeller-api"
133
- )
134
-
135
- def __eq__(self, other, return_diff=False):
136
- result = True
137
- diff = []
138
- try:
139
- for key, item in self.__dict__.items():
140
- try:
141
- if key in (
142
- "_filepath",
143
- "_raw_data",
144
- "_gxy_filepath",
145
- "_gxy_data",
146
- "_xmltree",
147
- "_xsd",
148
- "_xsdschema",
149
- "file",
150
- "_log_path",
151
- ):
152
- continue
153
- _result, diff = check_item_with_dataframe_equal(
154
- item,
155
- other.__dict__[key],
156
- name=f"{self._filetype}->{key}",
157
- diff=diff,
158
- special_types=(Unit, IIC, UrbanUnit, UrbanSubsection),
159
- )
160
- if not _result:
161
- result = False
162
- except KeyError as ke:
163
- result = False
164
- diff.append(
165
- (
166
- f"{self._filetype}->{key}",
167
- f"Key: '{ke.args[0]}' missing in other",
168
- )
169
- )
170
- continue
171
-
172
- except Exception as e:
173
- result = False
174
- diff.append((f"{self._filetype}->{key}", f"Error encountered: {e.args[0]}"))
175
-
176
- return (result, diff) if return_diff else result
1
+ """
2
+ Flood Modeller Python API
3
+ Copyright (C) 2024 Jacobs U.K. Limited
4
+
5
+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
6
+ as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
7
+
8
+ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
9
+ of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
10
+
11
+ You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.
12
+
13
+ If you have any query about this program or this License, please contact us at support@floodmodeller.com or write to the following
14
+ address: Jacobs UK Limited, Flood Modeller, Cottons Centre, Cottons Lane, London, SE1 2QG, United Kingdom.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ """ Holds the base file class for API file classes """
20
+
21
+ from pathlib import Path
22
+ from typing import NoReturn
23
+
24
+ from .backup import File
25
+ from .diff import check_item_with_dataframe_equal
26
+ from .to_from_json import Jsonable
27
+ from .units._base import Unit
28
+ from .units.iic import IIC
29
+ from .urban1d._base import UrbanSubsection, UrbanUnit
30
+ from .version import __version__
31
+
32
+
33
+ class FMFile(Jsonable):
34
+ """Base class for all Flood Modeller File types"""
35
+
36
+ _filetype: str | None = None
37
+ _suffix: str | None = None
38
+ MAX_DIFF = 25
39
+
40
+ def __init__(self, filepath: str | Path | None = None, **kwargs):
41
+ if filepath is not None:
42
+ self._filepath = Path(filepath).resolve() # save filepath to class
43
+ # Check if filepath valid
44
+ # * Add check or fix for path lengths greater than DOS standard length of 260 characters
45
+
46
+ if not self._filepath.suffix.lower() == self._suffix:
47
+ raise TypeError(
48
+ f"Given filepath does not point to a {self._filetype} file. Please point to the full path for a {self._filetype} file",
49
+ )
50
+ if not self._filepath.exists():
51
+ raise FileNotFoundError(
52
+ f"{self._filetype} file does not exist! If you are wanting to create a new {self._filetype}, initiate the class without a given "
53
+ f"filepath to create a new blank {self._filetype} or point the filepath of an existing {self._filetype} to use as a template, "
54
+ f"then use the .save() method to save to a new filepath",
55
+ )
56
+ # If the file is not a ZZN file, then perform a backup
57
+ # This performs a conditional back up, only copying the file if an equivalent copy doesn't already exist
58
+ if self._filetype != "ZZN":
59
+ file = File(path=self._filepath)
60
+ file.backup()
61
+ # Add the file object as a property to expose the restore method
62
+ self.file = file
63
+
64
+ def __repr__(self):
65
+ return f"<floodmodeller_api Class: {self._filetype}(filepath={self._filepath})>"
66
+
67
+ def _write(self):
68
+ raise NotImplementedError
69
+
70
+ def _read(self):
71
+ raise NotImplementedError
72
+
73
+ def _update(self):
74
+ """Updates the existing self._filetype based on any altered attributes"""
75
+ if self._filepath is None:
76
+ raise UserWarning(
77
+ f"{self._filetype} must be saved to a specific filepath before update() can be called.",
78
+ )
79
+
80
+ string = self._write()
81
+ with open(self._filepath, "w") as _file:
82
+ _file.write(string)
83
+ print(f"{self._filetype} File Updated!")
84
+
85
+ def _save(self, filepath):
86
+ filepath = Path(filepath).absolute()
87
+ if not filepath.suffix.lower() == self._suffix:
88
+ raise TypeError(
89
+ f'Given filepath does not point to a filepath suffixed "{self._suffix}". Please point to the full path to save the {self._filetype} file',
90
+ )
91
+
92
+ if not filepath.parent.exists():
93
+ Path.mkdir(filepath.parent)
94
+
95
+ string = self._write()
96
+ with open(filepath, "w") as _file:
97
+ _file.write(string)
98
+ self._filepath = filepath # Updates the filepath attribute to the given path
99
+
100
+ print(f"{self._filetype} File Saved to: {filepath}")
101
+
102
+ def _diff(self, other, force_print=False):
103
+ try:
104
+ if self._filetype != other._filetype:
105
+ raise TypeError("Cannot compare objects of different filetypes")
106
+ diff = self._get_diff(other)
107
+ if diff[0]:
108
+ print("No difference, files are equivalent")
109
+ else:
110
+ print(f"Files not equivalent, {len(diff[1])} difference(s) found:")
111
+ if len(diff[1]) > self.MAX_DIFF and not force_print:
112
+ print(f"[Showing first {self.MAX_DIFF} differences...] ")
113
+ print(
114
+ "\n".join(
115
+ [f" {name}: {reason}" for name, reason in diff[1][: self.MAX_DIFF]],
116
+ ),
117
+ )
118
+ print("\n...To see full list of all differences add force_print=True")
119
+ else:
120
+ print("\n".join([f" {name}: {reason}" for name, reason in diff[1]]))
121
+ except Exception as e:
122
+ self._handle_exception(e, when="compare")
123
+
124
+ def _get_diff(self, other):
125
+ return self.__eq__(other, return_diff=True) # pylint: disable=unnecessary-dunder-call
126
+
127
+ def _handle_exception(self, err, when) -> NoReturn:
128
+ tb = err.__traceback__
129
+ while tb.tb_next is not None:
130
+ tb = tb.tb_next
131
+ line_no = tb.tb_lineno
132
+ tb_path = Path(tb.tb_frame.f_code.co_filename)
133
+ fname = "/".join(tb_path.parts[-2:])
134
+
135
+ raise Exception(
136
+ "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
137
+ f"\nAPI Error: Problem encountered when trying to {when} {self._filetype} file {self._filepath}."
138
+ f"\n\nDetails: {__version__}-{fname}-{line_no}"
139
+ f"\nMsg: {err}"
140
+ "\n\nFor additional support, go to: https://github.com/People-Places-Solutions/floodmodeller-api",
141
+ )
142
+
143
+ def __eq__(self, other, return_diff=False):
144
+ result = True
145
+ diff = []
146
+ try:
147
+ for key, item in self.__dict__.items():
148
+ try:
149
+ if key in (
150
+ "_filepath",
151
+ "_raw_data",
152
+ "_gxy_filepath",
153
+ "_gxy_data",
154
+ "_xmltree",
155
+ "_xsd",
156
+ "_xsdschema",
157
+ "file",
158
+ "_log_path",
159
+ ):
160
+ continue
161
+ _result, diff = check_item_with_dataframe_equal(
162
+ item,
163
+ other.__dict__[key],
164
+ name=f"{self._filetype}->{key}",
165
+ diff=diff,
166
+ special_types=(Unit, IIC, UrbanUnit, UrbanSubsection),
167
+ )
168
+ if not _result:
169
+ result = False
170
+ except KeyError as ke:
171
+ result = False
172
+ diff.append(
173
+ (
174
+ f"{self._filetype}->{key}",
175
+ f"Key: '{ke.args[0]}' missing in other",
176
+ ),
177
+ )
178
+ continue
179
+
180
+ except Exception as e:
181
+ result = False
182
+ diff.append((f"{self._filetype}->{key}", f"Error encountered: {e.args[0]}"))
183
+
184
+ return (result, diff) if return_diff else result