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,24 +1,136 @@
1
- import os
2
-
3
- import pytest
4
-
5
- from floodmodeller_api import IEF
6
-
7
-
8
- @pytest.fixture
9
- def ief_fp(test_workspace):
10
- ief_fp = os.path.join(test_workspace, "network.ief")
11
- return ief_fp
12
-
13
-
14
- @pytest.fixture
15
- def data_before(ief_fp):
16
- with open(ief_fp, "r") as ief_file:
17
- data_before = ief_file.read()
18
- return data_before
19
-
20
-
21
- def test_ief_open_does_not_change_data(ief_fp, data_before):
22
- """IEF: Test str representation equal to ief file with no changes"""
23
- ief = IEF(ief_fp)
24
- assert ief._write() == data_before
1
+ from pathlib import Path
2
+ from unittest.mock import call, patch
3
+
4
+ import pytest
5
+
6
+ from floodmodeller_api import IEF
7
+ from floodmodeller_api.util import FloodModellerAPIError
8
+
9
+
10
+ @pytest.fixture()
11
+ def ief_fp(test_workspace: str) -> Path:
12
+ return Path(test_workspace, "network.ief")
13
+
14
+
15
+ @pytest.fixture()
16
+ def ief(ief_fp: Path) -> IEF:
17
+ return IEF(ief_fp)
18
+
19
+
20
+ @pytest.fixture()
21
+ def data_before(ief_fp: Path) -> str:
22
+ with open(ief_fp) as ief_file:
23
+ return ief_file.read()
24
+
25
+
26
+ @pytest.fixture()
27
+ def exe_bin(tmpdir) -> Path:
28
+ for exe in ["ISISf32.exe", "ISISf32_DoubleP.exe"]:
29
+ exe_path = Path(tmpdir, exe)
30
+ exe_path.touch()
31
+ return Path(tmpdir)
32
+
33
+
34
+ @pytest.fixture()
35
+ def p_open():
36
+ with patch("floodmodeller_api.ief.Popen") as p_open:
37
+ yield p_open
38
+
39
+
40
+ @pytest.fixture()
41
+ def sleep():
42
+ with patch("floodmodeller_api.ief.time.sleep") as sleep:
43
+ yield sleep
44
+
45
+
46
+ def test_ief_open_does_not_change_data(ief: IEF, data_before: str):
47
+ """IEF: Test str representation equal to ief file with no changes"""
48
+ assert ief._write() == data_before
49
+
50
+
51
+ @pytest.mark.usefixtures("log_timeout")
52
+ @pytest.mark.parametrize(
53
+ ("precision", "method", "amend", "exe"),
54
+ [
55
+ ("DEFAULT", "WAIT", False, "ISISf32.exe"),
56
+ ("DEFAULT", "WAIT", True, "ISISf32_DoubleP.exe"),
57
+ ("SINGLE", "WAIT", False, "ISISf32.exe"),
58
+ ("SINGLE", "WAIT", True, "ISISf32.exe"),
59
+ ("DOUBLE", "WAIT", False, "ISISf32_DoubleP.exe"),
60
+ ("DOUBLE", "WAIT", True, "ISISf32_DoubleP.exe"),
61
+ ("DEFAULT", "RETURN_PROCESS", False, "ISISf32.exe"),
62
+ ("DEFAULT", "RETURN_PROCESS", True, "ISISf32_DoubleP.exe"),
63
+ ("SINGLE", "RETURN_PROCESS", False, "ISISf32.exe"),
64
+ ("SINGLE", "RETURN_PROCESS", True, "ISISf32.exe"),
65
+ ("DOUBLE", "RETURN_PROCESS", False, "ISISf32_DoubleP.exe"),
66
+ ("DOUBLE", "RETURN_PROCESS", True, "ISISf32_DoubleP.exe"),
67
+ ],
68
+ )
69
+ def test_simulate(
70
+ ief: IEF,
71
+ ief_fp: Path,
72
+ exe_bin: Path,
73
+ p_open,
74
+ sleep,
75
+ precision: str,
76
+ method: str,
77
+ amend: bool,
78
+ exe: str,
79
+ ):
80
+ if amend:
81
+ ief.launchdoubleprecisionversion = 1
82
+
83
+ p_open.return_value.poll.side_effect = [None, None, 0]
84
+
85
+ exe_path = Path(exe_bin, exe)
86
+ ief.simulate(method=method, precision=precision, enginespath=str(exe_bin))
87
+
88
+ p_open.assert_called_once_with(f'"{exe_path}" -sd "{ief_fp}"', cwd=str(ief_fp.parent))
89
+
90
+ if method == "WAIT":
91
+ assert p_open.return_value.poll.call_count == 3
92
+ assert sleep.call_args_list[-3:] == [call(0.1), call(1), call(1)]
93
+
94
+
95
+ def test_simulate_error_without_bin(tmpdir, ief: IEF):
96
+ msg = (
97
+ r"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
98
+ r"\nAPI Error: Problem encountered when trying to simulate IEF file .*\."
99
+ r"\n"
100
+ r"\nDetails: .*-floodmodeller_api/ief\.py-\d+"
101
+ r"\nMsg: Flood Modeller non-default engine path not found! .*"
102
+ r"\n"
103
+ r"\nFor additional support, go to: https://github\.com/People-Places-Solutions/floodmodeller-api"
104
+ )
105
+ with pytest.raises(FloodModellerAPIError, match=msg):
106
+ ief.simulate(enginespath=str(Path(tmpdir, "bin")))
107
+
108
+
109
+ def test_simulate_error_without_exe(tmpdir, ief: IEF):
110
+ msg = (
111
+ r"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
112
+ r"\nAPI Error: Problem encountered when trying to simulate IEF file .*\."
113
+ r"\n"
114
+ r"\nDetails: .*-floodmodeller_api/ief\.py-\d+"
115
+ r"\nMsg: Flood Modeller engine not found! Expected location: .*"
116
+ r"\n"
117
+ r"\nFor additional support, go to: https://github\.com/People-Places-Solutions/floodmodeller-api"
118
+ )
119
+ with pytest.raises(FloodModellerAPIError, match=msg):
120
+ ief.simulate(enginespath=str(tmpdir))
121
+
122
+
123
+ def test_simulate_error_without_save():
124
+ ief = IEF()
125
+ ief._filepath = None
126
+ msg = (
127
+ r"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
128
+ r"\nAPI Error: Problem encountered when trying to simulate IEF file .*\."
129
+ r"\n"
130
+ r"\nDetails: .*-floodmodeller_api/ief\.py-\d+"
131
+ r"\nMsg: IEF must be saved to a specific filepath before simulate\(\) can be called\."
132
+ r"\n"
133
+ r"\nFor additional support, go to: https://github\.com/People-Places-Solutions/floodmodeller-api"
134
+ )
135
+ with pytest.raises(FloodModellerAPIError, match=msg):
136
+ ief.simulate()
@@ -1,48 +1,47 @@
1
- import os
2
- from pathlib import Path
3
-
4
- import pytest
5
-
6
- from floodmodeller_api import INP
7
-
8
-
9
- @pytest.fixture
10
- def inp_fp(test_workspace):
11
- return os.path.join(test_workspace, "network.inp")
12
-
13
-
14
- @pytest.fixture
15
- def data_before(inp_fp):
16
- return INP(inp_fp)._write()
17
-
18
-
19
- def test_inp_open_does_not_change_data(inp_fp, data_before):
20
- """INP: Test str representation equal to inp file with no changes"""
21
- inp = INP(inp_fp)
22
- assert inp._write() == data_before
23
-
24
-
25
- def test_section_name_and_snow_catch_factor_changes(inp_fp, data_before):
26
- """INP: Test changing and reverting section name and snow catch factor makes no changes"""
27
- inp = INP(inp_fp)
28
- prev_name = inp.raingauges["1"].name
29
- prev_scf = inp.raingauges["1"].snow_catch_factor
30
- inp.raingauges["1"].name = "check"
31
- inp.raingauges["1"].snow_catch_factor = 1.5
32
- assert inp._write() != data_before
33
-
34
- inp.raingauges["check"].name = prev_name
35
- inp.raingauges["check"].snow_catch_factor = prev_scf
36
-
37
- assert inp._write() == data_before
38
-
39
-
40
- def test_all_inp_files_in_folder_have_same_output(test_workspace):
41
- """INP: Check all '.inp' files in folder by reading the _write() output into a new INP instance and checking it stays the same."""
42
- for inpfile in Path(test_workspace).glob("*.inp"):
43
- inp = INP(inpfile)
44
- first_output = inp._write()
45
- inp.save("__temp.inp")
46
- second_output = INP("__temp.inp")._write()
47
- assert first_output == second_output
48
- os.remove("__temp.inp")
1
+ from pathlib import Path
2
+
3
+ import pytest
4
+
5
+ from floodmodeller_api import INP
6
+
7
+
8
+ @pytest.fixture
9
+ def inp_fp(test_workspace):
10
+ return Path(test_workspace, "network.inp")
11
+
12
+
13
+ @pytest.fixture
14
+ def data_before(inp_fp):
15
+ return INP(inp_fp)._write()
16
+
17
+
18
+ def test_inp_open_does_not_change_data(inp_fp, data_before):
19
+ """INP: Test str representation equal to inp file with no changes"""
20
+ inp = INP(inp_fp)
21
+ assert inp._write() == data_before
22
+
23
+
24
+ def test_section_name_and_snow_catch_factor_changes(inp_fp, data_before):
25
+ """INP: Test changing and reverting section name and snow catch factor makes no changes"""
26
+ inp = INP(inp_fp)
27
+ prev_name = inp.raingauges["1"].name
28
+ prev_scf = inp.raingauges["1"].snow_catch_factor
29
+ inp.raingauges["1"].name = "check"
30
+ inp.raingauges["1"].snow_catch_factor = 1.5
31
+ assert inp._write() != data_before
32
+
33
+ inp.raingauges["check"].name = prev_name
34
+ inp.raingauges["check"].snow_catch_factor = prev_scf
35
+
36
+ assert inp._write() == data_before
37
+
38
+
39
+ def test_all_inp_files_in_folder_have_same_output(test_workspace, tmpdir):
40
+ """INP: Check all '.inp' files in folder by reading the _write() output into a new INP instance and checking it stays the same."""
41
+ for inpfile in Path(test_workspace).glob("*.inp"):
42
+ inp = INP(inpfile)
43
+ first_output = inp._write()
44
+ new_path = Path(tmpdir) / "tmp.inp"
45
+ inp.save(new_path)
46
+ second_output = INP(new_path)._write()
47
+ assert first_output == second_output
@@ -0,0 +1,114 @@
1
+ import json
2
+ import os
3
+ from pathlib import Path
4
+
5
+ import pytest
6
+
7
+ from floodmodeller_api import DAT, IED, IEF, INP, XML2D
8
+ from floodmodeller_api.to_from_json import is_jsonable
9
+ from floodmodeller_api.util import read_file
10
+
11
+
12
+ def create_expected_json_files():
13
+ """Helper function to recreate all the expected JSON files if needed at any point due to updates
14
+ to the to_json code"""
15
+
16
+ test_workspace = Path(os.path.dirname(__file__), "test_data")
17
+ for file in [
18
+ "network.dat",
19
+ "network.ied",
20
+ "EX3.DAT",
21
+ "EX6.DAT",
22
+ "EX18.DAT",
23
+ "EX3.IEF",
24
+ "Domain1_Q.xml",
25
+ "Linked1D2D.xml",
26
+ ]:
27
+ file = Path(test_workspace, file)
28
+ obj = read_file(file)
29
+ new_file = file.with_name(f"{file.name.replace('.', '_')}_expected").with_suffix(".json")
30
+ with open(new_file, "w") as json_file:
31
+ json_file.write(obj.to_json())
32
+
33
+
34
+ @pytest.fixture
35
+ def dat_obj(test_workspace):
36
+ """JSON: To create the dat object for the tests"""
37
+ return DAT(Path(test_workspace, "EX18.DAT"))
38
+
39
+
40
+ @pytest.fixture
41
+ def json_expected(test_workspace):
42
+ """JSON: expected after passing to_json method"""
43
+ return Path(test_workspace, "EX18_DAT_expected.json")
44
+
45
+
46
+ def test_dat_json(dat_obj):
47
+ """JSON: To test if to_json runs without failing"""
48
+ assert dat_obj.to_json()
49
+
50
+
51
+ @pytest.fixture
52
+ def parameterised_objs_and_expected(test_workspace):
53
+ """JSON: expected after passing to_json method"""
54
+ return [
55
+ (DAT(test_workspace / "EX18.DAT"), test_workspace / "EX18_DAT_expected.json"),
56
+ (DAT(test_workspace / "network.dat"), test_workspace / "network_dat_expected.json"),
57
+ (IED(test_workspace / "network.ied"), test_workspace / "network_ied_expected.json"),
58
+ (DAT(test_workspace / "EX3.DAT"), test_workspace / "EX3_DAT_expected.json"),
59
+ (DAT(test_workspace / "EX6.DAT"), test_workspace / "EX6_DAT_expected.json"),
60
+ (IEF(test_workspace / "ex3.ief"), test_workspace / "EX3_IEF_expected.json"),
61
+ (XML2D(test_workspace / "Domain1_Q.xml"), test_workspace / "Domain1_Q_xml_expected.json"),
62
+ (XML2D(test_workspace / "Linked1D2D.xml"), test_workspace / "Linked1D2D_xml_expected.json"),
63
+ ]
64
+
65
+
66
+ def test_to_json_matches_expected(parameterised_objs_and_expected):
67
+ """JSON: To test if the json object produced in to_json is identical to the expected json file"""
68
+ for obj, json_expected in parameterised_objs_and_expected:
69
+ # First, to create and handle the json (str) object
70
+ # Fetch "Object Attributes" key only as we don't need to compare API versions
71
+ json_dict_from_obj = json.loads(obj.to_json())["Object Attributes"]
72
+
73
+ # Second, to handle the json file ..._expected.json which must be the same as the object created above.
74
+ json_dict_from_file = json.load(open(json_expected))["Object Attributes"] # noqa: SIM115
75
+
76
+ # keys to ignore when testing for equivalence
77
+ keys_to_remove = ["_filepath", "file", "_log_path"]
78
+ for key in keys_to_remove:
79
+ json_dict_from_obj.pop(key, None)
80
+ json_dict_from_file.pop(key, None)
81
+
82
+ assert json_dict_from_obj == json_dict_from_file
83
+
84
+
85
+ @pytest.mark.parametrize(
86
+ "api_class,file_extension_glob",
87
+ [
88
+ (DAT, "*.dat"),
89
+ (IED, "*.ied"),
90
+ (XML2D, "*.xml"),
91
+ (IEF, "*.ief"),
92
+ (INP, "*.inp"),
93
+ ],
94
+ )
95
+ def test_obj_reproduces_from_json_for_all_test_api_files(
96
+ test_workspace,
97
+ api_class,
98
+ file_extension_glob,
99
+ ):
100
+ """JSON: To test the from_json function, It should produce the same dat file from a json file"""
101
+ for file in Path(test_workspace).glob(file_extension_glob):
102
+ assert api_class(file) == api_class.from_json(api_class(file).to_json())
103
+
104
+
105
+ def test_is_jsonable_with_jsonable_object():
106
+ assert is_jsonable({"a": 1, "b": 2})
107
+
108
+
109
+ def test_is_jsonable_with_non_jsonable_object():
110
+ class NonJsonable:
111
+ def __init__(self):
112
+ pass
113
+
114
+ assert not is_jsonable(NonJsonable())
@@ -1,51 +1,102 @@
1
- import os
2
-
3
- import pandas as pd
4
- import pytest
5
-
6
- from floodmodeller_api import IEF, LF1
7
-
8
-
9
- @pytest.fixture
10
- def lf1_fp(test_workspace):
11
- return os.path.join(test_workspace, "ex3.lf1")
12
-
13
-
14
- def test_lf1_info_dict(lf1_fp):
15
- """LF1: Check info dictionary"""
16
- lf1 = LF1(lf1_fp)
17
- assert lf1.info["version"] == "5.0.0.7752"
18
- assert lf1.info["max_system_volume"] == 270549
19
- assert lf1.info["mass_balance_error"] == -0.03
20
- assert lf1.info["progress"] == 100
21
-
22
-
23
- def test_lf1_report_progress(lf1_fp):
24
- """LF1: Check report_progress()"""
25
- lf1 = LF1(lf1_fp)
26
- assert lf1.report_progress() == 100
27
-
28
-
29
- def test_lf1_to_dataframe(lf1_fp):
30
- """LF1: Check to_dataframe()"""
31
- lf1 = LF1(lf1_fp)
32
- df = lf1.to_dataframe()
33
- assert df.iloc[0, 3] == 6
34
- assert df.iloc[-1, -1] == 21.06
35
- assert df.iloc[4, 0] == -0.07
36
-
37
-
38
- def test_lf1_from_ief(lf1_fp, test_workspace):
39
- """LF1: Check IEF.get_lf1()"""
40
- lf1 = LF1(lf1_fp)
41
-
42
- ief_fp = os.path.join(test_workspace, "ex3.ief")
43
- ief = IEF(ief_fp)
44
- lf1_from_ief = ief.get_log()
45
-
46
- assert lf1._filepath == lf1_from_ief._filepath
47
- assert lf1.info == lf1_from_ief.info
48
- try:
49
- pd.testing.assert_frame_equal(lf1.to_dataframe(), lf1_from_ief.to_dataframe())
50
- except Exception:
51
- pytest.fail("Dataframes not equal")
1
+ from pathlib import Path
2
+ from unittest.mock import MagicMock, patch
3
+
4
+ import pandas as pd
5
+ import pytest
6
+ from freezegun import freeze_time
7
+
8
+ from floodmodeller_api import IEF, LF1
9
+ from floodmodeller_api.logs import create_lf
10
+
11
+
12
+ @pytest.fixture
13
+ def lf1_fp(test_workspace):
14
+ return Path(test_workspace, "ex3.lf1")
15
+
16
+
17
+ def test_lf1_info_dict(lf1_fp):
18
+ """LF1: Check info dictionary"""
19
+ lf1 = LF1(lf1_fp)
20
+ assert lf1.info["version"] == "5.0.0.7752"
21
+ assert lf1.info["max_system_volume"] == 270549
22
+ assert lf1.info["mass_balance_error"] == -0.03
23
+ assert lf1.info["progress"] == 100
24
+
25
+
26
+ def test_lf1_report_progress(lf1_fp):
27
+ """LF1: Check report_progress()"""
28
+ lf1 = LF1(lf1_fp)
29
+ assert lf1.report_progress() == 100
30
+
31
+
32
+ def test_lf1_to_dataframe(lf1_fp):
33
+ """LF1: Check to_dataframe()"""
34
+ lf1 = LF1(lf1_fp)
35
+ df = lf1.to_dataframe()
36
+ assert df.loc[df.index[0], "iter"] == 6
37
+ assert df.loc[df.index[-1], "outflow"] == 21.06
38
+ assert df.loc[df.index[4], "mass_error"] == -0.07
39
+
40
+
41
+ def test_lf1_from_ief(lf1_fp, test_workspace):
42
+ """LF1: Check IEF.get_lf1()"""
43
+ lf1 = LF1(lf1_fp)
44
+
45
+ ief_fp = Path(test_workspace, "ex3.ief")
46
+ ief = IEF(ief_fp)
47
+ lf1_from_ief = ief.get_log()
48
+
49
+ assert lf1._filepath == lf1_from_ief._filepath
50
+ assert lf1.info == lf1_from_ief.info
51
+ pd.testing.assert_frame_equal(lf1.to_dataframe(), lf1_from_ief.to_dataframe())
52
+
53
+
54
+ def test_log_file_unsupported(capsys):
55
+ lf = create_lf(None, "lf3")
56
+
57
+ assert lf is None
58
+ assert (
59
+ capsys.readouterr().out
60
+ == "No progress bar as log file must have suffix lf1 or lf2. Simulation will continue as usual.\n"
61
+ )
62
+
63
+
64
+ @pytest.mark.usefixtures("log_timeout")
65
+ def test_log_file_timeout(capsys):
66
+ lf_filepath = MagicMock()
67
+ lf_filepath.is_file.return_value = False
68
+ lf = create_lf(lf_filepath, "lf1")
69
+
70
+ assert lf is None
71
+ assert (
72
+ capsys.readouterr().out
73
+ == "No progress bar as log file is expected but not detected. Simulation will continue as usual.\n"
74
+ )
75
+
76
+
77
+ @pytest.mark.usefixtures("log_timeout")
78
+ @freeze_time("1970-01-01 00:00:00", tick=True)
79
+ def test_log_file_from_old_run(capsys):
80
+ lf_filepath = MagicMock()
81
+ lf_filepath.is_file.return_value = True
82
+ lf_filepath.stat.return_value.st_mtime = -10
83
+ lf = create_lf(lf_filepath, "lf1")
84
+
85
+ assert lf is None
86
+ assert (
87
+ capsys.readouterr().out
88
+ == "No progress bar as log file is from previous run. Simulation will continue as usual.\n"
89
+ )
90
+
91
+
92
+ @pytest.mark.usefixtures("log_timeout")
93
+ @freeze_time("1970-01-01 00:00:00", tick=True)
94
+ def test_log_file_found():
95
+ lf_filepath = MagicMock()
96
+ lf_filepath.is_file.return_value = True
97
+ lf_filepath.stat.return_value.st_mtime = -1
98
+ with patch("floodmodeller_api.logs.lf.LF1") as lf1:
99
+ lf = create_lf(lf_filepath, "lf1")
100
+
101
+ assert lf is not None
102
+ lf1.assert_called_once_with(lf_filepath)