""" Accounting period model. Model for getting details about accounting period """ from sqlalchemy import Column from sqlalchemy import Enum from sqlalchemy import func from sqlalchemy import Integer from sqlalchemy.exc import SQLAlchemyError from ows_accounting import response from ows_accounting.constants import error from ows_accounting.utils import mysql from ows_accounting.utils import sentry _cached_periods = [] class AccountingPeriod(mysql.BaseModel): """Class for accounting period model.""" __tablename__ = 'acct_period' period_id = Column('id', Integer, primary_key=True, autoincrement=True) year = Column('year', Integer) quarter = Column('quarter', Enum(*['1', '2', '3', '4'])) month = Column( 'month', Enum(*['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'])) def as_dict(self): """Return object as dict. Returns dict: Dictionary representation of the object """ return format_period_data(self) def format_period_data(period): """Changes monthly and quarterly period data into a single format. Returns dict: Dictionary representation of the period data """ period_dict = { 'year': period.year, 'quarter': int(period.quarter) } try: period_dict['month'] = int(period.month) period_dict['period_type'] = 'month' period_dict['first_period_id'] = period.period_id period_dict['last_period_id'] = period.period_id except AttributeError: period_dict['month'] = None period_dict['period_type'] = 'quarter' period_dict['first_period_id'] = period.first_period_id period_dict['last_period_id'] = period.last_period_id return period_dict def _get_cached_period_by_id(period_id): """Get period from cached list by id. Args: period_id (int): Accounting period Id. Returns: dict: Dictionary with accounting period data or None """ return next( (item for item in _cached_periods if item['first_period_id'] == period_id), None) def get_period_by_id(period_id): """Get accounting period by id. Args: period_id (int): Period Id. Returns: response.Response: Accounting period data or error in Response object. """ period = _get_cached_period_by_id(period_id) if period: return response.Response(period) try: with mysql.db_session(False) as session: period = session.query(AccountingPeriod).filter_by( period_id=period_id).first() if not period: return response.create_not_found_response( error.ERROR_MESSAGE_ACCOUNTING_PERIOD_NOT_FOUND) _cached_periods.append(format_period_data(period)) return response.Response(format_period_data(period)) except SQLAlchemyError as ex: if sentry.sentry_client: sentry.sentry_client.captureException() return response.create_fatal_response(str(ex)) def get_monthly_period_information(start_period, end_period): """Get monthly information for accounting periods in a certain range. Args: start_period (int): ID of the first period in the range. end_period (int): ID of the period to which the range extends (but does not include). Returns: response.Response: List of periods dicts or error in Response object. """ try: with mysql.db_session(False) as session: query = session.query(AccountingPeriod).filter( (AccountingPeriod.period_id >= start_period) & (AccountingPeriod.period_id <= end_period) ) periods = [ format_period_data(period) for period in query.all()] return response.Response(periods) except SQLAlchemyError as ex: if sentry.sentry_client: sentry.sentry_client.captureException() return response.create_fatal_response(str(ex)) def get_quarterly_period_information(start_period, end_period): """Get quarterly information for accounting periods in a certain range. Args: start_period (int): ID of the first period in the range. end_period (int): ID of the period to which the range extends (but does not include). Returns: response.Response: List of periods dicts or error in Response object. """ try: with mysql.db_session(False) as session: subquery = session.query( AccountingPeriod.year, AccountingPeriod.month, func.min(AccountingPeriod.period_id).label( 'first_period_id'), func.max(AccountingPeriod.period_id).label( 'last_period_id') ).group_by( AccountingPeriod.year, AccountingPeriod.quarter ).subquery() query = session.query( AccountingPeriod.year, AccountingPeriod.quarter, subquery.c.first_period_id, subquery.c.last_period_id ).join( subquery, (AccountingPeriod.year == subquery.c.year) & (AccountingPeriod.month == subquery.c.month) ).filter( (subquery.c.last_period_id >= start_period) & (subquery.c.last_period_id <= end_period) ).group_by( AccountingPeriod.year, AccountingPeriod.quarter ) periods = [ format_period_data( period) for period in query.all() if ( period.last_period_id - period.first_period_id == 2)] return response.Response(periods) except SQLAlchemyError as ex: if sentry.sentry_client: sentry.sentry_client.captureException() return response.create_fatal_response(str(ex))