Results: Prepare result save.

scenarios
Pierre-Antoine 2025-09-22 17:20:31 +02:00
parent 9c69f2ce6d
commit 3a3c4d9d73
2 changed files with 288 additions and 7 deletions

View File

@ -14,7 +14,9 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
import struct
import logging import logging
import itertools
import numpy as np import numpy as np
from copy import deepcopy from copy import deepcopy
@ -28,12 +30,10 @@ logger = logging.getLogger()
class Results(SQLSubModel): class Results(SQLSubModel):
_SQL_TABLE = "solver_results" def __init__(self, id=-1, study=None, solver=None,
def __init__(self, study=None, solver=None,
repertory="", name="0"): repertory="", name="0"):
super(Results, self).__init__( super(Results, self).__init__(
status=study.status, id=id, status=study.status,
owner_scenario=study.status.scenario.id owner_scenario=study.status.scenario.id
) )
@ -78,3 +78,122 @@ class Results(SQLSubModel):
self._repertory, self._repertory,
qlog=None, qlog=None,
) )
def timestamps_to_struct(self):
ts = self._meta_data["timestamps"]
sf = ">" + ''.join(itertools.repeat("d", len(ts)))
return struct.pack(sf, ts)
@classmethod
def _db_create(cls, execute, ext=""):
execute(f"""
CREATE TABLE results{ext} (
{cls.create_db_add_pamhyr_id()},
solver TEXT NOT NULL,
study_revision INTEGER NOT NULL,
creation_data DATE NOT NULL,
nb_timestamps INTEGER NOT NULL,
timestamps BLOB NOT NULL,
{Scenario.create_db_add_scenario()},
{Scenario.create_db_add_scenario_fk()},
PRIMARY KEY(pamhyr_id, scenario)
)
""")
return cls._create_submodel(execute)
@classmethod
def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".")
if major == "0" and int(minor) <= 2:
cls._db_create(execute)
return cls._update_submodel(execute, version, data)
@classmethod
def _db_load(cls, execute, data=None):
new = []
study = data['study']
status = data['status']
scenario = data["scenario"]
loaded = data['loaded_pid']
values = execute(
"SELECT pamhyr_id, solver_name, solver_type, " +
"study_revision, creation_data, nb_timestamps, timestamps, " +
"scenario " +
"FROM results " +
f"WHERE scenario = {scenario.id} " +
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) " +
"ORDER BY ind ASC"
)
for v in values:
it = iter(v)
pid = next(it)
solver = next(it)
revision = next(it)
creation_date = next(it)
nb_timestamps = next(it)
timestamps_bytes = next(it)
owner_scenario = next(it)
new_results = cls(
id=pid, status=status,
owner_scenario=owner_scenario
)
new_results.set("solver_name", solver_name)
new_results.set("solver_type", solver_type)
new_results.set("study_revision", revision)
new_results.set("creation_date", creation_date)
sf = ">" + ''.join(itertools.repeat("d", len(nb_timestamps)))
ts = struct.unpack(sf, timestamp_bytes)
new_results.set("timestamps", ts)
new_results._river = River._db_load(execute, data)
loaded.add(pid)
new.append(new_results)
return new
def _db_save(self, execute, data=None):
execute(
"DELETED FROM results " +
f"WHERE scenario = {self._owner_scenario}"
)
execute(
"DELETED FROM results_data " +
f"WHERE scenario = {self._owner_scenario}"
)
pid = self._pamhyr_id
if self._solver is None:
solver_name = self.get("solver_name")
solver_type = self.get("solver_type")
else:
solver_name = self._solver._name
solver_type = self._solver._type
ts = self.get("timestamps")
sf = ">" + ''.join(itertools.repeat("d", len(ts)))
execute(
"INSERT INTO " +
"results (pamhyr_id, solver_name, solver_type, " +
"study_revision, creation_data, nb_timestamps, timestamps, " +
"scenario) VALUES (?, ?, ?, ?, ?, ?, ?)",
self._pamhyr_id, solver_name, solver_type,
self._owner_scenario.revision, self.get("creation_data"),
len(ts), struct.pack(sf, ts), self._owner_scenario
)
data["result"] = self._pamhyr_id
self._river._db_save(execute, data)
return True

View File

@ -14,15 +14,25 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
import struct
import logging import logging
import itertools
from functools import reduce
from datetime import datetime from datetime import datetime
from Model.Tools.PamhyrDB import SQLSubModel
logger = logging.getLogger() logger = logging.getLogger()
class Profile(object): class Profile(SQLSubModel):
def __init__(self, profile, study): def __init__(self, profile, study):
super(Profile, self).__init__(
id=-1, status=study.status,
owner_scenario=study.status.scenario.id
)
self._study = study self._study = study
self._profile = profile # Source profile in the study self._profile = profile # Source profile in the study
self._data = {} # Dict of dict {<ts>: {<key>: <value>, ...}, ...} self._data = {} # Dict of dict {<ts>: {<key>: <value>, ...}, ...}
@ -65,9 +75,122 @@ class Profile(object):
def has_sediment(self): def has_sediment(self):
return any(map(lambda ts: "sl" in self._data[ts], self._data)) return any(map(lambda ts: "sl" in self._data[ts], self._data))
@classmethod
def _db_create(cls, execute, ext=""):
execute(f"""
CREATE TABLE results_data{ext}(
{cls.create_db_add_pamhyr_id()},
result INTEGER NOT NULL,
key TEXT NOT NULL,
reach INTEGER NOT NULL,
section INTEGER NOT NULL,
len_data INTEGER NOT NULL,
data BLOB NOT NULL,
{Scenario.create_db_add_scenario()},
{Scenario.create_db_add_scenario_fk()},
FOREIGN KEY(result) REFERENCES results(pamhyr_id),
FOREIGN KEY(reach) REFERENCES river_reach(pamhyr_id),
FOREIGN KEY(section)
REFERENCES geometry_profileXYZ(pamhyr_id),
PRIMARY KEY(pamhyr_id, result, key, scenario)
)
""")
class Reach(object): if ext == "_tmp":
return True
return cls._create_submodel(execute)
@classmethod
def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".")
if major == "0" and int(minor) <= 2:
cls._db_create(execute)
return cls._update_submodel(execute, version, data)
@classmethod
def _db_load(cls, execute, data=None):
new = []
study = data['study']
status = data['status']
scenario = data["scenario"]
loaded = data['loaded_pid']
timestamps = data['timestamps']
values = execute(
"SELECT pamhyr_id, result, key, " +
"reach, section, len_data, data, scenario " +
"FROM results_data " +
f"WHERE scenario = {scenario.id} " +
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})"
)
for v in values:
it = iter(v)
pid = next(it)
result = next(it)
key = next(it)
reach = next(it)
section = next(it)
len_data = next(it)
data = next(it)
owner_scenario = next(it)
new_data = cls(
id=pid, status=status,
owner_scenario=owner_scenario
)
sf = ">" + ''.join(itertools.repeat("f", len_data))
values = struct.unpack(sf, data)
for timestamp, value in zip(timestamps, values):
new_data.set(timestamp, key, value)
loaded.add(pid)
new.append(new_data)
return new
def get_keys(self):
return reduce(
lambda acc, ts: acc.union(d[ts].keys())
)
def _db_save(self, execute, data=None):
pid = self._pamhyr_id
result = data["result"]
for key in self.get_keys():
data = self.get_key(key)
sf = ">" + ''.join(itertools.repeat("f", len(data)))
data_bytes = struct.pack(sf, data)
execute(
"INSERT INTO " +
"results_data (pamhyr_id, result, , " +
"study_revision, key, len_data, data, " +
"scenario) VALUES (?, ?, ?, ?, ?, ?, ?)",
pid, result, self._owner_scenario.revision,
key, len(data), data_bytes,
self._owner_scenario
)
return True
class Reach(SQLSubModel):
def __init__(self, reach, study): def __init__(self, reach, study):
super(Reach, self).__init__(
id=-1, status=study.status,
owner_scenario=study.status.scenario.id
)
self._study = study self._study = study
self._reach = reach # Source reach in the study self._reach = reach # Source reach in the study
self._profiles = list( self._profiles = list(
@ -101,9 +224,31 @@ class Reach(object):
def has_sediment(self): def has_sediment(self):
return any(map(lambda profile: profile.has_sediment(), self._profiles)) return any(map(lambda profile: profile.has_sediment(), self._profiles))
@classmethod
def _db_create(cls, execute, ext=""):
return cls._create_submodel(execute)
class River(object): @classmethod
def _db_update(cls, execute, version, data=None):
return cls._update_submodel(execute, version, data)
@classmethod
def _db_load(cls, execute, data=None):
return cls._db_load(execute, data)
def _db_save(self, execute, data=None):
for profile in self._profiles:
data["profile"] = profile.geometry.pamhyr_id
profile._db_save(execute, data)
class River(SQLSubModel):
def __init__(self, study): def __init__(self, study):
super(River, self).__init__(
id=-1, status=study.status,
owner_scenario=study.status.scenario.id
)
self._study = study self._study = study
# Dict with timestamps as key # Dict with timestamps as key
@ -137,3 +282,20 @@ class River(object):
self._reachs self._reachs
) )
) )
@classmethod
def _db_create(cls, execute, ext=""):
return cls._create_submodel(execute)
@classmethod
def _db_update(cls, execute, version, data=None):
return cls._update_submodel(execute, version, data)
@classmethod
def _db_load(cls, execute, data=None):
return cls._db_load(execute, data)
def _db_save(self, execute, data=None):
for reach in self._reachs:
data["reach"] = reach.geometry.pamhyr_id
reach._db_save(execute, data)