floodmodeller-api 0.4.2.post1__py3-none-any.whl → 0.4.4__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 (178) hide show
  1. floodmodeller_api/__init__.py +8 -9
  2. floodmodeller_api/_base.py +169 -176
  3. floodmodeller_api/backup.py +273 -273
  4. floodmodeller_api/dat.py +889 -831
  5. floodmodeller_api/diff.py +136 -119
  6. floodmodeller_api/ied.py +302 -306
  7. floodmodeller_api/ief.py +553 -637
  8. floodmodeller_api/ief_flags.py +253 -253
  9. floodmodeller_api/inp.py +260 -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 +364 -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 +16 -8
  26. floodmodeller_api/test/test_backup.py +117 -117
  27. floodmodeller_api/test/test_conveyance.py +107 -0
  28. floodmodeller_api/test/test_dat.py +222 -92
  29. floodmodeller_api/test/test_data/All Units 4_6.DAT +1081 -1081
  30. floodmodeller_api/test/test_data/All Units 4_6.feb +1081 -1081
  31. floodmodeller_api/test/test_data/BRIDGE.DAT +926 -926
  32. floodmodeller_api/test/test_data/Culvert_Inlet_Outlet.dat +36 -36
  33. floodmodeller_api/test/test_data/Culvert_Inlet_Outlet.feb +36 -36
  34. floodmodeller_api/test/test_data/DamBreakADI.xml +52 -52
  35. floodmodeller_api/test/test_data/DamBreakFAST.xml +58 -58
  36. floodmodeller_api/test/test_data/DamBreakFAST_dy.xml +53 -53
  37. floodmodeller_api/test/test_data/DamBreakTVD.xml +55 -55
  38. floodmodeller_api/test/test_data/DefenceBreach.xml +53 -53
  39. floodmodeller_api/test/test_data/DefenceBreachFAST.xml +60 -60
  40. floodmodeller_api/test/test_data/DefenceBreachFAST_dy.xml +55 -55
  41. floodmodeller_api/test/test_data/Domain1+2_QH.xml +76 -76
  42. floodmodeller_api/test/test_data/Domain1_H.xml +41 -41
  43. floodmodeller_api/test/test_data/Domain1_Q.xml +41 -41
  44. floodmodeller_api/test/test_data/Domain1_Q_FAST.xml +48 -48
  45. floodmodeller_api/test/test_data/Domain1_Q_FAST_dy.xml +48 -48
  46. floodmodeller_api/test/test_data/Domain1_Q_xml_expected.json +263 -0
  47. floodmodeller_api/test/test_data/Domain1_W.xml +41 -41
  48. floodmodeller_api/test/test_data/EX1.DAT +321 -321
  49. floodmodeller_api/test/test_data/EX1.ext +107 -107
  50. floodmodeller_api/test/test_data/EX1.feb +320 -320
  51. floodmodeller_api/test/test_data/EX1.gxy +107 -107
  52. floodmodeller_api/test/test_data/EX17.DAT +421 -422
  53. floodmodeller_api/test/test_data/EX17.ext +213 -213
  54. floodmodeller_api/test/test_data/EX17.feb +422 -422
  55. floodmodeller_api/test/test_data/EX18.DAT +375 -375
  56. floodmodeller_api/test/test_data/EX18_DAT_expected.json +3876 -0
  57. floodmodeller_api/test/test_data/EX2.DAT +302 -302
  58. floodmodeller_api/test/test_data/EX3.DAT +926 -926
  59. floodmodeller_api/test/test_data/EX3_DAT_expected.json +16235 -0
  60. floodmodeller_api/test/test_data/EX3_IEF_expected.json +61 -0
  61. floodmodeller_api/test/test_data/EX6.DAT +2084 -2084
  62. floodmodeller_api/test/test_data/EX6.ext +532 -532
  63. floodmodeller_api/test/test_data/EX6.feb +2084 -2084
  64. floodmodeller_api/test/test_data/EX6_DAT_expected.json +31647 -0
  65. floodmodeller_api/test/test_data/Event Data Example.DAT +336 -336
  66. floodmodeller_api/test/test_data/Event Data Example.ext +107 -107
  67. floodmodeller_api/test/test_data/Event Data Example.feb +336 -336
  68. floodmodeller_api/test/test_data/Linked1D2D.xml +52 -52
  69. floodmodeller_api/test/test_data/Linked1D2DFAST.xml +53 -53
  70. floodmodeller_api/test/test_data/Linked1D2DFAST_dy.xml +48 -48
  71. floodmodeller_api/test/test_data/Linked1D2D_xml_expected.json +313 -0
  72. floodmodeller_api/test/test_data/blockage.dat +50 -50
  73. floodmodeller_api/test/test_data/blockage.ext +45 -45
  74. floodmodeller_api/test/test_data/blockage.feb +9 -9
  75. floodmodeller_api/test/test_data/blockage.gxy +71 -71
  76. floodmodeller_api/test/test_data/conveyance_test.dat +165 -0
  77. floodmodeller_api/test/test_data/conveyance_test.feb +116 -0
  78. floodmodeller_api/test/test_data/conveyance_test.gxy +85 -0
  79. floodmodeller_api/test/test_data/defaultUnits.dat +127 -127
  80. floodmodeller_api/test/test_data/defaultUnits.ext +45 -45
  81. floodmodeller_api/test/test_data/defaultUnits.feb +9 -9
  82. floodmodeller_api/test/test_data/defaultUnits.fmpx +58 -58
  83. floodmodeller_api/test/test_data/defaultUnits.gxy +85 -85
  84. floodmodeller_api/test/test_data/ex3.ief +20 -20
  85. floodmodeller_api/test/test_data/ex3.lf1 +2800 -2800
  86. floodmodeller_api/test/test_data/ex4.DAT +1374 -1374
  87. floodmodeller_api/test/test_data/ex4_changed.DAT +1374 -1374
  88. floodmodeller_api/test/test_data/example1.inp +329 -329
  89. floodmodeller_api/test/test_data/example2.inp +158 -158
  90. floodmodeller_api/test/test_data/example3.inp +297 -297
  91. floodmodeller_api/test/test_data/example4.inp +388 -388
  92. floodmodeller_api/test/test_data/example5.inp +147 -147
  93. floodmodeller_api/test/test_data/example6.inp +154 -154
  94. floodmodeller_api/test/test_data/expected_conveyance.csv +60 -0
  95. floodmodeller_api/test/test_data/jump.dat +176 -176
  96. floodmodeller_api/test/test_data/network.dat +1374 -1374
  97. floodmodeller_api/test/test_data/network.ext +45 -45
  98. floodmodeller_api/test/test_data/network.exy +1 -1
  99. floodmodeller_api/test/test_data/network.feb +45 -45
  100. floodmodeller_api/test/test_data/network.ied +45 -45
  101. floodmodeller_api/test/test_data/network.ief +20 -20
  102. floodmodeller_api/test/test_data/network.inp +147 -147
  103. floodmodeller_api/test/test_data/network.pxy +57 -57
  104. floodmodeller_api/test/test_data/network.zzd +122 -122
  105. floodmodeller_api/test/test_data/network_dat_expected.json +21837 -0
  106. floodmodeller_api/test/test_data/network_from_tabularCSV.csv +87 -87
  107. floodmodeller_api/test/test_data/network_ied_expected.json +287 -0
  108. floodmodeller_api/test/test_data/rnweir.dat +9 -9
  109. floodmodeller_api/test/test_data/rnweir.ext +45 -45
  110. floodmodeller_api/test/test_data/rnweir.feb +9 -9
  111. floodmodeller_api/test/test_data/rnweir.gxy +45 -45
  112. floodmodeller_api/test/test_data/rnweir_default.dat +74 -74
  113. floodmodeller_api/test/test_data/rnweir_default.ext +45 -45
  114. floodmodeller_api/test/test_data/rnweir_default.feb +9 -9
  115. floodmodeller_api/test/test_data/rnweir_default.fmpx +58 -58
  116. floodmodeller_api/test/test_data/rnweir_default.gxy +53 -53
  117. floodmodeller_api/test/test_data/unit checks.dat +16 -16
  118. floodmodeller_api/test/test_ied.py +29 -29
  119. floodmodeller_api/test/test_ief.py +136 -24
  120. floodmodeller_api/test/test_inp.py +47 -48
  121. floodmodeller_api/test/test_json.py +114 -0
  122. floodmodeller_api/test/test_logs_lf.py +102 -51
  123. floodmodeller_api/test/test_tool.py +165 -152
  124. floodmodeller_api/test/test_toolbox_structure_log.py +234 -239
  125. floodmodeller_api/test/test_xml2d.py +151 -156
  126. floodmodeller_api/test/test_zzn.py +36 -34
  127. floodmodeller_api/to_from_json.py +230 -0
  128. floodmodeller_api/tool.py +332 -329
  129. floodmodeller_api/toolbox/__init__.py +5 -5
  130. floodmodeller_api/toolbox/example_tool.py +45 -45
  131. floodmodeller_api/toolbox/model_build/__init__.py +2 -2
  132. floodmodeller_api/toolbox/model_build/add_siltation_definition.py +100 -98
  133. floodmodeller_api/toolbox/model_build/structure_log/__init__.py +1 -1
  134. floodmodeller_api/toolbox/model_build/structure_log/structure_log.py +287 -289
  135. floodmodeller_api/toolbox/model_build/structure_log_definition.py +76 -76
  136. floodmodeller_api/units/__init__.py +10 -10
  137. floodmodeller_api/units/_base.py +214 -212
  138. floodmodeller_api/units/boundaries.py +467 -467
  139. floodmodeller_api/units/comment.py +52 -55
  140. floodmodeller_api/units/conduits.py +382 -402
  141. floodmodeller_api/units/conveyance.py +301 -0
  142. floodmodeller_api/units/helpers.py +123 -131
  143. floodmodeller_api/units/iic.py +107 -101
  144. floodmodeller_api/units/losses.py +305 -306
  145. floodmodeller_api/units/sections.py +465 -446
  146. floodmodeller_api/units/structures.py +1690 -1683
  147. floodmodeller_api/units/units.py +93 -104
  148. floodmodeller_api/units/unsupported.py +44 -44
  149. floodmodeller_api/units/variables.py +87 -89
  150. floodmodeller_api/urban1d/__init__.py +11 -11
  151. floodmodeller_api/urban1d/_base.py +188 -179
  152. floodmodeller_api/urban1d/conduits.py +93 -85
  153. floodmodeller_api/urban1d/general_parameters.py +58 -58
  154. floodmodeller_api/urban1d/junctions.py +81 -79
  155. floodmodeller_api/urban1d/losses.py +81 -74
  156. floodmodeller_api/urban1d/outfalls.py +114 -110
  157. floodmodeller_api/urban1d/raingauges.py +111 -111
  158. floodmodeller_api/urban1d/subsections.py +92 -98
  159. floodmodeller_api/urban1d/xsections.py +147 -144
  160. floodmodeller_api/util.py +119 -21
  161. floodmodeller_api/validation/parameters.py +660 -660
  162. floodmodeller_api/validation/urban_parameters.py +388 -404
  163. floodmodeller_api/validation/validation.py +110 -108
  164. floodmodeller_api/version.py +1 -1
  165. floodmodeller_api/xml2d.py +632 -673
  166. floodmodeller_api/xml2d_template.py +37 -37
  167. floodmodeller_api/zzn.py +414 -363
  168. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/LICENSE.txt +13 -13
  169. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/METADATA +85 -82
  170. floodmodeller_api-0.4.4.dist-info/RECORD +185 -0
  171. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/WHEEL +1 -1
  172. floodmodeller_api/libifcoremd.dll +0 -0
  173. floodmodeller_api/test/test_data/EX3.bmp +0 -0
  174. floodmodeller_api/test/test_data/test_output.csv +0 -87
  175. floodmodeller_api/zzn_read.dll +0 -0
  176. floodmodeller_api-0.4.2.post1.dist-info/RECORD +0 -164
  177. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/entry_points.txt +0 -0
  178. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.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,169 @@
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 .util import FloodModellerAPIError, handle_exception
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)
43
+ # * Add check or fix for path lengths greater than DOS standard length of 260 characters
44
+
45
+ if not self._filepath.suffix.lower() == self._suffix:
46
+ raise TypeError(
47
+ f"Given filepath does not point to a {self._filetype} file. Please point to the full path for a {self._filetype} file",
48
+ )
49
+ if not self._filepath.exists():
50
+ raise FileNotFoundError(
51
+ f"{self._filetype} file does not exist! If you are wanting to create a new {self._filetype}, initiate the class without a given "
52
+ f"filepath to create a new blank {self._filetype} or point the filepath of an existing {self._filetype} to use as a template, "
53
+ f"then use the .save() method to save to a new filepath",
54
+ )
55
+ # If the file is not a ZZN file, then perform a backup
56
+ # This performs a conditional back up, only copying the file if an equivalent copy doesn't already exist
57
+ if self._filetype != "ZZN":
58
+ file = File(path=self._filepath)
59
+ file.backup()
60
+ # Add the file object as a property to expose the restore method
61
+ self.file = file
62
+
63
+ def __repr__(self):
64
+ filepath = "<in_memory>" if not hasattr(self, "_filepath") else self._filepath
65
+ return f"<floodmodeller_api Class: {self._filetype}(filepath={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
+ @handle_exception(when="compare")
103
+ def _diff(self, other, force_print=False):
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
+
122
+ def _get_diff(self, other):
123
+ return self.__eq__(other, return_diff=True) # pylint: disable=unnecessary-dunder-call
124
+
125
+ def _handle_exception(self, err, when) -> NoReturn:
126
+ raise FloodModellerAPIError(err, when, self._filetype, self._filepath) from err
127
+
128
+ def __eq__(self, other, return_diff=False):
129
+ result = True
130
+ diff = []
131
+ try:
132
+ for key, item in self.__dict__.items():
133
+ try:
134
+ if key in (
135
+ "_filepath",
136
+ "_raw_data",
137
+ "_gxy_filepath",
138
+ "_gxy_data",
139
+ "_xmltree",
140
+ "_xsd",
141
+ "_xsdschema",
142
+ "file",
143
+ "_log_path",
144
+ ):
145
+ continue
146
+ _result, diff = check_item_with_dataframe_equal(
147
+ item,
148
+ other.__dict__[key],
149
+ name=f"{self._filetype}->{key}",
150
+ diff=diff,
151
+ special_types=(Unit, IIC, UrbanUnit, UrbanSubsection),
152
+ )
153
+ if not _result:
154
+ result = False
155
+ except KeyError as ke:
156
+ result = False
157
+ diff.append(
158
+ (
159
+ f"{self._filetype}->{key}",
160
+ f"Key: '{ke.args[0]}' missing in other",
161
+ ),
162
+ )
163
+ continue
164
+
165
+ except Exception as e:
166
+ result = False
167
+ diff.append((f"{self._filetype}->{key}", f"Error encountered: {e.args[0]}"))
168
+
169
+ return (result, diff) if return_diff else result