floodmodeller-api 0.4.2__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 (178) 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 -838
  5. floodmodeller_api/diff.py +136 -119
  6. floodmodeller_api/ied.py +307 -311
  7. floodmodeller_api/ief.py +647 -646
  8. floodmodeller_api/ief_flags.py +253 -253
  9. floodmodeller_api/inp.py +266 -268
  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 -314
  21. floodmodeller_api/logs/lf_helpers.py +354 -346
  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 -154
  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 -330
  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 -94
  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 -72
  131. floodmodeller_api/units/__init__.py +10 -10
  132. floodmodeller_api/units/_base.py +214 -209
  133. floodmodeller_api/units/boundaries.py +467 -469
  134. floodmodeller_api/units/comment.py +52 -55
  135. floodmodeller_api/units/conduits.py +382 -403
  136. floodmodeller_api/units/helpers.py +123 -132
  137. floodmodeller_api/units/iic.py +107 -101
  138. floodmodeller_api/units/losses.py +305 -308
  139. floodmodeller_api/units/sections.py +444 -445
  140. floodmodeller_api/units/structures.py +1690 -1684
  141. floodmodeller_api/units/units.py +93 -102
  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 -177
  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 -107
  151. floodmodeller_api/urban1d/raingauges.py +111 -108
  152. floodmodeller_api/urban1d/subsections.py +92 -93
  153. floodmodeller_api/urban1d/xsections.py +147 -141
  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 -112
  158. floodmodeller_api/version.py +1 -1
  159. floodmodeller_api/xml2d.py +688 -684
  160. floodmodeller_api/xml2d_template.py +37 -37
  161. floodmodeller_api/zzn.py +387 -365
  162. {floodmodeller_api-0.4.2.dist-info → floodmodeller_api-0.4.3.dist-info}/LICENSE.txt +13 -13
  163. {floodmodeller_api-0.4.2.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.dist-info → floodmodeller_api-0.4.3.dist-info}/WHEEL +1 -1
  166. floodmodeller_api-0.4.3.dist-info/entry_points.txt +3 -0
  167. floodmodeller_api/libifcoremd.dll +0 -0
  168. floodmodeller_api/test/test_data/EX3.bmp +0 -0
  169. floodmodeller_api/test/test_data/test_output.csv +0 -87
  170. floodmodeller_api/zzn_read.dll +0 -0
  171. floodmodeller_api-0.4.2.data/scripts/fmapi-add_siltation.bat +0 -2
  172. floodmodeller_api-0.4.2.data/scripts/fmapi-add_siltation.py +0 -3
  173. floodmodeller_api-0.4.2.data/scripts/fmapi-structure_log.bat +0 -2
  174. floodmodeller_api-0.4.2.data/scripts/fmapi-structure_log.py +0 -3
  175. floodmodeller_api-0.4.2.data/scripts/fmapi-toolbox.bat +0 -2
  176. floodmodeller_api-0.4.2.data/scripts/fmapi-toolbox.py +0 -41
  177. floodmodeller_api-0.4.2.dist-info/RECORD +0 -169
  178. {floodmodeller_api-0.4.2.dist-info → floodmodeller_api-0.4.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,84 @@
1
+ from typing import Any
2
+
3
+ from . import DAT, IED, IEF, INP, LF1, LF2, XML2D, ZZN
4
+ from .backup import File
5
+ from .units import (
6
+ BLOCKAGE,
7
+ BRIDGE,
8
+ COMMENT,
9
+ CONDUIT,
10
+ CRUMP,
11
+ CULVERT,
12
+ FLAT_V_WEIR,
13
+ HTBDY,
14
+ IIC,
15
+ INTERPOLATE,
16
+ ORIFICE,
17
+ OUTFALL,
18
+ QHBDY,
19
+ QTBDY,
20
+ REFHBDY,
21
+ REPLICATE,
22
+ RIVER,
23
+ RNWEIR,
24
+ SLUICE,
25
+ SPILL,
26
+ UNSUPPORTED,
27
+ WEIR,
28
+ Variables,
29
+ )
30
+ from .urban1d.conduits import CONDUIT as CONDUIT_URBAN
31
+ from .urban1d.conduits import CONDUITS as CONDUITS_URBAN
32
+ from .urban1d.junctions import JUNCTION, JUNCTIONS
33
+ from .urban1d.losses import LOSS, LOSSES
34
+ from .urban1d.outfalls import OUTFALL as OUTFALL_URBAN
35
+ from .urban1d.outfalls import OUTFALLS as OUTFALLS_URBAN
36
+ from .urban1d.raingauges import RAINGAUGE, RAINGAUGES
37
+ from .urban1d.xsections import XSECTION, XSECTIONS
38
+
39
+ api_class_mapping: dict[str, Any] = {
40
+ "floodmodeller_api.dat.DAT": DAT,
41
+ "floodmodeller_api.ied.IED": IED,
42
+ "floodmodeller_api.ief.IEF": IEF,
43
+ "floodmodeller_api.inp.INP": INP,
44
+ "floodmodeller_api.lf.LF1": LF1,
45
+ "floodmodeller_api.lf.LF2": LF2,
46
+ "floodmodeller_api.xml2d.XML2D": XML2D,
47
+ "floodmodeller_api.zzn.ZZN": ZZN,
48
+ "floodmodeller_api.backup.File": File,
49
+ "floodmodeller_api.urban1d.junctions.JUNCTIONS": JUNCTIONS,
50
+ "floodmodeller_api.urban1d.junctions.JUNCTION": JUNCTION,
51
+ "floodmodeller_api.urban1d.outfalls.OUTFALLS": OUTFALLS_URBAN,
52
+ "floodmodeller_api.urban1d.outfalls.OUTFALL": OUTFALL_URBAN,
53
+ "floodmodeller_api.urban1d.raingauges.RAINGAUGES": RAINGAUGES,
54
+ "floodmodeller_api.urban1d.raingauges.RAINGAUGE": RAINGAUGE,
55
+ "floodmodeller_api.urban1d.conduits.CONDUITS": CONDUITS_URBAN,
56
+ "floodmodeller_api.urban1d.conduits.CONDUIT": CONDUIT_URBAN,
57
+ "floodmodeller_api.urban1d.losses.LOSSES": LOSSES,
58
+ "floodmodeller_api.urban1d.losses.LOSS": LOSS,
59
+ "floodmodeller_api.urban1d.xsections.XSECTIONS": XSECTIONS,
60
+ "floodmodeller_api.urban1d.xsections.XSECTION": XSECTION,
61
+ "floodmodeller_api.units.boundaries.HTBDY": HTBDY,
62
+ "floodmodeller_api.units.boundaries.QHBDY": QHBDY,
63
+ "floodmodeller_api.units.boundaries.QTBDY": QTBDY,
64
+ "floodmodeller_api.units.boundaries.REFHBDY": REFHBDY,
65
+ "floodmodeller_api.units.comment.COMMENT": COMMENT,
66
+ "floodmodeller_api.units.conduits.CONDUIT": CONDUIT,
67
+ "floodmodeller_api.units.iic.IIC": IIC,
68
+ "floodmodeller_api.units.losses.BLOCKAGE": BLOCKAGE,
69
+ "floodmodeller_api.units.losses.CULVERT": CULVERT,
70
+ "floodmodeller_api.units.sections.INTERPOLATE": INTERPOLATE,
71
+ "floodmodeller_api.units.sections.REPLICATE": REPLICATE,
72
+ "floodmodeller_api.units.sections.RIVER": RIVER,
73
+ "floodmodeller_api.units.structures.BRIDGE": BRIDGE,
74
+ "floodmodeller_api.units.structures.CRUMP": CRUMP,
75
+ "floodmodeller_api.units.structures.FLAT_V_WEIR": FLAT_V_WEIR,
76
+ "floodmodeller_api.units.structures.ORIFICE": ORIFICE,
77
+ "floodmodeller_api.units.structures.OUTFALL": OUTFALL,
78
+ "floodmodeller_api.units.structures.RNWEIR": RNWEIR,
79
+ "floodmodeller_api.units.structures.SLUICE": SLUICE,
80
+ "floodmodeller_api.units.structures.SPILL": SPILL,
81
+ "floodmodeller_api.units.structures.WEIR": WEIR,
82
+ "floodmodeller_api.units.unsupported.UNSUPPORTED": UNSUPPORTED,
83
+ "floodmodeller_api.units.variables.Variables": Variables,
84
+ }
@@ -1,4 +1,4 @@
1
- import os
2
- import sys
3
-
4
- sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
1
+ import os
2
+ import sys
3
+
4
+ sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
@@ -1,8 +1,9 @@
1
- import os
2
-
3
- import pytest
4
-
5
-
6
- @pytest.fixture(scope="session")
7
- def test_workspace():
8
- return os.path.join(os.path.dirname(__file__), "test_data")
1
+ import os
2
+ from pathlib import Path
3
+
4
+ import pytest
5
+
6
+
7
+ @pytest.fixture(scope="session")
8
+ def test_workspace():
9
+ return Path(os.path.dirname(__file__), "test_data")
@@ -1,117 +1,117 @@
1
- from pathlib import Path
2
-
3
- import pandas as pd
4
- import pytest
5
-
6
- from floodmodeller_api.backup import BackupControl, File
7
-
8
-
9
- @pytest.fixture
10
- def backup_control():
11
- # Use a different directory for testing
12
- return BackupControl()
13
-
14
-
15
- @pytest.fixture
16
- def file(test_workspace):
17
- test_file = Path(test_workspace, "EX1.DAT")
18
- file = File(test_file)
19
- # Make a backup to clear in test
20
- file.backup()
21
- return file
22
-
23
-
24
- def test_init_backup(backup_control):
25
- """Has the backup been initialised correctly?"""
26
- assert backup_control.backup_dir.exists()
27
- assert backup_control.backup_csv_path.exists()
28
-
29
-
30
- def test_generate_file_id(file, test_workspace):
31
- """Does this generate a consistent file ID for the same file on disk?"""
32
- # Test that the file ID is the same for the same path input
33
- file1 = File(Path(test_workspace, "EX1.DAT"))
34
- file2 = File(Path(test_workspace, "EX1.DAT"))
35
- assert file1.file_id == file2.file_id
36
-
37
-
38
- def test_clear_backup(file, test_workspace):
39
- """
40
- Does the the clear_backup method work correctly
41
- """
42
- # Clearing backup -------------------
43
- # Load a different file to check it isn't affected by the
44
- other_file = File(Path(test_workspace, "EX3.DAT"))
45
- # Assert there is a backup for the other file
46
- other_file.backup()
47
- # Clear the backups for the file to test backup functionality
48
- file.clear_backup()
49
- # Assert that clearing the backup has worked - there aren't any backups for the file
50
- assert len(file.list_backups()) == 0
51
- # And that clearing it hasn't affected backups for the other file
52
- assert len(other_file.list_backups()) > 0
53
-
54
-
55
- def test_backup_locations(file):
56
- """
57
- Does it make a backup in the right place?
58
- """
59
- # Making a backup --------------------
60
- file.clear_backup()
61
- # make a backup and check if file exists
62
- file.backup()
63
- backup_file_path = Path(file.backup_dir, file.backup_filename)
64
- assert backup_file_path.exists()
65
- # check if contents of backup file match the original file
66
- with open(backup_file_path, "r") as f1, open(file.path, "r") as f2:
67
- assert f1.read() == f2.read()
68
-
69
-
70
- def test_no_duplicate_backup(file, test_workspace):
71
- """The backup method should only backup if the file has changed"""
72
- # Don't Make Duplicate -------------------
73
- # Check that the file isn't backed up again if it hasn't changed
74
- the_same_file = File(Path(test_workspace, "EX1.DAT"))
75
- # Append something to the dttm string to ensure the filename is different to the previous backup
76
- # If the two File objects are created in the same second then then will have identical file names
77
- # The function should check for equivalence between file contents.
78
- the_same_file.dttm_str = the_same_file.dttm_str + "_1"
79
- # Generate a new file name
80
- the_same_file._generate_file_name()
81
- # Attempt a backup
82
- the_same_file.backup()
83
- # Check that the file hasn't been created
84
- duplicate_backup_path = Path(the_same_file.backup_dir, the_same_file.backup_filename)
85
- assert not duplicate_backup_path.exists()
86
-
87
-
88
- def test_backup_logs(file):
89
- """Are backups being logged in the CSV?"""
90
- # Clear the backup
91
- file.clear_backup()
92
- # There shouldn't be any edits in the csv
93
- backup_logs = pd.read_csv(file.backup_csv_path)
94
- backup_count = backup_logs[
95
- (backup_logs.file_id == file.file_id) & (backup_logs.dttm == file.dttm_str)
96
- ].shape[0]
97
- assert backup_count == 0
98
- # Make a backup and assert it is in the CSV
99
- file.backup()
100
- # Check edits to the backup CSV
101
- # Check a row has been added to the csv for the file & version
102
- backup_logs = pd.read_csv(file.backup_csv_path)
103
- backup_count = backup_logs[
104
- (backup_logs.file_id == file.file_id) & (backup_logs.dttm == file.dttm_str)
105
- ].shape[0]
106
- assert backup_count == 1
107
-
108
-
109
- def test_list_backups(file):
110
- """Does the list backups method work correctly?"""
111
- # First clear any backups that exist
112
- file.clear_backup()
113
- # make a backup and check if it appears in the backup list
114
- file.backup()
115
- backups = file.list_backups()
116
- expected_backup = Path(file.backup_dir, file.backup_filename)
117
- assert expected_backup in [backup.path for backup in backups]
1
+ from pathlib import Path
2
+
3
+ import pandas as pd
4
+ import pytest
5
+
6
+ from floodmodeller_api.backup import BackupControl, File
7
+
8
+
9
+ @pytest.fixture
10
+ def backup_control():
11
+ # Use a different directory for testing
12
+ return BackupControl()
13
+
14
+
15
+ @pytest.fixture
16
+ def file(test_workspace):
17
+ test_file = Path(test_workspace, "EX1.DAT")
18
+ file = File(test_file)
19
+ # Make a backup to clear in test
20
+ file.backup()
21
+ return file
22
+
23
+
24
+ def test_init_backup(backup_control):
25
+ """Has the backup been initialised correctly?"""
26
+ assert backup_control.backup_dir.exists()
27
+ assert backup_control.backup_csv_path.exists()
28
+
29
+
30
+ def test_generate_file_id(file, test_workspace):
31
+ """Does this generate a consistent file ID for the same file on disk?"""
32
+ # Test that the file ID is the same for the same path input
33
+ file1 = File(Path(test_workspace, "EX1.DAT"))
34
+ file2 = File(Path(test_workspace, "EX1.DAT"))
35
+ assert file1.file_id == file2.file_id
36
+
37
+
38
+ def test_clear_backup(file, test_workspace):
39
+ """
40
+ Does the the clear_backup method work correctly
41
+ """
42
+ # Clearing backup -------------------
43
+ # Load a different file to check it isn't affected by the
44
+ other_file = File(Path(test_workspace, "EX3.DAT"))
45
+ # Assert there is a backup for the other file
46
+ other_file.backup()
47
+ # Clear the backups for the file to test backup functionality
48
+ file.clear_backup()
49
+ # Assert that clearing the backup has worked - there aren't any backups for the file
50
+ assert len(file.list_backups()) == 0
51
+ # And that clearing it hasn't affected backups for the other file
52
+ assert len(other_file.list_backups()) > 0
53
+
54
+
55
+ def test_backup_locations(file):
56
+ """
57
+ Does it make a backup in the right place?
58
+ """
59
+ # Making a backup --------------------
60
+ file.clear_backup()
61
+ # make a backup and check if file exists
62
+ file.backup()
63
+ backup_file_path = Path(file.backup_dir, file.backup_filename)
64
+ assert backup_file_path.exists()
65
+ # check if contents of backup file match the original file
66
+ with open(backup_file_path) as f1, open(file.path) as f2:
67
+ assert f1.read() == f2.read()
68
+
69
+
70
+ def test_no_duplicate_backup(file, test_workspace):
71
+ """The backup method should only backup if the file has changed"""
72
+ # Don't Make Duplicate -------------------
73
+ # Check that the file isn't backed up again if it hasn't changed
74
+ the_same_file = File(Path(test_workspace, "EX1.DAT"))
75
+ # Append something to the dttm string to ensure the filename is different to the previous backup
76
+ # If the two File objects are created in the same second then then will have identical file names
77
+ # The function should check for equivalence between file contents.
78
+ the_same_file.dttm_str = the_same_file.dttm_str + "_1"
79
+ # Generate a new file name
80
+ the_same_file._generate_file_name()
81
+ # Attempt a backup
82
+ the_same_file.backup()
83
+ # Check that the file hasn't been created
84
+ duplicate_backup_path = Path(the_same_file.backup_dir, the_same_file.backup_filename)
85
+ assert not duplicate_backup_path.exists()
86
+
87
+
88
+ def test_backup_logs(file):
89
+ """Are backups being logged in the CSV?"""
90
+ # Clear the backup
91
+ file.clear_backup()
92
+ # There shouldn't be any edits in the csv
93
+ backup_logs = pd.read_csv(file.backup_csv_path)
94
+ backup_count = backup_logs[
95
+ (backup_logs.file_id == file.file_id) & (backup_logs.dttm == file.dttm_str)
96
+ ].shape[0]
97
+ assert backup_count == 0
98
+ # Make a backup and assert it is in the CSV
99
+ file.backup()
100
+ # Check edits to the backup CSV
101
+ # Check a row has been added to the csv for the file & version
102
+ backup_logs = pd.read_csv(file.backup_csv_path)
103
+ backup_count = backup_logs[
104
+ (backup_logs.file_id == file.file_id) & (backup_logs.dttm == file.dttm_str)
105
+ ].shape[0]
106
+ assert backup_count == 1
107
+
108
+
109
+ def test_list_backups(file):
110
+ """Does the list backups method work correctly?"""
111
+ # First clear any backups that exist
112
+ file.clear_backup()
113
+ # make a backup and check if it appears in the backup list
114
+ file.backup()
115
+ backups = file.list_backups()
116
+ expected_backup = Path(file.backup_dir, file.backup_filename)
117
+ assert expected_backup in [backup.path for backup in backups]
@@ -1,92 +1,221 @@
1
- import os
2
- from pathlib import Path
3
-
4
- import pytest
5
-
6
- from floodmodeller_api import DAT
7
- from floodmodeller_api.units import QTBDY
8
-
9
-
10
- @pytest.fixture
11
- def dat_fp(test_workspace):
12
- return os.path.join(test_workspace, "network.DAT")
13
-
14
-
15
- @pytest.fixture
16
- def data_before(dat_fp):
17
- return DAT(dat_fp)._write()
18
-
19
-
20
- def test_dat_str_not_changed_by_write(dat_fp, data_before):
21
- # TODO: Update this test - it isn't really testing anything since the behaviour of the fixture is exactly the same
22
- """DAT: Test str representation equal to dat file with no changes"""
23
- dat = DAT(dat_fp)
24
- assert dat._write() == data_before
25
-
26
-
27
- def test_changing_section_and_dist_works(dat_fp, data_before):
28
- """DAT: Test changing and reverting section name and dist to next makes no changes"""
29
- dat = DAT(dat_fp)
30
- prev_name = dat.sections["CSRD10"].name
31
- prev_dist = dat.sections["CSRD10"].dist_to_next
32
- dat.sections["CSRD10"].name = "check"
33
- dat.sections["CSRD10"].dist_to_next = 0.0
34
- assert dat._write() != data_before
35
-
36
- dat.sections["check"].name = prev_name
37
- dat.sections["check"].dist_to_next = prev_dist
38
- assert dat._write() == data_before
39
-
40
-
41
- def test_changing_and_reverting_qtbdy_hydrograph_works(dat_fp, data_before):
42
- """DAT: Test changing and reverting QTBDY hydrograph makes no changes"""
43
- dat = DAT(dat_fp)
44
- prev_qt = {}
45
- for name, unit in dat.boundaries.items():
46
- if isinstance(unit, QTBDY):
47
- prev_qt[name] = unit.data.copy()
48
- unit.data *= 2 # Multiply QT flow data by 2
49
- assert dat._write() != data_before
50
-
51
- for name, qt in prev_qt.items():
52
- dat.boundaries[name].data = qt # replace QT flow data with original
53
- assert dat._write() == data_before
54
-
55
-
56
- def test_dat_read_doesnt_change_data(test_workspace):
57
- """DAT: Check all '.dat' files in folder by reading the _write() output into a new DAT instance and checking it stays the same."""
58
- for datfile in Path(test_workspace).glob("*.dat"):
59
- dat = DAT(datfile)
60
- first_output = dat._write()
61
- dat.save("__temp.dat")
62
- second_dat = DAT("__temp.dat")
63
- assert dat == second_dat # Checks equivalence on the class itself
64
- second_output = second_dat._write()
65
- assert first_output == second_output
66
- os.remove("__temp.dat")
67
- try:
68
- os.remove("__temp.gxy")
69
- except FileNotFoundError:
70
- pass
71
-
72
-
73
- def test_insert_unit(test_workspace):
74
- dat_a = DAT(Path(test_workspace, "EX3.DAT"))
75
- dat_b = DAT(Path(Path(test_workspace, "EX6.DAT")))
76
- unit = dat_a.sections["20"]
77
- dat_b.insert_unit(unit, add_before=dat_b.sections["P4000"])
78
- # Check unit is added to sections
79
- assert "20" in dat_b.sections
80
- # Check unit in correct position in all units
81
- assert dat_b._all_units[8:10] == [unit, dat_b.sections["P4000"]]
82
-
83
-
84
- def test_remove_unit(test_workspace):
85
- dat_a = DAT(Path(test_workspace, "EX3.DAT"))
86
- unit = dat_a.sections["20"]
87
- prev_dat_struct_len = len(dat_a._dat_struct)
88
- dat_a.remove_unit(unit)
89
- assert "20" not in dat_a.sections
90
- assert unit not in dat_a._all_units
91
- assert [u for u in dat_a._dat_struct]
92
- assert (prev_dat_struct_len - len(dat_a._dat_struct)) == 1
1
+ from pathlib import Path
2
+ from unittest.mock import patch
3
+
4
+ import pytest
5
+
6
+ from floodmodeller_api import DAT
7
+ from floodmodeller_api.units import QTBDY
8
+
9
+
10
+ @pytest.fixture
11
+ def dat_fp(test_workspace):
12
+ return Path(test_workspace, "network.dat")
13
+
14
+
15
+ @pytest.fixture
16
+ def data_before(dat_fp):
17
+ return DAT(dat_fp)._write()
18
+
19
+
20
+ @pytest.fixture()
21
+ def dat_ex3(test_workspace):
22
+ return DAT(Path(test_workspace, "EX3.DAT"))
23
+
24
+
25
+ @pytest.fixture()
26
+ def units(dat_ex3):
27
+ unit_1 = dat_ex3.sections["20"]
28
+ unit_2 = dat_ex3.sections["40"]
29
+ unit_3 = dat_ex3.sections["60"]
30
+ return [unit_1, unit_2, unit_3]
31
+
32
+
33
+ @pytest.fixture()
34
+ def dat_ex6(test_workspace):
35
+ dat = DAT(Path(test_workspace, "EX6.DAT"))
36
+ with (
37
+ patch.object(dat, "_update_raw_data", wraps=dat._update_raw_data),
38
+ patch.object(dat, "_update_dat_struct", wraps=dat._update_dat_struct),
39
+ ):
40
+ yield dat
41
+
42
+
43
+ def test_dat_str_not_changed_by_write(dat_fp, data_before):
44
+ # TODO: Update this test - it isn't really testing anything since the behaviour of the fixture is exactly the same
45
+ """DAT: Test str representation equal to dat file with no changes"""
46
+ dat = DAT(dat_fp)
47
+ assert dat._write() == data_before
48
+
49
+
50
+ def test_changing_section_and_dist_works(dat_fp, data_before):
51
+ """DAT: Test changing and reverting section name and dist to next makes no changes"""
52
+ dat = DAT(dat_fp)
53
+ prev_name = dat.sections["CSRD10"].name
54
+ prev_dist = dat.sections["CSRD10"].dist_to_next
55
+ dat.sections["CSRD10"].name = "check"
56
+ dat.sections["CSRD10"].dist_to_next = 0.0
57
+ assert dat._write() != data_before
58
+
59
+ dat.sections["check"].name = prev_name
60
+ dat.sections["check"].dist_to_next = prev_dist
61
+ assert dat._write() == data_before
62
+
63
+
64
+ def test_changing_and_reverting_qtbdy_hydrograph_works(dat_fp, data_before):
65
+ """DAT: Test changing and reverting QTBDY hydrograph makes no changes"""
66
+ dat = DAT(dat_fp)
67
+ prev_qt = {}
68
+ for name, unit in dat.boundaries.items():
69
+ if isinstance(unit, QTBDY):
70
+ prev_qt[name] = unit.data.copy()
71
+ unit.data *= 2 # Multiply QT flow data by 2
72
+ assert dat._write() != data_before
73
+
74
+ for name, qt in prev_qt.items():
75
+ dat.boundaries[name].data = qt # replace QT flow data with original
76
+ assert dat._write() == data_before
77
+
78
+
79
+ def test_dat_read_doesnt_change_data(test_workspace, tmpdir):
80
+ """DAT: Check all '.dat' files in folder by reading the _write() output into a new DAT instance and checking it stays the same."""
81
+ for datfile in Path(test_workspace).glob("*.dat"):
82
+ dat = DAT(datfile)
83
+ first_output = dat._write()
84
+ new_path = Path(tmpdir) / "tmp.dat"
85
+ dat.save(new_path)
86
+ second_dat = DAT(new_path)
87
+ assert dat == second_dat # Checks equivalence on the class itself
88
+ second_output = second_dat._write()
89
+ assert first_output == second_output
90
+
91
+
92
+ def test_insert_unit_before(units, dat_ex6):
93
+ dat_ex6.insert_unit(units[0], add_before=dat_ex6.sections["P4000"])
94
+ assert "20" in dat_ex6.sections
95
+ assert dat_ex6._all_units[8:10] == [units[0], dat_ex6.sections["P4000"]]
96
+ dat_ex6._update_raw_data.assert_called_once()
97
+ dat_ex6._update_dat_struct.assert_called_once()
98
+
99
+
100
+ def test_insert_unit_after(units, dat_ex6):
101
+ dat_ex6.insert_unit(units[0], add_after=dat_ex6.sections["P4000"])
102
+ assert "20" in dat_ex6.sections
103
+ assert dat_ex6._all_units[8:10] == [dat_ex6.sections["P4000"], units[0]]
104
+ dat_ex6._update_raw_data.assert_called_once()
105
+ dat_ex6._update_dat_struct.assert_called_once()
106
+
107
+
108
+ def test_insert_unit_at(units, dat_ex6):
109
+ dat_ex6.insert_unit(units[0], add_at=2)
110
+ assert "20" in dat_ex6.sections
111
+ assert dat_ex6._all_units[2] == units[0]
112
+ dat_ex6._update_raw_data.assert_called_once()
113
+ dat_ex6._update_dat_struct.assert_called_once()
114
+
115
+
116
+ @pytest.mark.parametrize(
117
+ "kwargs",
118
+ [
119
+ {"add_at": 1, "add_before": 2},
120
+ {"add_at": 1, "add_after": 3},
121
+ {"add_before": 2, "add_after": 3},
122
+ {"add_at": 1, "add_before": 2, "add_after": 3},
123
+ ],
124
+ )
125
+ def test_insert_unit_too_many_arguments_error(dat_ex6, units, kwargs):
126
+ msg = (
127
+ r"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
128
+ r"\nAPI Error: Problem encountered when trying to insert unit DAT file .*\."
129
+ r"\n"
130
+ r"\nDetails: .*-floodmodeller_api/dat\.py-\d+"
131
+ r"\nMsg: Only one of add_at, add_before, or add_after required"
132
+ r"\n"
133
+ r"\nFor additional support, go to: https://github\.com/People-Places-Solutions/floodmodeller-api"
134
+ )
135
+ with pytest.raises(Exception, match=msg):
136
+ dat_ex6.insert_unit(units[0], **kwargs)
137
+
138
+
139
+ def test_insert_unit_no_arguments_error(dat_ex6, units):
140
+ msg = (
141
+ r"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
142
+ r"\nAPI Error: Problem encountered when trying to insert unit DAT file .*\."
143
+ r"\n"
144
+ r"\nDetails: .*-floodmodeller_api/dat\.py-\d+"
145
+ r"\nMsg: No positional argument given\. Please provide either add_before, add_at or add_after"
146
+ r"\n"
147
+ r"\nFor additional support, go to: https://github\.com/People-Places-Solutions/floodmodeller-api"
148
+ )
149
+ with pytest.raises(Exception, match=msg):
150
+ dat_ex6.insert_unit(units[0])
151
+
152
+
153
+ def test_insert_units_before(units, dat_ex6):
154
+ dat_ex6.insert_units(units, add_before=dat_ex6.sections["P4000"])
155
+ assert "20" in dat_ex6.sections
156
+ assert "40" in dat_ex6.sections
157
+ assert "60" in dat_ex6.sections
158
+ assert dat_ex6._all_units[8:12] == [*units, dat_ex6.sections["P4000"]]
159
+ dat_ex6._update_raw_data.assert_called_once()
160
+ dat_ex6._update_dat_struct.assert_called_once()
161
+
162
+
163
+ def test_insert_units_after(units, dat_ex6):
164
+ dat_ex6.insert_units(units, add_after=dat_ex6.sections["P4000"])
165
+ assert "20" in dat_ex6.sections
166
+ assert "40" in dat_ex6.sections
167
+ assert "60" in dat_ex6.sections
168
+ assert dat_ex6._all_units[8:12] == [dat_ex6.sections["P4000"], *units]
169
+ dat_ex6._update_raw_data.assert_called_once()
170
+ dat_ex6._update_dat_struct.assert_called_once()
171
+
172
+
173
+ def test_insert_units_at(units, dat_ex6):
174
+ dat_ex6.insert_units(units, add_at=2)
175
+ assert "20" in dat_ex6.sections
176
+ assert "40" in dat_ex6.sections
177
+ assert "60" in dat_ex6.sections
178
+ assert dat_ex6._all_units[2:5] == units
179
+ dat_ex6._update_raw_data.assert_called_once()
180
+ dat_ex6._update_dat_struct.assert_called_once()
181
+
182
+
183
+ def test_insert_units_at_end(units, dat_ex6):
184
+ dat_ex6.insert_units(units, add_at=-1)
185
+ assert "20" in dat_ex6.sections
186
+ assert "40" in dat_ex6.sections
187
+ assert "60" in dat_ex6.sections
188
+ assert dat_ex6._all_units[-3:] == units
189
+ dat_ex6._update_raw_data.assert_called_once()
190
+ dat_ex6._update_dat_struct.assert_called_once()
191
+
192
+
193
+ def test_remove_unit(dat_ex3):
194
+ unit = dat_ex3.sections["20"]
195
+ prev_dat_struct_len = len(dat_ex3._dat_struct)
196
+ dat_ex3.remove_unit(unit)
197
+ assert "20" not in dat_ex3.sections
198
+ assert unit not in dat_ex3._all_units
199
+ assert dat_ex3._dat_struct
200
+ assert (prev_dat_struct_len - len(dat_ex3._dat_struct)) == 1
201
+
202
+
203
+ def test_diff(test_workspace, capsys):
204
+ dat_ex4 = DAT(Path(test_workspace, "ex4.DAT"))
205
+ dat_ex4_changed = DAT(Path(test_workspace, "ex4_changed.DAT"))
206
+ dat_ex4.diff(dat_ex4_changed)
207
+ assert capsys.readouterr().out == (
208
+ "Files not equivalent, 12 difference(s) found:\n"
209
+ " DAT->structures->MILLAu->RNWEIR..MILLAu->upstream_crest_height: 1.07 != 1.37\n"
210
+ " DAT->structures->MILLBu->RNWEIR..MILLBu->upstream_crest_height: 0.43 != 0.73\n"
211
+ " DAT->structures->ROAD1->RNWEIR..ROAD1->upstream_crest_height: 2.02 != 2.32\n"
212
+ " DAT->structures->RAILRDu->RNWEIR..RAILRDu->upstream_crest_height: 1.75 != 2.05\n"
213
+ " DAT->structures->CSRD01u->RNWEIR..CSRD01u->upstream_crest_height: 0.81 != 1.11\n"
214
+ " DAT->structures->FOOTa->RNWEIR..FOOTa->upstream_crest_height: 2.47 != 2.77\n"
215
+ " DAT->_all_units->itm[28]->RNWEIR..MILLAu->upstream_crest_height: 1.07 != 1.37\n"
216
+ " DAT->_all_units->itm[29]->RNWEIR..MILLBu->upstream_crest_height: 0.43 != 0.73\n"
217
+ " DAT->_all_units->itm[42]->RNWEIR..ROAD1->upstream_crest_height: 2.02 != 2.32\n"
218
+ " DAT->_all_units->itm[57]->RNWEIR..RAILRDu->upstream_crest_height: 1.75 != 2.05\n"
219
+ " DAT->_all_units->itm[61]->RNWEIR..CSRD01u->upstream_crest_height: 0.81 != 1.11\n"
220
+ " DAT->_all_units->itm[73]->RNWEIR..FOOTa->upstream_crest_height: 2.47 != 2.77\n"
221
+ )