from . import DataImporter
|
|
|
|
import re
|
|
import requests
|
|
from typing import Tuple, List
|
|
from bs4 import BeautifulSoup
|
|
|
|
class ModoDataImporter( DataImporter ):
|
|
"""
|
|
Modo (https://modo.coop/) data importer.
|
|
|
|
Automatically connects to your account and retrieve transactions
|
|
"""
|
|
|
|
@classmethod
|
|
def name( cls ):
|
|
return "modo"
|
|
|
|
|
|
@classmethod
|
|
def verify( cls, config: dict, path: str ) -> Tuple[bool, List[str]]:
|
|
"""
|
|
Verifies that the configuration is correct
|
|
|
|
Args:
|
|
config: dict with password manager configuration
|
|
path: path to the "config" dict in the main configuration file
|
|
|
|
Returns:
|
|
bool: true if the config is correct
|
|
List[str]: list of errors if the config is not correct
|
|
"""
|
|
ok = True
|
|
errors = []
|
|
|
|
if "user_id" not in config:
|
|
ok = False
|
|
errors.append(f'Missing value "{path}.user_id"')
|
|
|
|
if "password_id" not in config:
|
|
ok = False
|
|
errors.append(f'Missing value "{path}.password_id"')
|
|
|
|
return (ok, errors)
|
|
|
|
|
|
def __init__( self, config: dict, **kwargs ):
|
|
self.name = str(config["name"])
|
|
self.user_id = str(config["user_id"])
|
|
self.pwd_id = str(config["password_id"])
|
|
|
|
self.regex_usage = re.compile("[A-Z][a-z]+ [0-9]{4} usage details")
|
|
self.regex_money = re.compile("-?\$[0-9]+\.[0-9]{2}")
|
|
self.regex_membership_fee = re.compile("annual membership fee")
|
|
self.regex_trip = re.compile("On .*, you drove ([0-9]+) km")
|
|
|
|
|
|
def retrieve( self ) -> List[str]:
|
|
"""
|
|
Retrieves the list of transactions from modo website
|
|
"""
|
|
raise NotImplementedError #FIXME: actually implement
|
|
|
|
|
|
def _parse_invoice( self, html: str ) -> List[str]:
|
|
"""
|
|
Parses an HTML modo invoice
|
|
|
|
This might be quite brittle and require maintenance if the modo
|
|
website ever change
|
|
"""
|
|
data = BeautifulSoup(html, "html.parser")
|
|
trips = data.find(
|
|
class_="m_invoice"
|
|
).find(
|
|
name="div",
|
|
class_="m_i_h",
|
|
string=self.regex_usage
|
|
).find_parent(
|
|
name="div",
|
|
id=re.compile("m_i_tier_[0-9]+")
|
|
).find_all(
|
|
class_="m_i_detail"
|
|
)
|
|
|
|
ret = []
|
|
for trip in trips:
|
|
text_tag = trip.find(class_="m_id_text")
|
|
if text_tag is None:
|
|
continue
|
|
amounts = trip.find_all(
|
|
class_="m_id_amount",
|
|
string=self.regex_money
|
|
)
|
|
if len(amounts) != 3:
|
|
continue
|
|
|
|
text = "".join(text_tag.text.splitlines())
|
|
|
|
ret.append(
|
|
amounts[0].text
|
|
+ "|"
|
|
+ self._parse_text(text)
|
|
)
|
|
|
|
return ret
|
|
|
|
|
|
def _parse_text( self, text: str ) -> str:
|
|
if self.regex_membership_fee.search(text):
|
|
return "fee"
|
|
|
|
trip = self.regex_trip.search(text)
|
|
if trip:
|
|
d = trip.group(1)
|
|
return f"trip|{d}km"
|
|
|
|
return "unknown"
|
|
|
|
|
|
#payload = {
|
|
# 'fMemberNumber': '',
|
|
# 'fPassword': ''
|
|
#}
|
|
#with requests.Session() as s:
|
|
# p = s.post("https://bookit.modo.coop/home/login", data=payload)
|
|
# print(p.text)
|
|
|
|
# r = s.get("https://bookit.modo.coop/account/invoices?invoice_id=#")
|
|
# print(r.text)
|