pages.py 9.66 KB
Newer Older
1 2
# -*- coding: utf-8 -*-

3
# Copyright(C) 2013-2015     Christophe Lampin
4
#
5
# This file is part of a weboob module.
6
#
7
# This weboob module is free software: you can redistribute it and/or modify
8 9 10 11
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
12
# This weboob module is distributed in the hope that it will be useful,
13 14 15 16 17
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
18
# along with this weboob module. If not, see <http://www.gnu.org/licenses/>.
19

ntome's avatar
ntome committed
20
from __future__ import unicode_literals
21 22 23 24

from datetime import datetime
import re
from decimal import Decimal
25

26
from weboob.browser.filters.html import Attr, XPathNotFound
27
from weboob.browser.pages import HTMLPage, RawPage, LoggedPage
28
from weboob.capabilities.bill import DocumentTypes, Subscription, Detail, Bill
Kitof's avatar
Kitof committed
29
from weboob.browser.filters.standard import CleanText, Regexp
30
from weboob.exceptions import BrowserUnavailable
31

32

33
# Ugly array to avoid the use of french locale
34

Nicolas Gattolin's avatar
Nicolas Gattolin committed
35
FRENCH_MONTHS = ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre']
36

37

38
class AmeliBasePage(HTMLPage):
39 40
    @property
    def logged(self):
Nicolas Gattolin's avatar
Nicolas Gattolin committed
41
        if self.doc.xpath('//a[contains(text(), "Déconnexion")]'):
42
            logged = True
43 44
        else:
            logged = False
45 46
        self.logger.debug('logged: %s' % (logged))
        return logged
47

48
    def is_error(self):
Nicolas Gattolin's avatar
Nicolas Gattolin committed
49
        errors = self.doc.xpath('//*[@id="r_errors"]')
50 51
        if errors:
            return errors[0].text_content()
52 53 54 55 56

        errors = CleanText('//p[@class="msg_erreur"]', default='')(self.doc)
        if errors:
            return errors

57 58 59 60
        errors = CleanText('//div[@class="zone-alerte"]/span')(self.doc)
        if errors:
            return errors

61 62
        return False

63

64 65
class LoginPage(AmeliBasePage):
    def login(self, login, password):
66 67 68 69
        form = self.get_form('//form[@name="connexionCompteForm"]')
        form['connexioncompte_2numSecuriteSociale'] = login.encode('utf8')
        form['connexioncompte_2codeConfidentiel'] = password.encode('utf8')
        form.submit()
70

71 72 73 74 75 76 77 78 79 80 81 82 83 84
    def locate_to_cgu_page(self):
        try:
            # they've put a head tag inside body, yes i know...
            url = Regexp(Attr('//div[@id="connexioncompte_2"]//meta', 'content'), r'url=(.*)')(self.doc)
        except XPathNotFound:
            # no cgu to validate
            return
        self.browser.location(url)


class CguPage(AmeliBasePage):
    def get_cgu(self):
        return CleanText('//div[@class="page_nouvelles_cgus"]/p[1]')(self.doc)

85

86
class HomePage(AmeliBasePage):
87
    pass
88

89

90 91
class AccountPage(AmeliBasePage):
    def iter_subscription_list(self):
Kitof's avatar
Kitof committed
92 93
        names_list = self.doc.xpath('//span[@class="NomEtPrenomLabel"]')
        fullname = CleanText(newlines=True).filter(names_list[0])
Laurent Bachelier's avatar
Laurent Bachelier committed
94
        number = re.sub(r'[^\d]+', '', CleanText('//span[@class="blocNumSecu"]', replace=[(' ', '')])(self.doc))
95 96
        sub = Subscription(number)
        sub._id = number
ntome's avatar
ntome committed
97
        sub.label = fullname
Kitof's avatar
Kitof committed
98
        firstname = CleanText('//span[@class="prenom-titulaire"]')(self.doc)
ntome's avatar
ntome committed
99
        sub.subscriber = firstname
100 101
        yield sub

102

Kitof's avatar
Kitof committed
103 104
class PaymentsPage(AmeliBasePage):
    def get_last_payments_url(self):
105 106 107 108 109 110
        begin_date = self.doc.xpath('//input[@id="paiements_1dateDebut"]/@data-mindate')[0]
        end_date = self.doc.xpath('//input[@id="paiements_1dateFin"]/@data-maxdate')[0]
        url = ('/PortailAS/paiements.do?actionEvt=afficherPaiementsComplementaires&DateDebut='
               + begin_date + '&DateFin=' + end_date +
               '&Beneficiaire=tout_selectionner&afficherReleves=false&afficherIJ=false&afficherInva=false'
               '&afficherRentes=false&afficherRS=false&indexPaiement=&idNotif=')
Kitof's avatar
Kitof committed
111
        return url
112

113 114

class LastPaymentsPage(LoggedPage, AmeliBasePage):
115
    def iter_last_payments(self):
Kitof's avatar
Kitof committed
116 117
        elts = self.doc.xpath('//li[@class="rowitem remboursement"]')
        for elt in elts:
118
            items = Regexp(CleanText('./@onclick'), r".*ajaxCallRemoteChargerDetailPaiement \('(\w+={0,2})', '(\w+)', '(\d+)', '(\d+)'\).*", '\\1,\\2,\\3,\\4')(elt).split(',')
Kitof's avatar
Kitof committed
119
            yield "/PortailAS/paiements.do?actionEvt=chargerDetailPaiements&idPaiement=" + items[0] + "&naturePaiement=" + items[1] + "&indexGroupe=" + items[2] + "&indexPaiement=" + items[3]
120

Kitof's avatar
Kitof committed
121 122 123 124 125 126
    def iter_documents(self, sub):
        elts = self.doc.xpath('//li[@class="rowdate"]')
        for elt in elts:
            try:
                elt.xpath('.//a[contains(@id,"lienPDFReleve")]')[0]
            except IndexError:
Laurent Bachelier's avatar
Laurent Bachelier committed
127
                continue
Kitof's avatar
Kitof committed
128 129 130 131 132 133
            date_str = elt.xpath('.//span[contains(@id,"moisEnCours")]')[0].text
            month_str = date_str.split()[0]
            date = datetime.strptime(re.sub(month_str, str(FRENCH_MONTHS.index(month_str) + 1), date_str), "%m %Y").date()
            bil = Bill()
            bil.id = sub._id + "." + date.strftime("%Y%m")
            bil.date = date
Nicolas Gattolin's avatar
Nicolas Gattolin committed
134
            bil.format = 'pdf'
135
            bil.type = DocumentTypes.BILL
Nicolas Gattolin's avatar
Nicolas Gattolin committed
136 137
            bil.label = date.strftime("%Y%m%d")
            bil.url = '/PortailAS/PDFServletReleveMensuel.dopdf?PDF.moisRecherche=' + date.strftime("%m%Y")
Kitof's avatar
Kitof committed
138 139 140
            yield bil

    def get_document(self, bill):
141
        self.location(bill.url, params=bill._args)
142

143

144
class PaymentDetailsPage(AmeliBasePage):
145
    def iter_payment_details(self, sub):
Kitof's avatar
Kitof committed
146
        id_str = self.doc.xpath('//div[@class="entete container"]/h2')[0].text.strip()
Nicolas Gattolin's avatar
Nicolas Gattolin committed
147
        m = re.match(r'.*le (.*) pour un montant de.*', id_str)
Kitof's avatar
Kitof committed
148 149 150 151 152 153
        if m:
            blocs_benes = self.doc.xpath('//span[contains(@id,"nomBeneficiaire")]')
            blocs_prestas = self.doc.xpath('//table[@id="tableauPrestation"]')
            i = 0
            last_bloc = len(blocs_benes)
            for i in range(0, last_bloc):
154
                bene = blocs_benes[i].text
155 156 157
                id_str = m.group(1)
                id_date = datetime.strptime(id_str, '%d/%m/%Y').date()
                id = sub._id + "." + datetime.strftime(id_date, "%Y%m%d")
Kitof's avatar
Kitof committed
158
                table = blocs_prestas[i].xpath('.//tr')
159
                line = 1
Florent Fourcot's avatar
Florent Fourcot committed
160
                last_date = None
161
                for tr in table:
162
                    tds = tr.xpath('.//td')
163 164
                    if len(tds) == 0:
                        continue
165

166
                    det = Detail()
167

Kitof's avatar
Kitof committed
168
                    # TO TEST : Indemnités journalières : Pas pu tester de cas de figure similaire dans la nouvelle mouture du site
Kitof's avatar
Kitof committed
169
                    if len(tds) == 4:
170
                        date_str = Regexp(pattern=r'.*<br/>(\d+/\d+/\d+)\).*').filter(tds[0].text)
171
                        det.id = id + "." + str(line)
ntome's avatar
ntome committed
172
                        det.label = tds[0].xpath('.//span')[0].text.strip()
173

Kitof's avatar
Kitof committed
174 175 176 177 178
                        jours = tds[1].text
                        if jours is None:
                            jours = '0'

                        montant = tds[2].text
179 180 181
                        if montant is None:
                            montant = '0'

Kitof's avatar
Kitof committed
182
                        price = tds[3].text
183 184 185 186
                        if price is None:
                            price = '0'

                        if date_str is None or date_str == '':
Nicolas Gattolin's avatar
Nicolas Gattolin committed
187
                            det.infos = ''
188 189
                            det.datetime = last_date
                        else:
Nicolas Gattolin's avatar
Nicolas Gattolin committed
190
                            det.infos = date_str + ' (' + re.sub(r'[^\d,-]+', '', jours) + 'j) * ' + re.sub(r'[^\d,-]+', '', montant) + '€'
191 192
                            det.datetime = datetime.strptime(date_str.split(' ')[3], '%d/%m/%Y').date()
                            last_date = det.datetime
Nicolas Gattolin's avatar
Nicolas Gattolin committed
193
                        det.price = Decimal(re.sub(r'[^\d,-]+', '', price).replace(',', '.'))
194

Kitof's avatar
Kitof committed
195 196
                    if len(tds) == 5:
                        date_str = Regexp(pattern=r'\w*(\d{2})/(\d{2})/(\d{4}).*', template='\\1/\\2/\\3', default="").filter("".join(tds[0].itertext()))
197
                        det.id = id + "." + str(line)
Nicolas Gattolin's avatar
Nicolas Gattolin committed
198
                        det.label = bene + ' - ' + tds[0].xpath('.//span')[0].text.strip()
199

Kitof's avatar
Kitof committed
200
                        paye = tds[1].text
201 202 203
                        if paye is None:
                            paye = '0'

Kitof's avatar
Kitof committed
204
                        base = tds[2].text
205 206 207
                        if base is None:
                            base = '0'

Kitof's avatar
Kitof committed
208 209
                        tdtaux = tds[3].xpath('.//span')[0].text
                        if tdtaux is None:
210
                            taux = '0'
Kitof's avatar
Kitof committed
211 212
                        else:
                            taux = tdtaux.strip()
213

Kitof's avatar
Kitof committed
214 215
                        tdprice = tds[4].xpath('.//span')[0].text
                        if tdprice is None:
216
                            price = '0'
Kitof's avatar
Kitof committed
217 218
                        else:
                            price = tdprice.strip()
219 220

                        if date_str is None or date_str == '':
Nicolas Gattolin's avatar
Nicolas Gattolin committed
221
                            det.infos = ''
222 223
                            det.datetime = last_date
                        else:
Nicolas Gattolin's avatar
Nicolas Gattolin committed
224
                            det.infos = ' Payé ' + re.sub(r'[^\d,-]+', '', paye) + '€ / Base ' + re.sub(r'[^\d,-]+', '', base) + '€ / Taux ' + re.sub(r'[^\d,-]+', '', taux) + '%'
225 226
                            det.datetime = datetime.strptime(date_str, '%d/%m/%Y').date()
                            last_date = det.datetime
Nicolas Gattolin's avatar
Nicolas Gattolin committed
227
                        det.price = Decimal(re.sub(r'[^\d,-]+', '', price).replace(',', '.'))
228 229 230 231
                    line = line + 1
                    yield det


232 233
class Raw(LoggedPage, RawPage):
    pass
234 235 236 237 238


class UnavailablePage(HTMLPage):
    def on_load(self):
        raise BrowserUnavailable(CleanText('//span[@class="texte-indispo"]')(self.doc))