diff --git a/TODO b/TODO index 819ce0c..5048c3d 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,7 @@ * history (Detail) vs Bill? -* _url ? +* Bill._url? * Update modules? + +* amazon.com is buggy +* LDLC is out of date +* Bouygues is out of date diff --git a/capabilities/bill.py b/capabilities/CapDocument.py similarity index 100% rename from capabilities/bill.py rename to capabilities/CapDocument.py diff --git a/capabilities/__init__.py b/capabilities/__init__.py index e69de29..951aa8e 100644 --- a/capabilities/__init__.py +++ b/capabilities/__init__.py @@ -0,0 +1,5 @@ +from . import CapDocument + +__all__ = [ + "CapDocument" +] diff --git a/cozyweboob.py b/cozyweboob.py index 01ac3ca..e978db4 100755 --- a/cozyweboob.py +++ b/cozyweboob.py @@ -1,15 +1,22 @@ #!/usr/bin/env python2 +""" +TODO +""" from __future__ import print_function import getpass +import importlib import json +import logging import sys from weboob.core import Weboob -from capabilities import bill from tools.jsonwriter import pretty_json +# Dynamically load capabilities conversion modules +CAPABILITIES_CONVERSION_MODULES = importlib.import_module("capabilities") + class WeboobProxy(object): """ @@ -70,25 +77,63 @@ def main(used_modules): # Fetch data for the specified modules fetched_data = {} - for module, parameters in used_modules.items(): - # TODO - fetched_data["bills"] = bill.to_cozy( - WeboobProxy( - module, - parameters - ).get_backend() - ) + logging.info("Start fetching from konnectors.") + for module in used_modules: + logging.info("Fetching data from module %s.", module["id"]) + # Get associated backend for this module + backend = WeboobProxy( + module["name"], + module["parameters"] + ).get_backend() + # List all supported capabilities + for capability in backend.iter_caps(): + # Convert capability class to string name + capability = capability.__name__ + try: + # Get conversion function for this capability + fetching_function = ( + getattr( + getattr( + CAPABILITIES_CONVERSION_MODULES, + capability + ), + "to_cozy" + ) + ) + logging.info("Fetching capability %s.", capability) + # Fetch data and store them + # TODO: Ensure there is no overwrite + fetched_data[module["id"]] = fetching_function(backend) + except AttributeError: + logging.error("%s capability is not implemented.", capability) + continue + logging.info("Done fetching from konnectors.") return fetched_data if __name__ == '__main__': try: - konnectors = json.load(sys.stdin) - except ValueError: - sys.exit("Invalid input") # TODO - - print( - pretty_json( - main(konnectors) + logging.basicConfig( + format='%(levelname)s: %(message)s', + level=logging.INFO ) - ) + try: + konnectors = json.load(sys.stdin) + # Handle missing passwords using getpass + for module in range(len(konnectors)): + for param in konnectors[module]["parameters"]: + if not konnectors[module]["parameters"][param]: + konnectors[module]["parameters"][param] = getpass.getpass( + "Password for module %s? " % konnectors[module]["id"] + ) + except ValueError: + logging.error("Invalid JSON input.") + sys.exit(-1) + + print( + pretty_json( + main(konnectors) + ) + ) + except KeyboardInterrupt: + pass diff --git a/konnectors.json.sample b/konnectors.json.sample index 9973b33..43827f6 100644 --- a/konnectors.json.sample +++ b/konnectors.json.sample @@ -1,7 +1,19 @@ -{ - "amazon": { - "website": "www.amazon.fr", - "email": "someone@example.com", - "password": "MY_AWESOME_PASSWORD" +[ + { + "id": "amazon.fr", + "name": "amazon", + "parameters": { + "website": "www.amazon.fr", + "email": "someone@example.com", + "password": "MY_AWESOME_PASSWORD" + } + }, + { + "id": "freemobile", + "name": "freemobile", + "parameters": { + "login": "12345678", + "password": "" + } } -} +] diff --git a/tools/jsonwriter.py b/tools/jsonwriter.py index e16d04b..ed5d06b 100644 --- a/tools/jsonwriter.py +++ b/tools/jsonwriter.py @@ -1,6 +1,6 @@ import json -from datetime import datetime +from datetime import date, datetime from decimal import Decimal @@ -9,7 +9,7 @@ class CustomJSONEncoder(json.JSONEncoder): Custom JSONEncoder to support more types. """ def default(self, o): - if isinstance(o, datetime): + if isinstance(o, datetime) or isinstance(o, date): # Serialize datetime objects to ISO dates return o.isoformat() elif isinstance(o, Decimal):