# -*- coding: utf-8 -*- from copy import copy, deepcopy from tools import trace, timer from functools import reduce from Model.DB import SQLSubModel class Data(SQLSubModel): def __init__(self, name:str = "", comment:str = "", reach = None, kp:float = 0.0, discharge:float = 0.0, height:float = 0.0, status = None): super(Data, self).__init__() self._status = status self._reach = reach self._name = name self._comment = comment self._kp = kp self._discharge = discharge self._speed = 0.0 self._elevation = 0.0 self._height = height if self._kp != 0.0: self._update_from_kp() if self._height != 0.0: self._update_from_height() if self._discharge != 0.0: self._update_from_discharge() @classmethod def _sql_create(cls, execute): execute(""" CREATE TABLE initial_conditions( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, ind INTEGER NOT NULL, name TEXT NOT NULL, comment TEXT NOT NULL, reach INTEGER, kp REAL NOT NULL, discharge REAL NOT NULL, height REAL NOT NULL, FOREIGN KEY(reach) REFERENCES river_reach(id) ) """) return cls._create_submodel(execute) @classmethod def _sql_update(cls, execute, version): return cls._update_submodel(execute, version) @classmethod def _sql_load(cls, execute, data = None): id = data["reach"].id table = execute( "SELECT ind, name, comment, kp, discharge, height " + "FROM initial_conditions " + f"WHERE reach = {id}" ) new = [] for _ in table: new.append(None) for row in table: ind = row[0] name = row[1] comment = row[2] kp = row[3] discharge = row[4] height = row[5] d = cls( reach = data["reach"], status = data["status"], name = name, comment = comment, kp = kp, discharge = discharge, height = height, ) new[ind] = d return new def _sql_save(self, execute, data = None): ind = data["ind"] execute( "INSERT INTO " + "initial_conditions(ind, name, comment, kp, discharge, height, reach) " + "VALUES (" + f"{ind}, '{self._sql_format(self.name)}', " + f"'{self._sql_format(self._comment)}', " + f"{self._kp}, {self._discharge}, {self._height}, " + f"{self._reach.id}" + ")" ) return True def copy(self): new = Data( name = self.name, comment = self._comment, kp = self._kp, discharge = self._discharge, height = self._height, reach = self._reach, status = self._status, ) return new @property def name(self): return self._name def __getitem__(self, key): val = None if key == "name": val = self._name elif key == "comment": val = self._comment elif key == "kp": val = self._kp elif key == "speed": val = self._speed elif key == "discharge": val = self._discharge elif key == "elevation": val = self._elevation elif key == "height": val = self._height return val def _update_get_min(self): profile = self._reach.reach.get_profiles_from_kp(self._kp) if len(profile) > 0: min = profile[0].z_min() else: min = 0.0 return min def _update_from_kp(self): min = self._update_get_min() self._elevation = min - self._height def _update_from_elevation(self): min = self._update_get_min() self._height = self._elevation - min def _update_from_height(self): min = self._update_get_min() self._elevation = self._height + min def _update_from_discharge(self): min = self._update_get_min() # print("TODO") def __setitem__(self, key, value): if key == "name": self._name = str(value) elif key == "comment": self._comment = str(value) elif key == "kp": self._kp = float(value) self._update_from_kp() elif key == "speed": # Not supposed to be modified self._speed = float(value) elif key == "discharge": self._discharge = float(value) self._update_from_discharge() elif key == "elevation": self._elevation = float(value) self._update_from_elevation() elif key == "height": self._height = float(value) self._update_from_height() self._status.modified() class InitialConditions(SQLSubModel): _sub_classes = [ Data ] def __init__(self, reach = None, status = None): super(InitialConditions, self).__init__() self._status = status self._reach = reach self._data = [] @classmethod def _sql_create(cls, execute): return cls._create_submodel(execute) @classmethod def _sql_update(cls, execute, version): return cls._update_submodel(execute, version) @classmethod def _sql_load(cls, execute, data = None): new = cls( reach = data["reach"], status = data["status"] ) new._data = Data._sql_load( execute, data = data ) if new._data is None: return None return new def _sql_save(self, execute, data = None): ok = True ind = 0 for d in self._data: data["ind"] = ind ok &= d._sql_save(execute, data) ind += 1 return ok def __len__(self): return len(self._data) @property def reach(self): return self._reach @reach.setter def reach(self, new): self._reach = reach self._status.modified() @property def data(self): return self._data.copy() @data.setter def data(self, data): self._data = data def get(self, index): return self._data[index] def set(self, index, data): self._data.insert(index, data) self._status.modified() def new(self, index): n = Data(reach = self._reach, status = self._status) self._data.insert(index, n) self._status.modified() def insert(self, index, data): self._data.insert(index, data) self._status.modified() def delete(self, data): self._data = list( filter( lambda x: x not in data, self._data ) ) self._status.modified() def delete_i(self, indexes): data = list( map( lambda x: x[1], filter( lambda x: x[0] in indexes, enumerate(self._data) ) ) ) self.delete(data) def sort(self, reverse=False, key=None): self._data.sort(reverse=reverse, key=key) self._status.modified() def _data_get(self, key): return list( map( lambda d: d[key], self._data ) ) def get_kp(self): return self._data_get("kp") def get_elevation(self): return self._data_get("elevation") def get_discharge(self): return self._data_get("discharge") def _sort_by_z_and_kp(self, profiles): profiles.sort( reverse = False, key = lambda p: p.kp ) first_z = profiles[0].z() last_z = profiles[-1].z() if first_z > last_z: profiles.sort( reverse = True, key = lambda p: p.kp ) def generate_growing_constante_height(self, height:float): self._data = [] profiles = self._reach.reach.profiles self._sort_by_z_and_kp(profiles) prev = None for profile in profiles: new = Data(reach = self._reach, status = self._status) new["kp"] = profile.kp if prev is None: new["elevation"] = profile.z_min() + height else: new["elevation"] = max( profile.z_min() + height, prev["elevation"] ) self._data.append(new) prev = new is_reverse = False if profiles[0].kp > profiles[-1].kp: is_reverse = True self._data.sort( reverse = not is_reverse, key = lambda d: d['kp'] ) def generate_discharge(self, discharge:float): self._new = [] for d in self._data: n = d.copy() n['discharge'] = discharge self._new.append(n) self._data = self._new