#!/usr/bin/python3
# -*- coding: utf-8 -*-
########################################################################
#
# This file is part of python module <pyspc>.
# Copyright (C) 2013-2021 R. Marty
# (renaud.marty@developpement-durable.gouv.fr)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program (see COPYING.txt).
# If not, see <http://www.gnu.org/licenses/>.
#
########################################################################
"""Webservice - Projet LAMEDO - BdImage."""
import collections
import itertools
import os.path
import re
import requests
import pyspc.core.exception as _exception
from pyspc.convention.lamedo import (
BDIMAGE_BANDS, BDIMAGE_FORECASTS, BDIMAGE_PRECISION, BDIMAGE_STATS)
BBox = collections.namedtuple('BBox', ['ul', 'lr', 'asstr'],
defaults=[None, None, None])
BBox.__doc__ = """Bounding Box pour BdImage
Attributes
----------
ul : tuple
Point haut gauche
lr : tuple
Point bas droite
asstr : str
Bounding Box sous forme de texte
"""
RE_STATUT = re.compile(r'<statut>0</statut>')
[docs]
class BdImage():
"""
Structure du client accédant aux données de BdImage.
Attributes
----------
proxies : None
Dictionnaire des proxys {'protocol': 'proxy'}
client : libbdimage.bdiws.Client
Client de connexion à BdImage
filename : None, str
Nom du dernier fichier écrit
timeout : int
Délai maximal de réception des requêtes. Défault: 60
url : None, str
URL de la dernière requête
"""
[docs]
def __init__(self, timeout=60):
"""
Initialise l'instance du webservice BdImage.
Parameters
----------
timeout : int
Délai maximal de réception des requêtes. Défault: 60
"""
super().__init__()
self.filename = None
self.url = None
self.timeout = timeout
self.proxies = None
self._client = self._constructor_client(
proxies=self.proxies, timeout=timeout)
self._webservices = {
(True, False, 'pixels'): self._client.getobsstatsbypixels,
(True, False, 'zones'): self._client.getobsstatsbyzones,
(True, True, 'bbox'): self._client.getobsvaluesbybbox,
(True, True, 'pixels'): self._client.getobsvaluesbypixels,
(True, True, 'zones'): self._client.getobsvaluesbyzones,
(False, False, 'pixels'):
self._client.getprevbynetworkstatsbypixels,
(False, False, 'zones'):
self._client.getprevbynetworkstatsbyzones,
(False, True, 'bbox'):
self._client.getprevbynetworkvaluesbybbox,
(False, True, 'pixels'):
self._client.getprevbynetworkvaluesbypixels,
(False, True, 'zones'):
self._client.getprevbynetworkvaluesbyzones,
}
def __str__(self):
"""Afficher les méta-données de l'instance BdImage."""
text = """
*************************************
********* WEBSERVICE - BdImage ******
*************************************
* BdIMAGES PROXIES = {proxies}
* BdIMAGES URL = {url}
* NOM FICHIER = {filename}
*************************************
"""
return text.format(**vars(self))
@property
def _constructor_client(self):
"""Importer libbdimage.bdiws.Client car lib non publique."""
from libbdimage.bdiws import Client
return Client
@property
def _constructor_specie(self):
"""Importer libbdimage.bdibase.Specie car lib non publique."""
from libbdimage.bdibase import Specie
return Specie
@property
def client(self):
"""Client BdImage."""
return self._client
@property
def webservices(self):
"""Webservices BdImage."""
return self._webservices
[docs]
def check_client(self):
"""
Contrôler si le client BdImage existe.
Raises
------
ValueError
Si le client est incorrect
"""
client = self._constructor_client
if not isinstance(self.client, client):
raise ValueError("Le Client BdImage n'en est pas un.")
[docs]
@classmethod
def check_image(cls, image):
"""
Contrôler s'il s'agit bien d'une image BdImage.
Parameters
----------
image : tuple
Image BdImage (type, sous-type, bande)
Raises
------
ValueError
Si l'image est incorrecte
See Also
--------
BdImage.get_datatypes
"""
test = image in cls.get_datatypes(asstr=isinstance(image, str))
if not test:
raise ValueError("Image mal renseignée")
[docs]
def check_precision(self, precision):
"""
Contrôler si la précision est autorisée.
Parameters
----------
precision : str
Qualité de la précision
Raises
------
ValueError
Si la précision est incorrecte
"""
self.check_client()
try:
self.client._check_precision(precision=precision)
except ValueError as ve:
raise ValueError("Précision mal renseignée") from ve
[docs]
def check_start(self, image=None, date=None):
"""
Contrôler si la date respecte le premier instant de disponibilité.
Parameters
----------
date : datetime.datetime
Date à tester
image : tuple
Tuple à 3 dimensions de l'identifiant de l'image
(type Image, sous type Image, bande).
Voir BdImage.get_datatypes() pour avoir la liste
des images autorisées
Raises
------
ValueError
Date est antérieure à la première date disponible de l'image
"""
_exception.check_dt(date)
self.check_image(image)
specie = self._constructor_specie(image[0], image[1])
test = date >= specie.firstdate
if not test:
raise ValueError("Date antérieure à la première date de l'image "
f"{image} : {date} / {specie.firstdate}")
[docs]
def check_stats(self, stats):
"""
Contrôler si la stat est autorisée.
Parameters
----------
stats : str
Longueur du retour des statistiques.
Raises
------
ValueError
Si la stat est incorrecte
"""
self.check_client()
try:
self.client._check_stats(stats=stats)
except ValueError as ve:
raise ValueError("Statistique mal renseignée") from ve
[docs]
def get(self, image=None, tdelta=None,
first_dtime=None, last_dtime=None, runtime=None,
domains=None, epsg='2154', stats=None, precision=None):
"""
Récupérer les données de la BdImage.
Parameters
----------
image : tuple
Tuple à 3 dimensions de l'identifiant de l'image
(type Image, sous type Image, bande).
tdelta : str, timedelta, None
Pas de temps de cumul de l'image
first_dtime : str, datetime, None
Première date de la collection d'images
last_dtime : str, datetime, None
Dernière date de la collection d'images
runtime : str, datetime, None
Instant de production de la prévision. Utilisé uniquement
si le type d'image est parmi BDIMAGE_FORECASTS
domains : str
Chaine de caractères définissant les domaines
epsg : str
Identifiant de la projection. Défaut: '2154' (Lambert 93)
stats : str
Longueur du retour des statistiques.
precision : str
Qualité de la précision.
Returns
-------
content : dict
Dictionnaire des retours XML
- clé: (domain, image,
first_dtime, last_dtime, runtime,
varname)
- valeur: contenu de la réponse XML
See Also
--------
pyspc.convention.lamedo.BDIMAGE_FORECASTS
BdImage.get_datatypes
BdImage.get_precision
BdImage.get_stats
BdImage.split_domains
"""
self.url = None
# ---------------------------------------------------------------------
# 0- Contrôles : par libbdimage
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# 1- Collection d'images
# ---------------------------------------------------------------------
try:
image_coll = _set_collection(
image=image, network=runtime,
start=first_dtime, stop=last_dtime, depth=tdelta)
except ValueError as ve:
if 'durée' in ve.args[0]:
_exception.Warning(
None,
"Une erreur survient à cause de la profondeur/durée. Une "
"tentative est réalisée en la fixant à None.")
image_coll = _set_collection(
image=image, network=runtime,
start=first_dtime, stop=last_dtime, depth=None)
else:
image_coll = None
# ---------------------------------------------------------------------
# 2- Domaines d'extraction
# ---------------------------------------------------------------------
content = collections.OrderedDict()
splitted_domains = split_domains(domains=domains)
# ---------------------------------------------------------------------
# 3 - Boucle sur les 3 types de domaine
# ---------------------------------------------------------------------
for domaintype, subdomains in splitted_domains.items():
if subdomains is None:
continue
ws_content = _request_bdimage(
bdimage=self, image_coll=image_coll, band=image[-1],
domain=subdomains, domaintype=domaintype,
epsg=epsg, stats=stats, precision=precision
)
if domaintype == 'zones':
key = ("+".join(subdomains), image,
first_dtime, last_dtime, runtime, tdelta)
elif domaintype == 'pixels':
key = "+".join([",".join([str(pp) for pp in p])
for p in subdomains])
key = (key, image,
first_dtime, last_dtime, runtime, tdelta)
elif domaintype == 'bbox':
key = (subdomains.asstr, image,
first_dtime, last_dtime, runtime, tdelta)
content.setdefault(key, ws_content)
# ---------------------------------------------------------------------
# 4- Renvoi des contenus XML
# ---------------------------------------------------------------------
return content
[docs]
def retrieve(self, dirname='.', domainname=None,
image=None, tdelta=None,
first_dtime=None, last_dtime=None, runtime=None,
domains=None, epsg='2154', stats=None, precision=None):
"""
Récupérer les données de la BdImage et les enregistrer dans des xml.
Parameters
----------
dirname : str
Répertoire local d'archivage des fichiers XML de BdImage
domainname : str
Nom des domaines géographiques. Si utilisé, le lieu est défini
par domainname-domaintype
Other Parameters
----------------
image : tuple
Tuple à 3 dimensions de l'identifiant de l'image
(type Image, sous type Image, bande).
tdelta : str, timedelta, None
Pas de temps de cumul de l'image
first_dtime : str, datetime, None
Première date de la collection d'images
last_dtime : str, datetime, None
Dernière date de la collection d'images
runtime : str, datetime, None
Instant de production de la prévision. Utilisé uniquement
si le type d'image est parmi BDIMAGE_FORECASTS
domains : str
Chaine de caractères définissant les domaines
epsg : str
Identifiant de la projection. Défaut: '2154' (Lambert 93)
stats : str
Longueur du retour des statistiques.
precision : str
Qualité de la précision.
Returns
-------
filenames : list
Fichiers XML enregistrés
See Also
--------
pyspc.convention.lamedo.BDIMAGE_FORECASTS
BdImage.get
"""
from libbdimage.bdibase import depth2str
# ---------------------------------------------------------------------
# 1- Récupération des contenus XML
# ---------------------------------------------------------------------
content = self.get(
image=image,
tdelta=tdelta,
first_dtime=first_dtime,
last_dtime=last_dtime,
runtime=runtime,
domains=domains,
epsg=epsg,
stats=stats,
precision=precision
)
# ---------------------------------------------------------------------
# 2- Enregistrement des contenus XML
# ---------------------------------------------------------------------
filenames = []
for key in content:
try:
if not RE_STATUT.search(str(content[key])):
raise ValueError
except (KeyError, ValueError):
_exception.Warning(
None, f"Contenu XML incorrect pour la clé {key}")
continue
code = key[0]
if isinstance(domainname, str):
splitted_domains = split_domains(domains=domains)
domaintype = [t
for t, s in splitted_domains.items()
if s is not None][0]
code = f"{domainname}-{domaintype}"
if key[-1] is None:
depth = '000000'
else:
depth = depth2str(key[-1])
if key[4] is None:
basename = f"{code}_{'-'.join(list(key[1]))}"\
f"_{key[2].strftime('%Y%m%d%H%M')}"\
f"-{key[3].strftime('%Y%m%d%H%M')}_{depth}.xml"
else:
basename = f"{code}_{'-'.join(list(key[1]))}"\
f"_{key[4].strftime('%Y%m%d%H%M')}"\
f"-{key[2].strftime('%Y%m%d%H%M')}"\
f"-{key[3].strftime('%Y%m%d%H%M')}_{depth}.xml"
filename = os.path.join(dirname, basename)
try:
with open(filename, 'w', encoding='utf-8', newline="\n") as f:
f.write(content[key].decode('utf-8'))
except FileNotFoundError:
_exception.Warning(
None, "Répertoire local inexistant ou nom de fichier trop "
f"long pour la clé {key}. Veuillez réduire le nombre de "
"pixels/zones ou utiliser l'argument domainname.")
continue
self.filename = filename
filenames.append(filename)
# ---------------------------------------------------------------------
# 3- Renvoi de la liste des fichiers
# ---------------------------------------------------------------------
return filenames
[docs]
@classmethod
def get_precision(cls):
"""Lister des précisions."""
return sorted(BDIMAGE_PRECISION)
[docs]
@classmethod
def get_stats(cls):
"""Lister des longueurs du retour des statistiques."""
return sorted(BDIMAGE_STATS)
[docs]
@classmethod
def get_datatypes(cls, asstr=False):
"""
Lister des images.
Parameters
----------
asstr : bool
Renvoyer une liste de str (True) ou de tuples (False)
Returns
-------
images : list
Liste des images : (type, soustype, bande) ou type_soustype_bande
"""
if asstr:
return sorted(itertools.chain.from_iterable(
[[f"{i}_{b}" for b in BDIMAGE_BANDS[i]]
for i in BDIMAGE_BANDS.keys()])
)
return sorted(itertools.chain.from_iterable(
[[(*i.split('_'), b) for b in BDIMAGE_BANDS[i]]
for i in BDIMAGE_BANDS.keys()])
)
def split_domains(domains=None, epsg='2154'):
"""
Définir les zones / pixels / bbox à partir d'une chaine de caractères.
Parameters
----------
domains : str
Chaine de caractères définissant les domaines
epsg : str
Identifiant de la projection. Défaut: '2154' (Lambert 93)
Returns
-------
dict
{'zones': list, None,
'pixels': libbdimage.bdipixel.Nppixels, None,
'bbox': Bbox, None}
Notes
-----
Définition des domaines géographiques
- Une zone est libellé ainsi: AB123456
- Une sous-bassin est libellé ainsi: AB123-AB456
- Un pixel est libellé ainsi: 1,1
- Un rectangle est libellé ainsi: Bx1,2,3,4
Pour considérer plusieurs zones ou pixels, il suffit de les "coller"
avec le signe '+'. Cette fonctionnalité est ignorée dans le cas
de rectangle
Les pixels et coordonnées des rectangles sont à renseigner en mètre.
Pour le rectangle, il faut renseigner, dans l'ordre
- préfixe 'Bx'
- coordonnée X du point en haut à gauche
- coordonnée Y du point en haut à gauche
- coordonnée X du point en bas à droite
- coordonnée Y du point en bas à droite
"""
from libbdimage.bdipixel import Nppixels
# ---------------------------------------------------------------------
# 0- Contrôles
# ---------------------------------------------------------------------
_exception.check_str(domains)
_exception.raise_valueerror(
domains.count('Bx') > 1,
"une seule Bounding Box est autorisée"
)
# ---------------------------------------------------------------------
# 1- Initialisation
# ---------------------------------------------------------------------
zones = []
pixels = []
bbox = None
# ---------------------------------------------------------------------
# 2- Découpage des domaines
# ---------------------------------------------------------------------
for d in domains.split('+'):
# Cas BOUNDING BOX
if d.startswith('Bx'):
b = d[2:].split(',')
if len(b) != 4:
_exception.Warning(
f"La Bounding Box '{d}' est incorrecte. Elle est ignorée")
continue
bbox = BBox((float(b[0]), float(b[1])),
(float(b[2]), float(b[3])),
d)
# Cas ZONES
elif d[0:2].isalpha() and ',' not in d[2:]:
zones.append(d)
# Cas PIXELS
elif d.replace(',', '').replace('.', '').isdigit():
p = [float(x) for x in d.split(',')]
if len(p) != 2:
_exception.Warning(
f"Le pixel '{d}' est incorrect. Il est ignoré")
continue
pixels.append(tuple(p))
# ---------------------------------------------------------------------
# 3- Renvoi des domaines
# ---------------------------------------------------------------------
return {'zones': zones if zones else None,
'pixels': Nppixels(pixels, epsg=epsg) if pixels else None,
'bbox': bbox}
def _request_bdimage(bdimage=None, image_coll=None, band=None,
domain=None, domaintype=None, epsg='2154',
stats=None, precision=None):
"""
Requêter BdImage.
Parameters
----------
bdimage : BdImage
Instance BdImage
image_coll : libbdimage.bdimage.Collection, libbdimage.bdimage.NCollection
Collection d'images
band : str
Bande de l'image (3e élément du tuple image)
domain : list, libbdimage.bdipixel.Nppixels, Bbox
Domaine géographique: liste de zones, Pixels, Bounding Box
domaintype : str
Type de domaine géographique, parmi ['zones', 'pixels', 'bbox']
epsg : str
Identifiant de la projection. Défaut: '2154' (Lambert 93)
stats : str
Longueur du retour des statistiques.
precision : str
Qualité de la précision.
Returns
-------
content : bytes, str
Contenu du XML BdImage
"""
# ---------------------------------------------------------------------
# 0- Initialisation
# ---------------------------------------------------------------------
params = {'bands': band}
# ---------------------------------------------------------------------
# 1- OBS ou PREV ?
# ---------------------------------------------------------------------
if image_coll.specie.family in BDIMAGE_FORECASTS:
isobs = False
params['ncollection'] = image_coll
else:
isobs = True
params['collection'] = image_coll
# ---------------------------------------------------------------------
# 2- STATS or VALUES ?
# ---------------------------------------------------------------------
if stats is None and precision is None:
isvalues = True
else:
isvalues = False
if stats is not None:
params['stats'] = stats
if precision is not None:
params['precision'] = precision
# ---------------------------------------------------------------------
# 3- DOMAIN : ZONES, PIXELS or BBOX ?
# ---------------------------------------------------------------------
params.update(_process_request_domaintype(
domaintype, domain, epsg, isvalues))
params['timeout'] = bdimage.timeout
# ---------------------------------------------------------------------
# 4- PARAMETRES et WEBSERVICE
# ---------------------------------------------------------------------
key = (isobs, isvalues, domaintype)
try:
ws = bdimage.webservices[key]
except KeyError as ke:
raise ValueError(
f"Aucun webservice correspond à :\nobs={key[0]}, values={key[1]}, "
f"domain={key[2]}") from ke
try:
content = ws(**params)
except requests.ConnectionError as rce:
_process_request_ConnectionError(rce)
except ValueError as ve:
_process_request_ValueError(ve, domain)
except TypeError as te:
_process_request_TypeError(te, domain)
except FileNotFoundError as fnfe:
raise ValueError(f"L'argument '{fnfe.filename}' est supposé être un "
"nom de fichier: celui-ci n'existe pas") from fnfe
except OSError as oe:
_process_request_OSError(oe, band, epsg, domain)
# ---------------------------------------------------------------------
# 5- RETOUR DU CONTENU
# ---------------------------------------------------------------------
bdimage.url = "{url}?{body}".format( # noqa # pylint: disable=consider-using-f-string
**vars(bdimage.client._response.request))
return content
def _process_request_domaintype(domaintype, domain, epsg, isvalues):
"""Paramétrage selon le type de domaine."""
params = {}
if domaintype == 'zones':
params['zones'] = domain
if isvalues:
params['epsg'] = epsg
elif domaintype == 'pixels':
params['pixels'] = domain
params['epsg'] = epsg
if isvalues:
params['coord'] = True
elif domaintype == 'bbox':
params['ul'] = domain.ul
params['lr'] = domain.lr
params['epsg'] = epsg
return params
def _process_request_ConnectionError(rce):
"""Traiter les erreurs de type requests.ConnectionError."""
url = rce.request.url
body = str(rce.request.body)
if 'Async' in body:
body = body.replace('\\r', '')
body = body.replace('\\n', '')
body = "&".join([str(b.strip('-').split('"')[1]) + '=' +
str(b.strip('-').split('"')[2])
for b in re.findall(r'name="\w*"\w*--', body)])
raise ValueError(
f"Erreur lors de la requête BdImage:\n{url}?{body}") from rce
def _process_request_ValueError(ve, domain):
"""Traiter les erreurs de type ValueError."""
if re.search(r"^invalid ul: .*$", ve.args[0]):
raise ValueError(
f"Point haut-gauche '{domain.ul}' est mal-formaté. ") from ve
if re.search(r"^invalid lr: .*$", ve.args[0]):
raise ValueError(
f"Point bas-droite '{domain.lr}' est mal-formaté. ") from ve
raise ValueError(ve.args[0]) from ve
def _process_request_TypeError(te, domain):
"""Traiter les erreurs de type TypeError."""
if te.args[0] == 'zones must be a filename or an iterable of zones '\
'codes':
raise ValueError(f"Les zones sont mal-formatées. '{domain}' "
"n'est ni un fichier ni une liste de zones") from te
if te.args[0] == "pixels must be a filename or an iterable of 'x,y'":
raise ValueError(f"Les pixels sont mal-formatés. '{domain}' "
"n'est ni un fichier ni une liste de 'x,y'") from te
if re.search(r"^invalid ul: .*$", te.args[0]):
raise ValueError(
f"Point haut-gauche '{domain.ul}' est mal-formaté. ") from te
if re.search(r"^invalid lr: .*$", te.args[0]):
raise ValueError(
f"Point bas-droite '{domain.lr}' est mal-formaté. ") from te
raise ValueError(te.args[0]) from te
def _process_request_OSError(oe, band, epsg, domain):
"""Traiter les erreurs de type OSError."""
if re.search(r"^invalid band name: '.*'$", oe.args[0]):
raise ValueError(
f"La bande '{band}' est mal-formatée. ") from oe
if re.search(r"^invalid epsg code: '.*'$", oe.args[0]):
raise ValueError(
f"L'argument epsg '{epsg}' est mal-formatée. ") from oe
if re.search(r"^async job .*, invalid epsg code: '.*'$", oe.args[0]):
raise ValueError(
f"L'argument epsg '{epsg}' est mal-formatée. ") from oe
if re.search(r"^async job .*, top must be greater than bottom$",
oe.args[0]):
raise ValueError(
"Incompatibilité entre les points haut-gauche et "
f"bas droite:\n{domain.ul} contre {domain.lr}") from oe
raise ValueError(oe.args[0]) from oe
def _set_collection(image=None, network=None,
start=None, stop=None, depth=None):
"""
Définir une collection d'images.
Parameters
----------
image : tuple
Tuple à 3 dimensions de l'identifiant de l'image
(type Image, sous type Image, bande).
network : str, datetime, None
Instant de production de la prévision. Utilisé uniquement
si le type d'image est parmi BDIMAGE_FORECASTS
start : str, datetime, None
Première date de la collection d'images
stop : str, datetime, None
Dernière date de la collection d'images
depth : str, timedelta, None
Pas de temps de cumul de l'image
Returns
-------
img_coll : libbdimage.bdimage.Collection, libbdimage.bdimage.NCollection
Collection d'images
See Also
--------
pyspc.convention.lamedo.BDIMAGE_FORECASTS
libbdimage.bdimage.Collection
libbdimage.bdimage.NCollection
"""
from libbdimage.bdimage import Collection, NCollection
# ---------------------------------------------------------------------
# 0- Informations sur les images
# ---------------------------------------------------------------------
families = sorted({i[0] for i in BdImage.get_datatypes()})
kinds = sorted({i[1] for i in BdImage.get_datatypes()
if i[0] == image[0]})
# ---------------------------------------------------------------------
# 1- Chargement de la collection
# ---------------------------------------------------------------------
try:
if image[0] in BDIMAGE_FORECASTS:
if network is None:
network = start
img_coll = NCollection(image[0], image[1], network=network,
start=start, stop=stop, depth=depth)
else:
img_coll = Collection(image[0], image[1], start=start,
stop=stop, depth=depth)
# ---------------------------------------------------------------------
# 2- Récupération / traduction des erreurs
# ---------------------------------------------------------------------
except ValueError as ve:
_process_collection_ValueError(ve, image, families, kinds,
start, stop, depth, network)
# ---------------------------------------------------------------------
# 3- Retour de la collection d'images
# ---------------------------------------------------------------------
return img_coll
def _process_collection_ValueError(ve, image, families, kinds,
start, stop, depth, network):
"""Traiter les erreurs de type ValueError."""
if re.search(r"^family '.*' not found$",
ve.args[0]):
raise ValueError(
f"Type d'image incorrect. '{image[0]}' n'est pas "
f"dans {families}") from ve
if re.search(r"^kind '.*' does not match with '.*' image$",
ve.args[0]):
raise ValueError(
f"Sous-type d'image incompatible avec le type '{image[0]}'. "
f"'{image[1]}' n'est pas dans {kinds}") from ve
if ve.args[0] == 'start must be like yyyymmddHHMM':
raise ValueError(
f"Date de début des données mal-formatée. '{start}' n'est pas de "
"la forme yyyymmddHHMM") from ve
if ve.args[0] == 'invalid start with seconds or microseconds':
raise ValueError(
f"Date de début des données mal-formatée. '{start}' ne doit pas "
"contenir de seconde, ni de micro-seconde") from ve
if ve.args[0] == 'stop must be like yyyymmddHHMM':
raise ValueError(
f"Date de fin des données mal-formatée. '{stop}' n'est pas de la "
"forme yyyymmddHHMM") from ve
if ve.args[0] == 'invalid stop with seconds or microseconds':
raise ValueError(
f"Date de fin des données mal-formatée. '{stop}' ne doit pas "
"contenir de seconde, ni de micro-seconde") from ve
if ve.args[0] == 'depth must be like [d*]ddHHMM':
raise ValueError(
f"Durée de cumul mal-formatée. '{depth}' n'est pas de la "
"forme ddHHMM") from ve
if re.search(r"^depth '.*' does not match with '.*' image$",
ve.args[0]):
raise ValueError(
f"La durée '{depth}' est incompatible avec "
f"{image[0]}_{image[1]}") from ve
if ve.args[0] == 'date must be like yyyymmddHHMM':
raise ValueError(
f"Date de prévision mal-formatée. '{network}' n'est pas de la "
"forme yyyymmddHHMM") from ve
if ve.args[0] == 'invalid date with seconds or microseconds':
raise ValueError(
f"Date de prévision mal-formatée. '{network}' ne doit pas "
f"contenir de seconde, ni de micro-seconde") from ve
if re.search(r"^network '.*' does not match with '.*'$",
ve.args[0]):
raise ValueError(
f"La date de prévision '{network}' est incompatible avec "
f"{image[0]}_{image[1]}") from ve
if ve.args[0] == 'network is required to set start or stop':
raise ValueError(
f"Date de prévision absente: {network}. ") from ve
raise ValueError("Une erreur inconnue est survenue lors de la "
"définition de la collection d'images") from ve