vortex-nwp 2.0.0b1__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.
- vortex/__init__.py +135 -0
- vortex/algo/__init__.py +12 -0
- vortex/algo/components.py +2136 -0
- vortex/algo/mpitools.py +1648 -0
- vortex/algo/mpitools_templates/envelope_wrapper_default.tpl +27 -0
- vortex/algo/mpitools_templates/envelope_wrapper_mpiauto.tpl +29 -0
- vortex/algo/mpitools_templates/wrapstd_wrapper_default.tpl +18 -0
- vortex/algo/serversynctools.py +170 -0
- vortex/config.py +115 -0
- vortex/data/__init__.py +13 -0
- vortex/data/abstractstores.py +1572 -0
- vortex/data/containers.py +780 -0
- vortex/data/contents.py +596 -0
- vortex/data/executables.py +284 -0
- vortex/data/flow.py +113 -0
- vortex/data/geometries.ini +2689 -0
- vortex/data/geometries.py +703 -0
- vortex/data/handlers.py +1021 -0
- vortex/data/outflow.py +67 -0
- vortex/data/providers.py +465 -0
- vortex/data/resources.py +201 -0
- vortex/data/stores.py +1271 -0
- vortex/gloves.py +282 -0
- vortex/layout/__init__.py +27 -0
- vortex/layout/appconf.py +109 -0
- vortex/layout/contexts.py +511 -0
- vortex/layout/dataflow.py +1069 -0
- vortex/layout/jobs.py +1276 -0
- vortex/layout/monitor.py +833 -0
- vortex/layout/nodes.py +1424 -0
- vortex/layout/subjobs.py +464 -0
- vortex/nwp/__init__.py +11 -0
- vortex/nwp/algo/__init__.py +12 -0
- vortex/nwp/algo/assim.py +483 -0
- vortex/nwp/algo/clim.py +920 -0
- vortex/nwp/algo/coupling.py +609 -0
- vortex/nwp/algo/eda.py +632 -0
- vortex/nwp/algo/eps.py +613 -0
- vortex/nwp/algo/forecasts.py +745 -0
- vortex/nwp/algo/fpserver.py +927 -0
- vortex/nwp/algo/ifsnaming.py +403 -0
- vortex/nwp/algo/ifsroot.py +311 -0
- vortex/nwp/algo/monitoring.py +202 -0
- vortex/nwp/algo/mpitools.py +554 -0
- vortex/nwp/algo/odbtools.py +974 -0
- vortex/nwp/algo/oopsroot.py +735 -0
- vortex/nwp/algo/oopstests.py +186 -0
- vortex/nwp/algo/request.py +579 -0
- vortex/nwp/algo/stdpost.py +1285 -0
- vortex/nwp/data/__init__.py +12 -0
- vortex/nwp/data/assim.py +392 -0
- vortex/nwp/data/boundaries.py +261 -0
- vortex/nwp/data/climfiles.py +539 -0
- vortex/nwp/data/configfiles.py +149 -0
- vortex/nwp/data/consts.py +929 -0
- vortex/nwp/data/ctpini.py +133 -0
- vortex/nwp/data/diagnostics.py +181 -0
- vortex/nwp/data/eda.py +148 -0
- vortex/nwp/data/eps.py +383 -0
- vortex/nwp/data/executables.py +1039 -0
- vortex/nwp/data/fields.py +96 -0
- vortex/nwp/data/gridfiles.py +308 -0
- vortex/nwp/data/logs.py +551 -0
- vortex/nwp/data/modelstates.py +334 -0
- vortex/nwp/data/monitoring.py +220 -0
- vortex/nwp/data/namelists.py +644 -0
- vortex/nwp/data/obs.py +748 -0
- vortex/nwp/data/oopsexec.py +72 -0
- vortex/nwp/data/providers.py +182 -0
- vortex/nwp/data/query.py +217 -0
- vortex/nwp/data/stores.py +147 -0
- vortex/nwp/data/surfex.py +338 -0
- vortex/nwp/syntax/__init__.py +9 -0
- vortex/nwp/syntax/stdattrs.py +375 -0
- vortex/nwp/tools/__init__.py +10 -0
- vortex/nwp/tools/addons.py +35 -0
- vortex/nwp/tools/agt.py +55 -0
- vortex/nwp/tools/bdap.py +48 -0
- vortex/nwp/tools/bdcp.py +38 -0
- vortex/nwp/tools/bdm.py +21 -0
- vortex/nwp/tools/bdmp.py +49 -0
- vortex/nwp/tools/conftools.py +1311 -0
- vortex/nwp/tools/drhook.py +62 -0
- vortex/nwp/tools/grib.py +268 -0
- vortex/nwp/tools/gribdiff.py +99 -0
- vortex/nwp/tools/ifstools.py +163 -0
- vortex/nwp/tools/igastuff.py +249 -0
- vortex/nwp/tools/mars.py +56 -0
- vortex/nwp/tools/odb.py +548 -0
- vortex/nwp/tools/partitioning.py +234 -0
- vortex/nwp/tools/satrad.py +56 -0
- vortex/nwp/util/__init__.py +6 -0
- vortex/nwp/util/async.py +184 -0
- vortex/nwp/util/beacon.py +40 -0
- vortex/nwp/util/diffpygram.py +359 -0
- vortex/nwp/util/ens.py +198 -0
- vortex/nwp/util/hooks.py +128 -0
- vortex/nwp/util/taskdeco.py +81 -0
- vortex/nwp/util/usepygram.py +591 -0
- vortex/nwp/util/usetnt.py +87 -0
- vortex/proxy.py +6 -0
- vortex/sessions.py +341 -0
- vortex/syntax/__init__.py +9 -0
- vortex/syntax/stdattrs.py +628 -0
- vortex/syntax/stddeco.py +176 -0
- vortex/toolbox.py +982 -0
- vortex/tools/__init__.py +11 -0
- vortex/tools/actions.py +457 -0
- vortex/tools/addons.py +297 -0
- vortex/tools/arm.py +76 -0
- vortex/tools/compression.py +322 -0
- vortex/tools/date.py +20 -0
- vortex/tools/ddhpack.py +10 -0
- vortex/tools/delayedactions.py +672 -0
- vortex/tools/env.py +513 -0
- vortex/tools/folder.py +663 -0
- vortex/tools/grib.py +559 -0
- vortex/tools/lfi.py +746 -0
- vortex/tools/listings.py +354 -0
- vortex/tools/names.py +575 -0
- vortex/tools/net.py +1790 -0
- vortex/tools/odb.py +10 -0
- vortex/tools/parallelism.py +336 -0
- vortex/tools/prestaging.py +186 -0
- vortex/tools/rawfiles.py +10 -0
- vortex/tools/schedulers.py +413 -0
- vortex/tools/services.py +871 -0
- vortex/tools/storage.py +1061 -0
- vortex/tools/surfex.py +61 -0
- vortex/tools/systems.py +3396 -0
- vortex/tools/targets.py +384 -0
- vortex/util/__init__.py +9 -0
- vortex/util/config.py +1071 -0
- vortex/util/empty.py +24 -0
- vortex/util/helpers.py +184 -0
- vortex/util/introspection.py +63 -0
- vortex/util/iosponge.py +76 -0
- vortex/util/roles.py +51 -0
- vortex/util/storefunctions.py +103 -0
- vortex/util/structs.py +26 -0
- vortex/util/worker.py +150 -0
- vortex_nwp-2.0.0b1.dist-info/LICENSE +517 -0
- vortex_nwp-2.0.0b1.dist-info/METADATA +50 -0
- vortex_nwp-2.0.0b1.dist-info/RECORD +146 -0
- vortex_nwp-2.0.0b1.dist-info/WHEEL +5 -0
- vortex_nwp-2.0.0b1.dist-info/top_level.txt +1 -0
vortex/tools/listings.py
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
"""Utility classes to read and compare IFS/Arpege listings."""
|
|
2
|
+
|
|
3
|
+
import copy
|
|
4
|
+
import re
|
|
5
|
+
from collections import OrderedDict, defaultdict, deque
|
|
6
|
+
|
|
7
|
+
import footprints
|
|
8
|
+
from arpifs_listings import cost_functions, jo_tables, listings, norms
|
|
9
|
+
from bronx.stdtypes.date import Date
|
|
10
|
+
from vortex.data.contents import FormatAdapterAbstractImplementation
|
|
11
|
+
|
|
12
|
+
from . import addons
|
|
13
|
+
|
|
14
|
+
#: No automatic export
|
|
15
|
+
__all__ = []
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def use_in_shell(sh, **kw):
|
|
19
|
+
"""Extend current shell with the arpifs_listings interface defined by optional arguments."""
|
|
20
|
+
kw['shell'] = sh
|
|
21
|
+
return footprints.proxy.addon(**kw)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ArpIfsListingDiff_Result:
|
|
25
|
+
"""Holds the detailed results of a listing comparison."""
|
|
26
|
+
|
|
27
|
+
def __init__(self, norms_eq, jos_eq, jos_diff):
|
|
28
|
+
self._norms_eq = norms_eq
|
|
29
|
+
self._jos_eq = jos_eq
|
|
30
|
+
self._jos_diff = jos_diff
|
|
31
|
+
|
|
32
|
+
def __str__(self):
|
|
33
|
+
return '{:s} | NormsOk={:b} JoTablesOk={:b}>'.format(
|
|
34
|
+
repr(self).rstrip('>'),
|
|
35
|
+
all(self._norms_eq.values()),
|
|
36
|
+
all(self._jos_eq.values())
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def differences(self):
|
|
40
|
+
"""Print a summary of the listing comparison."""
|
|
41
|
+
# print() # activation breaks test_arpifs_listings_integration.py
|
|
42
|
+
if self._norms_eq:
|
|
43
|
+
if all(self._norms_eq.values()):
|
|
44
|
+
print("Norms check succeeded for all steps.")
|
|
45
|
+
else:
|
|
46
|
+
print("Norms check succeeded for steps:\n {:s}".format(
|
|
47
|
+
"\n ".join([str(k) for k, v in self._norms_eq.items() if v])))
|
|
48
|
+
print("Norms check FAILED for steps:\n {:s}".format(
|
|
49
|
+
"\n ".join([str(k) for k, v in self._norms_eq.items() if not v])))
|
|
50
|
+
else:
|
|
51
|
+
print("No norms found in the new listing or no matching norms.")
|
|
52
|
+
# print() # activation breaks test_arpifs_listings_integration.py
|
|
53
|
+
if self._jos_eq:
|
|
54
|
+
diffprinted = False
|
|
55
|
+
for k, v in self._jos_eq.items():
|
|
56
|
+
if v:
|
|
57
|
+
print("JoTable check succeeded for: {:s}".format(k))
|
|
58
|
+
else:
|
|
59
|
+
print("JoTable check FAILED for: {:s}".format(k))
|
|
60
|
+
if not diffprinted:
|
|
61
|
+
todo = self._jos_diff[k]
|
|
62
|
+
for otype_k, otype_v in todo.items():
|
|
63
|
+
for sensor_k, sensor_v in otype_v.items():
|
|
64
|
+
for var_k, var_v in sensor_v.items():
|
|
65
|
+
if var_k == 'GLOBAL':
|
|
66
|
+
continue
|
|
67
|
+
print(" > {:s} > {:s} > {:4s} : d_n={:<9d} d_jo={:f}".format(
|
|
68
|
+
otype_k, sensor_k, var_k,
|
|
69
|
+
var_v['n']['diff'], var_v['jo']['diff']))
|
|
70
|
+
diffprinted = True
|
|
71
|
+
else:
|
|
72
|
+
print("No Jo-Tables were found or the number of Jo-Tables do not match.")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class ArpIfsListingDiff_Status:
|
|
76
|
+
"""Holds the status of a listing comparison."""
|
|
77
|
+
|
|
78
|
+
def __init__(self, norms_eq, jos_eq, jos_diff):
|
|
79
|
+
self._norms_ok = all(norms_eq.values())
|
|
80
|
+
self._jos_ok = all(jos_eq.values())
|
|
81
|
+
self._result = ArpIfsListingDiff_Result(norms_eq, jos_eq, jos_diff)
|
|
82
|
+
|
|
83
|
+
def __str__(self):
|
|
84
|
+
return '{:s} | rc={:b}>'.format(repr(self).rstrip('>'), bool(self))
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def result(self):
|
|
88
|
+
"""Return the detailed results of the comparison."""
|
|
89
|
+
return self._result
|
|
90
|
+
|
|
91
|
+
def __bool__(self):
|
|
92
|
+
return bool(self._norms_ok and self._jos_ok)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class ArpIfsListingsTool(addons.Addon):
|
|
96
|
+
"""Interface to arpifs_listings (designed as a shell Addon)."""
|
|
97
|
+
|
|
98
|
+
_footprint = dict(
|
|
99
|
+
info='Default arpifs_listings interface',
|
|
100
|
+
attr=dict(
|
|
101
|
+
kind=dict(
|
|
102
|
+
values=['arpifs_listings'],
|
|
103
|
+
),
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def arpifslist_diff(self, listing1, listing2):
|
|
108
|
+
"""Difference between two Arpege/IFS listing files.
|
|
109
|
+
|
|
110
|
+
Only Spectral/Gridpoint norms and JO-tables are compared.
|
|
111
|
+
|
|
112
|
+
:param listing1: first file to compare
|
|
113
|
+
:param listing2: second file to compare
|
|
114
|
+
:rtype: :class:`ArpIfsListingDiff_Status`
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
with open(listing1) as fh1:
|
|
118
|
+
l1_slurp = [l.rstrip("\n") for l in fh1]
|
|
119
|
+
with open(listing2) as fh2:
|
|
120
|
+
l2_slurp = [l.rstrip("\n") for l in fh2]
|
|
121
|
+
l1_normset = norms.NormsSet(l1_slurp)
|
|
122
|
+
l2_normset = norms.NormsSet(l2_slurp)
|
|
123
|
+
l1_jos = jo_tables.JoTables(listing1, l1_slurp)
|
|
124
|
+
l2_jos = jo_tables.JoTables(listing2, l2_slurp)
|
|
125
|
+
|
|
126
|
+
# The reference listing may contain more norms compared to the second one
|
|
127
|
+
norms_eq = OrderedDict()
|
|
128
|
+
if len(l2_normset):
|
|
129
|
+
if not l2_normset.steps_equal(l1_normset):
|
|
130
|
+
l1_tdict = OrderedDict()
|
|
131
|
+
for n in l1_normset:
|
|
132
|
+
l1_tdict[n.format_step()] = n
|
|
133
|
+
l2_tdict = OrderedDict()
|
|
134
|
+
for n in l2_normset:
|
|
135
|
+
l2_tdict[n.format_step()] = n
|
|
136
|
+
ikeys = set(l1_tdict.keys()) & set(l2_tdict.keys())
|
|
137
|
+
for k in ikeys:
|
|
138
|
+
norms_eq[k] = l1_tdict[k] == l2_tdict[k]
|
|
139
|
+
else:
|
|
140
|
+
for i, n in enumerate(l2_normset):
|
|
141
|
+
k = n.format_step()
|
|
142
|
+
norms_eq[k] = n == l1_normset[i]
|
|
143
|
+
|
|
144
|
+
jos_eq = OrderedDict()
|
|
145
|
+
jos_diff = OrderedDict()
|
|
146
|
+
if len(l2_jos):
|
|
147
|
+
if not l1_jos == l2_jos:
|
|
148
|
+
# If the JoTables list is not consistent: do nothing
|
|
149
|
+
if list(l1_jos.keys()) == list(l2_jos.keys()):
|
|
150
|
+
for table1, table2 in zip(l1_jos.values(), l2_jos.values()):
|
|
151
|
+
jos_eq[table1.name] = table1 == table2
|
|
152
|
+
if not jos_eq[table1.name]:
|
|
153
|
+
jos_diff[table1.name] = OrderedDict()
|
|
154
|
+
# We only save differences when deltaN or deltaJo != 0
|
|
155
|
+
for otype_k, otype_v in table2.compute_diff(table1).items():
|
|
156
|
+
otype_tmp = OrderedDict()
|
|
157
|
+
for sensor_k, sensor_v in otype_v.items():
|
|
158
|
+
sensor_tmp = OrderedDict()
|
|
159
|
+
for k, v in sensor_v.items():
|
|
160
|
+
if v['n']['diff'] != 0 or v['jo']['diff'] != 0:
|
|
161
|
+
sensor_tmp[k] = v
|
|
162
|
+
if len(sensor_tmp):
|
|
163
|
+
otype_tmp[sensor_k] = sensor_tmp
|
|
164
|
+
if len(otype_tmp):
|
|
165
|
+
jos_diff[table1.name][otype_k] = otype_tmp
|
|
166
|
+
else:
|
|
167
|
+
for k in l1_jos.keys():
|
|
168
|
+
jos_eq[k] = True
|
|
169
|
+
|
|
170
|
+
return ArpIfsListingDiff_Status(norms_eq, jos_eq, jos_diff)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class ArpifsListingsFormatAdapter(FormatAdapterAbstractImplementation):
|
|
174
|
+
_footprint = dict(
|
|
175
|
+
attr=dict(
|
|
176
|
+
format=dict(
|
|
177
|
+
values=['ARPIFSLIST', ],
|
|
178
|
+
),
|
|
179
|
+
)
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
def __init__(self, *kargs, **kwargs):
|
|
183
|
+
super().__init__(*kargs, **kwargs)
|
|
184
|
+
self._lines = None
|
|
185
|
+
self._normset = None
|
|
186
|
+
self._jotables = None
|
|
187
|
+
self._costs = None
|
|
188
|
+
self._end_is_reached = None
|
|
189
|
+
if not self.fmtdelayedopen:
|
|
190
|
+
self.normset
|
|
191
|
+
self.jotables
|
|
192
|
+
self.costs
|
|
193
|
+
self.flush_lines()
|
|
194
|
+
|
|
195
|
+
@property
|
|
196
|
+
def lines(self):
|
|
197
|
+
"""Return an array populated with the listing file lines."""
|
|
198
|
+
if self._lines is None:
|
|
199
|
+
with open(self.filename, self.openmode, encoding='utf-8', errors='replace') as f:
|
|
200
|
+
self._lines = [l.rstrip("\n") for l in f] # to remove trailing '\n'
|
|
201
|
+
return self._lines
|
|
202
|
+
|
|
203
|
+
def flush_lines(self):
|
|
204
|
+
"""By defaults, listing lines are cached (that consumes memory). This method clear the cache."""
|
|
205
|
+
self._lines = None
|
|
206
|
+
|
|
207
|
+
@property
|
|
208
|
+
def end_is_reached(self):
|
|
209
|
+
"""Return whether the end of CNT0 was reached."""
|
|
210
|
+
if self._end_is_reached is None:
|
|
211
|
+
self._end_is_reached = False
|
|
212
|
+
for line in self.lines:
|
|
213
|
+
if any([p in line for p in listings.OutputListing.patterns['end_is_reached']]):
|
|
214
|
+
self._end_is_reached = True
|
|
215
|
+
break
|
|
216
|
+
return self._end_is_reached
|
|
217
|
+
|
|
218
|
+
@property
|
|
219
|
+
def normset(self):
|
|
220
|
+
"""Return a :class:`arpifs_listings.norms.NormsSet` object."""
|
|
221
|
+
if self._normset is None:
|
|
222
|
+
self._normset = norms.NormsSet(self.lines)
|
|
223
|
+
if not self.fmtdelayedopen:
|
|
224
|
+
self.flush_lines()
|
|
225
|
+
return self._normset
|
|
226
|
+
|
|
227
|
+
@property
|
|
228
|
+
def jotables(self):
|
|
229
|
+
"""Return a :class:`arpifs_listings.jo_tables.JoTables` object."""
|
|
230
|
+
if self._jotables is None:
|
|
231
|
+
self._jotables = jo_tables.JoTables(self.filename, self.lines)
|
|
232
|
+
if not self.fmtdelayedopen:
|
|
233
|
+
self.flush_lines()
|
|
234
|
+
return self._jotables
|
|
235
|
+
|
|
236
|
+
@property
|
|
237
|
+
def cost_functions(self):
|
|
238
|
+
"""Return a :class:`arpifs_listings.jo_tables.JoTables` object."""
|
|
239
|
+
if self._costs is None:
|
|
240
|
+
self._costs = cost_functions.CostFunctions(self.filename, self.lines)
|
|
241
|
+
if not self.fmtdelayedopen:
|
|
242
|
+
self.flush_lines()
|
|
243
|
+
return self._costs
|
|
244
|
+
|
|
245
|
+
def __len__(self):
|
|
246
|
+
"""The number of lines in the listing."""
|
|
247
|
+
return len(self.lines)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
class ListBasedCutoffDispenser:
|
|
251
|
+
"""
|
|
252
|
+
From a dictionary of cutoff times (probably read from an extraction listing,
|
|
253
|
+
see :class:`BdmBufrListingsFormatAdapter`), for a given *obstype*, find the
|
|
254
|
+
best suited cutoff time.
|
|
255
|
+
|
|
256
|
+
The __call__ method takes a unique *obstype* argument. It will return the
|
|
257
|
+
best suited cutoff time for this particular *obstype*. N.B: If no exact
|
|
258
|
+
match is found, the latest cutoff time will be used.
|
|
259
|
+
"""
|
|
260
|
+
|
|
261
|
+
def __init__(self, cutoffs, fuse_per_obstype=False):
|
|
262
|
+
self._cutoffs = cutoffs
|
|
263
|
+
f_cutoffs = {}
|
|
264
|
+
for k, dates in cutoffs.items():
|
|
265
|
+
f_dates = [d for d in dates if d is not None]
|
|
266
|
+
if f_dates:
|
|
267
|
+
f_cutoffs[k] = f_dates
|
|
268
|
+
if f_cutoffs:
|
|
269
|
+
self._max_cutoff = max([max(dates) for dates in f_cutoffs.values()])
|
|
270
|
+
else:
|
|
271
|
+
self._max_cutoff = None
|
|
272
|
+
self._default_cutoffs = defaultdict(lambda: self._max_cutoff)
|
|
273
|
+
self._default_cutoffs.update({k: max(dates)
|
|
274
|
+
for k, dates in f_cutoffs.items()})
|
|
275
|
+
self._fuse_per_obstype = fuse_per_obstype
|
|
276
|
+
|
|
277
|
+
@property
|
|
278
|
+
def max_cutoff(self):
|
|
279
|
+
"""The latest cutoff time(accoss any available obstypes)."""
|
|
280
|
+
return self._max_cutoff
|
|
281
|
+
|
|
282
|
+
@property
|
|
283
|
+
def default_cutoffs(self):
|
|
284
|
+
"""A dictionary of the latest cutoff time for each of the obstypes."""
|
|
285
|
+
return self._default_cutoffs
|
|
286
|
+
|
|
287
|
+
def __call__(self, obstype):
|
|
288
|
+
"""Find the best suited cutoff time for *obstype*."""
|
|
289
|
+
obstype = obstype.lower()
|
|
290
|
+
if self._cutoffs.get(obstype, None) and not self._fuse_per_obstype:
|
|
291
|
+
item = self._cutoffs[obstype].popleft()
|
|
292
|
+
return item or self.default_cutoffs[obstype]
|
|
293
|
+
else:
|
|
294
|
+
return self.default_cutoffs[obstype]
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
class BdmBufrListingsFormatAdapter(FormatAdapterAbstractImplementation):
|
|
298
|
+
"""Read the content of a BDM extraction output listing."""
|
|
299
|
+
|
|
300
|
+
_footprint = dict(
|
|
301
|
+
attr=dict(
|
|
302
|
+
format=dict(
|
|
303
|
+
values=['BDMBUFR_LISTING', ],
|
|
304
|
+
),
|
|
305
|
+
)
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
_RE_OBSTYPE_GRP = re.compile(r"^.*tentative\s+(?:d')?extraction\s+pour\s+'?(?P<obstype>\w+)'?\b",
|
|
309
|
+
re.IGNORECASE)
|
|
310
|
+
_RE_OBSTYPE_CUT = re.compile(r"^.*cutoff\s+pour\s+'?(?P<obstype>\w+)'?\s*:\s*(?P<datetime>\d+)\b",
|
|
311
|
+
re.IGNORECASE)
|
|
312
|
+
|
|
313
|
+
def __init__(self, *kargs, **kwargs):
|
|
314
|
+
super().__init__(*kargs, **kwargs)
|
|
315
|
+
self._lines = None
|
|
316
|
+
self._cutoffs = defaultdict(deque)
|
|
317
|
+
if not self.fmtdelayedopen:
|
|
318
|
+
self.lines
|
|
319
|
+
|
|
320
|
+
@property
|
|
321
|
+
def lines(self):
|
|
322
|
+
"""Return an array populated with the listing file lines."""
|
|
323
|
+
if self._lines is None:
|
|
324
|
+
with open(self.filename, self.openmode, encoding='utf-8', errors='replace') as f:
|
|
325
|
+
self._lines = [l.rstrip("\n") for l in f] # to remove trailing '\n'
|
|
326
|
+
return self._lines
|
|
327
|
+
|
|
328
|
+
@property
|
|
329
|
+
def cutoffs(self):
|
|
330
|
+
"""
|
|
331
|
+
A dictionary of cutoff times for all of the obstypes available in the
|
|
332
|
+
listing.
|
|
333
|
+
"""
|
|
334
|
+
if not self._cutoffs:
|
|
335
|
+
cur_obstype = None
|
|
336
|
+
for line in self.lines:
|
|
337
|
+
l_match = self._RE_OBSTYPE_GRP.match(line)
|
|
338
|
+
if l_match:
|
|
339
|
+
if cur_obstype is not None:
|
|
340
|
+
self._cutoffs[cur_obstype].append(None)
|
|
341
|
+
cur_obstype = l_match.group('obstype').lower()
|
|
342
|
+
if cur_obstype:
|
|
343
|
+
l_match = self._RE_OBSTYPE_CUT.match(line)
|
|
344
|
+
if l_match and l_match.group('obstype').lower() == cur_obstype:
|
|
345
|
+
self._cutoffs[cur_obstype].append(Date(l_match.group('datetime')))
|
|
346
|
+
cur_obstype = None
|
|
347
|
+
if cur_obstype is not None:
|
|
348
|
+
self._cutoffs[cur_obstype].append(None)
|
|
349
|
+
return self._cutoffs
|
|
350
|
+
|
|
351
|
+
def cutoffs_dispenser(self, fuse_per_obstype=False):
|
|
352
|
+
"""Return a new :class:`CutoffDispenser` object."""
|
|
353
|
+
return ListBasedCutoffDispenser(copy.deepcopy(self.cutoffs),
|
|
354
|
+
fuse_per_obstype=fuse_per_obstype)
|