mirror of https://gitlab.com/pamhyr/pamhyr2
doc: dev: Add SQL section with examples.
parent
89353b656f
commit
6c9481296b
|
|
@ -139,45 +139,74 @@ https://doc.qt.io/qt-5/model-view-programming.html
|
||||||
|
|
||||||
** TODO Model
|
** TODO Model
|
||||||
|
|
||||||
- Model de donnée Python
|
The model is a set of Python classes. In Pamhyr2, this classes must
|
||||||
- Correspond à une sauvegarde SQL
|
respect some constraint. Each model class must inherits
|
||||||
|
=Model.Tools.SQLSubModel= abstract class, except the =Model.Study=
|
||||||
|
class who inherits =Model.Tools.SQLModel= (see [[SQL]]).
|
||||||
|
|
||||||
|
The model entry point is the Study class. It contains infomation about
|
||||||
|
the study: their name, description, time system, and so on. Their
|
||||||
|
contains a River object too. This river object inherits the network
|
||||||
|
graph and contains a list of =RiverNode= and a list of =RiverReach=
|
||||||
|
(an edge who contains a source node, and destination node).
|
||||||
|
=RiverReach= contrains geometry, so, the river network (node and edge)
|
||||||
|
associated with the geometry forms the basis of the model, and the
|
||||||
|
other components are linked to one of these basic components.
|
||||||
|
|
||||||
#+name: graph-model
|
#+name: graph-model
|
||||||
#+header: :results drawer
|
#+header: :results drawer
|
||||||
#+header: :exports results
|
#+header: :exports results
|
||||||
#+header: :post attr_wrap(width="16cm", data=*this*, name="graph-model", caption="Pamhyr2 model class dependencies", float="t")
|
#+header: :post attr_wrap(width="16cm", data=*this*, name="graph-model", caption="Pamhyr2 model class dependencies (A -> B means A can contain references to B)", float="t")
|
||||||
#+begin_src dot :file "images/graph-model.png" :cache no
|
#+begin_src dot :file "images/graph-model.png" :cache no
|
||||||
digraph {
|
digraph {
|
||||||
bgcolor="transparent";
|
bgcolor="transparent";
|
||||||
node[colorscheme=set19,shape=box,style="filled",fillcolor="2"];
|
node[colorscheme=set19,shape=box,style="filled",fillcolor="2"];
|
||||||
|
|
||||||
|
subgraph cluster0 {
|
||||||
|
style=dashed;
|
||||||
study[label="Study"];
|
study[label="Study"];
|
||||||
river[label="River"];
|
river[label="River"];
|
||||||
|
|
||||||
subgraph cluster00 {
|
subgraph cluster00 {
|
||||||
|
style=solid;
|
||||||
label="Network"
|
label="Network"
|
||||||
rnode[label="RiverNode"];
|
rnode[label="RiverNode"];
|
||||||
redge[label="RiverReach"];
|
redge[label="RiverReach"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subgraph cluster06 {
|
||||||
|
style=solid;
|
||||||
|
label="Greometry"
|
||||||
|
georeach[label="Reach"];
|
||||||
|
geocrosssection[label="Cross-section"];
|
||||||
|
geopoint[label="Point"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//subgraph cluster1 {
|
||||||
|
// style=dashed;
|
||||||
frictionlist[label="FrictionList"];
|
frictionlist[label="FrictionList"];
|
||||||
|
|
||||||
subgraph cluster01 {
|
subgraph cluster01 {
|
||||||
|
style=solid;
|
||||||
label="Stricklers";
|
label="Stricklers";
|
||||||
stricklers[label="Stricklers"];
|
stricklers[label="Stricklers"];
|
||||||
stricklerslist[label="StricklersList"];
|
stricklerslist[label="StricklersList"];
|
||||||
}
|
}
|
||||||
subgraph cluster02 {
|
subgraph cluster02 {
|
||||||
|
style=solid;
|
||||||
label="BoundaryCondition";
|
label="BoundaryCondition";
|
||||||
boundaryconditionlist[label="BoundaryConditionList"];
|
boundaryconditionlist[label="BoundaryConditionList"];
|
||||||
boundarycondition[label="BoundaryCondition"];
|
boundarycondition[label="BoundaryCondition"];
|
||||||
}
|
}
|
||||||
subgraph cluster03 {
|
subgraph cluster03 {
|
||||||
|
style=solid;
|
||||||
label="LateralContribution";
|
label="LateralContribution";
|
||||||
lateralcontributionlist[label="LateralContributionList"];
|
lateralcontributionlist[label="LateralContributionList"];
|
||||||
lateralcontribution[label="LateralContribution"];
|
lateralcontribution[label="LateralContribution"];
|
||||||
}
|
}
|
||||||
subgraph cluster04 {
|
subgraph cluster04 {
|
||||||
|
style=solid;
|
||||||
label="InitialConditions";
|
label="InitialConditions";
|
||||||
initialconditionsdict[label="InitialConditionsDict"];
|
initialconditionsdict[label="InitialConditionsDict"];
|
||||||
initialconditions[label="InitialConditions"];
|
initialconditions[label="InitialConditions"];
|
||||||
|
|
@ -186,20 +215,16 @@ https://doc.qt.io/qt-5/model-view-programming.html
|
||||||
solverparameterslist[label="SolverParametersList"];
|
solverparameterslist[label="SolverParametersList"];
|
||||||
|
|
||||||
subgraph cluster05 {
|
subgraph cluster05 {
|
||||||
|
style=solid;
|
||||||
label="Sediment";
|
label="Sediment";
|
||||||
sedimentlayerlist[label="SedimentLayerList"];
|
sedimentlayerlist[label="SedimentLayerList"];
|
||||||
sedimentlayer[label="SedimentLayer"];
|
sedimentlayer[label="SedimentLayer"];
|
||||||
layer[label="Layer"];
|
layer[label="Layer"];
|
||||||
}
|
}
|
||||||
|
//}
|
||||||
|
|
||||||
subgraph cluster06 {
|
subgraph cluster2 {
|
||||||
label="Greometry"
|
style=dashed;
|
||||||
georeach[label="Reach"];
|
|
||||||
geocrosssection[label="Cross-section"];
|
|
||||||
geopoint[label="Point"];
|
|
||||||
}
|
|
||||||
|
|
||||||
subgraph cluster07 {
|
|
||||||
label="Results"
|
label="Results"
|
||||||
results[label="Results"]
|
results[label="Results"]
|
||||||
rriver[label="River"];
|
rriver[label="River"];
|
||||||
|
|
@ -223,17 +248,161 @@ https://doc.qt.io/qt-5/model-view-programming.html
|
||||||
geocrosssection -> sedimentlayer;
|
geocrosssection -> sedimentlayer;
|
||||||
geopoint -> sedimentlayer;
|
geopoint -> sedimentlayer;
|
||||||
|
|
||||||
results -> study[style="dashed"];
|
results -> study;
|
||||||
results -> rriver;
|
results -> rriver;
|
||||||
rriver -> river[style="dashed"];
|
rriver -> river;
|
||||||
rriver -> rreach;
|
rriver -> rreach;
|
||||||
rreach -> georeach[style="dashed"];
|
rreach -> georeach;
|
||||||
rreach -> rcrosssection;
|
rreach -> rcrosssection;
|
||||||
rcrosssection -> geocrosssection[style="dashed"];
|
rcrosssection -> geocrosssection;
|
||||||
}
|
}
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
*** TODO SQL study file
|
*** TODO SQL
|
||||||
|
|
||||||
|
The model must be export to a database file to create a study save
|
||||||
|
file. This file use SQLite3[fn:sqlite] format and the extention
|
||||||
|
=.pamhyr=. So, each model componante must be register into this study
|
||||||
|
file. To create, update, set and get information into SQLite database
|
||||||
|
we use SQL command. The database use version number and some
|
||||||
|
modification could be perform to update database. For each model
|
||||||
|
componante, correspond one or more SQL table to store information. To
|
||||||
|
normalize the interaction with database we made two classes, SQLModel
|
||||||
|
and SQLSubModel. The Study class use SQLModel because is the top of
|
||||||
|
the model hierachy. The rest of model class inherits to SQLSubModel.
|
||||||
|
|
||||||
|
A class who inherits SQLSubModel, must implement some methods:
|
||||||
|
- =_sql_create=: Class method to create the database scheme
|
||||||
|
- =_sql_update=: Class method to update the database scheme if necessary
|
||||||
|
- =_sql_load=: Class method to load data from DB
|
||||||
|
- =_sql_save=: Method to save current object into DB
|
||||||
|
|
||||||
|
Class method take in arguments: The class (=cls=), a function to
|
||||||
|
execute SQL command into the database (=execute=). In addition, the
|
||||||
|
update method take the previous version of database, load method take
|
||||||
|
an optional arguments =data= if additional infomation ar needed, and
|
||||||
|
who can contains whatever you want. The method save take in arguments
|
||||||
|
the current object (=self=), a function to execute SQL command into
|
||||||
|
the database (=execute=), and optional data (=data=).
|
||||||
|
|
||||||
|
The class who inherits SQLSubModel can also define an class attribute
|
||||||
|
=_sub_classes= to set a formal class dependencies into database. This
|
||||||
|
attribute is use at database creation to create all table, and at
|
||||||
|
update to update all the database table. Let see examples of
|
||||||
|
SQLSubModel usage for two classes Foo and Bar with Foo contains list
|
||||||
|
of Bar (Listing [[sql-bar]] and [[sql-foo]]).
|
||||||
|
|
||||||
|
#+NAME: sql-bar
|
||||||
|
#+CAPTION: Exemple of class Bar inherits SQLSubModel.
|
||||||
|
#+begin_src python :python python3 :results output :noweb yes
|
||||||
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
|
|
||||||
|
class Bar(SQLSubModel):
|
||||||
|
_id_cnt = 0
|
||||||
|
def __init__(self, id = -1, x = 0, y = 0):
|
||||||
|
self._x = x
|
||||||
|
self._y = y
|
||||||
|
if id == -1:
|
||||||
|
self.id = Bar._id_cnt + 1
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
Bar._id_cnt = max(id, Bar._id_cnt+1)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _sql_create(cls, execute):
|
||||||
|
execute("""
|
||||||
|
CREATE TABLE bar (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
x INTEGER NOT NULL,
|
||||||
|
foo_id INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY(foo_id) REFERENCES foo(id),
|
||||||
|
)""")
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _sql_update(cls, execute, version):
|
||||||
|
# If version is lesser than 0.0.2, add column to bar table
|
||||||
|
major, minor, release = version.strip().split(".")
|
||||||
|
if major == minor == "0":
|
||||||
|
if int(release) < 2:
|
||||||
|
execute("ALTER TABLE bar ADD COLUMN y INTEGER")
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _sql_load(cls, execute, data = None):
|
||||||
|
new = []
|
||||||
|
table = execute(
|
||||||
|
f"SELECT id, x, y FROM bar WHERE foo_id = {data['id']}"
|
||||||
|
)
|
||||||
|
for row in table:
|
||||||
|
bar = cls(
|
||||||
|
id = row[0], x = row[1], y = row[2],
|
||||||
|
)
|
||||||
|
new.append(bar)
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _sql_save(self, execute, data = None):
|
||||||
|
execute("INSERT INTO bar (id,x,y,foo_id) VALUES " +
|
||||||
|
f"({self.id}, {self._x}, {self._y}, {data['id']})")
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
#+NAME: sql-foo
|
||||||
|
#+CAPTION: Exemple of class Foo inherits SQLSubModel.
|
||||||
|
#+begin_src python :python python3 :results output :noweb yes
|
||||||
|
class Foo(SQLSubModel):
|
||||||
|
_id_cnt = 0
|
||||||
|
_sub_classes = [Bar]
|
||||||
|
|
||||||
|
def __init__(self, id = -1, name = ""):
|
||||||
|
self._name = name
|
||||||
|
self._bar = []
|
||||||
|
# ...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _sql_create(cls, execute):
|
||||||
|
execute("""
|
||||||
|
CREATE TABLE foo (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _sql_update(cls, excute, version):
|
||||||
|
return cls._update_submodel(execute, version)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _sql_load(cls, execute, data = None):
|
||||||
|
new = []
|
||||||
|
table = execute(
|
||||||
|
"SELECT id, name FROM foo"
|
||||||
|
)
|
||||||
|
for row in table:
|
||||||
|
foo = cls(
|
||||||
|
id = row[0],
|
||||||
|
name = row[1],
|
||||||
|
)
|
||||||
|
data = {
|
||||||
|
"id": row[0], # Current Foo ID
|
||||||
|
}
|
||||||
|
foo._bar = Bar._sql_load(execute, data=data)
|
||||||
|
new.append(foo)
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _sql_save(self, execute, data = None):
|
||||||
|
execute(f"DELETE FROM foo WHERE id = {self.id}")
|
||||||
|
execute(f"DELETE FROM bar WHERE foo_id = {self.id}")
|
||||||
|
# Save new data
|
||||||
|
execute(f"INSERT INTO bar (id,name) VALUES ({self.id}, {self._name})")
|
||||||
|
data = {"id": self.id}
|
||||||
|
for bar in self._bar:
|
||||||
|
bar._sql_save(execute, data=data)
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
[fn:sqlite] The SQLite web site: https://www.sqlite.org/index.html
|
||||||
|
(last access 2023-09-20)
|
||||||
|
|
||||||
*** TODO List class
|
*** TODO List class
|
||||||
*** TODO Dict class
|
*** TODO Dict class
|
||||||
** TODO View
|
** TODO View
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue