aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas "Cakeisalie5" Touhey <thomas@touhey.fr>2018-10-28 14:55:01 +0100
committerThomas "Cakeisalie5" Touhey <thomas@touhey.fr>2018-10-28 14:55:01 +0100
commit53fe6171c34d30e45c2ae469594dd93c461e3ca4 (patch)
tree732c263f1bf6004475296f18c8c1a6c2da42847d
parent4fdafd307659e1caca974975a4e62a08ca21837f (diff)
Sessions reference, better error management.
-rw-r--r--docs/intranet/misc.rst7
-rwxr-xr-xsgdfi/_decode.py44
-rwxr-xr-xsgdfi/_intranet.py106
-rwxr-xr-xsgdfi/_manager.py75
-rwxr-xr-xsgdfi/_util.py41
5 files changed, 198 insertions, 75 deletions
diff --git a/docs/intranet/misc.rst b/docs/intranet/misc.rst
index 3e53c93..99be918 100644
--- a/docs/intranet/misc.rst
+++ b/docs/intranet/misc.rst
@@ -8,6 +8,13 @@ Other locations
A home page where you are redirected when you can find some tips and
announcements, plus the menu.
+``/erreurs/404.aspx``
+ A path has not been found.
+
+ ``aspxerrorpath``
+ The path of the error, e.g.
+ ``/Specialisation/Sgdf/PageNonExistante.aspx``.
+
``/erreurs/Erreur.aspx``
An error has occured and the user is redirected to this page, which
takes a few GET parameters:
diff --git a/sgdfi/_decode.py b/sgdfi/_decode.py
index 3a80b44..09352ed 100755
--- a/sgdfi/_decode.py
+++ b/sgdfi/_decode.py
@@ -23,6 +23,8 @@ 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
+from ._util import NotFoundError as _NotFoundError, \
+ ForbiddenError as _ForbiddenError, Pagination as _Pagination
__all__ = ["Decoder"]
@@ -30,14 +32,6 @@ __all__ = ["Decoder"]
# Utilities.
# ---
-# Pagination object.
-
-class _Pagination:
- def __init__(self):
- self.current = 0
- self.number = 0
- self.more = False
-
# As `type` is a local variable in some functions, we define an alias to
# the real function named `_type`.
@@ -110,8 +104,6 @@ class Decoder:
""" Return objects from SGDF's intranet. """
def __init__(self):
- self._pgn = _Pagination()
-
# Find out the AJAX code parts.
self._ajaxcodes = {}
@@ -182,11 +174,31 @@ class Decoder:
msg = f"Unknown HTML hint: {repr(hint)}"
raise ValueError(msg) from None
- content = _BeautifulSoup(inp, 'lxml')
- if hint == 'sic':
- return content
+ tree = _BeautifulSoup(inp, 'lxml')
+
+ # Check for an error div with a known error.
- result = func(content)
+ enode = tree.find(id = 'ctl00__upMainContent')
+ if enode is not None:
+ # Check if it is a 404 or a 403.
+
+ node = enode.find(attrs = {'class': 'interdit'})
+
+ if node is not None:
+ nftext = "Cette page est introuvable."
+ fptext = "Vous n'avez pas les droits nécessaires pour " \
+ "accéder à cette page."
+
+ if node.text == nftext:
+ raise _NotFoundError()
+ elif node.text == fptext:
+ raise _ForbiddenError()
+
+ # Treat the answer.
+
+ if hint == 'sic':
+ return tree
+ result = func(tree)
elif type == 'application/x-microsoft-ajax':
# The input might be a stream.
@@ -436,9 +448,7 @@ class Decoder:
numpages = 1
more = False
- self._pgn.current = curpage
- self._pgn.number = numpages
- self._pgn.more = more
+ elts.append(_Pagination(curpage, numpages, more))
# Récupération de la liste d'évènements.
diff --git a/sgdfi/_intranet.py b/sgdfi/_intranet.py
index 1b63762..0e54c36 100755
--- a/sgdfi/_intranet.py
+++ b/sgdfi/_intranet.py
@@ -24,59 +24,62 @@ from ._repr import IID as _IID
from ._util import InvalidCredentialsError as _InvalidCredentialsError, \
InvalidUserError as _InvalidUserError, \
InvalidPasswordError as _InvalidPasswordError, \
- UnauthorizedAccountError as _UnauthorizedAccountError
+ UnauthorizedAccountError as _UnauthorizedAccountError, \
+ RedirectError as _RedirectError, BadRequestError as _BadRequestError, \
+ NotFoundError as _NotFoundError, Pagination as _Pagination
__all__ = ["AnonymousIntranetSession", "IntranetSession"]
_monotime = lambda: _getclocktime(_MONOCLOCK)
-class RedirectError(Exception):
- """ The response is a redirection. """
-
- def __init__(self, location):
- super().__init__(f"Was redirected to {repr(location)}.")
- self.__location = location
-
- @property
- def location(self):
- """ The location to which we have been redirected. """
-
- return self.__location
-
-class BadRequestError(RedirectError):
- """ The response is a redirection to the bad request page. """
-
- def __init__(self, location):
- path = '/Specialisation/sgdf/erreurs/Erreur.aspx?' \
- + _urlencode({'aspxerrorpath': location})
- super().__init__(location)
+# ---
+# Définition des objets principaux.
+# ---
- self.__loc = location
+_mgrcls = None
- def __str__(self):
- return f"Erroneous request to {repr(self.__loc)}."
+def _import_manager():
+ """ Import the manager class.
+ Imported in a function because otherwise there would be a recursive
+ import. """
- @property
- def path(self):
- """ The error path. """
+ global _mgrcls
- return self.__loc
+ if _mgrcls is None:
+ from ._manager import Manager
+ _mgrcls = Manager
-# ---
-# Définition des objets principaux.
-# ---
+ return _mgrcls
class AnonymousIntranetSession:
""" Class for interacting with the intranet while not logged in. """
def __init__(self, manager, base = 'https://intranet.sgdf.fr',
debug = False):
- self.__mgr = manager
self.__base = base
self.__session = _Session()
self.__oldsess = None
self.__dbg = bool(debug or manager._debug)
+ self._ref(manager)
+
+ def _ref(self, manager):
+ """ Reference self into a manager. """
+
+ # If the manager doesn't exist, create it here.
+
+ _Manager = _import_manager()
+ if isinstance(manager, _Manager):
+ self.__mgr = manager
+ elif manager is not None:
+ raise ValueError("Expected a Manager.")
+ else:
+ self.__mgr = _Manager()
+
+ # Add self to the sessions manager.
+
+ self.__mgr.sessions.intranet._ref(self)
+
def __repr__(self):
return f"{self.__class__.__name__}()"
@@ -88,6 +91,12 @@ class AnonymousIntranetSession:
if self.__dbg:
_stderr.write(f"{self._log_prefix()} {' '.join(msg)}{_linesep}")
+ @property
+ def user(self):
+ """ Username set (placeholder). """
+
+ return None
+
# ---
# Base utilities.
# ---
@@ -141,15 +150,20 @@ class AnonymousIntranetSession:
try:
p = _urlparse(loc)
- assert p.path.casefold() in ('/erreurs/erreur.aspx',
- '/specialisation/sgdf/erreurs/erreur.aspx')
-
- path = _parse_qs(p.query)['aspxerrorpath'][0]
- raise BadRequestError(path) from None
+ path = p.path.casefold()
+
+ if path in ('/erreurs/erreur.aspx',
+ '/specialisation/sgdf/erreurs/erreur.aspx'):
+ path = _parse_qs(p.query)['aspxerrorpath'][0]
+ raise _BadRequestError() from None
+ elif path in ('/erreurs/404.aspx',
+ '/specialisation/sgdf/erreurs/404.aspx'):
+ path = _parse_qs(p.query)['aspxerrorpath'][0]
+ raise _NotFoundError() from None
except (AssertionError, KeyError, IndexError):
pass
- raise RedirectError(loc)
+ raise _RedirectError(loc)
# Check if we ought to ignore the thing.
@@ -280,13 +294,6 @@ class AnonymousIntranetSession:
else:
pkw['data'] = payload
- if self.__dbg:
- self.__log("Page POST arguments:")
- it = map(lambda x: (x, payload[x]), sorted(payload.keys()))
- for key, value in it:
- self.__log(f"{repr(key)}")
- self.__log(f"= {repr(value)}")
-
tm = _monotime()
r = self.__session.post(self.__base + path, headers = headers,
allow_redirects = False, **pkw)
@@ -483,7 +490,7 @@ class IntranetSession(AnonymousIntranetSession):
method = self.METHOD_FORM,
hint = 'sic',
new_session = True)
- except RedirectError:
+ except _RedirectError:
# We have successfully been redirected!
return
@@ -545,11 +552,12 @@ class IntranetSession(AnonymousIntranetSession):
# Récupération du document et décodage.
resp = self._get_ops_page(ent_type, ent_iid, page)
+ pgn = next(e for e in resp if isinstance(e, _Pagination))
- # Si `curpage < page`, alors c'est un bug connu de l'intranet
+ # Si `pgn.current < page`, alors c'est un bug connu de l'intranet
# où une page est déclarée mais inaccessible, on arrête donc là.
- if self._pgn.current < page:
+ if pgn.current < page:
break
# Ajout des activités à la liste totale.
@@ -558,7 +566,7 @@ class IntranetSession(AnonymousIntranetSession):
# Vérifions si on est arrivés au bout.
- if page == self._pgn.number and not self._pgn.more:
+ if page == pgn.number and not pgn.more:
break
return activities
diff --git a/sgdfi/_manager.py b/sgdfi/_manager.py
index 9a734fb..daeec9e 100755
--- a/sgdfi/_manager.py
+++ b/sgdfi/_manager.py
@@ -116,16 +116,18 @@ class _IntranetSessionsManager:
pass
def __getitem__(self, key):
+ if _isanon(key):
+ if self.__anon is None:
+ # The session will reference itself into this manager.
+
+ _AnonymousIntranetSession(self.__mgr)
+ return self.__anon
+
if _isany(key):
if self.__sessions:
return self.__sessions[0]
raise KeyError("no session available")
- if _isanon(key):
- if self.__anon is None:
- self.__anon = _AnonymousIntranetSession(self.__mgr)
- return self.__anon
-
try:
s = next(s for s in self.__sessions if s.user == key)
except StopIteration:
@@ -133,7 +135,7 @@ class _IntranetSessionsManager:
try:
i = int(key)
- except ValueError:
+ except (TypeError, ValueError):
raise KeyError("invalid key format")
return self.__sessions[key]
@@ -142,6 +144,60 @@ class _IntranetSessionsManager:
return s
+ def __delitem__(self, key):
+ if _isanon(key):
+ self.__anon = None
+ return
+
+ if _isany(key) or _isall(key):
+ self.__sessions = []
+ return
+
+ try:
+ idx = next(i for i, s in enumerate(self.__sessions) \
+ if s.user == key)
+ except StopIteration:
+ # Try and find it using an index.
+
+ try:
+ i = int(key)
+ except (TypeError, ValueError):
+ raise KeyError("invalid key format")
+
+ self.__sessions[idx]._deref()
+
+ def _ref(self, session):
+ """ A session comes to reference itself into this manager, we will
+ manage it. """
+
+ if isinstance(session, _IntranetSession):
+ if any(s is session for s in self.__sessions):
+ return
+ self.__sessions.append(s)
+ elif isinstance(session, _AnonymousIntranetSession):
+ if session is self.__anon:
+ return
+ elif self.__anon is None:
+ self.__anon = session
+ else:
+ raise ValueError("There is already an anonymous session here.")
+ else:
+ raise ValueError("Expected a session.")
+
+ def _deref(self, session):
+ """ A session comes to dereference itself from this manager, we
+ will manage it. """
+
+ if session is self.__anon:
+ self.__anon = None
+ else:
+ try:
+ idx = next(i for i, s in enumerate(self.__sessions) \
+ if s is session)
+ self.__sessions.pop(idx)
+ except StopIteration:
+ raise ValueError("Unreferenced session.") from None
+
def login(self, user, pw):
""" Add intranet credentials. """
@@ -157,15 +213,16 @@ class _IntranetSessionsManager:
self.__sessions.append(s)
if s is None:
- s = _IntranetSession(self.__mgr, user = user, pw = pw)
- self.__sessions.append(s)
+ # The session will reference itself into this manager.
+
+ _IntranetSession(self.__mgr, user = user, pw = pw)
return
if s.pw == pw:
return
# Different password than the one we know, we should try to
- # use the new password instead.
+ # use the new password instead (and NOT change it on the intranet).
s.pw = pw
diff --git a/sgdfi/_util.py b/sgdfi/_util.py
index db097d8..112f58c 100755
--- a/sgdfi/_util.py
+++ b/sgdfi/_util.py
@@ -527,4 +527,45 @@ class UnauthorizedAccountError(InvalidCredentialsError):
super().__init__(f"User with identifier {repr(user)} is not allowed " \
"to log in.", user = user)
+class RedirectError(Exception):
+ """ The response is a redirection. """
+
+ def __init__(self, location):
+ super().__init__(f"Was redirected to {repr(location)}.")
+ self.__location = location
+
+ @property
+ def location(self):
+ """ The location to which we have been redirected. """
+
+ return self.__location
+
+class BadRequestError(Exception):
+ """ The response is a redirection to the bad request page. """
+
+ def __init__(self):
+ super().__init__("Bad request.")
+
+class NotFoundError(Exception):
+ """ The response is a redirection to the not found page. """
+
+ def __init__(self):
+ super().__init__("Page not found.")
+
+class ForbiddenError(Exception):
+ """ The response is a redirection to the forbidden page. """
+
+ def __init__(self):
+ super().__init__("Access to this page was denied.")
+
+# ---
+# Other utilities.
+# ---
+
+class Pagination:
+ def __init__(self, current, number, more):
+ self.current = current
+ self.number = number
+ self.more = more
+
# End of file.