diff --git a/src/Model/Friction/Friction.py b/src/Model/Friction/Friction.py index d7b0386e..2ddbe9c6 100644 --- a/src/Model/Friction/Friction.py +++ b/src/Model/Friction/Friction.py @@ -39,7 +39,9 @@ class Friction(SQLSubModel): else: self.id = id - Friction._id_cnt = max(self.id, Friction._id_cnt+1) + Friction._id_cnt = max( + self.id, Friction._id_cnt + 1 + ) self._name = name self._edge = None diff --git a/src/Model/Scenario.py b/src/Model/Scenario.py new file mode 100644 index 00000000..4425af24 --- /dev/null +++ b/src/Model/Scenario.py @@ -0,0 +1,161 @@ +# Scenario.py -- Pamhyr +# Copyright (C) 2024 INRAE +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# -*- coding: utf-8 -*- + +from tools import logger_exception + +from Model.Tools.PamhyrDB import SQLSubModel + +class Scenario(SQLSubModel): + _id_cnt = 0 + _sub_classes = [] + + def __init__(self, + id: int = -1, + name: str = "", + description: str = "", + revision:int = 0, + parent=None, + status=None): + super(Scenario, self).__init__() + + self._set_id(id) + + self._name = name + self._description = description + self._revision = revision + self._parent = parent + self._status = status + + def _set_id(self, id): + if id == -1: + self._id = Scenario._id_cnt + else: + self._id = id + + Scenario._id_cnt = max( + self._id + 1, Scenario._id_cnt + 1 + ) + + @classmethod + def _db_create(cls, execute): + execute(""" + CREATE TABLE scenario( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + description TEXT NOT NULL, + revision INTEGER NOT NULL, + parent_id INTEGER REFERENCES scenario(id) + ) + """) + + cls._create_submodel(execute) + return True + + @classmethod + def _db_add_default(cls, execute): + execute( + "INSERT OR REPLACE INTO " + + "scenario(id, name, description, revision, parent_id) " + + "VALUES (" + + " 0, 'default', 'Default scenario',\n" + + " 0, NULL" + + ")" + ) + + @classmethod + def _db_update(cls, execute, version): + major, minor, release = version.strip().split(".") + if major == minor == "0": + if int(release) < 12: + cls._db_create(execute) + cls._db_add_default(execute) + + return True + + @classmethod + def _db_load(cls, execute, data=None): + scenarios = {} + + table = execute( + "SELECT id, name, description, revision, parent_id " + + "FROM scenario " + + "ORDER BY id ASC" + ) + + for row in table: + it = iter(row) + + id = next(it) + name = next(it) + desc = next(it) + revi = next(it) + parent = next(it) + + if parent is not None: + parent = scenarios[parent] + + new = cls( + id=id, name=name, description=desc, + revision=revi, parent=parent, + status=data["status"] + ) + scenarios[id] = new + + return scenarios + + def _db_save(self, execute, data=None): + parent = 'NULL' + if self.parent is not None: + parent = self.parent._id + + sql = ( + "INSERT OR REPLACE INTO " + + "scenario(id, name, description, revision, parent_id) " + + "VALUES (" + + f"{self._id}, "+ + f"'{self._db_format(self.name)}', " + + f"'{self._db_format(self.description)}', " + + f"{self._revision}, " + + f"{parent}" + + ")" + ) + execute(sql) + + return True + + @property + def name(self): + if self._name == "": + return f"Child of '{self._parent.name}'" + + return self._name + + @property + def description(self): + if self._description == "": + return f"Child of '{self._parent.name}'" + + return self._description + + @property + def revision(self): + return self._revision + + @property + def parent(self): + return self._parent diff --git a/src/Model/Scenarios.py b/src/Model/Scenarios.py new file mode 100644 index 00000000..a390f114 --- /dev/null +++ b/src/Model/Scenarios.py @@ -0,0 +1,60 @@ +# Scenarios.py -- Pamhyr +# Copyright (C) 2024 INRAE +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# -*- coding: utf-8 -*- + +from tools import logger_exception + +from Model.Tools.PamhyrDict import PamhyrModelDict +from Model.Scenario import Scenario + +class Scenarios(PamhyrModelDict): + _sub_classes = [ + Scenario, + ] + + @classmethod + def _db_load(cls, execute, data=None): + new = cls(status=data["status"]) + + new._dict = Scenario._db_load( + execute, + data=data + ) + + return new + + def _db_save(self, execute, data=None): + ok = True + if data is None: + data = {} + + scenarios = self._dict + for sid in scenarios: + ok &= scenarios[sid]._db_save(execute, data) + + return ok + + def get(self, key): + if key in self._dict: + return self._dict[key] + + return None + + def new(self, parent): + new = Scenario(parent=parent, status=self._status) + self.set(new._id, new) + return new diff --git a/src/Model/Study.py b/src/Model/Study.py index aed5d689..99236463 100644 --- a/src/Model/Study.py +++ b/src/Model/Study.py @@ -24,6 +24,8 @@ from datetime import datetime from tools import timer, timestamp from Model.Tools.PamhyrDB import SQLModel +from Model.Scenarios import Scenarios +from Model.Scenario import Scenario from Model.Saved import SavedStatus from Model.Serializable import Serializable from Model.Except import NotImplementedMethodeError @@ -36,12 +38,13 @@ logger = logging.getLogger() class Study(SQLModel): _sub_classes = [ + Scenario, River, ] def __init__(self, filename=None, init_new=True): # Metadata - self._version = "0.0.11" + self._version = "0.0.12" self.creation_date = datetime.now() self.last_modification_date = datetime.now() self.last_save_date = datetime.now() @@ -60,6 +63,15 @@ class Study(SQLModel): if init_new: # Study data + self.scenarios = Scenarios(status=self.status) + self.scenarios.set( + 0, + Scenario( + id=0, name='default', + description='Default scenario', + status=self.status, + ) + ) self._river = River(status=self.status) else: self._init_db_file(filename, is_new=False) @@ -289,22 +301,36 @@ class Study(SQLModel): ) new.creation_date = datetime.fromtimestamp( - float(new.execute( - "SELECT value FROM info WHERE key='creation_date'")[0]) + float( + new.execute( + "SELECT value FROM info WHERE key='creation_date'" + )[0] + ) ) new.last_save_date = datetime.fromtimestamp( - float(new.execute( - "SELECT value FROM info WHERE key='last_save_date'")[0]) + float( + new.execute( + "SELECT value FROM info WHERE key='last_save_date'" + )[0] + ) + ) + + data = {"status": new.status} + sql_exec = lambda sql: new.execute( + sql, + fetch_one=False, + commit=True + ) + + new.scenarios = Scenario._db_load( + sql_exec, + data=data ) # Load river data new._river = River._db_load( - lambda sql: new.execute( - sql, - fetch_one=False, - commit=True - ), - data={"status": new.status} + sql_exec, + data=data ) return new @@ -351,14 +377,19 @@ class Study(SQLModel): ) progress() - self._save_submodel([self._river], data=progress) + self._save_submodel( + [self.scenarios, self._river], + data=progress + ) self.commit() def sql_save_request_count(self): return self._count() def _count(self): - cnt = self._save_count([self._river]) + cnt = self._save_count( + [self.scenarios, self._river] + ) logger.debug(cnt) return cnt + 6