aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas "Cakeisalie5" Touhey <thomas@touhey.fr>2018-10-03 14:03:08 +0200
committerThomas "Cakeisalie5" Touhey <thomas@touhey.fr>2018-10-03 14:03:08 +0200
commit0c722731c2f36352ec35a09588de9e8e4cec298d (patch)
treeb0591bf9d9efb391f378d2bb29df38a4835ac8b9
parenta55a5cd710d91ebff438bef3fbd4a2cd748d94e6 (diff)
Started adding structures.
-rwxr-xr-xsgdfi/__main__.py30
-rwxr-xr-xsgdfi/_dbs/__init__.py5
-rwxr-xr-x[-rw-r--r--]sgdfi/_dbs/_funcs.py8
-rwxr-xr-xsgdfi/_dbs/_ops.py4
-rwxr-xr-xsgdfi/_dbs/_st.py95
-rwxr-xr-xsgdfi/_manager.py328
-rwxr-xr-xsgdfi/_repr.py41
-rwxr-xr-xsgdfi/_session.py53
-rwxr-xr-xsgdfi/_util.py33
9 files changed, 521 insertions, 76 deletions
diff --git a/sgdfi/__main__.py b/sgdfi/__main__.py
index 1132323..22916a2 100755
--- a/sgdfi/__main__.py
+++ b/sgdfi/__main__.py
@@ -17,9 +17,16 @@ def test_session():
s = _Session(user = user, pw = pw, save = True)
- ops = s.get_ops('4', "yCbyTmNDHpp8CotDhWoEkQ==")
- for op in ops:
- print(op)
+ #ret = s.get_ops('4', "yCbyTmNDHpp8CotDhWoEkQ==")
+ #ret = s.get_place('ONCt5ZDoIUZIXlYv9QyGuw==')
+ ret = s.get_structure('oA0pOxhIxVX5eh6fTNvgzg==')
+
+ if type(ret) == list:
+ for e in ret:
+ print(e)
+ print()
+ else:
+ print(ret)
def test_repr():
""" Test the representations. """
@@ -39,7 +46,6 @@ def test_repr():
ad.name = "TOUHEY Thomas"
op.related.add(ad)
- print(op)
def test_save():
""" Test the saving. """
@@ -55,16 +61,20 @@ def test_load():
from ._manager import Manager
man = Manager()
- ret = man.load("2018100222113100-operations.html")
- for op in ret:
- print(op)
- print()
+ ret = man.load("2018100313425000-structure_summary.html")
+
+ if type(ret) == list:
+ for e in ret:
+ print(e)
+ print()
+ else:
+ print(ret)
if __name__ == '__main__':
- test_repr()
+ #test_repr()
#test_repr2()
#test_session()
#test_save()
- #test_load()
+ test_load()
# End of file.
diff --git a/sgdfi/_dbs/__init__.py b/sgdfi/_dbs/__init__.py
index fbf8e53..d6f79a9 100755
--- a/sgdfi/_dbs/__init__.py
+++ b/sgdfi/_dbs/__init__.py
@@ -5,7 +5,8 @@
#******************************************************************************
""" Databases for SGDFi. """
-from ._ops import OperationType, OperationTypeData
-from ._funcs import Function, FunctionData
+from ._ops import OperationType, OperationTypeData
+from ._st import StructureType, StructureTypeData
+from ._funcs import Function, FunctionData
# End of file.
diff --git a/sgdfi/_dbs/_funcs.py b/sgdfi/_dbs/_funcs.py
index 32bbd9a..5162175 100644..100755
--- a/sgdfi/_dbs/_funcs.py
+++ b/sgdfi/_dbs/_funcs.py
@@ -437,17 +437,17 @@ class FunctionData:
# We suppose it's an intranet code such as "110", a private
# function code such as 8434, or a function name.
+ lead = value
if type(value) == str:
- value = value.strip().casefold()
+ lead = lead.strip().casefold()
try:
- self.__id = _FunctionLeads[value]
+ self.__id = _FunctionLeads[lead]
+ data = _FunctionData.get(self.__id, None)
except KeyError:
raise ValueError("Expected a valid function identifier.") \
from None
- data = _FunctionData.get(self.__id, None)
-
if data is not None:
self.__code = data[0]
self.__iid = data[1]
diff --git a/sgdfi/_dbs/_ops.py b/sgdfi/_dbs/_ops.py
index fdf8d0a..1ee3bd9 100755
--- a/sgdfi/_dbs/_ops.py
+++ b/sgdfi/_dbs/_ops.py
@@ -259,10 +259,10 @@ _OperationTypeIds = {a.strip().casefold(): b \
_OperationTypeNames = {b: a for a, b in _OperationTypeIds.items()}
class OperationTypeData:
- """ Operation type data (name and id). """
+ """ Operation type data (id, name). """
def __init__(self, value):
- def isid(id):
+ def isid(value):
try:
OperationType(value)
except ValueError:
diff --git a/sgdfi/_dbs/_st.py b/sgdfi/_dbs/_st.py
new file mode 100755
index 0000000..ef7ac1d
--- /dev/null
+++ b/sgdfi/_dbs/_st.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python3
+#******************************************************************************
+# Copyright (C) 2018 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr>
+# This file is part of the sgdfi project, which is MIT-licensed.
+#******************************************************************************
+""" Structure type reference for SGDFi. """
+
+from enum import Enum as _Enum, unique as _unique
+
+__all__ = ["StructureType", "StructureTypeData"]
+
+@_unique
+class StructureType(_Enum):
+ """ The default type (unknown). """
+ UNKNOWN = 0
+
+ """ Unité louveteaux-jeanettes. """
+ UNITE_8_11_ANS = 1
+
+ """ Unité pionniers-caravelles. """
+ UNITE_14_17_ANS = 2
+
+_StructureTypeData = {
+ StructureType.UNITE_8_11_ANS: "Unité 8-11 ans",
+ StructureType.UNITE_14_17_ANS: "Unité 14-17 ans",
+}
+
+_StructureTypeLeads = {}
+_StructureTypeLeads.update({name.strip().casefold(): i for i, name \
+ in _StructureTypeData.items()})
+
+class StructureTypeData:
+ """ Structure type data (id, name, …). """
+
+ def __init__(self, value):
+ def isid(id):
+ try:
+ StructureType(value)
+ except ValueError:
+ return False
+ return True
+
+ self.__id = None
+ self.__name = None
+ data = None
+
+ if isinstance(value, StructureTypeData):
+ self.__id = value.id
+ self.__name = value.name
+ elif isid(value):
+ value = StructureType(value)
+ self.__id = value.id
+
+ data = _StructureTypeData.get(value, None)
+ else:
+ lead = value
+ if type(lead) == str:
+ lead = lead.strip().casefold()
+
+ try:
+ self.__id = _StructureTypeLeads[lead]
+ data = _StructureTypeData.get(self.__id, None)
+ except KeyError:
+ if type(value) == str:
+ self.__name = value
+ else:
+ raise ValueError("Could not determine a structure type.") \
+ from None
+
+ if data is not None:
+ self.__name = data
+
+ def __repr__(self):
+ p = []
+ if self.__id is not None:
+ p.append(f"id = {repr(self.__id)}")
+ if self.__name is not None:
+ p.append(f"name = {repr(self.__name)}")
+
+ return f"{self.__class__.__name__}({', '.join(p)})"
+
+ @property
+ def id(self):
+ """ The type identifier, as one of the structure types defined in
+ the StructureType enumeration. """
+
+ return self.__id
+
+ @property
+ def name(self):
+ """ The type name as defined in the structure summary page. """
+
+ return self.__name
+
+# End of file.
diff --git a/sgdfi/_manager.py b/sgdfi/_manager.py
index 62cdd48..eebb7b0 100755
--- a/sgdfi/_manager.py
+++ b/sgdfi/_manager.py
@@ -32,7 +32,33 @@ from ._repr import Structure as _Structure, Adherent as _Adherent, \
__all__ = ["Manager"]
-# Internal class.
+# Data manager.
+
+class _DataManager:
+ """ The internal class to manage all data. """
+
+ def __init__(self):
+ self.__ops = {}
+
+ def add_op(self, op):
+ """ Add an operation. """
+
+ # TODO
+ pass
+
+ def add_place(self, pl):
+ """ Add a place. """
+
+ # TODO
+ pass
+
+ def add_structure(self, st):
+ """ Add a structure. """
+
+ # TODO
+ pass
+
+# Internal classes for the `_Manager` class.
class _Pagination:
def __init__(self):
@@ -40,6 +66,42 @@ class _Pagination:
self.number = 0
self.more = False
+class _OperationManager:
+ """ The operations manager. """
+
+ def __init__(self, main):
+ self.__main = main
+
+ def add(self, op):
+ if not isinstance(op, _Operation):
+ raise ValueError("Expected an 'Operation'.")
+
+ self.__main.add_op(op)
+
+class _PlaceManager:
+ """ The place manager. """
+
+ def __init__(self, main):
+ self.__main = main
+
+ def add(self, pl):
+ if not isinstance(pl, _Place):
+ raise ValueError("Expected a 'Place'.")
+
+ self.__main.add_place(pl)
+
+class _StructureManager:
+ """ The structure manager. """
+
+ def __init__(self, main):
+ self.__main = main
+
+ def add(self, st):
+ if not isinstance(st, _Structure):
+ raise ValueError("Expected a 'Structure'.")
+
+ self.__main.add_structure(st)
+
# The main class.
class Manager:
@@ -59,6 +121,31 @@ class Manager:
except FileExistsError:
pass
+ # Make the data manager.
+
+ data = _DataManager()
+ self.__ops = _OperationManager(data)
+ self.__pls = _PlaceManager(data)
+ self.__sts = _StructureManager(data)
+
+ @property
+ def operations(self):
+ """ The operations manager. """
+
+ return self.__ops
+
+ @property
+ def places(self):
+ """ The places manager. """
+
+ return self.__pls
+
+ @property
+ def structures(self):
+ """ The structures manager. """
+
+ return self.__sts
+
# ---
# Save file management.
# ---
@@ -141,8 +228,8 @@ class Manager:
if hint == 'operations':
func = self.__feed_html_operations
- elif hint == 'lieu':
- func = self.__feed_html_lieu
+ elif hint == 'place':
+ func = self.__feed_html_place
elif hint == 'personlist_fragment':
func = self.__feed_html_personlist_fragment
elif hint == 'person_summary_fragment':
@@ -376,18 +463,19 @@ class Manager:
la.name = name
op.related.add(la)
+ self.operations.add(op)
elts.append(op)
return elts
- def __feed_html_lieu(self, content):
+ def __feed_html_place(self, content):
""" Decode the HTML place from a BeautifulSoup decoded
content and feed it into the manager's places. """
parent = content.find(id = 'ctl00__upMainContent')
rp = 'ctl00_MainContent__resume__'
- place = Place()
+ place = _Place()
# Informations générales: Libellé.
@@ -417,134 +505,274 @@ class Manager:
continue
yield al.text.strip()
- place.address.lines = '\n'.join(lines())
+ place.address = '\n'.join(lines())
# Coordonnées: Adresse (code postal).
cp = parent.find(id = f'{rp}resumeAdresse__lbCodePostal')
- place.address.city.postal_code = cp.text
+ place.postal_code = cp.text
# Coordonnées: Adresse (nom de la commune).
vil = parent.find(id = f'{rp}resumeAdresse__lbVille')
- place.address.city.name = vil.text
+ place.town = vil.text
# Coordonnées: Adresse (pays).
pays = parent.find(id = f'{rp}resumeAdresse__lbPays')
- place.address.city.country = pays.text
+ place.country = pays.text
- # Coordonnées: département administratif. TODO
+ # Coordonnées: département administratif.
# Au format `XX - Nom du département` (où XX représente le numéro).
dept = parent.find(id = f'{rp}lbDepartementAdministratif')
- dept = dept.text
+ place.department = dept.text
- # Coordonnées: étranger. TODO
+ # Coordonnées: étranger.
# "Oui" ou "Non" selon si le lieu se trouve en France ou non… ?
etr = parent.find(id = f'{rp}lbEtranger')
- etr = (True, False)[etr.text == 'Non']
+ place.out_of_france = (True, False)[etr.text == 'Non']
- # Coordonnées: continent. TODO
+ # Coordonnées: continent.
# Nom du continent, e.g. « Europe ».
cont = parent.find(id = f'{rp}lbContinent')
- cont = cont.text
+ place.continent = cont.text
# Coordonnées: numéro de téléphone.
# Numéro de téléphone fixe associé au lieu.
phone = parent.find(id = f'{rp}lbTelephone')
- phone = phone.text
-
- if phone:
- place.phones.add(phone, OP_MAIN)
+ place.phone = phone.text
# Coordonnées: numéro de fax.
# Numéro de fax associé au lieu.
fax = parent.find(id = f'{rp}lbFax')
- fax = fax.text
-
- if fax:
- place.phones.add(fax, OP_FAX)
+ place.fax = fax.text
# Coordonnées: adresse de courriel.
# Adresse de courriel associée au lieu.
email = parent.find(id = f'{rp}lbCourriel')
- email = email.text
+ place.email = email.text
- if email:
- place.emails.add(email, OE_MAIN)
-
- # Informations complémentaires: numéro J&S. TODO
+ # Informations complémentaires: numéro J&S.
# XXX: ?? (vide sur les lieux explorés).
numjs = parent.find(id = f'{rp}lbNumeroJS')
- numjs = numjs.text
+ place.js = numjs.text
- # Informations complémentaires: hébergement "dur". TODO
+ # Informations complémentaires: hébergement "dur".
# "Oui" s'il y a un hébergement en "dur" sur le lieu, "Non" sinon.
hebd = parent.find(id = f'{rp}lbHebergementDur')
- hebd = (True, False)[hebd.text == 'Non']
+ place.hardwall = (True, False)[hebd.text == 'Non']
- # Informations complémentaires: numéro de local. TODO
+ # Informations complémentaires: numéro de local.
# XXX: ?? (vide sur les lieux explorés).
numloc = parent.find(id = f'{rp}lbNumeroLocal')
- numloc = numloc.text
+ place.localnumber = numloc.text
- # Informations complémentaires: propriétaire. TODO
+ # Informations complémentaires: propriétaire.
# Nom du propriétaire, e.g. « Bertrand DUPONT » (format libre).
prop = parent.find(id = f'{rp}lbProprietaire')
- prop = prop.text
+ place.owner = prop.text
- # Informations complémentaires: adresse du propriétaire. TODO
+ # Informations complémentaires: adresse du propriétaire.
# XXX: ?? (vide sur les lieux explorés).
addrp = parent.find(id = f'{rp}lbAdresseProprietaire')
- addrp = addrp.text
+ place.owner_address = addrp.text
- # Accès: numéro de carte IGN. TODO
+ # Accès: numéro de carte IGN.
# XXX: ?? (vide sur les lieux explorés).
ign = parent.find(id = f'{rp}lbNumeroCarteIGN')
- ign = ign.text
+ place.ign_num = ign.text
- # Accès: accès voiture. TODO
+ # Accès: accès voiture.
# "Oui" si le lieu est accessible en voiture, "Non" sinon.
voit = parent.find(id = f'{rp}lbAccesVoiture')
- voit = (True, False)[voit.text == 'Non']
+ place.car_access = (True, False)[voit.text == 'Non']
- # Accès: distance de la gare la plus proche. TODO
+ # Accès: distance de la gare la plus proche.
# Au format libre (e.g. « 5km »).
gard = parent.find(id = f'{rp}lbDistanceGare')
- gard = gard.text
+ place.closest_train_station_distance = gard.text
- # Accès: nom de la gare la plus proche. TODO
+ # Accès: nom de la gare la plus proche.
# Au format libre (e.g. « Saint julien du Sault »).
garn = parent.find(id = f'{rp}lbNomGare')
- garn = garn.text
+ place.closest_train_station = garn.text
- # Accès: distance de l'arrêt de bus le plus proche. TODO
+ # Accès: distance de l'arrêt de bus le plus proche.
# Au format libre.
busd = parent.find(id = f'{rp}lbDistanceArretBus')
- busd = busd.text
+ place.closest_bus_stop_distance = busd.text
- # Accès: nom de l'arrêt de bus le plus proche. TODO
+ # Accès: nom de l'arrêt de bus le plus proche.
# Au format libre.
busn = parent.find(id = f'{rp}lbNomArretBus')
- busn = busn.text
+ place.closest_bus_stop = busn.text
+
+ self.places.add(place)
+ return place
+
+ def __feed_html_structure_summary(self, content):
+ """ Decode the HTML summary page for a structure from a
+ BeautifulSoup decoded content and feed it into the
+ manager's structures. """
+
+ parent = content.find(id = 'ctl00_ctl00_MainContent_TabsContent_' \
+ 'TabContainerResumeStructure__tabResume')
+ rp = 'ctl00_ctl00_MainContent_TabsContent_TabContainerResumeStructure_' \
+ '_tabResume__resume__'
+
+ st = _Structure()
+
+ # Type de structure, parmi :
+ # - « Unité 8-11 ans » : unité louveteaux-jeanettes.
+
+ typ = parent.find(id = f"{rp}lblType")
+ st.type = typ.text
+
+ # Code de la structure.
+
+ code = parent.find(id = f"{rp}lblCodeStructure")
+ st.code = code.text
+
+ # Nom de la structure.
+
+ nom = parent.find(id = f"{rp}lblNom")
+ st.name = nom.text
+
+ # Description de la structure.
+
+ desc = parent.find(id = f"{rp}lblDescription")
+ st.description = desc.text
+
+ # Statut de la structure. TODO
+ # Par exemple, « Ouverte ».
+
+ status = parent.find(id = f"{rp}lblStatut")
+ st.status = status.text
+
+ # Hémisphère de la structure. TODO
+
+ hemis = parent.find(id = f"{rp}lblHemisphere")
+ st.hemisphere = hemis.text
+
+ # Adresse de la structure. TODO
+
+ def lines():
+ for i in range(1, 4):
+ try:
+ aid = f'{rp}resumeAdresse__lbLigne{i}'
+ al = parent.find(id = aid)
+ assert al != None
+ except:
+ continue
+ yield al.text.strip()
+
+ st.address = '\n'.join(lines())
+
+ # Code postal.
+
+ cp = parent.find(id = f"{rp}resumeAdresse__lbCodePostal")
+ st.postal_code = cp.text.strip()
+
+ # Nom de la commune.
+
+ vil = parent.find(id = f"{rp}resumeAdresse__lbVille")
+ st.town = vil.text.strip()
+
+ # Pays de la commune.
+
+ pays = parent.find(id = f"{rp}resumeAdresse__lbPays")
+ st.country = pays.text.strip()
+
+ # Département administratif.
+
+ dept = parent.find(id = f"{rp}lblDepartement")
+ st.department = dept.text.strip()
+
+ # Numéro de téléphone.
+
+ phone = parent.find(id = f"{rp}lblTelephone")
+ st.phone = phone.text.strip()
+
+ # Numéro de fax.
+
+ fax = parent.find(id = f"{rp}lblFax")
+ st.fax = fax.text.strip()
+
+ # Adresse de courriel.
+
+ email = parent.find(id = f"{rp}lblCourrier")
+ st.email = email.text.strip()
+
+ # Site web.
+
+ website = parent.find(id = f"{rp}hlSiteWeb")
+ try:
+ st.website = website['href']
+ except KeyError:
+ pass
+
+ # Date de début d'activité pour la saison courante.
+
+ deb = parent.find(id = f"{rp}lblDateDebutActivite")
+ st.start = deb.text
+
+ # Informations de localisation.
+
+ loc = parent.find(id = f"{rp}lblInfosLocalisation")
+ st.location_info = loc.text
+
+ # Coordonnées GPS. TODO
+
+ lat = parent.find(id = f"{rp}lblCoordonneesGPSLatitude")
+ st.gps_latitude = lat.text
+ lon = parent.find(id = f"{rp}lblCoordonneesGPSLongitude")
+ st.gps_longitude = lon.text
+
+ self.structures.add(st)
+ return st
+
+ def __feed_html_structure_hierarchy(self, content):
+ """ Decode the HTML hierarchy page for a structure from a
+ BeautifulSoup decoded content and feed it into the
+ manager's structures. """
+
+ parent = content.find(id = 'ctl00_ctl00_MainContent_TabsContent_' \
+ 'TabContainerResumeStructure__tabHierarche')
+
+ # Récupération de la structure parente. TODO
+ # `pid`: identifiant de la structure parente.
+ # `pcode`: code de la structure parente.
+ # `pname`: nom de la structure parente.
+
+ code = parent.find(id = 'ctl00_ctl00_MainContent_TabsContent_' \
+ 'TabContainerResumeStructure__tabHierarche__gvParents_ctl02_' \
+ '_hlStructure')
+ pid = _parse_qs(_urlparse(code['href']).query)['id'][0]
+ pid = _b64decode(pid)
+ pcode = code.text
+
+ p = parent.find(id = 'ctl00_ctl00_MainContent_TabsContent_' \
+ 'TabContainerResumeStructure__tabHierarche__gvParents')
+ pname = p.find_all('td')[1].text
- return [place]
+ #self.structures.add(st)
+ return None
# End of file.
diff --git a/sgdfi/_repr.py b/sgdfi/_repr.py
index d2f47da..b941422 100755
--- a/sgdfi/_repr.py
+++ b/sgdfi/_repr.py
@@ -13,7 +13,8 @@ from ._util import IID, Enum as _Enum, \
BoolProperty as _BoolProperty, EnumProperty as _EnumProperty, \
ArrayProperty as _ArrayProperty, TextProperty as _TextProperty
from ._dbs import OperationType, OperationTypeData as _OperationTypeData, \
- Function, FunctionData as _FunctionData
+ Function, FunctionData as _FunctionData, StructureType, \
+ StructureTypeData as _StructureTypeData
__all__ = ["IID", "Structure", "Adherent", "RallyRegistration", "Camp",
"Place", "Operation", "OperationType", "Function"]
@@ -60,6 +61,25 @@ class _OperationTypeProperty(_Property):
def delete(self):
self.__data = None
+class _StructureTypeProperty(_Property):
+ """ Structure type. """
+
+ def init(self):
+ self.__data = None
+
+ def get(self):
+ return self.__data
+
+ def set(self, value):
+ if value is None:
+ self.__data = None
+ return
+
+ self.__data = _StructureTypeData(value)
+
+ def delete(self):
+ self.__data = None
+
class _OperationFieldsProperty(_Property):
""" Operation fields. """
@@ -122,7 +142,26 @@ class Structure(_Base):
""" A structure (unit, group, territory, …). """
iid = _IIDProperty()
+ type = _StructureTypeProperty()
name = _TextProperty()
+ code = _TextProperty() # TODO: public code thingy?
+ # TODO: private code thingy?
+ description = _TextProperty(not_empty = True)
+ status = _TextProperty() # TODO: enum?
+ hemisphere = _TextProperty()
+ address = _TextProperty(not_empty = True)
+ postal_code = _TextProperty()
+ town = _TextProperty()
+ country = _TextProperty(not_empty = True)
+ department = _TextProperty()
+ phone = _TextProperty(not_empty = True)
+ fax = _TextProperty(not_empty = True)
+ email = _TextProperty(not_empty = True)
+ website = _TextProperty(not_empty = True)
+ start = _DateProperty()
+ location_info = _TextProperty(not_empty = True)
+ gps_latitude = _TextProperty(not_empty = True)
+ gps_longitude = _TextProperty(not_empty = True)
class Adherent(_Base):
""" A person who is part of the organization. """
diff --git a/sgdfi/_session.py b/sgdfi/_session.py
index f7b53aa..3396c8f 100755
--- a/sgdfi/_session.py
+++ b/sgdfi/_session.py
@@ -312,4 +312,57 @@ class Session(_Manager):
return activities
+ def get_place(self, pl_id):
+ """ Get a place's data. """
+
+ iid = _IID(pl_id)
+ path = '/Specialisation/Sgdf/Commun/ResumeLieuActivite.aspx' \
+ f'?id={iid.urlsafe()}'
+
+ # Récupération et décodage de la page.
+ # XXX: hack pour l'iid. Le récupérer directement sur la page ?
+
+ elts = self.get_page(path, hint = 'place')
+ pl.iid = iid
+ return pl
+
+ def get_structure(self, st_id):
+ """ Get a structure's data. """
+
+ iid = _IID(st_id)
+ path = '/Specialisation/Sgdf/structures/ResumeStructure.aspx' \
+ f'?id={iid.urlsafe()}'
+ stname = 'ctl00_ctl00_MainContent_TabsContent_' \
+ 'TabContainerResumeStructure_ClientState'
+ state = {
+ 'ActiveTabIndex': 1,
+ 'TabState': [True] * 10}
+ args = {
+ stname: _jsondumps(state),
+ 'ctl00': {
+ 'ctl00': {
+ '_ddDelegations': '0',
+ '_hidReferenceStatistiqueUtilisation': '-1',
+ '_tbRechAdherent': '',
+ '_tbRechStructure': ''}}}
+
+ # Récupération de l'onglet "Résumé".
+
+ state['ActiveTabIndex'] = 1
+ args[stname] = _jsondumps(state)
+ st = self.get_page(path, args, method = self.METHOD_FORM,
+ hint = "structure_summary")
+
+ # Récupération de l'onglet "Hiérarchie".
+
+ state['ActiveTabIndex'] = 0
+ args[stname] = _jsondumps(state)
+ st_updates = self.get_page(path, args, method = self.METHOD_FORM,
+ hint = "structure_hierarchy")
+
+ # FIXME: feed with iid
+ st.iid = iid
+
+ return st
+
# End of file.
diff --git a/sgdfi/_util.py b/sgdfi/_util.py
index 1156705..a119c83 100755
--- a/sgdfi/_util.py
+++ b/sgdfi/_util.py
@@ -13,6 +13,7 @@ from enum import Enum
from datetime import datetime as _datetime
from base64 import b64decode as _b64decode, b64encode as _b64encode
+import regex as _re
from pytz import timezone as _timezone
__all__ = ["IID", "Enum",
@@ -200,6 +201,8 @@ class IIDProperty(Property):
def delete(self):
self.__iid = value
+_datere = _re.compile('(?P<dom>[0-9]+)\/(?P<mon>[0-9]+)\/(?P<yr>[0-9]+)')
+
class DateProperty(Property):
""" A date property. """
@@ -216,8 +219,21 @@ class DateProperty(Property):
if value is None:
self.__date = value
return
- elif not isinstance(value, _datetime):
- raise TypeError("expected None or an instance of datetime")
+
+ if isinstance(value, _datetime):
+ pass # use the value as is.
+ elif type(value) == str:
+ m = _datere.match(value)
+ if m is None:
+ raise TypeError("uncorrectly formatted date string: " \
+ f"{repr(value)}")
+ else: # if m.group('yr'):
+ year = int(m.group('yr'))
+ mon = int(m.group('mon'))
+ dom = int(m.group('dom'))
+ value = _datetime(year, mon, dom)
+ else:
+ raise TypeError("Could not determinate a valid datetime.")
if value.tzinfo is None:
value = value.replace(tzinfo = _timezone('Europe/Paris'))
@@ -306,18 +322,20 @@ class ArrayProperty(Property):
class TextProperty(Property):
""" A text property.
`lines` represents the maximum number of allowed lines.
- `maxchars` represents the maximum number of characters. """
+ `maxchars` represents the maximum number of characters.
+ `not_empty`: if True, empty strings are considered as None. """
- def init(self, lines = 0, maxchars = 0):
+ def init(self, lines = 0, maxchars = 0, not_empty = False):
self.__value = None
self.__lines = lines
self.__maxchars = maxchars
+ self.__nempty = bool(not_empty)
def get(self):
return self.__value
def set(self, value):
- if value is None:
+ if value is None or (self.__nempty and value == ''):
self.__value = None
return
@@ -363,8 +381,9 @@ class Base:
def getproperty(self, name):
return super().__getattribute__(name)
- attrs = lambda: (f"{i} = {repr(getproperty(self, i).get())}" \
- for i in self.__findprops())
+ attrs = lambda: (f"{name} = {repr(val)}" for name, val \
+ in map(lambda x: (x, getproperty(self, x).get()), \
+ self.__findprops()) if val is not None)
return f"{self.__class__.__name__}({', '.join(attrs())})"