# -*- coding: utf-8 -*- import hql import itertools from ppms import ppms from ppms.constants import * from ppms.sql import sanitize_value, convert_planta_time_to_date __all__ = ['aggregate_periods', 'get_amount_of_children', 'reset_summarized_periods', 'delete_obsolete_periods', 'PeriodCreator', 'get_start_end_period_from_db', 'debug_period_creation', 'aggregate_periods_from_start_to_end', 'processInput_period_work'] DEBUG = False if DEBUG: msg = lambda *args, **kwargs: ppms.ui_message_box(*args, **kwargs) else: msg = lambda *args, **kwargs: None def aggregate_periods(parent_res_id, start_period, end_period): '''Aggregate periods onto a resource This method will keep aggregating the capacity to the parent until it has reached the top. If "summarize periods" is disabled then no aggregation takes place parent_res_id Unsanitized resource id start_period Unsanitized starting date, days since 01.01.1970 end_period Unsanitized end date, days since 01.01.1970 ''' rec_467 = ppms.search_record(467, [parent_res_id], [2653, 9753], True) if not rec_467: return amount_of_children = get_amount_of_children(parent_res_id) sanitized_parent_res_id = sanitize_value(parent_res_id) sanitized_start_period = convert_planta_time_to_date(start_period) sanitized_end_period = convert_planta_time_to_date(end_period) summarize_periods = rec_467.summarize_periods.get_value() if amount_of_children > 0 and summarize_periods: text = ppms.search_record(425, ['001563'], [296], True).get_di_by_id(296).get_value() ppms.ui_statusbar_set(text % parent_res_id) reset_aggregated_period_query = ppms.get_query('000690') query = reset_aggregated_period_query.format(parent_res_id=sanitized_parent_res_id, start_period=sanitized_start_period, end_period=sanitized_end_period) msg('reset aggregated periods', query) ppms.db_modify(query) aggregate_periods_query = ppms.get_query('000682') query = aggregate_periods_query.format(parent_res_id=sanitized_parent_res_id, start_period=sanitized_start_period, end_period=sanitized_end_period) msg('aggregate_periods', query) ppms.db_modify(query) add_own_cap_query = ppms.get_query('000691') query = add_own_cap_query.format(parent_res_id=sanitized_parent_res_id, start_period=sanitized_start_period, end_period=sanitized_end_period) msg('add own cap', query) ppms.db_modify(query) else: # If we don't have children or summarize_periods is False # we use our own values as aggregated values aggregate_periods_query = ppms.get_query('000686') query = aggregate_periods_query.format(parent_res_id=sanitized_parent_res_id, start_period=sanitized_start_period, end_period=sanitized_end_period) msg('use our own values', query) ppms.db_modify(query) # Keep aggregating to the top grandparent_res_id = rec_467.parent_res_id.get_value() aggregate_periods_from_start_to_end(grandparent_res_id) def aggregate_periods_from_start_to_end(res_id): msg('aggregate_periods_from_start_to_end', res_id) rec_467 = ppms.search_record(467, [res_id], [1230, 1231], True) if not rec_467: return start_period = rec_467.start_period.get_value() end_period = rec_467.end_period.get_value() aggregate_periods(res_id, start_period, end_period) def get_amount_of_children(parent_res_id): '''Retrieves how many resources have parent_res_id as parent''' child_query = ppms.get_query("000685") query = child_query.format(**locals()) return int(ppms.db_select(query)[0][0]) def reset_summarized_periods(res_id): '''Resets the aggregated DIs to zero''' delete_query = ppms.get_query('000684') query = delete_query.format(**locals()) msg('Dropping stuff', query) ppms.db_modify(query) def delete_obsolete_periods(res_id): '''Deletes periods that are outside the start/end range''' res_id = sanitize_value(res_id) delete_periods_out_of_range_query = ppms.get_query('000680') query = delete_periods_out_of_range_query.format(**locals()) msg('Deleting obsolete', query) ppms.db_modify(query) class PeriodCreator(object): '''Class to be used as context manager for generating period records Example usage: >>> with PeriodCreator() as creator: ... creator.add('R2', (100, 200)) ... creator.add('R41', (100, 800)) ''' def add(self, res_id, start_period, end_period): '''Add a resource + timeframe to generate periods for''' # Insert all period changes into a temporary table so they're visible # to the database res_id = sanitize_value(res_id) start_period = convert_planta_time_to_date(start_period) end_period = convert_planta_time_to_date(end_period) raw_temp_period_change_query = ppms.get_query('000678') query = raw_temp_period_change_query.format(**locals()) msg('Preparing new periods', query) ppms.db_modify(query) def _create_new_periods(self): '''Creates new periods This invokes a procedure on the database that creates periods based on what was inserted via the prepare_period_creation() method before Intended to be called automagically from within __exit__ ''' license = ppms.uvar_get(SYS_VAR_LICENSE) user = ppms.uvar_get(SYS_VAR_LOGGED_IN_USER) start_period_calculation_procedure_sql = ppms.get_query('000679') query = start_period_calculation_procedure_sql.format(**locals()) msg('Creating new periods', query) ppms.db_modify(query) self.changes.clear() def __enter__(self): self._commitstate = ppms.db_get_auto_commit() ppms.db_set_auto_commit(False) self.changes = set() return self def __exit__(self, type, value, tb): self._create_new_periods() ppms.db_commit() ppms.db_set_auto_commit(self._commitstate) def get_start_end_period_from_db(res_id): sanitized_res_id = sanitize_value(res_id) query = """select DI001230, DI001231 from DT467 where DI001218 = '{sanitized_res_id}'""".format(**locals()) result = ppms.db_select(query) try: start_period_db, end_period_db = result[0] except (IndexError, ValueError): start_period_db, end_period_db = 0, 0 return start_period_db, end_period_db def debug_period_creation(): '''Delete all period data and recreate it from scratch Use at your own risk :-) ''' with ppms.autocommit_disabled(): ppms.db_modify('DELETE DT468') # Change the start/end periods to a short timeframe today = ppms.uvar_get(SYS_VAR_TODAY) # We assume everything uses calendar BK calendar_rec = ppms.search_record(418, ['BK'], [476, 477], True) calendar_rec.start_period.set_value(today) calendar_rec.end_period.set_value(today + 15) calendar_rec.save() query = """update DT467 set DI001230 = '{start_date}', DI001231 = '{end_date}'""" start_date = convert_planta_time_to_date(today) end_date = convert_planta_time_to_date(today + 15) ppms.db_modify(query.format(**locals())) query = """select DI001218 from DT467""" resources = itertools.chain(*ppms.db_select(query)) res_dict = {} for res_id in resources: rec_467 = ppms.search_record(467, [res_id], [1230, 1231], True) start_period = rec_467.start_period.get_value() end_period = rec_467.end_period.get_value() res_dict[res_id] = (start_period, end_period) with PeriodCreator() as creator: for res_id, (start_period, end_period) in res_dict.items(): creator.add(res_id, start_period, end_period) for res_id, (start_period, end_period) in res_dict.items(): aggregate_periods(res_id, start_period, end_period) ppms.db_commit() def any_child_resource_has_workday(resource_id, parent_resource_id, period): parent_resource = ppms.search_record(467, [parent_resource_id], ['res_id']) for child_resource_record in parent_resource.get_children(467, ['res_id'], True): child_res_id = child_resource_record.res_id.get_value() if child_res_id == resource_id: continue child_period_record = ppms.search_record(468, [child_res_id, period], ['work']) if child_period_record and child_period_record.work.get_value(): return True return False def processInput_period_work(di, oldvalue): newvalue = di.get_value() rec = di.get_dtp_record() res_id = rec.res_id.get_value() period = rec.period.get_value() res_rec = ppms.search_record(467, [res_id], ['parent_res_id']) parent_res_id = res_rec.parent_res_id.get_value() if not parent_res_id: return newvalue parent_period = ppms.search_record(468, [parent_res_id, period], ['work']) if parent_period is not None: if newvalue and not parent_period.work.get_value(): parent_period.work.set_value(1) elif parent_period.work.get_value() and not newvalue: if any_child_resource_has_workday(res_id, parent_res_id, period): return newvalue parent_period.work.set_value(0) return newvalue processInput_period_work.deps = ("res_id", "period")