<reponame>jehboyes/planning_system
# pylint: disable=no-member
from collections import defaultdict
from getpass import getuser
from datetime import datetime
import click
from planning_system.db import DB
from planning_system.db.schema.tables.finance import f_set, finance, finance_instance
from planning_system.db import Base
from planning_system.db.schema.views import get_view_class


@click.command()
@click.argument("acad_year", type=int)
@click.argument("setcat", type=str)
@click.pass_obj
def save(config, acad_year, setcat):
    """
    Save all matching sets.

.. _finance-save:

    Create a finance instance for each set with the given 'ACAD_YEAR' and 'SETCAT'.
    Once Finances have been calculated from :ref:`v_calc_finances`, the net cost of
    non-income-generating cost centres is calculated using :ref:`v_calc_set_costing` and
    is recharged to income-generating cost centres (i.e. by adding balancing transactions).
    """
    with DB(config.uri, config.echo) as db:
        session = db.session()
        # Get sets to be updated
        sets = session.query(f_set.set_id).filter(f_set.acad_year == acad_year) \
                                          .filter(f_set.set_cat_id == setcat)
        # Calculate the actual finances
        config.verbose_print("Calculating finances...", nl=False)
        f_view = get_view_class("v_calc_finances", db.engine)
        calc_finances = session.query(f_view.account, f_view.period, f_view.amount, f_view.set_id) \
                               .filter(f_view.set_id.in_(sets))
        config.verbose_print("Complete.")
        
        config.verbose_print("Splitting finances by set...", nl=False)
        # COnvert the results to a dictionary by set_id for more transparent processing
        dict_finances = defaultdict(list)
        for r in calc_finances:
            dict_finances[r.set_id].append(r)
        config.verbose_print("Complete.")

        # For each set (wrapped for progress bar)
        set_instance_dict = {}
        
        # Redefine sets to get costc for progress bar updating
        sets = session.query(f_set.set_id, f_set.costc).filter(f_set.acad_year == acad_year) \
                                          .filter(f_set.set_cat_id == setcat)

        with click.progressbar(sets.all(), label="Working through sets", show_eta=False,
                               item_show_func=_progress_label, fill_char="£") as bar:
            for s in bar:
                # Make it a finance set
                i = finance_instance(created_by=getuser(),
                                     set_id=s.set_id, datestamp=datetime.now())
                session.add(i)
                session.flush()
                set_instance_dict[s.set_id] = i.instance_id
                # create a list of finance objects for buk inserting, way quicker than one by one
                finances = []
                for row in dict_finances[s.set_id]:
                    finances.append(finance(instance_id=i.instance_id,
                                            account=row.account, period=row.period, amount=row.amount))
                session.bulk_save_objects(finances)
        session.flush()
        session.commit()
        config.verbose_print("Calculating recharges...", nl=False)
        # Work out the recharges based on the values just input, which will then be added to the instances
        costing_view = get_view_class("v_calc_set_costing", db.engine)
        select_costings = session.query(costing_view.account,
                                        costing_view.period,
                                        costing_view.amount,
                                        costing_view.set_id) \
            .filter(costing_view.acad_year == acad_year) \
            .filter(costing_view.set_cat_id == setcat)
        # Aggregate to add to an instance
        agg_recharges = defaultdict(float)
        for costing in select_costings.all():
            agg_recharges[(costing.account, costing.set_id,
                           costing.period,)] += costing.amount
        finances = []
        for key, amount in agg_recharges.items():
            if amount != 0:
                account, set_id, period = key
                if set_id in set_instance_dict.keys():
                    finances.append(finance(instance_id=set_instance_dict[set_id],
                                            account=account,
                                            period=period,
                                            amount=amount))
                else:
                    print(f"Set {set_id} missing")

        session.bulk_save_objects(finances)
        session.commit()
        config.verbose_print("Complete.")


def _progress_label(s):
    if s is not None:
        return f"Processed {s.costc}"
    else:
        return f"Beginning processing"

# pylint: disable=no-member
from collections import defaultdict
from getpass import getuser
from datetime import datetime
import click
from planning_system.db import DB
from planning_system.db.schema.tables.finance import f_set, finance, finance_instance
from planning_system.db import Base
from planning_system.db.schema.views import get_view_class


@click.command()
@click.argument("acad_year", type=int)
@click.argument("setcat", type=str)
@click.pass_obj
def save(config, acad_year, setcat):
    """
    Save all matching sets.

.. _finance-save:

    Create a finance instance for each set with the given 'ACAD_YEAR' and 'SETCAT'.
    Once Finances have been calculated from :ref:`v_calc_finances`, the net cost of
    non-income-generating cost centres is calculated using :ref:`v_calc_set_costing` and
    is recharged to income-generating cost centres (i.e. by adding balancing transactions).
    """
    with DB(config.uri, config.echo) as db:
        session = db.session()
        # Get sets to be updated
        sets = session.query(f_set.set_id).filter(f_set.acad_year == acad_year) \
                                          .filter(f_set.set_cat_id == setcat)
        # Calculate the actual finances
        config.verbose_print("Calculating finances...", nl=False)
        f_view = get_view_class("v_calc_finances", db.engine)
        calc_finances = session.query(f_view.account, f_view.period, f_view.amount, f_view.set_id) \
                               .filter(f_view.set_id.in_(sets))
        config.verbose_print("Complete.")
        
        config.verbose_print("Splitting finances by set...", nl=False)
        # COnvert the results to a dictionary by set_id for more transparent processing
        dict_finances = defaultdict(list)
        for r in calc_finances:
            dict_finances[r.set_id].append(r)
        config.verbose_print("Complete.")

        # For each set (wrapped for progress bar)
        set_instance_dict = {}
        
        # Redefine sets to get costc for progress bar updating
        sets = session.query(f_set.set_id, f_set.costc).filter(f_set.acad_year == acad_year) \
                                          .filter(f_set.set_cat_id == setcat)

        with click.progressbar(sets.all(), label="Working through sets", show_eta=False,
                               item_show_func=_progress_label, fill_char="£") as bar:
            for s in bar:
                # Make it a finance set
                i = finance_instance(created_by=getuser(),
                                     set_id=s.set_id, datestamp=datetime.now())
                session.add(i)
                session.flush()
                set_instance_dict[s.set_id] = i.instance_id
                # create a list of finance objects for buk inserting, way quicker than one by one
                finances = []
                for row in dict_finances[s.set_id]:
                    finances.append(finance(instance_id=i.instance_id,
                                            account=row.account, period=row.period, amount=row.amount))
                session.bulk_save_objects(finances)
        session.flush()
        session.commit()
        config.verbose_print("Calculating recharges...", nl=False)
        # Work out the recharges based on the values just input, which will then be added to the instances
        costing_view = get_view_class("v_calc_set_costing", db.engine)
        select_costings = session.query(costing_view.account,
                                        costing_view.period,
                                        costing_view.amount,
                                        costing_view.set_id) \
            .filter(costing_view.acad_year == acad_year) \
            .filter(costing_view.set_cat_id == setcat)
        # Aggregate to add to an instance
        agg_recharges = defaultdict(float)
        for costing in select_costings.all():
            agg_recharges[(costing.account, costing.set_id,
                           costing.period,)] += costing.amount
        finances = []
        for key, amount in agg_recharges.items():
            if amount != 0:
                account, set_id, period = key
                if set_id in set_instance_dict.keys():
                    finances.append(finance(instance_id=set_instance_dict[set_id],
                                            account=account,
                                            period=period,
                                            amount=amount))
                else:
                    print(f"Set {set_id} missing")

        session.bulk_save_objects(finances)
        session.commit()
        config.verbose_print("Complete.")


def _progress_label(s):
    if s is not None:
        return f"Processed {s.costc}"
    else:
        return f"Beginning processing"
# pylint: disable=no-member Save all matching sets.  .. _finance-save:      Create a finance instance for each set with the given 'ACAD_YEAR' and 'SETCAT'.     Once Finances have been calculated from :ref:`v_calc_finances`, the net cost of     non-income-generating cost centres is calculated using :ref:`v_calc_set_costing` and     is recharged to income-generating cost centres (i.e. by adding balancing transactions). # Get sets to be updated # Calculate the actual finances # COnvert the results to a dictionary by set_id for more transparent processing # For each set (wrapped for progress bar) # Redefine sets to get costc for progress bar updating # Make it a finance set # create a list of finance objects for buk inserting, way quicker than one by one # Work out the recharges based on the values just input, which will then be added to the instances # Aggregate to add to an instance