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.
- floodmodeller_api/__init__.py +8 -9
- floodmodeller_api/_base.py +184 -176
- floodmodeller_api/backup.py +273 -273
- floodmodeller_api/dat.py +909 -838
- floodmodeller_api/diff.py +136 -119
- floodmodeller_api/ied.py +307 -311
- floodmodeller_api/ief.py +647 -646
- floodmodeller_api/ief_flags.py +253 -253
- floodmodeller_api/inp.py +266 -268
- floodmodeller_api/libs/libifcoremd.dll +0 -0
- floodmodeller_api/libs/libifcoremt.so.5 +0 -0
- floodmodeller_api/libs/libifport.so.5 +0 -0
- floodmodeller_api/{libmmd.dll → libs/libimf.so} +0 -0
- floodmodeller_api/libs/libintlc.so.5 +0 -0
- floodmodeller_api/libs/libmmd.dll +0 -0
- floodmodeller_api/libs/libsvml.so +0 -0
- floodmodeller_api/libs/libzzn_read.so +0 -0
- floodmodeller_api/libs/zzn_read.dll +0 -0
- floodmodeller_api/logs/__init__.py +2 -2
- floodmodeller_api/logs/lf.py +320 -314
- floodmodeller_api/logs/lf_helpers.py +354 -346
- floodmodeller_api/logs/lf_params.py +643 -529
- floodmodeller_api/mapping.py +84 -0
- floodmodeller_api/test/__init__.py +4 -4
- floodmodeller_api/test/conftest.py +9 -8
- floodmodeller_api/test/test_backup.py +117 -117
- floodmodeller_api/test/test_dat.py +221 -92
- floodmodeller_api/test/test_data/All Units 4_6.DAT +1081 -1081
- floodmodeller_api/test/test_data/All Units 4_6.feb +1081 -1081
- floodmodeller_api/test/test_data/BRIDGE.DAT +926 -926
- floodmodeller_api/test/test_data/Culvert_Inlet_Outlet.dat +36 -36
- floodmodeller_api/test/test_data/Culvert_Inlet_Outlet.feb +36 -36
- floodmodeller_api/test/test_data/DamBreakADI.xml +52 -52
- floodmodeller_api/test/test_data/DamBreakFAST.xml +58 -58
- floodmodeller_api/test/test_data/DamBreakFAST_dy.xml +53 -53
- floodmodeller_api/test/test_data/DamBreakTVD.xml +55 -55
- floodmodeller_api/test/test_data/DefenceBreach.xml +53 -53
- floodmodeller_api/test/test_data/DefenceBreachFAST.xml +60 -60
- floodmodeller_api/test/test_data/DefenceBreachFAST_dy.xml +55 -55
- floodmodeller_api/test/test_data/Domain1+2_QH.xml +76 -76
- floodmodeller_api/test/test_data/Domain1_H.xml +41 -41
- floodmodeller_api/test/test_data/Domain1_Q.xml +41 -41
- floodmodeller_api/test/test_data/Domain1_Q_FAST.xml +48 -48
- floodmodeller_api/test/test_data/Domain1_Q_FAST_dy.xml +48 -48
- floodmodeller_api/test/test_data/Domain1_Q_xml_expected.json +263 -0
- floodmodeller_api/test/test_data/Domain1_W.xml +41 -41
- floodmodeller_api/test/test_data/EX1.DAT +321 -321
- floodmodeller_api/test/test_data/EX1.ext +107 -107
- floodmodeller_api/test/test_data/EX1.feb +320 -320
- floodmodeller_api/test/test_data/EX1.gxy +107 -107
- floodmodeller_api/test/test_data/EX17.DAT +421 -422
- floodmodeller_api/test/test_data/EX17.ext +213 -213
- floodmodeller_api/test/test_data/EX17.feb +422 -422
- floodmodeller_api/test/test_data/EX18.DAT +375 -375
- floodmodeller_api/test/test_data/EX18_DAT_expected.json +3876 -0
- floodmodeller_api/test/test_data/EX2.DAT +302 -302
- floodmodeller_api/test/test_data/EX3.DAT +926 -926
- floodmodeller_api/test/test_data/EX3_DAT_expected.json +16235 -0
- floodmodeller_api/test/test_data/EX3_IEF_expected.json +61 -0
- floodmodeller_api/test/test_data/EX6.DAT +2084 -2084
- floodmodeller_api/test/test_data/EX6.ext +532 -532
- floodmodeller_api/test/test_data/EX6.feb +2084 -2084
- floodmodeller_api/test/test_data/EX6_DAT_expected.json +31647 -0
- floodmodeller_api/test/test_data/Event Data Example.DAT +336 -336
- floodmodeller_api/test/test_data/Event Data Example.ext +107 -107
- floodmodeller_api/test/test_data/Event Data Example.feb +336 -336
- floodmodeller_api/test/test_data/Linked1D2D.xml +52 -52
- floodmodeller_api/test/test_data/Linked1D2DFAST.xml +53 -53
- floodmodeller_api/test/test_data/Linked1D2DFAST_dy.xml +48 -48
- floodmodeller_api/test/test_data/Linked1D2D_xml_expected.json +313 -0
- floodmodeller_api/test/test_data/blockage.dat +50 -50
- floodmodeller_api/test/test_data/blockage.ext +45 -45
- floodmodeller_api/test/test_data/blockage.feb +9 -9
- floodmodeller_api/test/test_data/blockage.gxy +71 -71
- floodmodeller_api/test/test_data/defaultUnits.dat +127 -127
- floodmodeller_api/test/test_data/defaultUnits.ext +45 -45
- floodmodeller_api/test/test_data/defaultUnits.feb +9 -9
- floodmodeller_api/test/test_data/defaultUnits.fmpx +58 -58
- floodmodeller_api/test/test_data/defaultUnits.gxy +85 -85
- floodmodeller_api/test/test_data/ex3.ief +20 -20
- floodmodeller_api/test/test_data/ex3.lf1 +2800 -2800
- floodmodeller_api/test/test_data/ex4.DAT +1374 -1374
- floodmodeller_api/test/test_data/ex4_changed.DAT +1374 -1374
- floodmodeller_api/test/test_data/example1.inp +329 -329
- floodmodeller_api/test/test_data/example2.inp +158 -158
- floodmodeller_api/test/test_data/example3.inp +297 -297
- floodmodeller_api/test/test_data/example4.inp +388 -388
- floodmodeller_api/test/test_data/example5.inp +147 -147
- floodmodeller_api/test/test_data/example6.inp +154 -154
- floodmodeller_api/test/test_data/jump.dat +176 -176
- floodmodeller_api/test/test_data/network.dat +1374 -1374
- floodmodeller_api/test/test_data/network.ext +45 -45
- floodmodeller_api/test/test_data/network.exy +1 -1
- floodmodeller_api/test/test_data/network.feb +45 -45
- floodmodeller_api/test/test_data/network.ied +45 -45
- floodmodeller_api/test/test_data/network.ief +20 -20
- floodmodeller_api/test/test_data/network.inp +147 -147
- floodmodeller_api/test/test_data/network.pxy +57 -57
- floodmodeller_api/test/test_data/network.zzd +122 -122
- floodmodeller_api/test/test_data/network_dat_expected.json +21837 -0
- floodmodeller_api/test/test_data/network_from_tabularCSV.csv +87 -87
- floodmodeller_api/test/test_data/network_ied_expected.json +287 -0
- floodmodeller_api/test/test_data/rnweir.dat +9 -9
- floodmodeller_api/test/test_data/rnweir.ext +45 -45
- floodmodeller_api/test/test_data/rnweir.feb +9 -9
- floodmodeller_api/test/test_data/rnweir.gxy +45 -45
- floodmodeller_api/test/test_data/rnweir_default.dat +74 -74
- floodmodeller_api/test/test_data/rnweir_default.ext +45 -45
- floodmodeller_api/test/test_data/rnweir_default.feb +9 -9
- floodmodeller_api/test/test_data/rnweir_default.fmpx +58 -58
- floodmodeller_api/test/test_data/rnweir_default.gxy +53 -53
- floodmodeller_api/test/test_data/unit checks.dat +16 -16
- floodmodeller_api/test/test_ied.py +29 -29
- floodmodeller_api/test/test_ief.py +125 -24
- floodmodeller_api/test/test_inp.py +47 -48
- floodmodeller_api/test/test_json.py +114 -0
- floodmodeller_api/test/test_logs_lf.py +48 -51
- floodmodeller_api/test/test_tool.py +165 -154
- floodmodeller_api/test/test_toolbox_structure_log.py +234 -239
- floodmodeller_api/test/test_xml2d.py +151 -156
- floodmodeller_api/test/test_zzn.py +36 -34
- floodmodeller_api/to_from_json.py +218 -0
- floodmodeller_api/tool.py +332 -330
- floodmodeller_api/toolbox/__init__.py +5 -5
- floodmodeller_api/toolbox/example_tool.py +45 -45
- floodmodeller_api/toolbox/model_build/__init__.py +2 -2
- floodmodeller_api/toolbox/model_build/add_siltation_definition.py +100 -94
- floodmodeller_api/toolbox/model_build/structure_log/__init__.py +1 -1
- floodmodeller_api/toolbox/model_build/structure_log/structure_log.py +287 -289
- floodmodeller_api/toolbox/model_build/structure_log_definition.py +76 -72
- floodmodeller_api/units/__init__.py +10 -10
- floodmodeller_api/units/_base.py +214 -209
- floodmodeller_api/units/boundaries.py +467 -469
- floodmodeller_api/units/comment.py +52 -55
- floodmodeller_api/units/conduits.py +382 -403
- floodmodeller_api/units/helpers.py +123 -132
- floodmodeller_api/units/iic.py +107 -101
- floodmodeller_api/units/losses.py +305 -308
- floodmodeller_api/units/sections.py +444 -445
- floodmodeller_api/units/structures.py +1690 -1684
- floodmodeller_api/units/units.py +93 -102
- floodmodeller_api/units/unsupported.py +44 -44
- floodmodeller_api/units/variables.py +87 -89
- floodmodeller_api/urban1d/__init__.py +11 -11
- floodmodeller_api/urban1d/_base.py +188 -177
- floodmodeller_api/urban1d/conduits.py +93 -85
- floodmodeller_api/urban1d/general_parameters.py +58 -58
- floodmodeller_api/urban1d/junctions.py +81 -79
- floodmodeller_api/urban1d/losses.py +81 -74
- floodmodeller_api/urban1d/outfalls.py +114 -107
- floodmodeller_api/urban1d/raingauges.py +111 -108
- floodmodeller_api/urban1d/subsections.py +92 -93
- floodmodeller_api/urban1d/xsections.py +147 -141
- floodmodeller_api/util.py +77 -21
- floodmodeller_api/validation/parameters.py +660 -660
- floodmodeller_api/validation/urban_parameters.py +388 -404
- floodmodeller_api/validation/validation.py +110 -112
- floodmodeller_api/version.py +1 -1
- floodmodeller_api/xml2d.py +688 -684
- floodmodeller_api/xml2d_template.py +37 -37
- floodmodeller_api/zzn.py +387 -365
- {floodmodeller_api-0.4.2.dist-info → floodmodeller_api-0.4.3.dist-info}/LICENSE.txt +13 -13
- {floodmodeller_api-0.4.2.dist-info → floodmodeller_api-0.4.3.dist-info}/METADATA +82 -82
- floodmodeller_api-0.4.3.dist-info/RECORD +179 -0
- {floodmodeller_api-0.4.2.dist-info → floodmodeller_api-0.4.3.dist-info}/WHEEL +1 -1
- floodmodeller_api-0.4.3.dist-info/entry_points.txt +3 -0
- floodmodeller_api/libifcoremd.dll +0 -0
- floodmodeller_api/test/test_data/EX3.bmp +0 -0
- floodmodeller_api/test/test_data/test_output.csv +0 -87
- floodmodeller_api/zzn_read.dll +0 -0
- floodmodeller_api-0.4.2.data/scripts/fmapi-add_siltation.bat +0 -2
- floodmodeller_api-0.4.2.data/scripts/fmapi-add_siltation.py +0 -3
- floodmodeller_api-0.4.2.data/scripts/fmapi-structure_log.bat +0 -2
- floodmodeller_api-0.4.2.data/scripts/fmapi-structure_log.py +0 -3
- floodmodeller_api-0.4.2.data/scripts/fmapi-toolbox.bat +0 -2
- floodmodeller_api-0.4.2.data/scripts/fmapi-toolbox.py +0 -41
- floodmodeller_api-0.4.2.dist-info/RECORD +0 -169
- {floodmodeller_api-0.4.2.dist-info → floodmodeller_api-0.4.3.dist-info}/top_level.txt +0 -0
floodmodeller_api/backup.py
CHANGED
|
@@ -1,273 +1,273 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Flood Modeller Python API
|
|
3
|
-
Copyright (C)
|
|
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
|
-
import
|
|
18
|
-
|
|
19
|
-
import
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
from
|
|
23
|
-
from
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
backup
|
|
54
|
-
|
|
55
|
-
#
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
self.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
""
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
path (
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
Generates the
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
self.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
""
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
"""
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
""
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
"""
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
#
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
#
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
# TODO: Return the file path?
|
|
263
|
-
|
|
264
|
-
def clear_backup(self):
|
|
265
|
-
"""
|
|
266
|
-
Clears all backups for the file and removes entries from the logs
|
|
267
|
-
"""
|
|
268
|
-
# Clear the backup
|
|
269
|
-
super().clear_backup(file_id=self.file_id)
|
|
270
|
-
# Drop the files entries from the log
|
|
271
|
-
backup_logs = pd.read_csv(self.backup_csv_path)
|
|
272
|
-
backup_logs = backup_logs[backup_logs.file_id != self.file_id]
|
|
273
|
-
backup_logs.to_csv(self.backup_csv_path, index=False)
|
|
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
|
+
import filecmp
|
|
20
|
+
import re
|
|
21
|
+
import tempfile
|
|
22
|
+
from datetime import datetime
|
|
23
|
+
from hashlib import sha1
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
from shutil import copy
|
|
26
|
+
|
|
27
|
+
import pandas as pd
|
|
28
|
+
|
|
29
|
+
from .to_from_json import Jsonable
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class BackupControl(Jsonable):
|
|
33
|
+
"""
|
|
34
|
+
The BackupControl class provides functionality for creating and managing file backups.
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
Attributes:
|
|
38
|
+
temp_dir (str): The temporary directory used for backups.
|
|
39
|
+
backup_dirname (str): The name of the backup directory.
|
|
40
|
+
backup_dir (str): The full path to the backup directory.
|
|
41
|
+
backup_csv_path (str): The full path to the backup CSV file.
|
|
42
|
+
|
|
43
|
+
Methods:
|
|
44
|
+
_init_backup(): Initialises the backup directory and creates a CSV file for logging backup information.
|
|
45
|
+
clear_backup(): Removes all backup files in the backup directory.
|
|
46
|
+
|
|
47
|
+
Usage:
|
|
48
|
+
The BackUp class can be used to create backups of files and directories. The backups are stored in a temporary
|
|
49
|
+
directory and are logged in a CSV file. The backups can be cleared using the clear_backup method.
|
|
50
|
+
|
|
51
|
+
Example:
|
|
52
|
+
# Create a new BackUp object
|
|
53
|
+
backup = BackUp(backup_directory_name='my_backups')
|
|
54
|
+
|
|
55
|
+
# Create a backup of a file
|
|
56
|
+
backup_file_path = '/path/to/my/file.txt'
|
|
57
|
+
backup.backup_file(backup_file_path)
|
|
58
|
+
|
|
59
|
+
# Clear all backups
|
|
60
|
+
backup.clear_backup()
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(self):
|
|
64
|
+
"""
|
|
65
|
+
Initialises a new BackUp object.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
backup_directory_name (str): The name of the backup directory. Defaults to "floodmodeller_api_backup".
|
|
69
|
+
"""
|
|
70
|
+
self.temp_dir = tempfile.gettempdir()
|
|
71
|
+
self.backup_dirname = "floodmodeller_api_backup"
|
|
72
|
+
self.backup_dir = Path(self.temp_dir, self.backup_dirname)
|
|
73
|
+
self.backup_csv_path = Path(self.backup_dir, "file-backups.csv")
|
|
74
|
+
self._init_backup()
|
|
75
|
+
|
|
76
|
+
def _init_backup(self):
|
|
77
|
+
"""
|
|
78
|
+
Initialises the backup directory and creates a CSV file for logging backup information.
|
|
79
|
+
"""
|
|
80
|
+
# Create the backup directory if it doesn't exist
|
|
81
|
+
if not self.backup_dir.exists():
|
|
82
|
+
self.backup_dir.mkdir()
|
|
83
|
+
print(f"{self.__class__.__name__}: Initialised backup directory at {self.backup_dir}")
|
|
84
|
+
|
|
85
|
+
# Create the backup CSV file if it doesn't exist
|
|
86
|
+
if not self.backup_csv_path.exists():
|
|
87
|
+
with open(self.backup_csv_path, "w") as f:
|
|
88
|
+
f.write("path,file_id,dttm\n")
|
|
89
|
+
|
|
90
|
+
def clear_backup(self, file_id="*"):
|
|
91
|
+
"""
|
|
92
|
+
Removes all backup files in the backup directory.
|
|
93
|
+
Args:
|
|
94
|
+
file_id (str): The ID of the file to clear, default value is "*" to clear all files
|
|
95
|
+
If this is called from the file class then the file Id of that file will be used
|
|
96
|
+
"""
|
|
97
|
+
# If the user wants to clear a specific file, then suffix * to match it
|
|
98
|
+
if file_id != "*":
|
|
99
|
+
file_id = f"{file_id}*"
|
|
100
|
+
|
|
101
|
+
files = self.backup_dir.glob(file_id)
|
|
102
|
+
for f in files:
|
|
103
|
+
Path(f).unlink()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def parse_backup_dttm(path):
|
|
107
|
+
# Extract datetime from string
|
|
108
|
+
datetime_str = re.search(r"\d{4}-\d{2}-\d{2}-\d{2}-\d{2}-\d{2}", path).group(0)
|
|
109
|
+
# Convert datetime string to datetime object
|
|
110
|
+
return datetime.strptime(datetime_str, "%Y-%m-%d-%H-%M-%S")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class BackupFile:
|
|
114
|
+
"""
|
|
115
|
+
Defines a backed up file and functionality to restore it
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
path (str): The path to the file that you want to back up or retrieve.
|
|
119
|
+
file_id (str): A unique identifier for the file generated by hashing its absolute path.
|
|
120
|
+
|
|
121
|
+
Attributes:
|
|
122
|
+
path (Path): The absolute path to the original file.
|
|
123
|
+
dttm (str): The datetime that the original file was loaded and backed up in the format '%Y-%m-%d-%H-%M-%S'.
|
|
124
|
+
Identifies a unique backup.
|
|
125
|
+
file_id (str): A unique identifier for the file generated by hashing its absolute path.
|
|
126
|
+
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
def __init__(self, file_id: str, path: str):
|
|
130
|
+
self.file_id = file_id
|
|
131
|
+
self.path = path
|
|
132
|
+
self.dttm = parse_backup_dttm(str(path))
|
|
133
|
+
|
|
134
|
+
def restore(self, to):
|
|
135
|
+
"""
|
|
136
|
+
Restore the file from the last backup if one exists.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
to (str): The path to where you want to restore the file.
|
|
140
|
+
"""
|
|
141
|
+
copy(self.path, to)
|
|
142
|
+
|
|
143
|
+
def __repr__(self):
|
|
144
|
+
return f"BackupFile(file_id={self.file_id}, path = {self.path})"
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class File(BackupControl):
|
|
148
|
+
"""
|
|
149
|
+
Provides functionality to backup files and retrieve them.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
path (str): The path to the file that you want to back up or retrieve.
|
|
153
|
+
|
|
154
|
+
Attributes:
|
|
155
|
+
path (Path): The absolute path to the original file.
|
|
156
|
+
ext (str): The file extension.
|
|
157
|
+
dttm_str (str): The current date and time as a string in the format '%Y-%m-%d-%H-%M-%S'.
|
|
158
|
+
file_id (str): A unique identifier for the file generated by hashing its absolute path.
|
|
159
|
+
backup_dir (str): The path to the directory where backup files will be saved.
|
|
160
|
+
backup_filename (str): The name of the backup file, constructed from the unique file id, the datetime it was loaded and the extension.
|
|
161
|
+
backup_csv_path (str): The path to the backup CSV file where information about each backup is logged.
|
|
162
|
+
|
|
163
|
+
Methods:
|
|
164
|
+
backup(self) -> None:
|
|
165
|
+
Makes a backup of the file. Backups are saved in the users Temporary Files (see `tempfile.gettempdir()` or `File.backup_dir`).
|
|
166
|
+
|
|
167
|
+
restore(self, to:str) -> None:
|
|
168
|
+
Restores the file from the last backup if one exists.
|
|
169
|
+
|
|
170
|
+
_generate_file_id(self) -> None:
|
|
171
|
+
Generates the file's unique identifier as using a hash of the absolute file path.
|
|
172
|
+
|
|
173
|
+
_generate_file_name(self) -> None:
|
|
174
|
+
Generates the name of the file, constructed from the unique file id, the datetime it was loaded and the extension.
|
|
175
|
+
This allows the file to be backed up as a unique version of a particular file at a particular time.
|
|
176
|
+
|
|
177
|
+
_make_backup(self) -> None:
|
|
178
|
+
Makes a backup of the file. This function copies the file to the backup directory with a unique filename.
|
|
179
|
+
It also logs a row in the backup csv file to help find a particular backup when inspecting the file system.
|
|
180
|
+
|
|
181
|
+
list_backups(self) -> List[str]:
|
|
182
|
+
Lists backed up versions of the File, ordered from newest to oldest.
|
|
183
|
+
|
|
184
|
+
Note:
|
|
185
|
+
Inherits from the BackUp class.
|
|
186
|
+
|
|
187
|
+
Example:
|
|
188
|
+
>>> file = File('path/to/my/file.txt')
|
|
189
|
+
>>> file.backup()
|
|
190
|
+
>>> file.restore('path/to/my/restored_file.txt')
|
|
191
|
+
"""
|
|
192
|
+
|
|
193
|
+
def __init__(self, path: str | Path = "", from_json: bool = False, **args):
|
|
194
|
+
# TODO: Make protected properties so they can't be manipulated
|
|
195
|
+
self.path = Path(path)
|
|
196
|
+
# Check if the file exists
|
|
197
|
+
if not self.path.exists():
|
|
198
|
+
raise OSError("File not found!")
|
|
199
|
+
self.ext = self.path.suffix
|
|
200
|
+
self.dttm_str = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
|
|
201
|
+
self._generate_file_id()
|
|
202
|
+
self._generate_file_name()
|
|
203
|
+
super().__init__(**args)
|
|
204
|
+
|
|
205
|
+
def __repr__(self):
|
|
206
|
+
"""Print method"""
|
|
207
|
+
return f"Flood Modeller {self.ext} File\nPath: {self.path.__str__()}\nID: {self.file_id}"
|
|
208
|
+
|
|
209
|
+
def _generate_file_id(self):
|
|
210
|
+
"""
|
|
211
|
+
Generate the file's unique identifier as using a hash of the absolute file path
|
|
212
|
+
"""
|
|
213
|
+
# hash the absolute path becuase the same file name / directroy structure may be mirrored across projects
|
|
214
|
+
# TODO: Use a function that produces a shorter has to make interpretation of the directory easier
|
|
215
|
+
fp_bytes = str(self.path.absolute()).encode()
|
|
216
|
+
self.file_id = sha1(fp_bytes).hexdigest()
|
|
217
|
+
|
|
218
|
+
def _generate_file_name(self) -> None:
|
|
219
|
+
"""
|
|
220
|
+
Generate the name of the file, constructed from the unique file id, the datetime it was loaded and the extension.
|
|
221
|
+
This allows the file to be backed up as a unique version of a particular file at a particular time.
|
|
222
|
+
"""
|
|
223
|
+
self.backup_filename = self.file_id + "_" + self.dttm_str + self.ext
|
|
224
|
+
|
|
225
|
+
def _make_backup(self) -> None:
|
|
226
|
+
"""
|
|
227
|
+
Make a backup of the file. This function copies the file to the backup directory with a unique filename.
|
|
228
|
+
It also logs a row in the backup csv file to help find a particular backup when inspecting the file system.
|
|
229
|
+
"""
|
|
230
|
+
# Construct the path and copy the file
|
|
231
|
+
backup_filepath = Path(self.backup_dir, self.backup_filename)
|
|
232
|
+
copy(self.path, backup_filepath)
|
|
233
|
+
# Log an entry to the csv to make it easy to find the file
|
|
234
|
+
# TODO: Only log file_id and poath, don't log duplicate lines. Needs to be fast so it doesn't slow FMFile down
|
|
235
|
+
log_str = f"{str(self.path)},{self.file_id},{self.dttm_str}\n"
|
|
236
|
+
with open(self.backup_csv_path, "a") as f:
|
|
237
|
+
f.write(log_str)
|
|
238
|
+
|
|
239
|
+
def list_backups(self) -> list:
|
|
240
|
+
"""
|
|
241
|
+
List backed up versions of the File, ordered from newest to oldest.
|
|
242
|
+
"""
|
|
243
|
+
backup_files = list(self.backup_dir.glob(f"{self.file_id}*"))
|
|
244
|
+
backup_files.sort(reverse=True)
|
|
245
|
+
if len(backup_files) <= 0:
|
|
246
|
+
return []
|
|
247
|
+
return [BackupFile(file_id=self.file_id, path=path) for path in backup_files]
|
|
248
|
+
|
|
249
|
+
def backup(self) -> None:
|
|
250
|
+
"""
|
|
251
|
+
High level method to make a backup of the file.
|
|
252
|
+
This function will make a backup of a file, only if there isn't already an equivalent backup in the temporary folder.
|
|
253
|
+
Backups are saved in the users Temporary Files (see `tempfile.gettempdir()` or `File.backup_dir`).
|
|
254
|
+
"""
|
|
255
|
+
# get the backups of that file
|
|
256
|
+
backups = self.list_backups()
|
|
257
|
+
# If there aren't any backups then backup the file
|
|
258
|
+
if len(backups) == 0 or not filecmp.cmp(self.path, backups[0].path):
|
|
259
|
+
self._make_backup()
|
|
260
|
+
# If the file doesn't match the last backup then do a back up
|
|
261
|
+
# TODO: Use FloodModeller API implemented equivalence testing. This is implemented at a higher level than FMFile where this method is called.
|
|
262
|
+
# TODO: Return the file path?
|
|
263
|
+
|
|
264
|
+
def clear_backup(self):
|
|
265
|
+
"""
|
|
266
|
+
Clears all backups for the file and removes entries from the logs
|
|
267
|
+
"""
|
|
268
|
+
# Clear the backup
|
|
269
|
+
super().clear_backup(file_id=self.file_id)
|
|
270
|
+
# Drop the files entries from the log
|
|
271
|
+
backup_logs = pd.read_csv(self.backup_csv_path)
|
|
272
|
+
backup_logs = backup_logs[backup_logs.file_id != self.file_id]
|
|
273
|
+
backup_logs.to_csv(self.backup_csv_path, index=False)
|