summary history files

foreigneffigy/foreigneffigy.py
from datetime import datetime as dt
from fake_useragent import UserAgent
from model import sa, sessionmaker
from pprint import pprint
from sqlalchemy.orm.exc import NoResultFound
from validate import validate_date
import logging
import configparser
import datetime
import base64
import click
import model
import requests
import sys


logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


class ForeignEffigyError(Exception):
    pass


class ForeignEffigy(requests.Session):

    def __init__(self, config, contract, db_session, debug=False):
        self.config = config
        self.contract = contract
        self.username = config[str(self.contract.id)]['username']
        self.password = config[str(self.contract.id)]['password']

        self.db_session = db_session
        self.session = requests.Session()
        self.session.headers = {'User-Agent': self.user_agent}
        self.debug = debug

    @property
    def daily_supply_charge(self):
        """ The daily supply charge.

        If daily_supply_charge exists within config, use it.
        """
        try:
            return float(
                self.config[str(self.contract.id)]
                ['daily_supply_charge']
            )
        except KeyError:
            return None

    def login(self):
        url = base64.urlsafe_b64decode(
            'aHR0cHM6Ly93d3cub3JpZ2luZW5lcmd5LmNvbS5hdS9iaW4vb3JpZ2luLXV'
            'pL2xvZ2lub3JpZ2lu'
        )
        refer = base64.urlsafe_b64decode(
            'aHR0cHM6Ly93d3cub3JpZ2luZW5lcmd5LmNvbS5hdS9sb2dpbi5odG1s'
        )
        payload = {'username': self.username, 'password': self.password}
        self.session.headers.update({'Referer': refer})
        resp = self.session.post(
            url,
            headers=self.session.headers,
            data=payload
        )
        if not resp.json()['success']:
            raise ForeignEffigyError('Failed to login.')

    @property
    def division_id(self):
        return int('01')

    @property
    def user_agent(self):
        try:
            return self.__user_agent
        except AttributeError:
            ua = UserAgent()
            return ua.msie

    @user_agent.setter
    def user_agent(self, user_agent):
        return self.__user_agent

    def energy_usage(self, start_date, end_date, aggregation_level='HOUR'):
        url = base64.urlsafe_b64decode(
            'aHR0cHM6Ly93d3cub3JpZ2luZW5lcmd5LmNvbS5hdS9iaW4vb3JpZ2luLXVp'
            'L2dldFVzYWdlR3JhcGg='
        )
        referer = base64.urlsafe_b64decode(
            'aHR0cHM6Ly93d3cub3JpZ2luZW5lcmd5LmNvbS5hdS9mb3ItaG9tZS9t'
            'eS1hY2NvdW50L3VzYWdlLmh0bWw='
        )
        params = {
            'contractId': self.contract.id,
            'divisionId': self.division_id,
            'startDate': dt.strftime(start_date, "%d/%m/%Y"),
            'endDate': dt.strftime(end_date, "%d/%m/%Y"),
            'aggregationLevel': aggregation_level
        }
        self.session.headers.update({'Referer': referer, 'Accept': '*/*'})
        resp = self.session.get(
            url, headers=self.session.headers, params=params
        )
        self.usage = resp.json()

    def _add_daily_supply_charge(self, usage):
        """Manually add supply charge to hourly usage.

        Provider stopped including supply charge with usage data so we
        have to manually add it in if it exists within the config.
        """
        if self.daily_supply_charge:
            usage['feedinCost'] = self.daily_supply_charge / 24
            usage['feedinConsumption'] = 1
            usage['feedinConsumptionUom'] = 'cents'
        return usage

    @property
    def account(self):
        """ Return account properties. """
        url = base64.urlsafe_b64decode(
            'aHR0cHM6Ly93d3cub3JpZ2luZW5lcmd5LmNvbS5hdS9iaW4vb3JpZ2luLX'
            'VpL2dldEFjY291bnRQcm9wZXJ0aWVz'
        )
        referer = base64.urlsafe_b64decode(
            'aHR0cHM6Ly93d3cub3JpZ2luZW5lcmd5LmNvbS5hdS9mb3ItaG9tZS9teS1'
            'hY2NvdW50L3VzYWdlLmh0bWw='
        )
        self.session.headers.update({'Referer': referer})
        resp = self.session.get(url, headers=self.session.headers)
        return resp.json()

    def update_db(self):
        data = self.usage.get(str(self.contract.id))
        for date, interval in data.items():
            for hourly, usage in interval.items():
                usage = self._add_daily_supply_charge(usage)
                logger.debug(usage)
                hour = dt.strptime(hourly, '%d %B, %Y %H:%M')
                energy_usage = model.EnergyUsage(
                    date=hour,
                    concession_consumption=usage['concessionConsumption'],
                    concession_cost=usage['concessionCost'],
                    consumption=usage['consumption'],
                    consumption_uom=usage['consumptionUom'],
                    cost=usage['cost'],
                    energy_consumption=usage['energyConsumption'],
                    energy_cost=usage['energyCost'],
                    energy_service_consumption=(
                        usage['energyServiceConsumption']
                    ),
                    energy_service_cost=usage['energyServiceCost'],
                    feedin_consumption=usage['feedinConsumption'],
                    feedin_consumption_uom=usage['feedinConsumptionUom'],
                    feedin_cost=usage['feedinCost'],
                    solar_present=usage['solarPresent'],
                    value_pot=usage['valuePOT'],
                    contract_id=self.contract.id
                )
                self.db_session.add(energy_usage)
                try:
                    self.db_session.commit()
                except sa.exc.IntegrityError as e:
                    # Most probably a duplicate. Rollback and move on.
                    self.db_session.rollback()


@click.command()
@click.option('--db-file', default='fe.db')
@click.option('--start-date', callback=validate_date)
@click.option('--end-date', callback=validate_date)
@click.option('--conf-file', required=True)
@click.option('--debug', is_flag=True, default=False)
def foreigneffigy(db_file, start_date, end_date, conf_file, debug):

    if debug:
        logger.setLevel(logging.DEBUG)

    config = configparser.ConfigParser()
    config.read(conf_file)

    engine = sa.create_engine('sqlite:///{0}'.format(db_file))
    model.Base.metadata.create_all(engine)
    Session = sessionmaker(bind=engine)
    session = Session()

    if start_date is None:
        start_date = datetime.datetime.now() - datetime.timedelta(days=7)

    if end_date is None:
        end_date = datetime.datetime.now()

    if end_date < start_date:
        raise click.BadParameter("End date must be greater than start date.")

    for section in config.sections():
        try:
            contract = (
                session.query(model.Contract).filter_by(id=section).one()
            )
        except sa.orm.exc.NoResultFound:
            contract = model.Contract(id=section)
            session.add(contract)
            session.commit()

        fe = ForeignEffigy(
            config,
            contract=contract,
            db_session=session,
            debug=debug
        )
        fe.login()
        # Display account properties if we're debugging.
        if debug:
            logger.debug(fe.account)
        fe.energy_usage(start_date, end_date)
        fe.update_db()


if __name__ == '__main__':
    foreigneffigy()