vortex-nwp 2.0.0__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 (144) hide show
  1. vortex/__init__.py +159 -0
  2. vortex/algo/__init__.py +13 -0
  3. vortex/algo/components.py +2462 -0
  4. vortex/algo/mpitools.py +1953 -0
  5. vortex/algo/mpitools_templates/__init__.py +1 -0
  6. vortex/algo/mpitools_templates/envelope_wrapper_default.tpl +27 -0
  7. vortex/algo/mpitools_templates/envelope_wrapper_mpiauto.tpl +29 -0
  8. vortex/algo/mpitools_templates/wrapstd_wrapper_default.tpl +18 -0
  9. vortex/algo/serversynctools.py +171 -0
  10. vortex/config.py +112 -0
  11. vortex/data/__init__.py +19 -0
  12. vortex/data/abstractstores.py +1510 -0
  13. vortex/data/containers.py +835 -0
  14. vortex/data/contents.py +622 -0
  15. vortex/data/executables.py +275 -0
  16. vortex/data/flow.py +119 -0
  17. vortex/data/geometries.ini +2689 -0
  18. vortex/data/geometries.py +799 -0
  19. vortex/data/handlers.py +1230 -0
  20. vortex/data/outflow.py +67 -0
  21. vortex/data/providers.py +487 -0
  22. vortex/data/resources.py +207 -0
  23. vortex/data/stores.py +1390 -0
  24. vortex/data/sync_templates/__init__.py +0 -0
  25. vortex/gloves.py +309 -0
  26. vortex/layout/__init__.py +20 -0
  27. vortex/layout/contexts.py +577 -0
  28. vortex/layout/dataflow.py +1220 -0
  29. vortex/layout/monitor.py +969 -0
  30. vortex/nwp/__init__.py +14 -0
  31. vortex/nwp/algo/__init__.py +21 -0
  32. vortex/nwp/algo/assim.py +537 -0
  33. vortex/nwp/algo/clim.py +1086 -0
  34. vortex/nwp/algo/coupling.py +831 -0
  35. vortex/nwp/algo/eda.py +840 -0
  36. vortex/nwp/algo/eps.py +785 -0
  37. vortex/nwp/algo/forecasts.py +886 -0
  38. vortex/nwp/algo/fpserver.py +1303 -0
  39. vortex/nwp/algo/ifsnaming.py +463 -0
  40. vortex/nwp/algo/ifsroot.py +404 -0
  41. vortex/nwp/algo/monitoring.py +263 -0
  42. vortex/nwp/algo/mpitools.py +694 -0
  43. vortex/nwp/algo/odbtools.py +1258 -0
  44. vortex/nwp/algo/oopsroot.py +916 -0
  45. vortex/nwp/algo/oopstests.py +220 -0
  46. vortex/nwp/algo/request.py +660 -0
  47. vortex/nwp/algo/stdpost.py +1641 -0
  48. vortex/nwp/data/__init__.py +30 -0
  49. vortex/nwp/data/assim.py +380 -0
  50. vortex/nwp/data/boundaries.py +314 -0
  51. vortex/nwp/data/climfiles.py +521 -0
  52. vortex/nwp/data/configfiles.py +153 -0
  53. vortex/nwp/data/consts.py +954 -0
  54. vortex/nwp/data/ctpini.py +149 -0
  55. vortex/nwp/data/diagnostics.py +209 -0
  56. vortex/nwp/data/eda.py +147 -0
  57. vortex/nwp/data/eps.py +432 -0
  58. vortex/nwp/data/executables.py +1045 -0
  59. vortex/nwp/data/fields.py +111 -0
  60. vortex/nwp/data/gridfiles.py +380 -0
  61. vortex/nwp/data/logs.py +584 -0
  62. vortex/nwp/data/modelstates.py +363 -0
  63. vortex/nwp/data/monitoring.py +193 -0
  64. vortex/nwp/data/namelists.py +696 -0
  65. vortex/nwp/data/obs.py +840 -0
  66. vortex/nwp/data/oopsexec.py +74 -0
  67. vortex/nwp/data/providers.py +207 -0
  68. vortex/nwp/data/query.py +206 -0
  69. vortex/nwp/data/stores.py +160 -0
  70. vortex/nwp/data/surfex.py +337 -0
  71. vortex/nwp/syntax/__init__.py +9 -0
  72. vortex/nwp/syntax/stdattrs.py +437 -0
  73. vortex/nwp/tools/__init__.py +10 -0
  74. vortex/nwp/tools/addons.py +40 -0
  75. vortex/nwp/tools/agt.py +67 -0
  76. vortex/nwp/tools/bdap.py +59 -0
  77. vortex/nwp/tools/bdcp.py +41 -0
  78. vortex/nwp/tools/bdm.py +24 -0
  79. vortex/nwp/tools/bdmp.py +54 -0
  80. vortex/nwp/tools/conftools.py +1661 -0
  81. vortex/nwp/tools/drhook.py +66 -0
  82. vortex/nwp/tools/grib.py +294 -0
  83. vortex/nwp/tools/gribdiff.py +104 -0
  84. vortex/nwp/tools/ifstools.py +203 -0
  85. vortex/nwp/tools/igastuff.py +273 -0
  86. vortex/nwp/tools/mars.py +68 -0
  87. vortex/nwp/tools/odb.py +657 -0
  88. vortex/nwp/tools/partitioning.py +258 -0
  89. vortex/nwp/tools/satrad.py +71 -0
  90. vortex/nwp/util/__init__.py +6 -0
  91. vortex/nwp/util/async.py +212 -0
  92. vortex/nwp/util/beacon.py +40 -0
  93. vortex/nwp/util/diffpygram.py +447 -0
  94. vortex/nwp/util/ens.py +279 -0
  95. vortex/nwp/util/hooks.py +139 -0
  96. vortex/nwp/util/taskdeco.py +85 -0
  97. vortex/nwp/util/usepygram.py +697 -0
  98. vortex/nwp/util/usetnt.py +101 -0
  99. vortex/proxy.py +6 -0
  100. vortex/sessions.py +374 -0
  101. vortex/syntax/__init__.py +9 -0
  102. vortex/syntax/stdattrs.py +867 -0
  103. vortex/syntax/stddeco.py +185 -0
  104. vortex/toolbox.py +1117 -0
  105. vortex/tools/__init__.py +20 -0
  106. vortex/tools/actions.py +523 -0
  107. vortex/tools/addons.py +316 -0
  108. vortex/tools/arm.py +96 -0
  109. vortex/tools/compression.py +325 -0
  110. vortex/tools/date.py +27 -0
  111. vortex/tools/ddhpack.py +10 -0
  112. vortex/tools/delayedactions.py +782 -0
  113. vortex/tools/env.py +541 -0
  114. vortex/tools/folder.py +834 -0
  115. vortex/tools/grib.py +738 -0
  116. vortex/tools/lfi.py +953 -0
  117. vortex/tools/listings.py +423 -0
  118. vortex/tools/names.py +637 -0
  119. vortex/tools/net.py +2124 -0
  120. vortex/tools/odb.py +10 -0
  121. vortex/tools/parallelism.py +368 -0
  122. vortex/tools/prestaging.py +210 -0
  123. vortex/tools/rawfiles.py +10 -0
  124. vortex/tools/schedulers.py +480 -0
  125. vortex/tools/services.py +940 -0
  126. vortex/tools/storage.py +996 -0
  127. vortex/tools/surfex.py +61 -0
  128. vortex/tools/systems.py +3976 -0
  129. vortex/tools/targets.py +440 -0
  130. vortex/util/__init__.py +9 -0
  131. vortex/util/config.py +1122 -0
  132. vortex/util/empty.py +24 -0
  133. vortex/util/helpers.py +216 -0
  134. vortex/util/introspection.py +69 -0
  135. vortex/util/iosponge.py +80 -0
  136. vortex/util/roles.py +49 -0
  137. vortex/util/storefunctions.py +129 -0
  138. vortex/util/structs.py +26 -0
  139. vortex/util/worker.py +162 -0
  140. vortex_nwp-2.0.0.dist-info/METADATA +67 -0
  141. vortex_nwp-2.0.0.dist-info/RECORD +144 -0
  142. vortex_nwp-2.0.0.dist-info/WHEEL +5 -0
  143. vortex_nwp-2.0.0.dist-info/licenses/LICENSE +517 -0
  144. vortex_nwp-2.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,74 @@
1
+ """
2
+ Various Resources for executables used by the OOPS software.
3
+ """
4
+
5
+ from vortex.data.executables import NWPModel
6
+ from ..syntax.stdattrs import gvar, arpifs_cycle, executable_flavour_deco
7
+ from ..syntax.stdattrs import oops_run, known_oops_testcomponent_runs
8
+ from vortex.syntax.stddeco import namebuilding_append
9
+
10
+ #: No automatic export
11
+ __all__ = []
12
+
13
+
14
+ @namebuilding_append("src", lambda self: self.run)
15
+ class OOPSBinary(NWPModel):
16
+ """Yet an other OOPS Binary."""
17
+
18
+ _footprint = [
19
+ arpifs_cycle,
20
+ gvar,
21
+ oops_run,
22
+ executable_flavour_deco,
23
+ dict(
24
+ info="OOPS Binary: an OOPS binary, dedicated to a task (a run in OOPS namespace).",
25
+ attr=dict(
26
+ kind=dict(
27
+ values=[
28
+ "oopsbinary",
29
+ ],
30
+ ),
31
+ gvar=dict(
32
+ default="master_[run]",
33
+ ),
34
+ run=dict(
35
+ outcast=known_oops_testcomponent_runs,
36
+ ),
37
+ ),
38
+ ),
39
+ ]
40
+
41
+ @property
42
+ def realkind(self):
43
+ return "oopsbinary"
44
+
45
+ def command_line(self, configfile):
46
+ """
47
+ Build command line for execution as a single string.
48
+ """
49
+ cmdline = "{}".format(configfile)
50
+ return cmdline
51
+
52
+
53
+ class OOPSTestComponent(OOPSBinary):
54
+ """Binary for OOPS Tests of components."""
55
+
56
+ _footprint = dict(
57
+ info="OOPS Component Test: can run a sub-test or a family of sub-tests",
58
+ attr=dict(
59
+ run=dict(
60
+ values=known_oops_testcomponent_runs,
61
+ outcast=[],
62
+ ),
63
+ ),
64
+ )
65
+
66
+ def command_line(self, configfile, test_type=None):
67
+ """
68
+ Build command line for execution as a single string.
69
+ """
70
+ cmdline = ""
71
+ if test_type is not None:
72
+ cmdline += "-t {} ".format(test_type)
73
+ cmdline += super().command_line(configfile)
74
+ return cmdline
@@ -0,0 +1,207 @@
1
+ """
2
+ Common Providers.
3
+
4
+ For now, only the BDPE access is available here (Base de Donnée des Produits Élaborés).
5
+ This provider should work both on Soprano servers and on HPC, be it experimentally for
6
+ certain parameters combinations.
7
+ """
8
+
9
+ from bronx.fancies import loggers
10
+ from bronx.stdtypes.date import Time
11
+ from vortex.data.providers import Provider
12
+ from vortex.syntax.stdattrs import DelayedEnvValue, Namespace, namespacefp
13
+ from vortex.util.config import GenericConfigParser
14
+
15
+ #: No automatic export
16
+ __all__ = []
17
+
18
+ logger = loggers.getLogger(__name__)
19
+
20
+
21
+ class BdpeError(Exception):
22
+ """Base for Bdpe errors."""
23
+
24
+ pass
25
+
26
+
27
+ class BdpeConfigurationError(BdpeError):
28
+ """Missing BDPE product description."""
29
+
30
+ pass
31
+
32
+
33
+ class BdpeMismatchError(BdpeError):
34
+ """BDPE product description does not match ressource description."""
35
+
36
+ pass
37
+
38
+
39
+ class BdpeProvider(Provider):
40
+ """
41
+ Provider to resources stored in the BDPE database.
42
+
43
+ The BDPE only knows about product ids, base datetime, and terms.
44
+ A dedicated ini file describes the relationship between such ids and
45
+ Vortex resources. This link could be used to deduce the BDPE id from
46
+ the resource (a la footprints). For now, it is only checked that the
47
+ resource attributes are compatible with the product description.
48
+
49
+ Canvas of a complete uri:
50
+ bdpe://bdpe.archive.fr/EXPE/date/BDPE_num+term
51
+
52
+ The EXPE part is built from the footprint attributes that correspond
53
+ to env variables used by the underlying tool we use: preferred_target,
54
+ forbidden_target, aso., so that the BDPE store can retrieve them from
55
+ the uri (See :class:`BdpeStore`).
56
+
57
+ When a resource has no ``date`` attribute, the most recent data is
58
+ extracted from the BDPE (this feature may be used for Alert Models).
59
+ """
60
+
61
+ _footprint = [
62
+ namespacefp,
63
+ dict(
64
+ info="BDPE provider",
65
+ attr=dict(
66
+ namespace=dict(
67
+ default=Namespace("bdpe.archive.fr"),
68
+ values=["bdpe.archive.fr"],
69
+ ),
70
+ bdpeid=dict(),
71
+ preferred_target=dict(
72
+ info="The database we'd like to get the data from - See the BDPE documentation.",
73
+ optional=True,
74
+ default=DelayedEnvValue("BDPE_CIBLE_PREFEREE", "SEC"),
75
+ values=[
76
+ "OPER",
77
+ "INT",
78
+ "SEC",
79
+ "oper",
80
+ "int",
81
+ "sec",
82
+ ],
83
+ ),
84
+ forbidden_target=dict(
85
+ info="The database we don't want to access - See the BDPE documentation.",
86
+ optional=True,
87
+ default=DelayedEnvValue("BDPE_CIBLE_INTERDITE", "OPER"),
88
+ values=[
89
+ "OPER",
90
+ "INT",
91
+ "SEC",
92
+ "oper",
93
+ "int",
94
+ "sec",
95
+ ],
96
+ ),
97
+ soprano_domain=dict(
98
+ info="Databases priorities profile - See the BDPE documentation.",
99
+ optional=True,
100
+ default=DelayedEnvValue("DOMAINE_SOPRA", "dev"),
101
+ values=["oper", "int", "dev"],
102
+ ),
103
+ allow_archive=dict(
104
+ info="Allow the use of the archive version of the BDPE databases.",
105
+ optional=True,
106
+ type=bool,
107
+ default=False,
108
+ ),
109
+ bdpe_timeout=dict(
110
+ info="Seconds before abandoning a request.",
111
+ optional=True,
112
+ type=int,
113
+ default=10,
114
+ ),
115
+ bdpe_retries=dict(
116
+ info="Number of retries when a request fails.",
117
+ optional=True,
118
+ type=int,
119
+ default=3,
120
+ ),
121
+ config=dict(
122
+ info="A ready to use configuration file object for this storage place.",
123
+ type=GenericConfigParser,
124
+ optional=True,
125
+ default=None,
126
+ ),
127
+ inifile=dict(
128
+ info=(
129
+ "The name of the configuration file that will be used (if "
130
+ + "**config** is not provided."
131
+ ),
132
+ optional=True,
133
+ default="@bdpe-map-resources.ini",
134
+ ),
135
+ ),
136
+ fastkeys={"bdpeid"},
137
+ ),
138
+ ]
139
+
140
+ def __init__(self, *args, **kw):
141
+ logger.debug("BDPE provider init %s", self.__class__)
142
+ super().__init__(*args, **kw)
143
+ self._actual_config = self.config
144
+ if self._actual_config is None:
145
+ self._actual_config = GenericConfigParser(inifile=self.inifile)
146
+
147
+ @property
148
+ def realkind(self):
149
+ return "bdpe"
150
+
151
+ def scheme(self, resource):
152
+ """A dedicated scheme."""
153
+ return "bdpe"
154
+
155
+ def netloc(self, resource):
156
+ """The actual netloc is the ``namespace`` attribute of the current provider."""
157
+ return self.namespace.netloc
158
+
159
+ def basename(self, resource):
160
+ """Something like 'BDPE_num+term'."""
161
+ myterm = getattr(resource, "term", Time(0))
162
+ if int(myterm) < 0:
163
+ myterm = Time(9000) - myterm
164
+ return "BDPE_{}+{!s}".format(self.bdpeid, myterm)
165
+
166
+ def pathname(self, resource):
167
+ """Something like 'PREFERRED_FORBIDDEN_DOMAIN_ARCHIVE_TIMEOUT_RETRIES/date/'."""
168
+ try:
169
+ requested_date = resource.date.vortex()
170
+ except AttributeError:
171
+ requested_date = "most_recent"
172
+ return "{}_{}_{}_{}_{}_{}/{}".format(
173
+ self.preferred_target,
174
+ self.forbidden_target,
175
+ self.soprano_domain,
176
+ self.allow_archive,
177
+ self.bdpe_timeout,
178
+ self.bdpe_retries,
179
+ requested_date,
180
+ )
181
+
182
+ def uri(self, resource):
183
+ """
184
+ Overridden to check the resource attributes against
185
+ the BDPE product description from the .ini file.
186
+ """
187
+ # check that the product is described in the configuration file
188
+ if not self._actual_config.has_section(self.bdpeid):
189
+ fmt = 'Missing product n°{} in BDPE configuration file\n"{}"'
190
+ raise BdpeConfigurationError(
191
+ fmt.format(self.bdpeid, self.config.file)
192
+ )
193
+
194
+ # resource description: rely on the footprint_export (also used to JSONise resources).
195
+ rsrcdict = {k: str(v) for k, v in resource.footprint_export().items()}
196
+
197
+ # check the BDPE pairs against the resource's
198
+ for k, v in self._actual_config.items(self.bdpeid):
199
+ if k not in rsrcdict:
200
+ raise BdpeMismatchError(
201
+ 'Missing key "{}" in resource'.format(k)
202
+ )
203
+ if rsrcdict[k] != v:
204
+ fmt = 'Bad value for key "{}": rsrc="{}" bdpe="{}"'
205
+ raise BdpeMismatchError(fmt.format(k, rsrcdict[k], v))
206
+
207
+ return super().uri(resource)
@@ -0,0 +1,206 @@
1
+ """
2
+ Resources for query files used for extractions in various databases.
3
+ """
4
+
5
+ import re
6
+
7
+ from bronx.fancies import loggers
8
+
9
+ from vortex.data.outflow import StaticResource
10
+ from ..syntax.stdattrs import gvar
11
+ from vortex.data.contents import AlmostListContent, DataTemplate
12
+
13
+ #: No automatic export
14
+ __all__ = []
15
+
16
+ logger = loggers.getLogger(__name__)
17
+
18
+
19
+ class Query(StaticResource):
20
+ """Class to deal with queries."""
21
+
22
+ _abstract = True
23
+ _footprint = [
24
+ gvar,
25
+ dict(
26
+ info="Abstract class for queries.",
27
+ attr=dict(
28
+ gvar=dict(values=["extract_stuff"], default="extract_stuff"),
29
+ source=dict(),
30
+ origin=dict(),
31
+ ),
32
+ ),
33
+ ]
34
+
35
+ def gget_urlquery(self):
36
+ """GGET specific query : ``extract``."""
37
+ return "extract=" + self.source
38
+
39
+
40
+ class BDAPQuery(Query):
41
+ """Class to deal with BDAP queries."""
42
+
43
+ _footprint = dict(
44
+ info="BDAP query",
45
+ attr=dict(
46
+ kind=dict(values=["bdap_query"]),
47
+ origin=dict(default="bdap", values=["bdap"], optional=True),
48
+ ),
49
+ )
50
+
51
+ @property
52
+ def realkind(self):
53
+ return "bdap_query"
54
+
55
+
56
+ class BDMPQuery(Query):
57
+ """Class to deal with BDMP queries."""
58
+
59
+ _footprint = dict(
60
+ info="BDMP query",
61
+ attr=dict(
62
+ kind=dict(values=["bdmp_query"]),
63
+ origin=dict(default="bdmp", values=["bdmp"], optional=True),
64
+ ),
65
+ )
66
+
67
+ @property
68
+ def realkind(self):
69
+ return "bdmp_query"
70
+
71
+
72
+ class BDCPQuery(Query):
73
+ """Class to deal with BDCP queries."""
74
+
75
+ _footprint = dict(
76
+ info="BDCP query",
77
+ attr=dict(
78
+ kind=dict(values=["bdcp_query"]),
79
+ origin=dict(default="bdcp", values=["bdcp"], optional=True),
80
+ ),
81
+ )
82
+
83
+ @property
84
+ def realkind(self):
85
+ return "bdcp_query"
86
+
87
+
88
+ class StaticCutoffDispenser:
89
+ """
90
+ From a dictionary of cutoff times, for a given *obstype*, find the
91
+ best suited cutoff time.
92
+
93
+ The __call__ method takes a unique *obstype* argument. It will return the
94
+ best suited cutoff time for this particular *obstype*. N.B: If no exact
95
+ match is found, the default cutoff time will be used.
96
+ """
97
+
98
+ def __init__(self, default_cutoff, obstype_cutoffs=None):
99
+ self._default_cutoff = default_cutoff
100
+ if obstype_cutoffs:
101
+ self._cutoffs = {
102
+ k: {o.lower() for o in v} for k, v in obstype_cutoffs.items()
103
+ }
104
+ else:
105
+ self._cutoffs = {}
106
+
107
+ @property
108
+ def max_cutoff(self):
109
+ return self._default_cutoff
110
+
111
+ def __call__(self, obstype):
112
+ """Find the best suited cutoff time for *obstype*."""
113
+ obstype = obstype.lower()
114
+ found = None
115
+ for k, v in self._cutoffs.items():
116
+ if obstype in v:
117
+ found = k
118
+ if found:
119
+ return found
120
+ else:
121
+ return self._default_cutoff
122
+
123
+
124
+ class BDMQueryContent(AlmostListContent):
125
+ """Read the content of BDM query file."""
126
+
127
+ _RE_OBSTYPE = re.compile(r"^(\s*)(OBS\s+TYPE\s*):(\s+)(\w+)$")
128
+
129
+ def add_cutoff_info(self, cutoffs_dispenser):
130
+ """
131
+ Using a :class:`vortex.tools.listings.ListBasedCutoffDispenser` or
132
+ :class:`StaticCutoffDispenser` object, add the cutoff related
133
+ information in the BDM query.
134
+ """
135
+ if cutoffs_dispenser.max_cutoff is None:
136
+ logger.warning(
137
+ "The cutoffs_dispenser is empty. No cutoff data can be retrieved"
138
+ )
139
+ else:
140
+ xdata = list()
141
+ for line in self:
142
+ xdata.append(line)
143
+ l_match = self._RE_OBSTYPE.match(line)
144
+ if l_match:
145
+ cutoff_fmt = (
146
+ "{0:s}{1:<"
147
+ + str(len(l_match.group(2)))
148
+ + "s}:{2:s}{3.ymdhms:s}\n"
149
+ )
150
+ cutoff_date = cutoffs_dispenser(l_match.group(4))
151
+ xdata.append(
152
+ cutoff_fmt.format(
153
+ l_match.group(1),
154
+ "CUTOFF",
155
+ l_match.group(3),
156
+ cutoff_date,
157
+ )
158
+ )
159
+ logger.info(
160
+ "CUTOFF=%s added for obstype < %s >.",
161
+ cutoff_date.ymdhms,
162
+ l_match.group(4),
163
+ )
164
+ self._data = xdata
165
+
166
+
167
+ class BDMQuery(Query):
168
+ """Class to deal with BDM queries."""
169
+
170
+ _footprint = dict(
171
+ info="BDM query",
172
+ attr=dict(
173
+ kind=dict(values=["bdm_query"]),
174
+ origin=dict(default="bdm", values=["bdm"], optional=True),
175
+ clscontents=dict(
176
+ default=BDMQueryContent,
177
+ ),
178
+ ),
179
+ )
180
+
181
+ @property
182
+ def realkind(self):
183
+ return "bdm_query"
184
+
185
+
186
+ class MarsQuery(Query):
187
+ """Class to deal with Mars queries"""
188
+
189
+ _footprint = dict(
190
+ info="Mars query",
191
+ attr=dict(
192
+ kind=dict(values=["mars_query"]),
193
+ origin=dict(
194
+ default="mars",
195
+ values=[
196
+ "mars",
197
+ ],
198
+ optional=True,
199
+ ),
200
+ clscontents=dict(default=DataTemplate),
201
+ ),
202
+ )
203
+
204
+ @property
205
+ def realkind(self):
206
+ return "mars_query"
@@ -0,0 +1,160 @@
1
+ # pylint: disable=unused-argument
2
+
3
+ """
4
+ Common stores.
5
+
6
+ For now, only the BDPE store is available here, dedicated to BDPE extraction.
7
+ """
8
+
9
+ import footprints
10
+ import vortex
11
+ from bronx.fancies import loggers
12
+ from bronx.stdtypes import date
13
+ from vortex.data.abstractstores import Store
14
+ from vortex.syntax.stdattrs import compressionpipeline
15
+
16
+ #: No automatic export
17
+ __all__ = []
18
+
19
+ logger = loggers.getLogger(__name__)
20
+
21
+
22
+ class BdpeStore(Store):
23
+ """Access items stored in the BDPE database (get only)."""
24
+
25
+ _footprint = [
26
+ compressionpipeline,
27
+ dict(
28
+ info="Access the BDPE database",
29
+ attr=dict(
30
+ scheme=dict(
31
+ values=["bdpe"],
32
+ ),
33
+ netloc=dict(
34
+ values=["bdpe.archive.fr"],
35
+ ),
36
+ ),
37
+ priority=dict(level=footprints.priorities.top.DEFAULT),
38
+ ),
39
+ ]
40
+
41
+ @property
42
+ def realkind(self):
43
+ return "bdpe"
44
+
45
+ def bdpelocate(self, remote, options):
46
+ """Reasonably close to whatever 'remote location' could mean.
47
+
48
+ e.g.: ``bdpe://bdpe.archive.fr/EXPE/date/BDPE_num+term``
49
+ """
50
+ return self.scheme + "://" + self.netloc + remote["path"]
51
+
52
+ def bdpecheck(self, remote, options):
53
+ """Cannot check a BDPE call a priori."""
54
+ logger.warning("A BdpeStore is not able to perform CHECKs.")
55
+ return False
56
+
57
+ def bdpeput(self, local, remote, options):
58
+ """Cannot write to the BDPE (See :class:`BdpeService`)."""
59
+ logger.error("A BdpeStore is not able to perform PUTs.")
60
+ return False
61
+
62
+ def bdpedelete(self, remote, options):
63
+ """Cannot delete a BDPE product."""
64
+ logger.error("A BdpeStore is not able to perform DELETEs.")
65
+ return False
66
+
67
+ def bdpeget(self, remote, local, options):
68
+ """Real extraction from the BDPE database."""
69
+
70
+ # Check that local is a file (i.e not a virtual container)
71
+ if not isinstance(local, str):
72
+ raise TypeError(
73
+ "The BDPE provider can not deal with virtual containers"
74
+ )
75
+
76
+ # remote['path'] looks like '/OPER_SEC_DEV_True_10_3/20151105T0000P/BDPE_42+06:00'
77
+ _, targetmix, str_date, more = remote["path"].split("/")
78
+ p_target, f_target, domain, s_archive, timeout, retries = (
79
+ targetmix.split("_")
80
+ )
81
+ productid, str_term = more[5:].split("+")
82
+
83
+ # the 'oper' domain is allowed only to the operational suite
84
+ if domain == "oper":
85
+ if not vortex.ticket().glove.profile == "oper":
86
+ logger.warning(
87
+ "Only profile 'oper' can use 'soprano_domain=oper'. Using 'dev' instead."
88
+ )
89
+ domain = "dev"
90
+
91
+ if str_date == "most_recent":
92
+ bdpe_date = "/"
93
+ else:
94
+ bdpe_date = date.Date(str_date).ymdhms
95
+ bdpe_term = date.Time(str_term).fmtraw
96
+ args = [
97
+ productid, # id
98
+ bdpe_date, # date: yyyymmddhhmmss
99
+ bdpe_term, # term: HHHHmm
100
+ local, # local filename
101
+ ]
102
+ extraenv = dict(
103
+ BDPE_CIBLE_PREFEREE=p_target,
104
+ BDPE_CIBLE_INTERDITE=f_target,
105
+ DOMAINE_SOPRA=domain,
106
+ BDPE_TIMEOUT=timeout,
107
+ BDPE_RETRYS=retries,
108
+ )
109
+ if s_archive == "True":
110
+ extraenv["BDPE_LECTURE_ARCHIVE_AUTORISEE"] = "oui"
111
+
112
+ wsinterpreter = self.system.default_target.get(
113
+ "bdpe:wsclient_interpreter", None
114
+ )
115
+ wscommand = self.system.default_target.get("bdpe:wsclient_path", None)
116
+ if wscommand is None:
117
+ raise RuntimeError(
118
+ "bdpe:wsclient_path has to be set in the target config"
119
+ )
120
+
121
+ args.insert(0, wscommand)
122
+ if wsinterpreter is not None:
123
+ args.insert(0, wsinterpreter)
124
+
125
+ logger.debug("lirepe_cmd: %s", " ".join(args))
126
+
127
+ with self.system.env.delta_context(**extraenv):
128
+ rc = self.system.spawn(args, output=False, fatal=False)
129
+ rc = rc and self.system.path.exists(local)
130
+
131
+ diagfile = local + ".diag"
132
+ if not rc:
133
+ logger.warning(
134
+ "Something went wrong with the following command: %s",
135
+ " ".join(args),
136
+ )
137
+ if not rc or bdpe_date == "/":
138
+ if self.system.path.exists(diagfile):
139
+ logger.warning("The %s file is:", diagfile)
140
+ self.system.cat(diagfile)
141
+ if rc and self._actual_cpipeline:
142
+ # Deal with compressed files in the BDPE using the optional attribute
143
+ # store_compressed of the BDPE store.
144
+ tempfile = local + self._actual_cpipeline.suffix
145
+ rc = rc and self.system.mv(local, tempfile)
146
+ self._actual_cpipeline.file2uncompress(tempfile, local)
147
+ rc = rc and self.system.path.exists(local)
148
+ if not rc:
149
+ logger.warning(
150
+ "Something went wrong while uncompressing the file %s.",
151
+ tempfile,
152
+ )
153
+
154
+ # Final step : deal with format specific packing
155
+ rc = rc and self.system.forceunpack(local, fmt=options.get("fmt"))
156
+
157
+ if self.system.path.exists(diagfile):
158
+ self.system.remove(diagfile)
159
+
160
+ return rc