From 7599809e322edbacf301d6588a500087d265153a Mon Sep 17 00:00:00 2001 From: Florian Duguet Date: Tue, 28 May 2019 18:35:38 +0200 Subject: [PATCH] [ameli] new module --- modules/ameli/__init__.py | 26 ++++++++++++ modules/ameli/browser.py | 79 ++++++++++++++++++++++++++++++++++++ modules/ameli/module.py | 70 ++++++++++++++++++++++++++++++++ modules/ameli/pages.py | 85 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 260 insertions(+) create mode 100644 modules/ameli/__init__.py create mode 100644 modules/ameli/browser.py create mode 100644 modules/ameli/module.py create mode 100644 modules/ameli/pages.py diff --git a/modules/ameli/__init__.py b/modules/ameli/__init__.py new file mode 100644 index 0000000000..915e0d0401 --- /dev/null +++ b/modules/ameli/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2019 Budget Insight +# +# This file is part of a weboob module. +# +# This weboob module is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This weboob module 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this weboob module. If not, see . + +from __future__ import unicode_literals + + +from .module import AmeliModule + + +__all__ = ['AmeliModule'] diff --git a/modules/ameli/browser.py b/modules/ameli/browser.py new file mode 100644 index 0000000000..bedddad2d9 --- /dev/null +++ b/modules/ameli/browser.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2019 Budget Insight +# +# This file is part of a weboob module. +# +# This weboob module is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This weboob module 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this weboob module. If not, see . + +from __future__ import unicode_literals + +from datetime import date +from time import time +from dateutil.relativedelta import relativedelta + +from weboob.browser import LoginBrowser, URL, need_login +from .pages import LoginPage, SubscriptionPage, DocumentsPage + + +class AmeliBrowser(LoginBrowser): + BASEURL = 'https://assure.ameli.fr' + + login_page = URL(r'/PortailAS/appmanager/PortailAS/assure\?_nfpb=true&connexioncompte_2actionEvt=afficher.*', LoginPage) + subscription_page = URL(r'/PortailAS/appmanager/PortailAS/assure\?_nfpb=true&_pageLabel=as_info_perso_page.*', SubscriptionPage) + documents_page = URL(r'/PortailAS/paiements.do', DocumentsPage) + + def do_login(self): + self.login_page.go() + self.page.login(self.username, self.password) + + @need_login + def iter_subscription(self): + self.subscription_page.go() + return self.page.iter_subscriptions() + + @need_login + def iter_documents(self, subscription): + end_date = date.today() + + start_date = end_date - relativedelta(years=1) + # FUN FACT, website tell us documents are available for 6 months + # let's suppose today is 28/05/19, website frontend limit DateDebut to 28/11/18 but we can get a little bit more + # by setting a previous date and get documents that are no longer available for simple user + + params = { + 'Beneficiaire': 'tout_selectionner', + 'DateDebut': start_date.strftime('%d/%m/%Y'), + 'DateFin': end_date.strftime('%d/%m/%Y'), + 'actionEvt': 'afficherPaiementsComplementaires', + 'afficherIJ': 'false', + 'afficherInva': 'false', + 'afficherPT': 'false', + 'afficherRS': 'false', + 'afficherReleves': 'false', + 'afficherRentes': 'false', + 'idNoCache': int(time()*1000) + } + + # the second request is stateful + # first value of actionEvt is afficherPaiementsComplementaires to get all payments from last 6 months + # (start_date 6 months in the past is needed but not enough) + self.documents_page.go(params=params) + + # then we set Rechercher to actionEvt to filter for this subscription, within last 6 months + # without first request we would have filter for this subscription but within last 2 months + params['actionEvt'] = 'Rechercher' + params['Beneficiaire'] = subscription._param + self.documents_page.go(params=params) + return self.page.iter_documents(subid=subscription.id) diff --git a/modules/ameli/module.py b/modules/ameli/module.py new file mode 100644 index 0000000000..a07d811d64 --- /dev/null +++ b/modules/ameli/module.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2019 Budget Insight +# +# This file is part of a weboob module. +# +# This weboob module is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This weboob module 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this weboob module. If not, see . + + +from __future__ import unicode_literals + +from weboob.capabilities.base import find_object +from weboob.tools.backend import Module, BackendConfig +from weboob.capabilities.bill import CapDocument, Document, DocumentTypes, SubscriptionNotFound, DocumentNotFound +from weboob.tools.value import ValueBackendPassword + +from .browser import AmeliBrowser + + +__all__ = ['AmeliModule'] + + +class AmeliModule(Module, CapDocument): + NAME = 'ameli' + DESCRIPTION = "le site de l'Assurance Maladie en ligne" + MAINTAINER = 'Florian Duguet' + EMAIL = 'florian.duguet@budget-insight.com' + LICENSE = 'LGPLv3+' + VERSION = '1.6' + + BROWSER = AmeliBrowser + + CONFIG = BackendConfig(ValueBackendPassword('login', label='Mon numero de sécurité sociale', regexp=r'^\d{13}$', masked=False), + ValueBackendPassword('password', label='Mon code (4 à 13 chiffres)', regexp=r'^\d{4,13}', masked=True)) + + accepted_document_types = (DocumentTypes.BILL,) + + def create_default_browser(self): + return self.create_browser(self.config['login'].get(), self.config['password'].get()) + + def iter_subscription(self): + return self.browser.iter_subscription() + + def get_subscription(self, _id): + return find_object(self.iter_subscription(), id=_id, error=SubscriptionNotFound) + + def iter_documents(self, subscription): + return self.browser.iter_documents(subscription) + + def get_document(self, _id): + subid = _id.rsplit('_', 1)[0] + subscription = self.get_subscription(subid) + return find_object(self.iter_documents(subscription), id=_id, error=DocumentNotFound) + + def download_document(self, document): + if not isinstance(document, Document): + document = self.get_document(document) + + return self.browser.open(document.url).content diff --git a/modules/ameli/pages.py b/modules/ameli/pages.py new file mode 100644 index 0000000000..b058574ae7 --- /dev/null +++ b/modules/ameli/pages.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2019 Budget Insight +# +# This file is part of a weboob module. +# +# This weboob module is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This weboob module 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this weboob module. If not, see . + +from __future__ import unicode_literals + +import re + +from weboob.browser.elements import method, ListElement, ItemElement +from weboob.browser.filters.html import Attr, Link +from weboob.browser.filters.standard import CleanText, Regexp, CleanDecimal, Currency, Field, Format, Env +from weboob.browser.pages import LoggedPage, HTMLPage, PartialHTMLPage +from weboob.capabilities.bill import Subscription, Bill +from weboob.tools.date import parse_french_date + + +class LoginPage(HTMLPage): + def login(self, username, password): + form = self.get_form(id='connexioncompte_2connexionCompteForm') + form['connexioncompte_2numSecuriteSociale'] = username + form['connexioncompte_2codeConfidentiel'] = password + form.submit() + + +class SubscriptionPage(LoggedPage, HTMLPage): + @method + class iter_subscriptions(ListElement): + item_xpath = '//div[@id="corps-de-la-page"]//div[@class="tableau"]/div' + + class item(ItemElement): + klass = Subscription + + obj__labelid = Attr('.', 'aria-labelledby') + + def obj__birthdate(self): + return CleanText('//button[@id="%s"]//td[@class="dateNaissance"]' % Field('_labelid')(self))(self) + + def obj_id(self): + # DON'T TAKE social security number for id because it's a very confidential data, take birth date instead + return ''.join(re.findall(r'\d+', Field('_birthdate')(self))) + + def obj__param(self): + reversed_date = ''.join(reversed(re.findall(r'\d+', Field('_birthdate')(self)))) + name = CleanText('//button[@id="%s"]//td[@class="nom"]' % Field('_labelid')(self))(self) + return '%s!-!%s!-!1' % (reversed_date, name) + + obj_subscriber = CleanText('.//span[@class="NomEtPrenomLabel"]') + obj_label = obj_subscriber + + +class DocumentsPage(LoggedPage, PartialHTMLPage): + @method + class iter_documents(ListElement): + item_xpath = '//ul[@id="unordered_list"]//li[has-class("rowitem")]' + + class item(ItemElement): + klass = Bill + + obj_id = Format('%s_%s', Env('subid'), Regexp(Field('url'), r'idPaiement=(.*)')) + obj_label = CleanText('.//div[has-class("col-label")]') + obj_price = CleanDecimal.French('.//div[has-class("col-montant")]/span') + obj_currency = Currency('.//div[has-class("col-montant")]/span') + obj_url = Link('.//a[@class="downdetail"]') + obj_format = 'pdf' + + def obj_date(self): + year = Regexp(CleanText('./preceding-sibling::li[@class="rowdate"]//span[@class="mois"]'), r'(\d+)')(self) + day_month = CleanText('.//div[has-class("col-date")]/span')(self) + + return parse_french_date(day_month + ' ' + year) -- GitLab