Commit 6c948129 authored by Pierre-Antoine Rouby's avatar Pierre-Antoine Rouby
Browse files

doc: dev: Add SQL section with examples.

Showing with 254 additions and 85 deletions
+254 -85
......@@ -139,101 +139,270 @@ https://doc.qt.io/qt-5/model-view-programming.html
** TODO Model
- Model de donnée Python
- Correspond à une sauvegarde SQL
The model is a set of Python classes. In Pamhyr2, this classes must
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
#+header: :results drawer
#+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
digraph {
bgcolor="transparent";
node[colorscheme=set19,shape=box,style="filled",fillcolor="2"];
study[label="Study"];
river[label="River"];
subgraph cluster00 {
label="Network"
rnode[label="RiverNode"];
redge[label="RiverReach"];
}
frictionlist[label="FrictionList"];
subgraph cluster01 {
label="Stricklers";
stricklers[label="Stricklers"];
stricklerslist[label="StricklersList"];
}
subgraph cluster02 {
label="BoundaryCondition";
boundaryconditionlist[label="BoundaryConditionList"];
boundarycondition[label="BoundaryCondition"];
}
subgraph cluster03 {
label="LateralContribution";
lateralcontributionlist[label="LateralContributionList"];
lateralcontribution[label="LateralContribution"];
}
subgraph cluster04 {
label="InitialConditions";
initialconditionsdict[label="InitialConditionsDict"];
initialconditions[label="InitialConditions"];
}
solverparameterslist[label="SolverParametersList"];
subgraph cluster05 {
label="Sediment";
sedimentlayerlist[label="SedimentLayerList"];
sedimentlayer[label="SedimentLayer"];
layer[label="Layer"];
}
subgraph cluster06 {
label="Greometry"
georeach[label="Reach"];
geocrosssection[label="Cross-section"];
geopoint[label="Point"];
}
bgcolor="transparent";
node[colorscheme=set19,shape=box,style="filled",fillcolor="2"];
subgraph cluster0 {
style=dashed;
study[label="Study"];
river[label="River"];
subgraph cluster00 {
style=solid;
label="Network"
rnode[label="RiverNode"];
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"];
subgraph cluster01 {
style=solid;
label="Stricklers";
stricklers[label="Stricklers"];
stricklerslist[label="StricklersList"];
}
subgraph cluster02 {
style=solid;
label="BoundaryCondition";
boundaryconditionlist[label="BoundaryConditionList"];
boundarycondition[label="BoundaryCondition"];
}
subgraph cluster03 {
style=solid;
label="LateralContribution";
lateralcontributionlist[label="LateralContributionList"];
lateralcontribution[label="LateralContribution"];
}
subgraph cluster04 {
style=solid;
label="InitialConditions";
initialconditionsdict[label="InitialConditionsDict"];
initialconditions[label="InitialConditions"];
}
solverparameterslist[label="SolverParametersList"];
subgraph cluster05 {
style=solid;
label="Sediment";
sedimentlayerlist[label="SedimentLayerList"];
sedimentlayer[label="SedimentLayer"];
layer[label="Layer"];
}
//}
subgraph cluster2 {
style=dashed;
label="Results"
results[label="Results"]
rriver[label="River"];
rreach[label="Reach"];
rcrosssection[label="Cross-section"];
}
study -> river;
river -> rnode;
river -> redge;
redge -> rnode;
river -> boundaryconditionlist -> boundarycondition -> rnode;
river -> lateralcontributionlist -> lateralcontribution -> redge;
river -> initialconditionsdict -> initialconditions;
initialconditions -> redge;
river -> stricklerslist -> stricklers;
river -> solverparameterslist;
river -> sedimentlayerlist -> sedimentlayer -> layer;
redge -> frictionlist -> stricklers;
redge -> georeach -> geocrosssection -> geopoint;
geocrosssection -> sedimentlayer;
geopoint -> sedimentlayer;
results -> study;
results -> rriver;
rriver -> river;
rriver -> rreach;
rreach -> georeach;
rreach -> rcrosssection;
rcrosssection -> geocrosssection;
}
#+end_src
subgraph cluster07 {
label="Results"
results[label="Results"]
rriver[label="River"];
rreach[label="Reach"];
rcrosssection[label="Cross-section"];
}
*** 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
study -> river;
river -> rnode;
river -> redge;
redge -> rnode;
river -> boundaryconditionlist -> boundarycondition -> rnode;
river -> lateralcontributionlist -> lateralcontribution -> redge;
river -> initialconditionsdict -> initialconditions;
initialconditions -> redge;
river -> stricklerslist -> stricklers;
river -> solverparameterslist;
river -> sedimentlayerlist -> sedimentlayer -> layer;
redge -> frictionlist -> stricklers;
redge -> georeach -> geocrosssection -> geopoint;
geocrosssection -> sedimentlayer;
geopoint -> sedimentlayer;
results -> study[style="dashed"];
results -> rriver;
rriver -> river[style="dashed"];
rriver -> rreach;
rreach -> georeach[style="dashed"];
rreach -> rcrosssection;
rcrosssection -> geocrosssection[style="dashed"];
}
#+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
*** TODO SQL study file
[fn:sqlite] The SQLite web site: https://www.sqlite.org/index.html
(last access 2023-09-20)
*** TODO List class
*** TODO Dict class
** TODO View
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment