def test_custom_calculated_field(self, field_name: str, scope_code: str, function_code: str, return_type: str) \ -> Optional[Any]: from src.db_models import CalculatedField from src import configuration # imports global configuration from src import configuration if not configuration.update_run_background or not self.current_status_raw: self.update_status() field = CalculatedField( id=-1, name=field_name, description="", return_type=return_type, calc_fn=function_code, scope_id=1 # TODO ) current_item = {} # TODO generate current item and index based on the scope self._add_user_defined_calculated_field( field, current_item, initial_status=self.initial_status_raw, current_status=self.current_status_raw, position_list=self.car_positions_raw, lap_list=self.lap_list_raw, charging_process_list=self.charging_process_list_raw, total=self.total_raw, forecast=self.forecast_raw, current_item_index=None, configuration=configuration, now_dt=pendulum.now(tz='utc')) return current_item
def _enhance_forecast(self, forecast: Dict[str, Any], dt: pendulum.DateTime, *, initial_status, current_status, position_list, lap_list, total, charging_process_list, _forecast=None, configuration: Configuration) -> Dict[str, Any]: """ Add calculated fields for forecast :param forecast: :return: the enhanced version (note it does in place enhancements, changes the parameter) """ # add hardcoded calculated fields from src.data_processor.calculated_fields_forecast import add_calculated_fields add_calculated_fields(current_item=forecast, initial_status=initial_status, current_status=current_status, position_list=position_list, lap_list=lap_list, total=total, charging_process_list=charging_process_list, forecast=forecast, configuration=configuration, current_item_index=None, now_dt=dt) # add user-defined (db) calculated fields from src.db_models import CalculatedField db_calculated_fields = CalculatedField.get_all_by_scope( CalculatedFieldScopeEnum.FORECAST.value) for db_calculated_field in db_calculated_fields: self._add_user_defined_calculated_field( db_calculated_field, forecast, initial_status=initial_status, current_status=current_status, position_list=position_list, lap_list=lap_list, total=total, charging_process_list=charging_process_list, forecast=forecast, configuration=configuration, current_item_index=None, now_dt=dt) return forecast
def _enhance_positions( self, positions: List[Dict[str, Any]], dt: pendulum.DateTime, *, initial_status, current_status, _position_list=None, lap_list, total, charging_process_list, forecast, configuration: Configuration) -> List[Dict[str, Any]]: # add calculated fields # !! note this operation is expensive as it runs on lot of records from src.data_processor.calculated_fields_positions import add_calculated_fields from src.db_models import CalculatedField db_calculated_fields = CalculatedField.get_all_by_scope( CalculatedFieldScopeEnum.POSITION.value) for i in range(len(positions)): add_calculated_fields(current_item=positions[i], initial_status=initial_status, current_status=current_status, position_list=positions, lap_list=lap_list, total=total, charging_process_list=charging_process_list, forecast=forecast, configuration=configuration, current_item_index=i, now_dt=dt) for field_description in db_calculated_fields: self._add_user_defined_calculated_field( field_description, positions[i], initial_status=initial_status, current_status=current_status, position_list=positions, lap_list=lap_list, total=total, charging_process_list=charging_process_list, forecast=forecast, configuration=configuration, current_item_index=i, now_dt=dt, ) return positions
def _enhance_laps(self, laps: List[Dict[str, Any]], dt: pendulum.DateTime, *, initial_status, current_status, position_list, _lap_list=None, total, charging_process_list, forecast, configuration: Configuration) -> List[Dict[str, Any]]: from src.data_processor.calculated_fields_laps import add_calculated_fields from src.db_models import CalculatedField db_calculated_fields = CalculatedField.get_all_by_scope( CalculatedFieldScopeEnum.POSITION.value) for i in range(len(laps)): add_calculated_fields(current_item=laps[i], initial_status=initial_status, current_status=current_status, position_list=position_list, lap_list=laps, total=total, charging_process_list=charging_process_list, forecast=forecast, configuration=configuration, current_item_index=i, now_dt=dt) for field_description in db_calculated_fields: self._add_user_defined_calculated_field( field_description, laps[i], initial_status=initial_status, current_status=current_status, position_list=position_list, lap_list=laps, total=total, charging_process_list=charging_process_list, forecast=forecast, configuration=configuration, current_item_index=i, now_dt=dt, ) return laps
def _enhance_charging_processes( self, charging_processes: List[Dict[str, Any]], dt: pendulum.DateTime, *, initial_status, current_status, position_list, lap_list, total, forecast, _charging_process_list, configuration: Configuration) -> List[Dict[str, Any]]: """ Add calculated fields for charigng processes :param forecast: :return: the enhanced version (note it does in place enhancements, changes the parameter) """ from src.data_processor.calculated_fields_charges import add_calculated_fields from src.db_models import CalculatedField db_calculated_fields = CalculatedField.get_all_by_scope( CalculatedFieldScopeEnum.POSITION.value) for i in range(len(charging_processes)): add_calculated_fields(current_item=charging_processes[i], initial_status=initial_status, current_status=current_status, position_list=position_list, lap_list=lap_list, total=total, charging_process_list=charging_processes, forecast=forecast, configuration=configuration, current_item_index=i, now_dt=dt) for field_description in db_calculated_fields: self._add_user_defined_calculated_field( field_description, charging_processes[i], initial_status=initial_status, current_status=current_status, position_list=position_list, lap_list=lap_list, total=total, charging_process_list=charging_processes, forecast=forecast, configuration=configuration, current_item_index=i, now_dt=dt, ) return charging_processes
def save_calculated_fields(field_scope_code: str, req_data: CalculatedFieldApiList) -> int: from src import db field_scope: Optional[FieldScope] = FieldScope.query.filter_by( code=field_scope_code).first() if not field_scope: field_scope = FieldScope(code=field_scope_code, title=req_data.title) db.session.add(field_scope) else: field_scope.title = req_data.title db.session.commit() order_key = 1 db_obj_list = [] # transform to objects, add order key for item in req_data.items: db_obj = CalculatedField(**item.dict(), order_key=order_key, scope_id=field_scope.id) db_obj_list.append(db_obj) order_key += 1 try: calculated_fields = CalculatedField.query.filter_by( scope_id=field_scope.id).all() # cleanup old records for cf in calculated_fields: db.session.delete(cf) db.session.commit() # write new records for obj in db_obj_list: db.session.add(obj) db.session.commit() except Exception as ex: db.session.rollback() raise ex return len(db_obj_list)
def index(self): form = TestCalculatedFieldForm() from src import configuration, db from src.db_models import FieldScope, CalculatedField form.field_scope.choices = [(g.code, g) for g in FieldScope.query.all()] if form.validate_on_submit(): try: return_value = data_processor.test_custom_calculated_field( '__test_field__', form.field_scope.data, form.fn_code.data, "") # TODO the return type is not needed for now flash( Markup( f"Return value is <b>{return_value['__test_field__']}</b>" ), "info") if form.add.data: scope = FieldScope.query.filter_by( code=form.field_scope.data).first() cf_ok = CalculatedField.query.filter_by( scope_id=scope.id).order_by( CalculatedField.order_key.desc()).first() order_key = 1 if cf_ok: order_key = cf_ok.order_key + 1 cf = CalculatedField(name=form.name.data, description=form.description.data, return_type=form.return_type.data, calc_fn=form.fn_code.data, scope_id=scope.id, order_key=order_key) try: db.session.add(cf) db.session.commit() flash( f"Calculated field {form.name.data} stored to database for code {form.field_scope.data}", "info") except Exception as ex: db.session.rollback() flash(f"Can't add {form.name.data}: {ex}", "error") except Exception as ex: flash(f"{type(ex).__name__}: {ex}", "error") laps = data_processor.get_laps_raw() lap = laps[-2 if len(laps) > 1 else -1] lap = { k: v for k, v in lap.items() if not isinstance(v, dict) and not isinstance(v, list) } return self.render( 'admin/test_calculated_field.html', form=form, current_status=data_processor.get_status_raw(), position=data_processor.get_positions_raw()[-1], lap=lap, charging=data_processor.get_charging_process_list_raw()[-1], total=data_processor.get_total_raw(), forecast=data_processor.get_forecast_raw(), configuration=configuration.dict(), post_url=url_for('test_calculated_field.index'), with_categories=True)