aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Touhey <thomas@touhey.fr>2019-09-29 17:51:06 +0200
committerThomas Touhey <thomas@touhey.fr>2019-09-29 17:51:06 +0200
commit89c0f6a4dacb74e7a50df9a13d962e6ecc5074ac (patch)
treefd0eee1a30f593223e1b815fbc1768f8356f897c
parentd2983f5eef8715e2935cf8b721f4f45c41b8703c (diff)
Just a push of my latest toyings aroundHEADmaster
-rw-r--r--LICENSE.txt2
-rw-r--r--docs/environment/index.rst2
-rw-r--r--docs/environment/intranet/adherents.rst29
-rwxr-xr-xsgdfi/__init__.py4
-rwxr-xr-xsgdfi/_dbs/__init__.py4
-rwxr-xr-xsgdfi/_dbs/_code.py4
-rwxr-xr-xsgdfi/_dbs/_dept.py14
-rwxr-xr-xsgdfi/_dbs/_event.py4
-rwxr-xr-xsgdfi/_dbs/_funcdata.py4
-rwxr-xr-xsgdfi/_dbs/_funcs.py27
-rwxr-xr-xsgdfi/_dbs/_ops.py14
-rwxr-xr-xsgdfi/_dbs/_regime.py4
-rwxr-xr-xsgdfi/_dbs/_st.py15
-rwxr-xr-xsgdfi/_dbs/_stspe.py12
-rwxr-xr-xsgdfi/_dbs/_ststatus.py8
-rwxr-xr-xsgdfi/_decode.py116
-rwxr-xr-xsgdfi/_intranet.py112
-rwxr-xr-xsgdfi/_manager.py30
-rwxr-xr-xsgdfi/_repr.py40
-rwxr-xr-xsgdfi/_util.py28
-rwxr-xr-xsgdfi/_version.py4
-rwxr-xr-xsgdfi/test/__init__.py11
22 files changed, 281 insertions, 207 deletions
diff --git a/LICENSE.txt b/LICENSE.txt
index 45ccebe..d1defab 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (C) 2018 Thomas Touhey <thomas@touhey.fr>
+Copyright (C) 2018-2019 Thomas Touhey <thomas@touhey.fr>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the “Software”), to deal
diff --git a/docs/environment/index.rst b/docs/environment/index.rst
index 0549879..4868c8e 100644
--- a/docs/environment/index.rst
+++ b/docs/environment/index.rst
@@ -111,6 +111,8 @@ on the main website. These redirects are the following:
`<https://www.sgdf.fr/actualites-chefs-cadres/toute-l-actualites-chefs-cadres/les-actualites/2417-refonte-identite-visuelle-reponds-a-l-enquete>`_.
- `connecte.sgdf.fr <https://connecte.sgdf.fr/>`_:
`<https://www.sgdf.fr/component/content/article/2481-connecte-accueil>`_.
+- `ouverture.sgdf.fr <https://ouverture.sgdf.fr/>`_:
+ `<https://www.sgdf.fr/creer-un-groupe-scouts-et-guides-de-france>`_.
Technical domains
-----------------
diff --git a/docs/environment/intranet/adherents.rst b/docs/environment/intranet/adherents.rst
index fe5e454..7cb2a6b 100644
--- a/docs/environment/intranet/adherents.rst
+++ b/docs/environment/intranet/adherents.rst
@@ -72,4 +72,31 @@ Paths
A form for giving one's skills.
``/WebServices/AutoCompleteCodePostalVille.asmx/GetCompletionList``
- A web service for getting data
+ A web service for completing postal codes. Uses the following POST
+ arguments:
+
+ ``prefixText``
+ The prefix text (search), e.g. ``750``.
+
+ ``count``
+ The number of results expected, as a decimal number, e.g. ``20``.
+
+``/WebServices/AutoCompleteCodePostalVille.asmx/GetCompletionListPourCommuneLieuActivite``
+ A web service for completing postal codes. Uses the following POST
+ arguments:
+
+ ``prefixText``
+ The prefix text (search), e.g. ``750``.
+
+ ``count``
+ The number of results expected, as a decimal number, e.g. ``20``.
+
+``/WebServices/AutoCompleteCodePostalVille.asmx/GetCompletionListPourCommuneNaissance``
+ A web service for completing postal codes. Uses the following POST
+ arguments:
+
+ ``prefixText``
+ The prefix text (search), e.g. ``750``.
+
+ ``count``
+ The number of results expected, as a decimal number, e.g. ``20``.
diff --git a/sgdfi/__init__.py b/sgdfi/__init__.py
index 97b5dea..b51f926 100755
--- a/sgdfi/__init__.py
+++ b/sgdfi/__init__.py
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
-#******************************************************************************
+#**************************************************************************
# Copyright (C) 2018 Thomas Touhey <thomas@touhey.fr>
# This file is part of the sgdfi project, which is MIT-licensed.
-#******************************************************************************
+#**************************************************************************
""" SGDFi allows you to interact with SGDF's intranet. """
from ._version import version
diff --git a/sgdfi/_dbs/__init__.py b/sgdfi/_dbs/__init__.py
index e37ca74..8bc7dec 100755
--- a/sgdfi/_dbs/__init__.py
+++ b/sgdfi/_dbs/__init__.py
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
-#******************************************************************************
+#**************************************************************************
# Copyright (C) 2018 Thomas Touhey <thomas@touhey.fr>
# This file is part of the sgdfi project, which is MIT-licensed.
-#******************************************************************************
+#**************************************************************************
""" Databases for SGDFi. """
from ._ops import OperationType, OperationTypeData
diff --git a/sgdfi/_dbs/_code.py b/sgdfi/_dbs/_code.py
index d8cce03..1d8434b 100755
--- a/sgdfi/_dbs/_code.py
+++ b/sgdfi/_dbs/_code.py
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
-#******************************************************************************
+#**************************************************************************
# Copyright (C) 2018 Thomas Touhey <thomas@touhey.fr>
# This file is part of the sgdfi project, which is MIT-licensed.
-#******************************************************************************
+#**************************************************************************
""" Code decoding for SGDFi. """
from enum import Enum as _Enum, unique as _unique
diff --git a/sgdfi/_dbs/_dept.py b/sgdfi/_dbs/_dept.py
index 25c2f42..c5e1144 100755
--- a/sgdfi/_dbs/_dept.py
+++ b/sgdfi/_dbs/_dept.py
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
-#******************************************************************************
+#**************************************************************************
# Copyright (C) 2018 Thomas Touhey <thomas@touhey.fr>
# This file is part of the sgdfi project, which is MIT-licensed.
-#******************************************************************************
+#**************************************************************************
""" Department reference for SGDFi. """
from enum import Enum as _Enum, unique as _unique
@@ -589,9 +589,11 @@ _DepartmentLeads.update({ii: i for i, (dcode, name, ii) \
_DepartmentLeads.update({str(ii): i for i, (dcode, name, ii) \
in _DepartmentData.items()})
_DepartmentLeads.update({_normalize(f"{dcode} - {name}"): i \
- for i, (dcode, name, ii) in _DepartmentData.items() if dcode is not None})
+ for i, (dcode, name, ii) in _DepartmentData.items() \
+ if dcode is not None})
_DepartmentLeads.update({_normalize(f"{name} - {dcode}"): i \
- for i, (dcode, name, ii) in _DepartmentData.items() if dcode is not None})
+ for i, (dcode, name, ii) in _DepartmentData.items() \
+ if dcode is not None})
class DepartmentData:
""" Department data (id, name, …). """
@@ -655,8 +657,8 @@ class DepartmentData:
@property
def id(self):
- """ The department identifier, as one of the structure types defined in
- the Department enumeration. """
+ """ The department identifier, as one of the structure types
+ defined in the Department enumeration. """
return self.__id
diff --git a/sgdfi/_dbs/_event.py b/sgdfi/_dbs/_event.py
index b25c27a..4cd4bce 100755
--- a/sgdfi/_dbs/_event.py
+++ b/sgdfi/_dbs/_event.py
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
-#******************************************************************************
+#**************************************************************************
# Copyright (C) 2018 Thomas Touhey <thomas@touhey.fr>
# This file is part of the sgdfi project, which is MIT-licensed.
-#******************************************************************************
+#**************************************************************************
""" Event type reference for SGDFi. """
from enum import Enum as _Enum, unique as _unique
diff --git a/sgdfi/_dbs/_funcdata.py b/sgdfi/_dbs/_funcdata.py
index fbc7bb0..8df963b 100755
--- a/sgdfi/_dbs/_funcdata.py
+++ b/sgdfi/_dbs/_funcdata.py
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
-#******************************************************************************
+#**************************************************************************
# Copyright (C) 2018 Thomas Touhey <thomas@touhey.fr>
# This file is part of the sgdfi project, which is MIT-licensed.
-#******************************************************************************
+#**************************************************************************
'Generated functions reference for `_funcs.py`.'
from enum import Enum as _Enum
diff --git a/sgdfi/_dbs/_funcs.py b/sgdfi/_dbs/_funcs.py
index d70e69a..9dc2861 100755
--- a/sgdfi/_dbs/_funcs.py
+++ b/sgdfi/_dbs/_funcs.py
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
-#******************************************************************************
+#**************************************************************************
# Copyright (C) 2018 Thomas Touhey <thomas@touhey.fr>
# This file is part of the sgdfi project, which is MIT-licensed.
-#******************************************************************************
+#**************************************************************************
""" Functions reference for SGDFi. """
from ._funcdata import Function, FunctionData as _FunctionData
@@ -87,8 +87,8 @@ class FunctionRawData:
def add_cname_suffix(self, suffix):
cnames = [cn + suffix for cn in self.__cnames]
- return FunctionRawData(self.__iid, self.__code, self.__nm, self.__nf,
- cnames, self.__srcorder)
+ return FunctionRawData(self.__iid, self.__code, self.__nm,
+ self.__nf, cnames, self.__srcorder)
@property
def srcorder(self):
@@ -114,23 +114,26 @@ class FunctionRawData:
# - the full feminine string (e.g. "110 (JEANETTE)").
# - the full string (e.g. "110 (LOUVETEAU / JEANETTE)").
+_sc = lambda x: x.strip().casefold()
+
_FunctionLeads = {}
-_FunctionLeads.update({nouns[0].strip().casefold(): i for i, (c, ii, *nouns) \
- in _FunctionData.items()})
-_FunctionLeads.update({nouns[1].strip().casefold(): i for i, (c, ii, *nouns) \
- in _FunctionData.items() if len(nouns) > 1})
-_FunctionLeads.update({ii: i for i, (c, ii, *nouns) in _FunctionData.items()})
+_FunctionLeads.update({nouns[0].strip().casefold(): i \
+ for i, (c, ii, *nouns) in _FunctionData.items()})
+_FunctionLeads.update({nouns[1].strip().casefold(): i \
+ for i, (c, ii, *nouns) in _FunctionData.items() if len(nouns) > 1})
+_FunctionLeads.update({ii: i \
+ for i, (c, ii, *nouns) in _FunctionData.items()})
_FunctionLeads.update({str(ii): i for i, (c, ii, *nouns) \
in _FunctionData.items()})
_FunctionLeads.update({c.casefold(): i for i, (c, ii, *nouns) \
in _FunctionData.items()})
_FunctionLeads.update({int(c): i for i, (c, ii, *nouns) \
in _FunctionData.items() if _isint(c)})
-_FunctionLeads.update({f"{c} ({nouns[0]})".strip().casefold(): i \
+_FunctionLeads.update({_sc(f"{c} ({nouns[0]})"): i \
for i, (c, ii, *nouns) in _FunctionData.items()})
-_FunctionLeads.update({f"{c} ({nouns[1]})".strip().casefold(): i \
+_FunctionLeads.update({_sc(f"{c} ({nouns[1]})"): i \
for i, (c, ii, *nouns) in _FunctionData.items() if len(nouns) > 1})
-_FunctionLeads.update({f"{c} ({nouns[0]} / {nouns[1]})".strip().casefold(): i \
+_FunctionLeads.update({_sc(f"{c} ({nouns[0]} / {nouns[1]})"): i \
for i, (c, ii, *nouns) in _FunctionData.items() if len(nouns) > 1})
class FunctionData:
diff --git a/sgdfi/_dbs/_ops.py b/sgdfi/_dbs/_ops.py
index 7d3dd03..f631d01 100755
--- a/sgdfi/_dbs/_ops.py
+++ b/sgdfi/_dbs/_ops.py
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
-#******************************************************************************
+#**************************************************************************
# Copyright (C) 2018 Thomas Touhey <thomas@touhey.fr>
# This file is part of the sgdfi project, which is MIT-licensed.
-#******************************************************************************
+#**************************************************************************
""" Operations reference for SGDFi. """
from enum import Enum as _Enum, unique as _unique
@@ -29,7 +29,8 @@ class OperationType(_Enum):
""" Animation / Création d'une tâche. """
ANIM_TASK_NEW = 50
- """ Bulletin d'adhésion / Impression locale d'un bulletin d'adhésion. """
+ """ Bulletin d'adhésion / Impression locale d'un bulletin
+ d'adhésion. """
BUL_PRINT = 3
""" Camp / Ajout d'un animateur à un camp. """
@@ -178,7 +179,8 @@ _OperationTypeData = {
OperationType.ANIM_TASK_NEW: \
("Animation / Création d'une tâche",),
OperationType.BUL_PRINT: \
- ("Bulletin d'adhésion / Impression locale d'un bulletin d'adhésion",),
+ ("Bulletin d'adhésion / Impression locale d'un bulletin "
+ "d'adhésion",),
OperationType.CAMP_ANIM_ADD: \
("Camp / Ajout d'un animateur à un camp",),
OperationType.CAMP_DIR_ADD: \
@@ -341,8 +343,8 @@ class OperationTypeData:
@property
def id(self):
- """ The type identifier, as one of the operation types defined in the
- OperationType enumeration. """
+ """ The type identifier, as one of the operation types defined in
+ the OperationType enumeration. """
return self.__id
diff --git a/sgdfi/_dbs/_regime.py b/sgdfi/_dbs/_regime.py
index 2e9c2f0..7115a5c 100755
--- a/sgdfi/_dbs/_regime.py
+++ b/sgdfi/_dbs/_regime.py
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
-#******************************************************************************
+#**************************************************************************
# Copyright (C) 2018 Thomas Touhey <thomas@touhey.fr>
# This file is part of the sgdfi project, which is MIT-licensed.
-#******************************************************************************
+#**************************************************************************
""" Allocations regime reference for SGDFi. """
from enum import Enum as _Enum, unique as _unique
diff --git a/sgdfi/_dbs/_st.py b/sgdfi/_dbs/_st.py
index 4cca2dc..46be987 100755
--- a/sgdfi/_dbs/_st.py
+++ b/sgdfi/_dbs/_st.py
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
-#******************************************************************************
+#**************************************************************************
# Copyright (C) 2018 Thomas 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
@@ -60,6 +60,7 @@ class StructureType(_Enum):
# - name.
# - internal code.
+_ma = "Membres associés "
_StructureTypeData = {
StructureType.UNKNOWN: ("Tous", -1),
@@ -68,9 +69,9 @@ _StructureTypeData = {
StructureType.SOMMET: ("Sommet", 1207),
StructureType.TERRITOIRE: ("Territoire", 1203),
StructureType.GROUPE: ("Groupe", 1200),
- StructureType.ASSOCIES_N: ("Membres associés National", 1206),
- StructureType.ASSOCIES_T: ("Membres associés Territorial", 1204),
- StructureType.ASSOCIES_L: ("Membres associés local", 1201),
+ StructureType.ASSOCIES_N: (f"{_ma}National", 1206),
+ StructureType.ASSOCIES_T: (f"{_ma}Territorial", 1204),
+ StructureType.ASSOCIES_L: (f"{_ma}local", 1201),
StructureType.CENTRE_NATIONAL: ("Centre National", 1205),
StructureType.UNITE_FARFADET: ("Unité Farfadet", 1208),
@@ -126,8 +127,8 @@ class StructureTypeData:
if type(value) == str:
self.__name = value
else:
- raise ValueError("Could not determine a structure type.") \
- from None
+ raise ValueError("Could not determine a structure "
+ "type.") from None
if data is not None:
self.__name = data[0]
diff --git a/sgdfi/_dbs/_stspe.py b/sgdfi/_dbs/_stspe.py
index 03bde17..b1102da 100755
--- a/sgdfi/_dbs/_stspe.py
+++ b/sgdfi/_dbs/_stspe.py
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
-#******************************************************************************
+#**************************************************************************
# Copyright (C) 2018 Thomas Touhey <thomas@touhey.fr>
# This file is part of the sgdfi project, which is MIT-licensed.
-#******************************************************************************
+#**************************************************************************
""" Structure speciality reference for SGDFi. """
from enum import Enum as _Enum, unique as _unique
@@ -80,8 +80,8 @@ class StructureSpecialityData:
if type(value) == str:
self.__name = value
else:
- raise ValueError("Could not determine a structure type.") \
- from None
+ raise ValueError("Could not determine a structure "
+ "type.") from None
if data is not None:
self.__name = data[0]
@@ -100,8 +100,8 @@ class StructureSpecialityData:
@property
def id(self):
- """ The speciality identifier, as one of the structure types defined in
- the StructureSpeciality enumeration. """
+ """ The speciality identifier, as one of the structure types
+ defined in the StructureSpeciality enumeration. """
return self.__id
diff --git a/sgdfi/_dbs/_ststatus.py b/sgdfi/_dbs/_ststatus.py
index 2144cc9..f8f27e9 100755
--- a/sgdfi/_dbs/_ststatus.py
+++ b/sgdfi/_dbs/_ststatus.py
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
-#******************************************************************************
+#**************************************************************************
# Copyright (C) 2018 Thomas Touhey <thomas@touhey.fr>
# This file is part of the sgdfi project, which is MIT-licensed.
-#******************************************************************************
+#**************************************************************************
""" Structure status reference for SGDFi. """
from enum import Enum as _Enum, unique as _unique
@@ -36,8 +36,8 @@ _StructureStatusData = {
}
_StructureStatusLeads = {}
-_StructureStatusLeads.update({name.strip().casefold(): i for i, (name, ii) \
- in _StructureStatusData.items()})
+_StructureStatusLeads.update({name.strip().casefold(): i \
+ for i, (name, ii) in _StructureStatusData.items()})
_StructureStatusLeads.update({ii: i for i, (name, ii) \
in _StructureStatusData.items()})
_StructureStatusLeads.update({str(ii): i for i, (name, ii) \
diff --git a/sgdfi/_decode.py b/sgdfi/_decode.py
index bcb502f..de49d02 100755
--- a/sgdfi/_decode.py
+++ b/sgdfi/_decode.py
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
-#******************************************************************************
+#**************************************************************************
# Copyright (C) 2018 Thomas Touhey <thomas@touhey.fr>
# This file is part of the sgdfi project, which is MIT-licensed.
-#******************************************************************************
+#**************************************************************************
""" Main object to decode files and dumps from SGDF's intranet. """
from itertools import count as _count
@@ -10,7 +10,7 @@ from datetime import datetime as _datetime, date as _date
from re import finditer as _rfindter, sub as _rreplace, split as _rsplit
from io import (IOBase as _IOBase, StringIO as _StringIO,
BytesIO as _BytesIO, TextIOWrapper as _TextIOWrapper)
-from base64 import b64decode as _b64decode
+from base64 import b64decode as _b64decode, b64encode as _b64encode
from urllib.parse import (urlparse as _urlparse, parse_qs as _parse_qs,
unquote as _unquote)
from html import unescape as _htmlunescape
@@ -21,8 +21,9 @@ from bs4 import BeautifulSoup as _BeautifulSoup
from pandas import read_excel as _read_excel
from ._repr import (Structure as _Structure, Adherent as _Adherent,
- Place as _Place, RallyRegistration as _RallyRegistration, Camp as _Camp,
- Operation as _Operation, FunctionRawData as _FunctionRawData)
+ Place as _Place, RallyRegistration as _RallyRegistration,
+ Camp as _Camp, Operation as _Operation,
+ FunctionRawData as _FunctionRawData)
from ._util import (NotFoundError as _NotFoundError,
ForbiddenError as _ForbiddenError, Pagination as _Pagination)
@@ -49,8 +50,8 @@ class _AjaxField:
self.__text = str(text)
def __repr__(self):
- return f'Field(id = {repr(self.code)}, name = {repr(self.name)}, ' \
- f'attrib = {repr(self.value)})'
+ return f'Field(id = {repr(self.code)}, ' \
+ f'name = {repr(self.name)}, attrib = {repr(self.value)})'
@property
def code(self):
@@ -186,8 +187,8 @@ class Decoder:
if node is not None:
nftext = "Cette page est introuvable."
- fptext = "Vous n'avez pas les droits nécessaires pour " \
- "accéder à cette page."
+ fptext = "Vous n'avez pas les droits nécessaires " \
+ "pour accéder à cette page."
if node.text == nftext:
raise _NotFoundError()
@@ -215,9 +216,9 @@ class Decoder:
else:
raise ValueError("Invalid input type")
- # Answers to Microsoft Ajax calls are a set of elements separated
- # by pipes ('|'), and must be read four by four as described
- # in the intranet's Ajax documentation.
+ # Answers to Microsoft Ajax calls are a set of elements
+ # separated by pipes ('|'), and must be read four by four as
+ # described in the intranet's Ajax documentation.
resp = []
raw = iter(content.split('|')[:-1])
@@ -402,7 +403,8 @@ class Decoder:
assert hint != None
func = getattr(self, f"_decode_json_{hint}")
except (AssertionError, AttributeError):
- raise ValueError(f"Invalid JSON hint: {hint}") from None
+ raise ValueError(f"Invalid JSON hint: {hint}") \
+ from None
# Then load using the standard json module.
# If it is a {'d': '…'} payload, it is a WebService answer and
@@ -590,7 +592,8 @@ class Decoder:
place.description = desc.text
# Informations générales: Fiche. TODO
- # XXX: absence de champ… faut voir ce que ça donne quand c'est rempli ?
+ # XXX: absence de champ… faut voir ce que ça donne quand
+ # c'est rempli ?
# Coordonnées: Adresse (lignes).
# Avec plusieurs lignes du type `lbLigne1`, `lbLigne2`, `lbLigne3`.
@@ -901,7 +904,7 @@ class Decoder:
code = parent.find(id = 'ctl00_ctl00_MainContent_TabsContent_' \
'TabContainerResumeStructure__tabHierarche__gvParents_ctl02_' \
- '_hlStructure')
+ '_hlCode')
pid = _parse_qs(_urlparse(code['href']).query)['id'][0]
pid = _b64decode(pid)
pcode = code.text
@@ -917,13 +920,15 @@ class Decoder:
# HTML (Ajax) fragments decoding.
# ---
- _decode_ajax_intranet_adherentlist_codes = (9192, 9431, 9449, 15819, 17464,
- 17721, 20723, 22607, 22864)
- _decode_ajax_intranet_adherent_summary_codes = (21686,)
- _decode_ajax_intranet_adherent_family_codes = (7190,)
- _decode_ajax_intranet_calendar_month_codes = (23896, 24180, 24954, 26883,
- 26931, 27407, 27933, 28026, 29232, 31576, 33283, 33288, 33438, 38663,
- 39664, 40051, 47167)
+ # FIXME: very probably unstable?
+ #
+ #_decode_ajax_intranet_adherentlist_codes = (9192, 9431, 9449, 15819,
+ # 17464, 17721, 20723, 22607, 22864)
+ #_decode_ajax_intranet_adherent_summary_codes = (21686,)
+ #_decode_ajax_intranet_adherent_family_codes = (7190,)
+ #_decode_ajax_intranet_calendar_month_codes = (23896, 24180, 24954,
+ # 26883, 26931, 27407, 27933, 28026, 29232, 31576, 33283, 33288,
+ # 33438, 38663, 39664, 40051, 47167)
def _decode_html_intranet_adherentlist_fragment(self, tree):
""" Decode the HTML operations from a BeautifulSoup decoded
@@ -939,7 +944,8 @@ class Decoder:
# Get the people.
- for line in parent.find('tr', attrs = {'class': ['ligne1', 'ligne2']}):
+ for line in parent.find('tr', attrs = {'class': ['ligne1',
+ 'ligne2']}):
try:
_, name, adh, fct, st, finfct, cp, vil, \
finadh, *_ = line.children
@@ -1016,6 +1022,7 @@ class Decoder:
'TabContainerResumeAdherent__tabResume')
rpre = 'ctl00_ctl00_MainContent_TabsContent_' \
'TabContainerResumeAdherent__tabResume__resume__'
+ ipre = f'{rpre}modeleIndividu__'
def checked(x):
try:
@@ -1027,7 +1034,7 @@ class Decoder:
# IID (from the print button).
- iid = tree.find(id = '_btnPrint')
+ iid = tree.find(id = 'ctl00_ctl00__btnPrint__button')
iid = iid.attrs['onclick']
iid = iid[iid.find('ResumeAdherent.aspx?id=') + 23:]
iid = iid[:iid.find('&')]
@@ -1079,7 +1086,7 @@ class Decoder:
# Onglet "Personne" : nom de jeune fille.
- nomn = parent.find(id = f'{rpre}modeleIndividu__lblNomJeuneFille2')
+ nomn = parent.find(id = f'{ipre}lblNomJeuneFille2')
p.name.last = nomn.text
# Onglet "Personne" : adresse postale. TODO
@@ -1087,17 +1094,16 @@ class Decoder:
lines = []
for i in _count(1):
try:
- aid = f'{rpre}modeleIndividu__resumeAdresse__lbLigne{i}'
+ aid = f'{ipre}resumeAdresse__lbLigne{i}'
al = parent.find(id = aid)
assert al != None
except:
break
lines.append(al.text.strip())
- cp = parent.find(id = f'{rpre}modeleIndividu__resumeAdresse_' \
- '_lbCodePostal')
- vil = parent.find(id = f'{rpre}modeleIndividu__resumeAdresse__lbVille')
- pays = parent.find(id = f'{rpre}modeleIndividu__resumeAdresse__lbPays')
+ cp = parent.find(id = f'{ipre}resumeAdresse__lbCodePostal')
+ vil = parent.find(id = f'{ipre}resumeAdresse__lbVille')
+ pays = parent.find(id = f'{ipre}resumeAdresse__lbPays')
#p.address.text = '\n'.join(lines)
#p.address.city.name = vil.text.strip()
@@ -1106,7 +1112,7 @@ class Decoder:
# Onglet "Personne" : NPAI. TODO
- npai = parent.find(id = f'{rpre}modeleIndividu__lblNpai')
+ npai = parent.find(id = f'{ipre}lblNpai')
npai = npai.text
# Onglet "Personne" : numéros de téléphone utilisés. TODO
@@ -1116,18 +1122,18 @@ class Decoder:
x = x[1:]
return '+33' + x
- dph = parent.find(id = f'{rpre}modeleIndividu__lblTelephoneDomicile')
+ dph = parent.find(id = f'{ipre}lblTelephoneDomicile')
dph = dph.text
- pph = parent.find(id = f'{rpre}modeleIndividu__lblTelephoneBureau')
+ pph = parent.find(id = f'{ipre}lblTelephoneBureau')
pph = pph.text
- cph1 = parent.find(id = f'{rpre}modeleIndividu__lblTelephonePortable1')
+ cph1 = parent.find(id = f'{ipre}lblTelephonePortable1')
cph1 = cph1.text
- cph2 = parent.find(id = f'{rpre}modeleIndividu__lblTelephonePortable2')
+ cph2 = parent.find(id = f'{ipre}lblTelephonePortable2')
cph2 = cph2.text
# Onglet "Personne" : adresses de courriel utilisées. TODO
- iem = parent.find(id = f'{rpre}modeleIndividu__hlCourrielPersonnel')
+ iem = parent.find(id = f'{ipre}hlCourrielPersonnel')
iem = iem.text
pem = parent.find(id = f'{rpre}modeleIndividu_' \
'_hlCourrielProfessionnel')
@@ -1135,7 +1141,7 @@ class Decoder:
# Onglet "Personne" : date de naissance.
- bd = parent.find(id = f'{rpre}modeleIndividu__lblDateNaissance')
+ bd = parent.find(id = f'{ipre}lblDateNaissance')
bd = bd.text
mday, mon, year = map(int, bd.split('/'))
@@ -1143,11 +1149,11 @@ class Decoder:
# Onglet "Personne" : commune de naissance.
- bpp = parent.find(id = f'{rpre}modeleIndividu__lbCPNaissance')
+ bpp = parent.find(id = f'{ipre}lbCPNaissance')
bpp = bpp.text
- bpn = parent.find(id = f'{rpre}modeleIndividu__lbCommuneNaissance')
+ bpn = parent.find(id = f'{ipre}lbCommuneNaissance')
bpn = bpn.text
- bpi = parent.find(id = f'{rpre}modeleIndividu__lbCodeInsee')
+ bpi = parent.find(id = f'{ipre}lbCodeInsee')
bpi = bpi.text
p.birth_town = bpn
@@ -1156,18 +1162,18 @@ class Decoder:
# Onglet "Personne" : profession actuelle. TODO
- pro = parent.find(id = f'{rpre}modeleIndividu__lblProfession')
+ pro = parent.find(id = f'{ipre}lblProfession')
pro = pro.text
# Onglet "Personne" : régime d'allocations et numéro
# d'allocataire. TODO
- nal = parent.find(id = f'{rpre}modeleIndividu__lblNumeroAllocataire')
+ nal = parent.find(id = f'{ipre}lblNumeroAllocataire')
nal = nal.text
- caf = parent.find(id = f'{rpre}modeleIndividu__cbRegimeGeneral')
- msa = parent.find(id = f'{rpre}modeleIndividu__cbRegimeMSA')
- aut = parent.find(id = f'{rpre}modeleIndividu__cbRegimeEtranger')
+ caf = parent.find(id = f'{ipre}cbRegimeGeneral')
+ msa = parent.find(id = f'{ipre}cbRegimeMSA')
+ aut = parent.find(id = f'{ipre}cbRegimeEtranger')
#p.allocations.number = (None, nal)[nal != '.']
#if checked(caf) or checked(mar):
@@ -1180,7 +1186,8 @@ class Decoder:
# Onglet "Personne" : autorise les interventions chirurgicales.
- med = parent.find(id = f'{rpre}cbAutorisationInterventionChirurgicale')
+ med = parent.find(id = f'{rpre}cbAutorisationIntervention'
+ 'Chirurgicale')
p.medical_surgical_measures = checked(med)
# Onglet "Personne" : droit à l'image accordé à l'association.
@@ -1226,8 +1233,8 @@ class Decoder:
# Onglet "Famille" : relations. TODO
#
- # Ici, on a du <a><br><a><br>…<a><br>, où le `href` contient l'URL de
- # l'adhérent sous forme absolue mais relative au site, i.e.
+ # Ici, on a du <a><br><a><br>…<a><br>, où le `href` contient l'URL
+ # de l'adhérent sous forme absolue mais relative au site, i.e.
#
# /Specialisation/Sgdf/adherents/ResumeAdherent.aspx?id=<b64 id>
#
@@ -1276,7 +1283,8 @@ class Decoder:
span = div.find('span')['onmouseover']
span = span[span.find("'") + 1:]
- spanidx = next(_rfinditer(r"($|[^\\])(\\{2})*\'", span)).end()
+ spanidx = next(_rfinditer(r"($|[^\\])(\\{2})*\'",
+ span)).end()
span = span[:spanidx - 1]
span = _rreplace(r"($|[^\\])\\*'", r"\1'", span)
span = _htmlunescape(span)
@@ -1388,8 +1396,8 @@ class Decoder:
# ---
def _decode_xls_intranet_operations(self, data):
- """ Decode the XLS documents for operations ("Journal_activites.xls")
- from a pandas decoded content. """
+ """ Decode the XLS documents for operations
+ ("Journal_activites.xls") from a pandas decoded content. """
print(content)
raise NotImplementedError
@@ -1465,7 +1473,8 @@ class Decoder:
while True:
try:
- cname = next(cn for cn in funcs.keys() if len(funcs[cn]) > 1)
+ cname = next(cn for cn in funcs.keys() \
+ if len(funcs[cn]) > 1)
except StopIteration:
break
@@ -1477,7 +1486,8 @@ class Decoder:
codes = [func.code for func in disf]
sfix = [_sfix(code) for code in codes]
if len(sfix) > len(set(sfix)):
- raise ValueError(f"Could not deduplicate using scale: {codes}")
+ raise ValueError(f"Could not deduplicate using scale: "
+ f"{codes}")
for func, suffix in zip(disf, sfix):
# Delete all the current function references.
diff --git a/sgdfi/_intranet.py b/sgdfi/_intranet.py
index a4248fc..9c4ddc8 100755
--- a/sgdfi/_intranet.py
+++ b/sgdfi/_intranet.py
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
-#******************************************************************************
+#**************************************************************************
# Copyright (C) 2018 Thomas Touhey <thomas@touhey.fr>
# This file is part of the sgdfi project, which is MIT-licensed.
-#******************************************************************************
+#**************************************************************************
""" Main object to interact with SGDF's intranet. """
from os import linesep as _linesep
@@ -100,7 +100,8 @@ class AnonymousIntranetSession:
def __log(self, *msg):
if self.__dbg:
- _stderr.write(f"{self._log_prefix()} {' '.join(msg)}{_linesep}")
+ _stderr.write(f"{self._log_prefix()} "
+ f"{' '.join(msg)}{_linesep}")
@property
def user(self):
@@ -154,8 +155,8 @@ class AnonymousIntranetSession:
The hint is for decoding the content, according to the
context. The `session` argument is `None` if we shall use the
- object's requests Session object, or the requests Session object
- to use. """
+ object's requests Session object, or the requests Session
+ object to use. """
if new_session:
self.__oldsession = self.__session
@@ -215,8 +216,8 @@ class AnonymousIntranetSession:
if ct == 'text/plain':
self.__log(f"Decoding AJAX response{htext}.")
- return self.__mgr.feed(r.text, 'application/x-microsoft-ajax',
- hint)
+ return self.__mgr.feed(r.text,
+ 'application/x-microsoft-ajax', hint)
elif ct == 'text/csv':
self.__log(f"Decoding CSV content{htext}.")
@@ -294,7 +295,20 @@ class AnonymousIntranetSession:
payload = {}
payload.update(df)
- # Ensuite, ajoutons les arguments de l'utilisateur.
+ # Vérifions deux ou trois trucs pour l'AJAX.
+
+ if method == self.METHOD_AJAX:
+ payload['__ASYNCPOST'] = 'true'
+
+ # Suppression des boutons oui, non et plus tard pour
+ # l'AJAX et le POST (ça a l'air de faire déconner les requêtes).
+
+ for k in ('Oui', 'Non', 'PlusTard'):
+ if 'ctl00$ctl00$_btn' + k in payload:
+ del payload['ctl00$ctl00$_btn' + k]
+
+ # Ensuite, ajoutons les arguments de l'utilisateur, en supprimant
+ # là où l'utilisateur a mis None à la place d'une valeur.
def recursive_args(elements, prefix = ''):
for key in elements:
@@ -303,17 +317,18 @@ class AnonymousIntranetSession:
recursive_args(value, prefix + key + '$')
continue
- payload[prefix + key] = value
-
- recursive_args(args)
+ if value is not None:
+ payload[prefix + key] = value
+ else:
+ # Keys are copied into a separate list to avoid
+ # dictionary changing size during iteration error.
- # On enlève ensuite ce qui est à supprimer selon l'utilisateur,
- # à savoir ce qui vaut `None`.
+ for pkey in list(payload.keys()):
+ if pkey == prefix + key \
+ or pkey.startswith(prefix + key + '$'):
+ del payload[pkey]
- keys = list(payload.keys())
- for key in keys:
- if payload[key] is None:
- del payload[key]
+ recursive_args(args)
# ---
# Préparation des cookies et exécution de la requête.
@@ -335,10 +350,16 @@ class AnonymousIntranetSession:
pkw['data'] = payload
headers['Referer'] = self.__base + path
+ if self.__dbg:
+ self.__log(f"Field payload:")
+ for key, value in payload.items():
+ self.__log(f" {key}: {value}")
+
tm = _monotime()
r = self.__session.post(self.__base + path, headers = headers,
allow_redirects = False, **pkw)
- self.__log(f"Page POST request delay: {round(_monotime() - tm, 4)}s")
+ self.__log("Page POST request delay: "
+ f"{round(_monotime() - tm, 4)}s")
return ret(r)
@@ -368,8 +389,8 @@ class AnonymousIntranetSession:
if espan is not None:
error = espan.text.strip()
if error.startswith("Le compte associé à l'identifiant '") \
- and error.endswith("' ne donne pas le droit d'utiliser cette " \
- "application"):
+ and error.endswith("' ne donne pas le droit d'utiliser "
+ "cette application"):
raise _UnauthorizedAccountError(code)
raise _InvalidUserError(user = code)
@@ -409,8 +430,8 @@ class AnonymousIntranetSession:
etx = espan.text.strip()
noinfo = "Nous ne pouvons pas vous donner votre numéro " \
- "d’adhérent pour la raison suivante : Pas de résultat avec " \
- "les informations fournies"
+ "d’adhérent pour la raison suivante : Pas de résultat " \
+ "avec les informations fournies"
if etx == noinfo:
raise ValueError("no result found")
@@ -485,8 +506,8 @@ class IntranetSession(AnonymousIntranetSession):
'/WebServices/AutoComplete.asmx/GetFonctions'
args = {'id_token': 'undefined', 'q': ''}
- return self.get_page(path, method = self.METHOD_JSONPOST, args = args,
- hint = 'intranet_functions')
+ return self.get_page(path, method = self.METHOD_JSONPOST,
+ args = args, hint = 'intranet_functions')
# ---
# Session-related.
@@ -502,10 +523,10 @@ class IntranetSession(AnonymousIntranetSession):
try:
result = self.get_page('/Default.aspx', {
- 'ctl00': {
- 'MainContent': {
- 'login': self.__user,
- 'password': pw}}},
+ 'login': self.__user,
+ 'password': pw,
+ 'EnvoyerNumeroAdherent': None,
+ 'Envoyer': None},
method = self.METHOD_FORM,
hint = 'sic',
new_session = True)
@@ -710,17 +731,15 @@ class IntranetSession(AnonymousIntranetSession):
'TabState': [True] * 10}
args = {
stname: _jsondumps(state),
- 'ctl00': {
- 'ctl00': {
- '_ddDelegations': '0',
- '_hidReferenceStatistiqueUtilisation': '-1',
- '_tbRechAdherent': '',
- '_tbRechStructure': ''}}}
+ '__EVENTTARGET': 'ctl00$ctl00$MainContent$TabsContent' \
+ '$TabContainerResumeStructure',
+ '__EVENTARGUMENT': 'activeTabChanged:1'}
# Récupération de l'onglet "Résumé".
state['ActiveTabIndex'] = 1
args[stname] = _jsondumps(state)
+ args['__EVENTARGUMENT'] = 'activeTabChanged:1'
st = self.get_page(path, args, method = self.METHOD_FORM,
hint = "intranet_structure_summary")
@@ -728,6 +747,7 @@ class IntranetSession(AnonymousIntranetSession):
state['ActiveTabIndex'] = 0
args[stname] = _jsondumps(state)
+ args['__EVENTARGUMENT'] = 'activeTabChanged:0'
st_updates = self.get_page(path, args, method = self.METHOD_FORM,
hint = "intranet_structure_hierarchy")
@@ -755,26 +775,32 @@ class IntranetSession(AnonymousIntranetSession):
'TabContainerResumeAdherent_ClientState'
state = {
'ActiveTabIndex': 0,
- 'TabState': [True] * 11}
+ 'TabState': [True] * 9}
args = {
stname: _jsondumps(state),
+ '__EVENTTARGET': 'ctl00$ctl00$MainContent' \
+ '$TabsContent$TabContainerResumeAdherent',
+ '__EVENTARGUMENT': 'activeTabChanged:0',
'ctl00': {
'ctl00': {
- 'ScriptManager1': 'ctl00$ctl00$_upMainContent' \
- '|ctl00$ctl00$MainContent$TabsContent$' \
+ 'MainContent$TabsContent': None,
+ 'ScriptManager1': 'ctl00$ctl00$_upMainContent|' \
+ 'ctl00$ctl00$MainContent$TabsContent$' \
'TabContainerResumeAdherent'}}}
# Récupération du premier onglet, "Personne".
state['ActiveTabIndex'] = 0
args[stname] = _jsondumps(state)
+ args['__EVENTARGUMENT'] = 'activeTabChanged:0'
result = self.get_page(path, args, method = self.METHOD_AJAX,
hint = 'intranet_adherent_summary')
#state['ActiveTabIndex'] = 1
#args[stname] = _jsondumps(state)
+ #args['__EVENTARGUMENT'] = 'activeTabChanged:1'
#result = self.get_page(path, args, method = self.METHOD_AJAX,
- # hint = 'person_family')
+ # hint = 'intranet_person_family')
return result
@@ -801,11 +827,11 @@ class IntranetSession(AnonymousIntranetSession):
# Get the page corresponding to the selected month.
#
# FIXME: Remove the following :
- # - `ctl00$MainContent$_navigateur$_ddStructure`: 1061745 (XXX: valeur
- # principale sélectionnée, probablement ID interne)
- # - `ctl00$MainContent$_navigateur$_hidCodeStructure`: 119101231 (code
- # public de la structure), toujours le code de structure lié à
- # la délégation courante et non celui de la structure.
+ # - `ctl00$MainContent$_navigateur$_ddStructure`: 1061745
+ # (XXX: valeur principale sélectionnée, probablement ID interne)
+ # - `ctl00$MainContent$_navigateur$_hidCodeStructure`: 119101231
+ # (code public de la structure), toujours le code de structure
+ # lié à la délégation courante et non celui de la structure.
path = '/Specialisation/Sgdf/animation/ConsulterCalendrier.aspx'
return self.get_page(path, {
diff --git a/sgdfi/_manager.py b/sgdfi/_manager.py
index 1296614..f5ab0e8 100755
--- a/sgdfi/_manager.py
+++ b/sgdfi/_manager.py
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
-#******************************************************************************
+#**************************************************************************
# Copyright (C) 2018 Thomas Touhey <thomas@touhey.fr>
# This file is part of the sgdfi project, which is MIT-licensed.
-#******************************************************************************
+#**************************************************************************
""" Definition of the main object to decode and manage representations, and
manage sessions. """
@@ -19,12 +19,12 @@ from datetime import datetime as _datetime, date as _date
from appdirs import user_cache_dir as _user_cache_dir
-from ._repr import Base as _Base, Structure as _Structure, \
- Adherent as _Adherent, Place as _Place, \
- RallyRegistration as _RallyRegistration, Camp as _Camp, \
- Operation as _Operation, FunctionRawData as _FunctionRawData
-from ._intranet import AnonymousIntranetSession as _AnonymousIntranetSession, \
- IntranetSession as _IntranetSession
+from ._repr import (Base as _Base, Structure as _Structure,
+ Adherent as _Adherent, Place as _Place,
+ RallyRegistration as _RallyRegistration, Camp as _Camp,
+ Operation as _Operation, FunctionRawData as _FunctionRawData)
+from ._intranet import (IntranetSession as _IntranetSession,
+ AnonymousIntranetSession as _AnonymousIntranetSession)
from ._decode import Decoder as _Decoder
__all__ = ["Manager"]
@@ -183,7 +183,8 @@ class _IntranetSessionsManager:
elif self.__anon is None:
self.__anon = session
else:
- raise ValueError("There is already an anonymous session here.")
+ raise ValueError("There is already an anonymous "
+ "session here.")
else:
raise ValueError("Expected a session.")
@@ -302,7 +303,7 @@ class _StructureEventsManager:
def __getitem__(self, key):
if isinstance(key, slice):
- # The mgr.events[<iid>][datetime(y1, m1, d1):datetime(y2, m2, d2)]
+ # The mgr.events[<iid>][dt(y1, m1, d1):dt(y2, m2, d2)]
# notations was used. The step is not read.
start = _dt(key.start) if key.start is not None else None
@@ -473,7 +474,8 @@ class Manager(_Decoder):
try:
id = int(id)
except ValueError:
- raise ValueError("expected an integer as an identifier")
+ raise ValueError("expected an integer as an "
+ "identifier")
elif type(id) != int:
raise ValueError("expected an integer as an identifier")
if id < 0 or id > 99:
@@ -602,7 +604,8 @@ class Manager(_Decoder):
# Warning: if it's a stream, we're about to empty it and make
# it empty for the decoder, which is not what we want.
# So we make a bytes or str object out of the stream in order
- # to be sure to be able to read it twice (seeking might not work).
+ # to be sure to be able to read it twice (seeking might not
+ # work).
if isinstance(inp, _RawIOBase):
inp = inp.read()
@@ -649,7 +652,8 @@ class Manager(_Decoder):
print("", file = to)
print("__all__ = [\"Function\", \"FunctionData\"]", file = to)
print("", file = to)
- print("# This file was generated, do not edit it manually!", file = to)
+ print("# This file was generated, do not edit it manually!",
+ file = to)
print("", file = to)
# Enumeration definition.
diff --git a/sgdfi/_repr.py b/sgdfi/_repr.py
index b09f37e..c009485 100755
--- a/sgdfi/_repr.py
+++ b/sgdfi/_repr.py
@@ -1,25 +1,26 @@
#!/usr/bin/env python3
-#******************************************************************************
+#**************************************************************************
# Copyright (C) 2018 Thomas Touhey <thomas@touhey.fr>
# This file is part of the sgdfi project, which is MIT-licensed.
-#******************************************************************************
+#**************************************************************************
""" Main object to interact with SGDF's intranet. """
import regex as _re
-from ._util import IID, Enum as _Enum, \
- Base, Property as _Property, \
- IIDProperty as _IIDProperty, DateProperty as _DateProperty, \
- BoolProperty as _BoolProperty, EnumProperty as _EnumProperty, \
- ArrayProperty as _ArrayProperty, TextProperty as _TextProperty, \
- ObjectProperty as _ObjectProperty, ValueProperty as _ValueProperty
-from ._dbs import Department, DepartmentData as _DepartmentData, \
- OperationType, OperationTypeData as _OperationTypeData, \
- Function, FunctionRawData, FunctionData as _FunctionData, StructureType, \
- StructureTypeData as _StructureTypeData, StructureStatus, \
- StructureStatusData as _StructureStatusData, StructureSpeciality, \
- StructureSpecialityData as _StructureSpecialityData, \
- AllocationsRegime, EventTypeData as _EventTypeData, Code as _Code
+from ._util import (IID, Enum as _Enum, Base,
+ Property as _Property, IIDProperty as _IIDProperty,
+ DateProperty as _DateProperty, BoolProperty as _BoolProperty,
+ EnumProperty as _EnumProperty, ArrayProperty as _ArrayProperty,
+ TextProperty as _TextProperty, ObjectProperty as _ObjectProperty,
+ ValueProperty as _ValueProperty)
+from ._dbs import (Department, DepartmentData as _DepartmentData,
+ OperationType, OperationTypeData as _OperationTypeData,
+ Function, FunctionRawData, FunctionData as _FunctionData,
+ StructureType, StructureTypeData as _StructureTypeData,
+ StructureStatus, StructureStatusData as _StructureStatusData,
+ StructureSpeciality, EventTypeData as _EventTypeData,
+ StructureSpecialityData as _StructureSpecialityData,
+ AllocationsRegime, Code as _Code)
__all__ = ["Base", "IID", "Title", "Structure", "Adherent",
"RallyRegistration", "Camp", "Place", "Operation", "OperationType",
@@ -94,7 +95,8 @@ class _Name:
def __str__(self):
ops = []
- titl = {Title.MISTER: "M.", Title.MISS: "Mme"}.get(self.__title, None)
+ titl = {Title.MISTER: "M.", Title.MISS: "Mme"}.get(self.__title,
+ None)
if titl is not None:
ops.append(titl)
@@ -295,7 +297,8 @@ class Adherent(_Base):
birth_town = _TextProperty()
profession = _TextProperty()
allocations_beneficiary_number = _TextProperty()
- allocations_beneficiary_regime = _EnumProperty(enum = AllocationsRegime)
+ allocations_beneficiary_regime = _EnumProperty(\
+ enum = AllocationsRegime)
image_rights = _BoolProperty()
medical_surgical_measures = _BoolProperty()
civil_liability_insurance = _BoolProperty()
@@ -329,7 +332,8 @@ class Place(_Base):
department = _TextProperty(doc = "Name of the related " \
"administrative department.")
out_of_france = _BoolProperty()
- continent = _TextProperty(doc = "Name of the continent the place is in.")
+ continent = _TextProperty(doc = "Name of the continent the place "
+ "is in.")
phone = _TextProperty()
fax = _TextProperty()
email = _TextProperty()
diff --git a/sgdfi/_util.py b/sgdfi/_util.py
index e78bc35..cb2cd86 100755
--- a/sgdfi/_util.py
+++ b/sgdfi/_util.py
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
-#******************************************************************************
+#**************************************************************************
# Copyright (C) 2018 Thomas Touhey <thomas@touhey.fr>
# This file is part of the sgdfi project, which is MIT-licensed.
-#******************************************************************************
+#**************************************************************************
""" Base objects for the intranet objects representations, never used
by the end user of the module. """
@@ -17,10 +17,10 @@ import regex as _re
from pytz import timezone as _timezone
__all__ = ["IID", "Enum",
- "Property", "IIDProperty", "DateProperty", "BoolProperty", "EnumProperty",
- "ArrayProperty", "TextProperty", "ObjectProperty", "ValueProperty", "Base",
- "InvalidCredentialsError", "InvalidUserError", "InvalidPasswordError",
- "UnauthorizedAccountError"]
+ "Property", "IIDProperty", "DateProperty", "BoolProperty",
+ "EnumProperty", "ArrayProperty", "TextProperty", "ObjectProperty",
+ "ValueProperty", "Base", "InvalidCredentialsError", "InvalidUserError",
+ "InvalidPasswordError", "UnauthorizedAccountError"]
# ---
# Attribute helpers.
@@ -115,8 +115,10 @@ class _Array:
def add(self, elt):
""" Add an element into the array. """
- if self.__types and not any(isinstance(elt, t) for t in self.__types):
- raise TypeError(f"element to add is of unknown type {type(elt)}")
+ if self.__types and not any(isinstance(elt, t) \
+ for t in self.__types):
+ raise TypeError("element to add is of unknown type "
+ f"{type(elt)}")
if any(elt is e or elt == e for e in self.__list):
raise ValueError("element already inserted")
self.__list.append(elt)
@@ -308,7 +310,8 @@ class ArrayProperty(Property):
self.__array = _Array(types)
def defaultdoc(self, element, name):
- return f"The {name.replace('_', ' ')} array for the {element.lower()}."
+ return f"The {name.replace('_', ' ')} array for the " \
+ f"{element.lower()}."
def get(self):
return self.__array
@@ -513,7 +516,8 @@ class InvalidUserError(InvalidCredentialsError):
""" Exception for when the user identifier is invalid. """
def __init__(self, user):
- super().__init__(f"Invalid identifier ({repr(user)}).", user = user)
+ super().__init__(f"Invalid identifier ({repr(user)}).",
+ user = user)
class InvalidPasswordError(InvalidCredentialsError):
""" Exception for when the password is invalid. """
@@ -526,8 +530,8 @@ class UnauthorizedAccountError(InvalidCredentialsError):
""" Exception for when the user is not allowed to log in. """
def __init__(self, user):
- super().__init__(f"User with identifier {repr(user)} is not allowed " \
- "to log in.", user = user)
+ super().__init__(f"User with identifier {repr(user)} is not "
+ "allowed to log in.", user = user)
class RedirectError(Exception):
""" The response is a redirection. """
diff --git a/sgdfi/_version.py b/sgdfi/_version.py
index 5f419a2..66a8fea 100755
--- a/sgdfi/_version.py
+++ b/sgdfi/_version.py
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
-#******************************************************************************
+#**************************************************************************
# Copyright (C) 2018 Thomas Touhey <thomas@touhey.fr>
# This file is part of the sgdfi project, which is MIT-licensed.
-#******************************************************************************
+#**************************************************************************
""" This tiny submodule only contains the version, for other components to
include it more easily. """
diff --git a/sgdfi/test/__init__.py b/sgdfi/test/__init__.py
deleted file mode 100755
index 9862b81..0000000
--- a/sgdfi/test/__init__.py
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env python3
-#******************************************************************************
-# Copyright (C) 2018 Thomas Touhey <thomas@touhey.fr>
-# This file is part of the sgdfi project, which is MIT-licensed.
-#******************************************************************************
-""" Unit tests for the `sgdfi` Python module. """
-
-# This file is only there to indicate that the folder is a module.
-# It doesn't actually contain code.
-
-# End of file.