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