mirror of
https://gitlab.com/pamhyr/pamhyr2.git
synced 2026-10-30 03:20:41 +01:00
Compare commits
24
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fecd29666 | ||
|
|
71e1324a0f | ||
|
|
07cae542b0 | ||
|
|
b693f67b99 | ||
|
|
18a3d51d75 | ||
|
|
118a39bd5c | ||
|
|
2009e8223b | ||
|
|
bdaa38ea5c | ||
|
|
713732b1dd | ||
|
|
f053539cdd | ||
|
|
fc6916bb3e | ||
|
|
97c26cb4fb | ||
|
|
10c98bb96e | ||
|
|
7c2d9891d0 | ||
|
|
3c3ac1f1df | ||
|
|
80af278a0f | ||
|
|
f254eaee25 | ||
|
|
5adb0bdee5 | ||
|
|
64c99d46d4 | ||
|
|
0f0defb36f | ||
|
|
1e1ee67d4b | ||
|
|
5bc514c565 | ||
|
|
1da3461dfe | ||
|
|
dd5eecb1e2 |
No files matched your search
+3
-9
@@ -41,14 +41,8 @@ __old__
|
|||||||
|
|
||||||
*-venv
|
*-venv
|
||||||
|
|
||||||
mage8/
|
mage8
|
||||||
adists/
|
adists
|
||||||
rubarbe/
|
|
||||||
|
|
||||||
linux/
|
|
||||||
windows/
|
|
||||||
|
|
||||||
install/
|
|
||||||
|
|
||||||
### END CUSTOM ###
|
### END CUSTOM ###
|
||||||
|
|
||||||
@@ -630,7 +624,7 @@ TSWLatexianTemp*
|
|||||||
|
|
||||||
# auto folder when using emacs and auctex
|
# auto folder when using emacs and auctex
|
||||||
./auto/*
|
./auto/*
|
||||||
# *.el
|
*.el
|
||||||
|
|
||||||
# expex forward references with \gathertags
|
# expex forward references with \gathertags
|
||||||
*-tags.tex
|
*-tags.tex
|
||||||
|
|||||||
+9
-49
@@ -1,5 +1,5 @@
|
|||||||
# .gitlab-ci.yml -- Pamhyr gitlab-ci
|
# .gitlab-ci.yml -- Pamhyr gitlab-ci
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -24,13 +24,6 @@ stages:
|
|||||||
- antivirus
|
- antivirus
|
||||||
- release
|
- release
|
||||||
|
|
||||||
#########################
|
|
||||||
# PYINSTALLER VARIABLES #
|
|
||||||
#########################
|
|
||||||
|
|
||||||
variables:
|
|
||||||
HIDDEN_IMPORTS: "--hiddenimport rasterio --collect-submodules rasterio"
|
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# DOWNLOADS #
|
# DOWNLOADS #
|
||||||
#############
|
#############
|
||||||
@@ -138,6 +131,7 @@ dl-rubar-windows:
|
|||||||
- cd rubar-windows
|
- cd rubar-windows
|
||||||
- curl -L -o rubar.tgz https://forge.inrae.fr/river-hydraulics/rubarbe/-/releases/$RUBAR_VERSION/downloads/packages/rubar_windows.tgz
|
- curl -L -o rubar.tgz https://forge.inrae.fr/river-hydraulics/rubarbe/-/releases/$RUBAR_VERSION/downloads/packages/rubar_windows.tgz
|
||||||
- tar xf rubar.tgz
|
- tar xf rubar.tgz
|
||||||
|
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- rubar-windows/rubarbe.exe
|
- rubar-windows/rubarbe.exe
|
||||||
@@ -180,7 +174,7 @@ unittest:
|
|||||||
- job: set-version
|
- job: set-version
|
||||||
artifacts: true
|
artifacts: true
|
||||||
script:
|
script:
|
||||||
- python3.12 -m venv venv
|
- python3 -m venv venv
|
||||||
- . venv/bin/activate
|
- . venv/bin/activate
|
||||||
- pip3 install -U pip
|
- pip3 install -U pip
|
||||||
- pip3 install -r ./full-requirements.txt
|
- pip3 install -r ./full-requirements.txt
|
||||||
@@ -198,7 +192,7 @@ test-pep8:
|
|||||||
- job: unittest
|
- job: unittest
|
||||||
script:
|
script:
|
||||||
# Setup virtual env
|
# Setup virtual env
|
||||||
- python3.12 -m venv venv
|
- python3 -m venv venv
|
||||||
- . venv/bin/activate
|
- . venv/bin/activate
|
||||||
- pip3 install -U pip
|
- pip3 install -U pip
|
||||||
- pip3 install -r ./requirements.txt
|
- pip3 install -r ./requirements.txt
|
||||||
@@ -276,7 +270,7 @@ build-developers-doc:
|
|||||||
- ./build.sh
|
- ./build.sh
|
||||||
# Create html documentation
|
# Create html documentation
|
||||||
- cd ../../src
|
- cd ../../src
|
||||||
- python3.12 -m venv doc-venv
|
- python3 -m venv doc-venv
|
||||||
- . doc-venv/bin/activate
|
- . doc-venv/bin/activate
|
||||||
- pip3 install -U pip
|
- pip3 install -U pip
|
||||||
- pip3 install -r ../full-requirements.txt
|
- pip3 install -r ../full-requirements.txt
|
||||||
@@ -326,13 +320,13 @@ build-linux:
|
|||||||
- mkdir -p linux
|
- mkdir -p linux
|
||||||
- cd linux
|
- cd linux
|
||||||
# Setup virtual env
|
# Setup virtual env
|
||||||
- python3.12 -m venv linux-venv
|
- python3 -m venv linux-venv
|
||||||
- . linux-venv/bin/activate
|
- . linux-venv/bin/activate
|
||||||
- pip3 install -U pip
|
- pip3 install -U pip
|
||||||
- pip3 install -r ../requirements.txt
|
- pip3 install -r ../requirements.txt
|
||||||
- pip3 install -U -r ../requirements.txt
|
- pip3 install -U -r ../requirements.txt
|
||||||
# Run Pyinstaller
|
# Run Pyinstaller
|
||||||
- pyinstaller -y $HIDDEN_IMPORTS --paths linux-venv/lib/python3.12/site-packages ../src/pamhyr.py
|
- pyinstaller -y --paths linux-venv/lib/python3.8/site-packages ../src/pamhyr.py
|
||||||
# Create directory
|
# Create directory
|
||||||
- mkdir -p pamhyr
|
- mkdir -p pamhyr
|
||||||
- mkdir -p pamhyr/_internal
|
- mkdir -p pamhyr/_internal
|
||||||
@@ -440,7 +434,7 @@ build-windows:
|
|||||||
- python -m pip install -U -r ..\requirements.txt
|
- python -m pip install -U -r ..\requirements.txt
|
||||||
# Run Pyinstaller
|
# Run Pyinstaller
|
||||||
# - pyinstaller --noconsole -y ..\src\pamhyr.py
|
# - pyinstaller --noconsole -y ..\src\pamhyr.py
|
||||||
- pyinstaller -i ../src/View/ui/ressources/icon.ico $HIDDEN_IMPORTS --hide-console hide-early -y ..\src\pamhyr.py
|
- pyinstaller -i ../src/View/ui/ressources/icon.ico --hide-console hide-early -y ..\src\pamhyr.py
|
||||||
# Create directory
|
# Create directory
|
||||||
- mkdir pamhyr
|
- mkdir pamhyr
|
||||||
- dir
|
- dir
|
||||||
@@ -619,40 +613,6 @@ pkg-check-clamav:
|
|||||||
# RELEASE #
|
# RELEASE #
|
||||||
###########
|
###########
|
||||||
|
|
||||||
shadow-release:
|
|
||||||
stage: release
|
|
||||||
tags:
|
|
||||||
- release
|
|
||||||
- linux
|
|
||||||
needs:
|
|
||||||
- job: linux-package-tar
|
|
||||||
artifacts: true
|
|
||||||
- job: windows-package-zip
|
|
||||||
artifacts: true
|
|
||||||
- job: windows-package-exe
|
|
||||||
artifacts: true
|
|
||||||
# - job: build-users-doc
|
|
||||||
# artifacts: true
|
|
||||||
- job: build-developers-doc
|
|
||||||
artifacts: true
|
|
||||||
- job: pkg-hash
|
|
||||||
artifacts: true
|
|
||||||
rules:
|
|
||||||
- if: $CI_COMMIT_BRANCH != 'master' && $CI_COMMIT_TAG
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- linux/pamhyr-gnulinux.tar.xz
|
|
||||||
- windows/pamhyr-windows.zip
|
|
||||||
- windows/pamhyr-windows.exe
|
|
||||||
- linux/pamhyr-gnulinux.tar.xz.sha256
|
|
||||||
- windows/pamhyr-windows.zip.sha256
|
|
||||||
- windows/pamhyr-windows.exe.sha256
|
|
||||||
- doc/dev/documentation.pdf
|
|
||||||
# - doc/users/documentation.pdf
|
|
||||||
script:
|
|
||||||
- cd packages
|
|
||||||
|
|
||||||
|
|
||||||
tag-release:
|
tag-release:
|
||||||
stage: release
|
stage: release
|
||||||
tags:
|
tags:
|
||||||
@@ -672,7 +632,7 @@ tag-release:
|
|||||||
- job: pkg-hash
|
- job: pkg-hash
|
||||||
artifacts: true
|
artifacts: true
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_BRANCH == 'master' && $CI_COMMIT_TAG
|
- if: $CI_COMMIT_TAG
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- linux/pamhyr-gnulinux.tar.xz
|
- linux/pamhyr-gnulinux.tar.xz
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
Sylvain COULIBALY, INRAE, 2020 - 2022
|
Sylvain COULIBALY, INRAE, 2020 - 2022
|
||||||
Théophile TERRAZ, INRAE, 2022 - 2024
|
Théophile TERRAZ, INRAE, 2022 - 2024
|
||||||
Pierre-Antoine ROUBY, INRAE, 2023 - 2025
|
Pierre-Antoine ROUBY, INRAE, 2023 - 2024
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
Pamhyr is a free and open source graphical user interface for 1D hydro-sedimentary
|
Pamhyr is a free and open source graphical user interface for 1D hydro-sedimentary
|
||||||
modelling of rivers.
|
modelling of rivers.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -18,12 +18,11 @@ modelling of rivers.
|
|||||||
+ Define river sedimentary layers
|
+ Define river sedimentary layers
|
||||||
+ Save the study in a unique file
|
+ Save the study in a unique file
|
||||||
+ Run Mage 8 on a study and visualize results
|
+ Run Mage 8 on a study and visualize results
|
||||||
+ Create and edit study alternative scenario tree
|
|
||||||
|
|
||||||
Let see the
|
Let see the
|
||||||
[documentation](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home)
|
[documentation](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home)
|
||||||
([:fr:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home-fr),
|
([:fr:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home-fr),
|
||||||
[:gb:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home-en))
|
[:gb:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home-en))
|
||||||
for more details.
|
for more details.
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
@@ -31,11 +30,11 @@ for more details.
|
|||||||
### GNU/Linux
|
### GNU/Linux
|
||||||
|
|
||||||
See documentation:
|
See documentation:
|
||||||
- [French :fr:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home/fr/Install%20on%20GNULinux)
|
- [French :fr:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home/fr/Install%20on%20GNULinux)
|
||||||
- [English :gb:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home/en/Install%20on%20GNULinux)
|
- [English :gb:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home/en/Install%20on%20GNULinux)
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
See documentation:
|
See documentation:
|
||||||
- [French :fr:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home/fr/Install%20on%20Windows)
|
- [French :fr:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home/fr/Install%20on%20Windows)
|
||||||
- [English :gb:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home/en/Install%20on%20Windows)
|
- [English :gb:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home/en/Install%20on%20Windows)
|
||||||
+15
-20
@@ -1,23 +1,18 @@
|
|||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
|
|
||||||
FILES="documentation.org scenario.org"
|
echo " PDF documentation.org"
|
||||||
|
emacs -nw --batch \
|
||||||
|
--eval "(require 'org)" \
|
||||||
|
--eval "(require 'ox-latex)" \
|
||||||
|
--eval "(load-file \"../tools/setup.el\")" \
|
||||||
|
--file ./documentation.org \
|
||||||
|
-f org-latex-export-to-pdf
|
||||||
|
|
||||||
for file in $FILES
|
echo " HTML documentation.org"
|
||||||
do
|
emacs -nw --batch \
|
||||||
echo " PDF $file"
|
--eval "(require 'org)" \
|
||||||
emacs -nw --batch \
|
--eval "(require 'ox-latex)" \
|
||||||
--eval "(require 'org)" \
|
--eval "(require 'ox-html)" \
|
||||||
--eval "(require 'ox-latex)" \
|
--eval "(load-file \"../tools/setup.el\")" \
|
||||||
--eval "(load-file \"../tools/setup.el\")" \
|
--file ./documentation.org \
|
||||||
--file ./$file \
|
-f org-html-export-to-html
|
||||||
-f org-latex-export-to-pdf \
|
|
||||||
|
|
||||||
echo " HTML $file"
|
|
||||||
emacs -nw --batch \
|
|
||||||
--eval "(require 'org)" \
|
|
||||||
--eval "(require 'ox-latex)" \
|
|
||||||
--eval "(require 'ox-html)" \
|
|
||||||
--eval "(load-file \"../tools/setup.el\")" \
|
|
||||||
--file ./$file \
|
|
||||||
-f org-html-export-to-html
|
|
||||||
done
|
|
||||||
@@ -1,661 +0,0 @@
|
|||||||
# senario.org -- Pamhyr developers documentation
|
|
||||||
# Copyright (C) 2023-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 <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
#+STARTUP: indent show2levels
|
|
||||||
|
|
||||||
#+INCLUDE: ../tools/macro.org
|
|
||||||
#+INCLUDE: ../tools/latex.org
|
|
||||||
|
|
||||||
#+TITLE: Scenario implementation in Pamhyr2: \textbf{Preparatory work}
|
|
||||||
#+SUBTITLE: Version: {{{version}}}
|
|
||||||
#+AUTHOR: {{{INRAE}}}
|
|
||||||
|
|
||||||
#+OPTIONS: toc:t
|
|
||||||
#+LANGUAGE: UKenglish
|
|
||||||
|
|
||||||
#+latex: \newpage
|
|
||||||
|
|
||||||
* COMMENT tools
|
|
||||||
|
|
||||||
#+begin_src emacs-lisp :export none
|
|
||||||
(add-to-list 'org-structure-template-alist
|
|
||||||
'("p" "#+begin_src python :python python3 :results output\n\n#+end_src"))
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
#+RESULTS:
|
|
||||||
#+begin_example
|
|
||||||
((p #+begin_src python :python python3 :results output
|
|
||||||
|
|
||||||
,#+end_src) (d . #+name: graph-XXX
|
|
||||||
,#+header: :results drawer
|
|
||||||
,#+header: :exports results
|
|
||||||
,#+header: :post attr_wrap(width="12cm", data=*this*, name="graph-XXX", caption="Graph XXX", float="t")
|
|
||||||
,#+begin_src dot :file "images/graph-XXX.png" :cache no
|
|
||||||
digraph {
|
|
||||||
bgcolor="transparent";
|
|
||||||
node[colorscheme=set19,shape=box,style="filled",fillcolor=9];
|
|
||||||
}
|
|
||||||
,#+end_src) (p . src python :python python3 :results output :noweb yes
|
|
||||||
|
|
||||||
src) (t . EXPORT latex
|
|
||||||
\begin{table}
|
|
||||||
\centering
|
|
||||||
\resizebox{0.6\linewidth}{!}{
|
|
||||||
|
|
||||||
}
|
|
||||||
%\vspace{0.5pt}
|
|
||||||
%\caption{Number of tests by errors types}
|
|
||||||
\end{table}
|
|
||||||
,#+E) (B . src shell :session *shell* :results output :exports both) (a . export ascii) (c . center) (C . comment) (e . example) (E . export) (h . export html) (l . export latex) (q . quote) (s . src) (v . verse))
|
|
||||||
#+end_example
|
|
||||||
|
|
||||||
#+name: dbg-execute
|
|
||||||
#+begin_src python :python python3 :results output :noweb yes
|
|
||||||
def execute(db, query):
|
|
||||||
print(query)
|
|
||||||
return db.execute(query)
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
#+RESULTS: dbg-execute
|
|
||||||
|
|
||||||
#+name: execute
|
|
||||||
#+begin_src python :python python3 :results output :noweb yes
|
|
||||||
def execute(db, query):
|
|
||||||
return db.execute(query)
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
#+RESULTS: execute
|
|
||||||
|
|
||||||
* Principe
|
|
||||||
|
|
||||||
** Scenario
|
|
||||||
|
|
||||||
A scenario is defined by a name, a description and a parent
|
|
||||||
scenario. If a scenario as no parent, parent value is =None=. So, in
|
|
||||||
Pamhyr2 the list of scenario must by a tree with a root defined by a
|
|
||||||
default scenarios with no parent.
|
|
||||||
|
|
||||||
#+begin_src sql
|
|
||||||
CREATE TABLE scenario(
|
|
||||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
||||||
name TEXT NOT NULL, -- Name of the scenario
|
|
||||||
description TEXT NOT NULL, -- Rich text description
|
|
||||||
parent_id INTEGER REFERENCES scenario(id) -- Recursive references
|
|
||||||
-- for parent scenario
|
|
||||||
)
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
** Data
|
|
||||||
|
|
||||||
Each data is associated with one and only one scenario. This senario
|
|
||||||
is defined by the =id=. So, each data structure who can saved in DB
|
|
||||||
must add the following lines in table définition:
|
|
||||||
|
|
||||||
#+begin_src sql
|
|
||||||
scenario_id INTEGER NOT NULL,
|
|
||||||
FOREIGN KEY(scenario_id) REFERENCES scenario(id)
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
To avoid, at least, memory usage exploitation, by default, a scenario
|
|
||||||
must saved only the modified data from its parent. This contrains
|
|
||||||
require to wrap any data modification to update the data scenario id
|
|
||||||
to current scenario id in source code. In addition, we need to modify
|
|
||||||
the data loading méthode to take in considération this recursive
|
|
||||||
dependencies.
|
|
||||||
|
|
||||||
#+CAPTION: Recursive loading method
|
|
||||||
#+begin_src python :python python3 :results output :noweb yes
|
|
||||||
def rec_load(cls, db, current_scenario):
|
|
||||||
# Default case, end of recursion
|
|
||||||
if current_scenario is None:
|
|
||||||
return [] # Default value
|
|
||||||
|
|
||||||
table = db.execute(f"<MY SELECT> WHERE scenario_id = {current_scenario}")
|
|
||||||
|
|
||||||
# If no data for this scenario, recursion
|
|
||||||
if len(table) == 0:
|
|
||||||
parent_id = db.get_parent_id(current_scenario)
|
|
||||||
return cls.rec_load(db, parent_id)
|
|
||||||
|
|
||||||
# Otherelse, parse data and return...
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
With this method, modify a data in parent scenario, modify this data
|
|
||||||
in child scenario too. Unless is value has been previously modified in
|
|
||||||
the child scenario. Deny tree node modification, except for leaf.
|
|
||||||
|
|
||||||
This method work, but it dont allow to delete data un child scenarios,
|
|
||||||
because if we dont found data for this scenario we search for parent
|
|
||||||
scenario. So, we need a condition to stop recursion in case of data
|
|
||||||
deletion in child scenario. To stop the recurtion, we can use a dummy
|
|
||||||
data flag. By default, this flags is =false=, but if this flags is
|
|
||||||
=true= the data is ignored at loading.
|
|
||||||
|
|
||||||
So, we can use this flags for each data structure who can saved in DB:
|
|
||||||
|
|
||||||
#+begin_src sql
|
|
||||||
is_dummy BOOLEAN, -- True if this data as been deleted
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
Modify the loading method:
|
|
||||||
|
|
||||||
#+CAPTION: Recursive loading method with possible dummy data
|
|
||||||
#+begin_src python :python python3 :results output :noweb yes
|
|
||||||
def rec_load_2(cls, db, current_scenario):
|
|
||||||
# Default case, end of recursion
|
|
||||||
if current_scenario is None:
|
|
||||||
return [] # Default value
|
|
||||||
|
|
||||||
table = db.execute(f"<MY SELECT> WHERE scenario_id = {current_scenario}")
|
|
||||||
|
|
||||||
# If no data for this scenario, recursion
|
|
||||||
if len(table) == 0:
|
|
||||||
parent_id = db.get_parent_id(current_scenario)
|
|
||||||
return cls.rec_load(db, parent_id)
|
|
||||||
|
|
||||||
for data in table:
|
|
||||||
if is_dummy(data):
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Otherelse, parse data ...
|
|
||||||
|
|
||||||
# return ...
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
#+RESULTS:
|
|
||||||
|
|
||||||
And add a data reduction method:
|
|
||||||
|
|
||||||
#+caption: Data reduction method for data subtree
|
|
||||||
#+begin_src python :python python3 :results output :noweb yes
|
|
||||||
def reduce_change(self):
|
|
||||||
# Get the greater scenario
|
|
||||||
scenar = reduce(
|
|
||||||
lambda acc, x: max(acc, x._scenario),
|
|
||||||
self._values,
|
|
||||||
self._scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
# Apply change on this subtree
|
|
||||||
self._scenario = scenar
|
|
||||||
|
|
||||||
if is_dummy(self): # There are no more subtree
|
|
||||||
self.values = []
|
|
||||||
else: # Update data subtree scenario id
|
|
||||||
for value in self._values:
|
|
||||||
value.set_scenario(self._scenario)
|
|
||||||
# Dummy data is no more necessary in this subtree
|
|
||||||
self._values = list(
|
|
||||||
filter(
|
|
||||||
lambda v: not is_dummy(v),
|
|
||||||
self._values
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Keep data id unique for all subtree
|
|
||||||
self.get_new_db_id()
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
#+RESULTS:
|
|
||||||
|
|
||||||
#+latex: \newpage
|
|
||||||
|
|
||||||
* Prototype
|
|
||||||
|
|
||||||
** Scenario
|
|
||||||
|
|
||||||
#+name: scenario
|
|
||||||
#+begin_src python :python python3 :results output :noweb yes
|
|
||||||
class Scenario():
|
|
||||||
def __init__(self, id, name, parent):
|
|
||||||
self._id = id
|
|
||||||
self._name = name
|
|
||||||
self._parent = parent
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create_db(cls, db):
|
|
||||||
execute(
|
|
||||||
db, """
|
|
||||||
CREATE TABLE scenario(
|
|
||||||
id INTEGER PRIMARY KEY,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
parent_id INTEGER REFERENCES scenario(id)
|
|
||||||
)
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def load_db(cls, db):
|
|
||||||
cur = execute(
|
|
||||||
db, f"SELECT * FROM scenario"
|
|
||||||
)
|
|
||||||
table = cur.fetchall()
|
|
||||||
|
|
||||||
new = []
|
|
||||||
for values in table:
|
|
||||||
new.append(
|
|
||||||
cls(
|
|
||||||
values[0], values[1], values[2],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return new
|
|
||||||
|
|
||||||
def save(self, db):
|
|
||||||
sid = 'NULL' if self._parent is None else self._parent
|
|
||||||
|
|
||||||
execute(
|
|
||||||
db,
|
|
||||||
"INSERT INTO scenario (id, name, parent_id) " +
|
|
||||||
f"VALUES ({self._id}, '{self._name}', {sid})"
|
|
||||||
)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return f"Scenario {{ {self._id}, {self._name}, parent {self._parent} }}"
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self._name}({self._id})"
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
#+RESULTS: scenario
|
|
||||||
|
|
||||||
** Data
|
|
||||||
|
|
||||||
#+name: data-b
|
|
||||||
#+begin_src python :python python3 :results output :noweb yes
|
|
||||||
class B():
|
|
||||||
def __init__(self, value, scenario):
|
|
||||||
self._value = value
|
|
||||||
self._scenario = scenario
|
|
||||||
self._dummy = False
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create_db(cls, db):
|
|
||||||
execute(db, """
|
|
||||||
CREATE TABLE b(
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
dummy_data BOOLEAN,
|
|
||||||
x INTEGER,
|
|
||||||
a_id INTEGER NOT NULL,
|
|
||||||
scenario_id INTEGER NOT NULL,
|
|
||||||
FOREIGN KEY(a_id) REFERENCES a(id),
|
|
||||||
FOREIGN KEY(scenario_id) REFERENCES scenario(id)
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
|
|
||||||
def save(self, db, a_id):
|
|
||||||
execute(
|
|
||||||
db,
|
|
||||||
"INSERT INTO b (x, dummy_data, a_id, scenario_id) " +
|
|
||||||
f"VALUES ({self._value}, {self._dummy}, {a_id}, {self._scenario})"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def load_db(cls, db, a_id, senar):
|
|
||||||
if senar is None:
|
|
||||||
return []
|
|
||||||
|
|
||||||
cur = execute(
|
|
||||||
db,
|
|
||||||
"SELECT x, scenario_id, dummy_data FROM b " +
|
|
||||||
f"WHERE scenario_id = {senar} AND a_id = {a_id}"
|
|
||||||
)
|
|
||||||
table = cur.fetchall()
|
|
||||||
|
|
||||||
if len(table) == 0:
|
|
||||||
parent = execute(
|
|
||||||
db,
|
|
||||||
f"SELECT parent_id FROM scenario WHERE id = {senar}"
|
|
||||||
).fetchone()
|
|
||||||
|
|
||||||
return cls.load_db(db, parent[0])
|
|
||||||
|
|
||||||
new = []
|
|
||||||
for values in table:
|
|
||||||
if values[2]: # Is dummy
|
|
||||||
continue
|
|
||||||
|
|
||||||
new.append(cls(values[0], values[1]))
|
|
||||||
|
|
||||||
return new
|
|
||||||
|
|
||||||
def set_value(self, value, scenario):
|
|
||||||
self._value = value
|
|
||||||
self._scenario = scenario
|
|
||||||
|
|
||||||
def set_dummy(self, scenario):
|
|
||||||
self._scenario = scenario
|
|
||||||
self._dummy = True
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
if self._dummy:
|
|
||||||
return ""
|
|
||||||
return f"{self._value}"
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
if self._dummy:
|
|
||||||
return ""
|
|
||||||
return f"{self._value}"
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
#+RESULTS: data-b
|
|
||||||
|
|
||||||
#+name: data-a
|
|
||||||
#+begin_src python :python python3 :results output :noweb yes
|
|
||||||
class A():
|
|
||||||
def __init__(self, id, values, scenario):
|
|
||||||
self._id = id
|
|
||||||
self._values = values
|
|
||||||
self._scenario = scenario
|
|
||||||
self._dummy = False
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create_db(cls, db):
|
|
||||||
execute(db, """
|
|
||||||
CREATE TABLE a(
|
|
||||||
id INTEGER PRIMARY KEY,
|
|
||||||
dummy_data BOOLEAN,
|
|
||||||
scenario_id INTEGER,
|
|
||||||
FOREIGN KEY(scenario_id) REFERENCES scenario(id)
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
|
|
||||||
def save(self, db):
|
|
||||||
self.reduce_change()
|
|
||||||
|
|
||||||
execute(db, f"DELETE FROM a WHERE scenario_id = {self._scenario}")
|
|
||||||
execute(
|
|
||||||
db,
|
|
||||||
"INSERT INTO a (id, dummy_data, scenario_id) " +
|
|
||||||
f"VALUES ({self._id}, {self._dummy}, {self._scenario})"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(
|
|
||||||
db,
|
|
||||||
"DELETE FROM b " +
|
|
||||||
f"WHERE scenario_id = {self._scenario} " +
|
|
||||||
f"AND a_id = {self._id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
for value in self._values:
|
|
||||||
value.save(db, self._id)
|
|
||||||
|
|
||||||
def reduce_change(self):
|
|
||||||
diff_scenar = reduce(
|
|
||||||
lambda acc, x: max(acc, x._scenario),
|
|
||||||
self._values,
|
|
||||||
self._scenario
|
|
||||||
)
|
|
||||||
print(f" ~> Reduce: {self._scenario} to {diff_scenar}")
|
|
||||||
self._scenario = diff_scenar
|
|
||||||
|
|
||||||
# Reduce new scenario value on each value
|
|
||||||
for value in self._values:
|
|
||||||
value._scenario = self._scenario
|
|
||||||
|
|
||||||
# Delete useless dummy data
|
|
||||||
self._values = list(
|
|
||||||
filter(
|
|
||||||
lambda v: not v._dummy,
|
|
||||||
self._values
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# HACK: keep id unique
|
|
||||||
self._id = self._id * 100 + diff_scenar
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def load_db(cls, db, senar):
|
|
||||||
if senar is None:
|
|
||||||
return []
|
|
||||||
|
|
||||||
cur = execute(
|
|
||||||
db,
|
|
||||||
f"SELECT id, scenario_id, dummy_data FROM a WHERE scenario_id = {senar}"
|
|
||||||
)
|
|
||||||
table = cur.fetchall()
|
|
||||||
|
|
||||||
if len(table) == 0:
|
|
||||||
parent = execute(
|
|
||||||
db,
|
|
||||||
f"SELECT parent_id FROM scenario WHERE id = {senar}"
|
|
||||||
).fetchone()
|
|
||||||
|
|
||||||
return cls.load_db(db, parent[0])
|
|
||||||
|
|
||||||
new = []
|
|
||||||
for values in table:
|
|
||||||
if values[2]:
|
|
||||||
continue
|
|
||||||
|
|
||||||
bb = B.load_db(db, values[0], senar)
|
|
||||||
new.append(cls(values[0], bb, values[1]))
|
|
||||||
|
|
||||||
return new
|
|
||||||
|
|
||||||
def set_dummy(self, scenario):
|
|
||||||
self._scenario = scenario
|
|
||||||
self._dummy = True
|
|
||||||
|
|
||||||
def add(self, b):
|
|
||||||
self._values.append(b)
|
|
||||||
|
|
||||||
def delete(self, index, scenario):
|
|
||||||
self._values[index].set_dummy(scenario)
|
|
||||||
self._scenario = scenario
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
if self._dummy:
|
|
||||||
return "{ }"
|
|
||||||
return f"{{ {self._values}, ({self._scenario}) }}"
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
if self._dummy:
|
|
||||||
return "{ }"
|
|
||||||
return f"{{ {list(map(str, self._values))}, ({self._scenario}) }}"
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
#+RESULTS: data-a
|
|
||||||
|
|
||||||
** Full prototype
|
|
||||||
|
|
||||||
#+begin_src python :python python3 :results output :exports both :noweb yes :cache yes
|
|
||||||
from functools import reduce
|
|
||||||
|
|
||||||
def execute(db, query):
|
|
||||||
# print(query)
|
|
||||||
return db.execute(query)
|
|
||||||
|
|
||||||
def get_tree(db):
|
|
||||||
from treelib import Node, Tree
|
|
||||||
|
|
||||||
tree = Tree()
|
|
||||||
|
|
||||||
for senar in db.execute("SELECT * FROM scenario").fetchall():
|
|
||||||
if senar[1] is None:
|
|
||||||
tree.create_node(
|
|
||||||
str(Scenario(senar[0], senar[1], senar[2])) +
|
|
||||||
" : " + str(A.load_db(db, senar[0])[0]),
|
|
||||||
senar[0]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
tree.create_node(
|
|
||||||
str(Scenario(senar[0], senar[1], senar[2])) +
|
|
||||||
" : " + str(A.load_db(db, senar[0])[0]),
|
|
||||||
senar[0],
|
|
||||||
parent=senar[2]
|
|
||||||
)
|
|
||||||
|
|
||||||
return tree
|
|
||||||
|
|
||||||
### --- Scenario ---
|
|
||||||
<<scenario>>
|
|
||||||
|
|
||||||
### --- class B ---
|
|
||||||
<<data-b>>
|
|
||||||
|
|
||||||
### --- class A ---
|
|
||||||
<<data-a>>
|
|
||||||
|
|
||||||
### --- Script ---
|
|
||||||
import sqlite3
|
|
||||||
import pprint
|
|
||||||
|
|
||||||
pp = pprint.PrettyPrinter(width=79, compact=True, depth=6)
|
|
||||||
|
|
||||||
with sqlite3.connect(":memory:") as db:
|
|
||||||
cur = db.cursor()
|
|
||||||
|
|
||||||
Scenario.create_db(cur)
|
|
||||||
A.create_db(cur)
|
|
||||||
B.create_db(cur)
|
|
||||||
|
|
||||||
print("--- Default scenario (s0) ---")
|
|
||||||
|
|
||||||
# One scenario
|
|
||||||
|
|
||||||
s0 = Scenario(0, "s0", None)
|
|
||||||
a0 = A(
|
|
||||||
1,
|
|
||||||
[
|
|
||||||
B(0, s0._id),
|
|
||||||
B(1, s0._id),
|
|
||||||
B(2, s0._id),
|
|
||||||
],
|
|
||||||
s0._id
|
|
||||||
)
|
|
||||||
|
|
||||||
s0.save(cur)
|
|
||||||
a0.save(cur)
|
|
||||||
|
|
||||||
print("--- New scenario (s1) -- copy without modification ---")
|
|
||||||
|
|
||||||
# New scenario
|
|
||||||
|
|
||||||
s1 = Scenario(1, "s1", s0._id)
|
|
||||||
s1.save(cur)
|
|
||||||
|
|
||||||
print("--- New scenario (s2) -- data modification ---")
|
|
||||||
|
|
||||||
# New scenario 2 with data
|
|
||||||
|
|
||||||
s2 = Scenario(2, "s2", s0._id)
|
|
||||||
s2.save(cur)
|
|
||||||
|
|
||||||
a2 = a0
|
|
||||||
bb = list(map(
|
|
||||||
lambda b: b.set_value(b._value + 1, s2._id),
|
|
||||||
a2._values
|
|
||||||
))
|
|
||||||
|
|
||||||
a2.save(cur)
|
|
||||||
|
|
||||||
print("--- New scenario (s3) -- delete data ---")
|
|
||||||
|
|
||||||
# New scenario 3 with data
|
|
||||||
|
|
||||||
s3 = Scenario(3, "s3", s0._id)
|
|
||||||
s3.save(cur)
|
|
||||||
|
|
||||||
a3 = A.load_db(cur, s3._id)[0]
|
|
||||||
a3._values[0].set_dummy(s3._id)
|
|
||||||
a3.save(cur)
|
|
||||||
|
|
||||||
print("--- New scenario (s4) -- new data ---")
|
|
||||||
|
|
||||||
s4 = Scenario(4, "s4", s3._id)
|
|
||||||
s4.save(cur)
|
|
||||||
|
|
||||||
a4 = A.load_db(cur, s3._id)[0]
|
|
||||||
a4.add(B(666, s4._id))
|
|
||||||
a4.save(cur)
|
|
||||||
|
|
||||||
print("--- Scenario tree ---")
|
|
||||||
|
|
||||||
tree = get_tree(db)
|
|
||||||
print(tree)
|
|
||||||
|
|
||||||
print("--- Modify s0 data ---")
|
|
||||||
|
|
||||||
aa = A.load_db(db, s0._id)[0]
|
|
||||||
bb = list(map(
|
|
||||||
lambda b: b.set_value(b._value * 100 - 1, s0._id),
|
|
||||||
aa._values
|
|
||||||
))
|
|
||||||
aa.save(db)
|
|
||||||
|
|
||||||
print("--- Scenario tree ---")
|
|
||||||
|
|
||||||
tree = get_tree(db)
|
|
||||||
print(tree)
|
|
||||||
|
|
||||||
print("--- DB ---")
|
|
||||||
|
|
||||||
ss = db.execute("SELECT * FROM scenario").fetchall()
|
|
||||||
print(f"+ scenario ({len(ss)}):")
|
|
||||||
pp.pprint(ss)
|
|
||||||
|
|
||||||
aa = db.execute("SELECT * FROM a").fetchall()
|
|
||||||
print(f"+ a ({len(aa)}):")
|
|
||||||
pp.pprint(aa)
|
|
||||||
|
|
||||||
bb = db.execute("SELECT * FROM b").fetchall()
|
|
||||||
print(f"+ b ({len(bb)}):")
|
|
||||||
pp.pprint(bb)
|
|
||||||
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
#+RESULTS[96c84f23be557425ae2822ef7f174175ef69f9b9]:
|
|
||||||
#+begin_example
|
|
||||||
--- Default scenario (s0) ---
|
|
||||||
~> Reduce: 0 to 0
|
|
||||||
--- New scenario (s1) -- copy without modification ---
|
|
||||||
--- New scenario (s2) -- data modification ---
|
|
||||||
~> Reduce: 0 to 2
|
|
||||||
--- New scenario (s3) -- delete data ---
|
|
||||||
~> Reduce: 0 to 3
|
|
||||||
--- New scenario (s4) -- new data ---
|
|
||||||
~> Reduce: 3 to 4
|
|
||||||
--- Scenario tree ---
|
|
||||||
s0(0) : { ['0', '1', '2'], (0) }
|
|
||||||
├── s1(1) : { ['0', '1', '2'], (0) }
|
|
||||||
├── s2(2) : { ['1', '2', '3'], (2) }
|
|
||||||
└── s3(3) : { ['1', '2'], (3) }
|
|
||||||
└── s4(4) : { ['1', '2', '666'], (4) }
|
|
||||||
|
|
||||||
--- Modify s0 data ---
|
|
||||||
~> Reduce: 0 to 0
|
|
||||||
--- Scenario tree ---
|
|
||||||
s0(0) : { ['-1', '99', '199'], (0) }
|
|
||||||
├── s1(1) : { ['-1', '99', '199'], (0) }
|
|
||||||
├── s2(2) : { ['1', '2', '3'], (2) }
|
|
||||||
└── s3(3) : { ['1', '2'], (3) }
|
|
||||||
└── s4(4) : { ['1', '2', '666'], (4) }
|
|
||||||
|
|
||||||
--- DB ---
|
|
||||||
+ scenario (5):
|
|
||||||
[(0, 's0', None), (1, 's1', 0), (2, 's2', 0), (3, 's3', 0), (4, 's4', 3)]
|
|
||||||
+ a (4):
|
|
||||||
[(10000, 0, 0), (10002, 0, 2), (10003, 0, 3), (1000304, 0, 4)]
|
|
||||||
+ b (14):
|
|
||||||
[(1, 0, 0, 100, 0), (2, 0, 1, 100, 0), (3, 0, 2, 100, 0), (4, 0, 1, 10002, 2),
|
|
||||||
(5, 0, 2, 10002, 2), (6, 0, 3, 10002, 2), (7, 0, 1, 10003, 3),
|
|
||||||
(8, 0, 2, 10003, 3), (9, 0, 1, 1000304, 4), (10, 0, 2, 1000304, 4),
|
|
||||||
(11, 0, 666, 1000304, 4), (12, 0, -1, 10000, 0), (13, 0, 99, 10000, 0),
|
|
||||||
(14, 0, 199, 10000, 0)]
|
|
||||||
#+end_example
|
|
||||||
Whitespace-only changes.
@@ -1,525 +0,0 @@
|
|||||||
# presentation.org -- Pamhyr
|
|
||||||
# Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
#+STARTUP: indent hideblocks content beamer
|
|
||||||
|
|
||||||
#+TITLE: \textbf{Pamhyr2}
|
|
||||||
#+SUBTITLE: Scenarios managements
|
|
||||||
|
|
||||||
# #+AUTHOR: Pierre-Antoine Rouby
|
|
||||||
#+LATEX_HEADER: \author[Pierre-Antoine Rouby]{\textbf{Pierre-Antoine Rouby}}
|
|
||||||
#+EMAIL: pierre-antoine.rouby@inrae.fr
|
|
||||||
#+INSTITUTE: INRAE Lyon-Villerbanne, teams River Hydraulics
|
|
||||||
#+LATEX_HEADER: \institute{INRAE Lyon-Villerbanne, River Hydraulics team}
|
|
||||||
#+DATE: 9th November
|
|
||||||
|
|
||||||
#+KEYWORDS: g ui, hydraulics, sedimentary, hydro-sedimentary, 1D modelling, freesoftware, foss
|
|
||||||
|
|
||||||
#+OPTIONS: H:2 toc:t num:t author:nil
|
|
||||||
#+LATEX_CLASS: beamer
|
|
||||||
#+LATEX_CLASS_OPTIONS: [slideopt,A4,showboxes,svgnames,aspectratio=169]
|
|
||||||
# #+LATEX_CLASS_OPTIONS: [presentation]
|
|
||||||
#+BEAMER_THEME: default
|
|
||||||
#+COLUMNS: %45ITEM %10BEAMER_ENV(Env) %10BEAMER_ACT(Act) %4BEAMER_COL(Col)
|
|
||||||
|
|
||||||
#+LANGUAGE: fr
|
|
||||||
#+LATEX_HEADER: \usepackage[frenchb]{babel}
|
|
||||||
|
|
||||||
#+BEGIN_SRC emacs-lisp :results silent :exports none
|
|
||||||
(setq org-latex-pdf-process (list
|
|
||||||
"latexmk -pdflatex='lualatex -shell-escape -interaction nonstopmode' -pdf -f %f"))
|
|
||||||
|
|
||||||
(add-to-list 'org-latex-packages-alist '("" "minted"))
|
|
||||||
(setq org-latex-listings 'minted)
|
|
||||||
(setq org-src-fontify-natively t)
|
|
||||||
|
|
||||||
(org-add-link-type
|
|
||||||
"cite" 'ebib
|
|
||||||
(lambda (path desc format)
|
|
||||||
(cond
|
|
||||||
((eq format 'html)
|
|
||||||
(format "(<cite>%s</cite>)" path))
|
|
||||||
((eq format 'text)
|
|
||||||
(format "[%s]" path))
|
|
||||||
((eq format 'latex)
|
|
||||||
(if (or (not desc) (equal 0 (search "cite:" desc)))
|
|
||||||
(format "\\cite{%s}" path)
|
|
||||||
(format "\\cite[%s][%s]{%s}"
|
|
||||||
(cadr (split-string desc ";"))
|
|
||||||
(car (split-string desc ";")) path))))))
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
# Wrapper
|
|
||||||
#+NAME: attr_wrap
|
|
||||||
#+HEADER: :var width="\\textwidth"
|
|
||||||
#+HEADER: :var caption=""
|
|
||||||
#+HEADER: :var smallcaption=""
|
|
||||||
#+HEADER: :var name=""
|
|
||||||
#+HEADER: :var data=""
|
|
||||||
#+HEADER: :var float="nil"
|
|
||||||
#+BEGIN_SRC sh :results output :exports none
|
|
||||||
echo "#+CAPTION[$smallcaption]: $caption"
|
|
||||||
echo "#+NAME: $name"
|
|
||||||
echo "#+ATTR_LATEX: :width $width :float $float"
|
|
||||||
echo "$data"
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
#+LATEX_HEADER: \setbeamertemplate{footline}[frame number]
|
|
||||||
#+LATEX_HEADER: \setbeamertemplate{headline}{}
|
|
||||||
#+LATEX_HEADER: \usepackage{multirow}
|
|
||||||
#+LATEX_HEADER: \usepackage{fontawesome5}
|
|
||||||
#+LATEX_HEADER: \usepackage{tcolorbox}
|
|
||||||
#+LATEX_HEADER: \usepackage{tikz}
|
|
||||||
|
|
||||||
#+LATEX_HEADER: \logo{\includegraphics[width=.1\textwidth]{../../../src/View/ui/ressources/Logo-INRAE.png}}
|
|
||||||
|
|
||||||
#+LATEX_HEADER: \newcommand{\R}[1]{\rotatebox[origin=c]{90}{#1}}
|
|
||||||
|
|
||||||
# Beamer colors
|
|
||||||
#+LATEX_HEADER: \setbeamersize{text margin left=0.5cm,text margin right=0.5cm}
|
|
||||||
#+LATEX_HEADER: \setbeamerfont{alerted text}{series=\bfseries}
|
|
||||||
#+LATEX_HEADER: \setbeamerfont{example text}{series=\bfseries}
|
|
||||||
|
|
||||||
#+LATEX_HEADER: \definecolor{inrae_1}{RGB}{102,193,191}
|
|
||||||
#+LATEX_HEADER: \definecolor{inrae_2}{RGB}{0,150,152}
|
|
||||||
#+LATEX_HEADER: \definecolor{inrae_3}{RGB}{39,86,98}
|
|
||||||
|
|
||||||
#+LATEX_HEADER: \setbeamercolor{alerted text}{fg=inrae_1}
|
|
||||||
#+LATEX_HEADER: \setbeamercolor{structure}{fg=inrae_2}
|
|
||||||
#+LATEX_HEADER: \setbeamercolor{normal text}{fg=inrae_3}
|
|
||||||
#+LATEX_HEADER: \setbeamercolor*{author}{fg=inrae_3}
|
|
||||||
|
|
||||||
#+LATEX_HEADER: \usepackage[absolute,showboxes,overlay]{textpos}
|
|
||||||
|
|
||||||
#+LATEX_HEADER: \TPshowboxesfalse
|
|
||||||
#+LATEX_HEADER: \textblockorigin{5mm}{0mm}
|
|
||||||
|
|
||||||
# Biblio
|
|
||||||
#+LATEX_HEADER: \usepackage{natbib}
|
|
||||||
# #+LATEX_HEADER: \bibliographystyle{apalike}
|
|
||||||
#+LATEX_HEADER: \setbeamertemplate{bibliography item}{}
|
|
||||||
#+LATEX_HEADER: \renewcommand\bibfont{\scriptsize}
|
|
||||||
#+LATEX_HEADER: \setbeamertemplate{frametitle continuation}[from second]
|
|
||||||
#+LATEX_HEADER: \setbeamercolor*{bibliography entry title}{fg=inrae_3}
|
|
||||||
#+LATEX_HEADER: \setbeamercolor*{bibliography entry author}{fg=inrae_3}
|
|
||||||
#+LATEX_HEADER: \setbeamercolor*{bibliography entry location}{fg=inrae_3}
|
|
||||||
|
|
||||||
#+LATEX_HEADER: \usepackage{bbding}
|
|
||||||
#+LATEX_HEADER: \usepackage{pdfpages}
|
|
||||||
|
|
||||||
# Renew title page
|
|
||||||
#+LATEX_HEADER: \defbeamertemplate*{title page}{customized}[1][]
|
|
||||||
#+LATEX_HEADER: {
|
|
||||||
# #+LATEX_HEADER: {
|
|
||||||
# #+LATEX_HEADER: \usebeamerfont{date}\usebeamercolor[fg]{date}
|
|
||||||
# #+LATEX_HEADER: \tikz[overlay,remember picture]
|
|
||||||
# #+LATEX_HEADER: \node[xshift=-1cm,yshift=-1cm,text width=10cm] at (current page.north east) {
|
|
||||||
# #+LATEX_HEADER: 7$^{th}$ International Conference\\
|
|
||||||
# #+LATEX_HEADER: 8TH-10TH November\\
|
|
||||||
# #+LATEX_HEADER: EDF Lab - CHATOU, FRANCE
|
|
||||||
# #+LATEX_HEADER: }
|
|
||||||
# #+LATEX_HEADER: }
|
|
||||||
#+LATEX_HEADER: {
|
|
||||||
#+LATEX_HEADER: \usebeamerfont{title}\inserttitle\par
|
|
||||||
#+LATEX_HEADER: \usebeamerfont{title}\usebeamercolor[fg]{title}
|
|
||||||
#+LATEX_HEADER: }
|
|
||||||
#+LATEX_HEADER: \bigskip
|
|
||||||
#+LATEX_HEADER: \begin{center}
|
|
||||||
#+LATEX_HEADER: \usebeamerfont{subtitle}\usebeamercolor[fg]{subtitle}
|
|
||||||
#+LATEX_HEADER: {
|
|
||||||
#+LATEX_HEADER: \LARGE\insertsubtitle\par
|
|
||||||
#+LATEX_HEADER: }
|
|
||||||
#+LATEX_HEADER: \bigskip
|
|
||||||
#+LATEX_HEADER: \usebeamerfont{author}\usebeamercolor[fg]{author}\insertauthor\par
|
|
||||||
#+LATEX_HEADER: \bigskip
|
|
||||||
#+LATEX_HEADER: \usebeamerfont{institute}\usebeamercolor[fg]{institute}\insertinstitute\par
|
|
||||||
#+LATEX_HEADER: \bigskip
|
|
||||||
#+LATEX_HEADER: \usebeamerfont{date}\usebeamercolor[fg]{date}\insertdate\par
|
|
||||||
#+LATEX_HEADER: \usebeamercolor[fg]{titlegraphic}\inserttitlegraphic
|
|
||||||
#+LATEX_HEADER: \end{center}
|
|
||||||
#+LATEX_HEADER: }
|
|
||||||
|
|
||||||
# #+LATEX_HEADER: \newcommand{\tred}[1]{\textcolor{rouge_inrae}{#1}}
|
|
||||||
|
|
||||||
#+TODO: TODO WIP TOREVIEW | DONE
|
|
||||||
|
|
||||||
#+MACRO: pamhyr \textsc{PAMHyR}
|
|
||||||
#+MACRO: pamhyr2 \textsc{Pamhyr2}
|
|
||||||
|
|
||||||
#+MACRO: pause \pause
|
|
||||||
#+MACRO: framebreak \framebreak
|
|
||||||
#+MACRO: no
|
|
||||||
# #+MACRO: OK \checkmark
|
|
||||||
#+MACRO: OK \faIcon{check}
|
|
||||||
#+MACRO: bf \textbf{$1}
|
|
||||||
|
|
||||||
# #+latex: \frame{\tocpage}
|
|
||||||
|
|
||||||
* Problématique
|
|
||||||
|
|
||||||
** Problématique
|
|
||||||
|
|
||||||
*** Utilisateur
|
|
||||||
|
|
||||||
- Définire une version modifier de l'étude (changement dans les
|
|
||||||
données)
|
|
||||||
- Comparé des résultats entre une variante et l'étude
|
|
||||||
|
|
||||||
{{{pause}}}
|
|
||||||
|
|
||||||
**** COMMENT Développeur
|
|
||||||
:PROPERTIES:
|
|
||||||
:BEAMER_COL: 0.5
|
|
||||||
:BEAMER_ENV: block
|
|
||||||
:END:
|
|
||||||
|
|
||||||
- Un unique fichier
|
|
||||||
- Limité la duplication de donnée à sauvegarder
|
|
||||||
- Garder une bonne cohérence entre données et résultats
|
|
||||||
|
|
||||||
{{{pause}}}
|
|
||||||
|
|
||||||
*** Scénarios
|
|
||||||
:PROPERTIES:
|
|
||||||
:BEAMER_COL: 1
|
|
||||||
:BEAMER_ENV: definition
|
|
||||||
:END:
|
|
||||||
|
|
||||||
Un scénario est une variate d'une étude.
|
|
||||||
|
|
||||||
** Problématique -- Avancer
|
|
||||||
|
|
||||||
*** Utilisateur
|
|
||||||
|
|
||||||
- Définire une version modifier d'un senarios
|
|
||||||
- Comparé des résultats entre plusieurs senarios
|
|
||||||
|
|
||||||
{{{pause}}}
|
|
||||||
|
|
||||||
**** COMMENT Développeur
|
|
||||||
:PROPERTIES:
|
|
||||||
:BEAMER_COL: 0.5
|
|
||||||
:BEAMER_ENV: block
|
|
||||||
:END:
|
|
||||||
|
|
||||||
- Un unique fichier
|
|
||||||
- Limité la duplication de donnée à sauvegarder
|
|
||||||
- Garder une bonne cohérence entre données et résultats
|
|
||||||
|
|
||||||
{{{pause}}}
|
|
||||||
|
|
||||||
*** Scénarios
|
|
||||||
:PROPERTIES:
|
|
||||||
:BEAMER_COL: 1
|
|
||||||
:BEAMER_ENV: definition
|
|
||||||
:END:
|
|
||||||
|
|
||||||
Un scénario peut être l'étude ou une variate d'un autre scénario.
|
|
||||||
|
|
||||||
** Problématique -- Encore plus avancer
|
|
||||||
|
|
||||||
*** Utilisateur
|
|
||||||
|
|
||||||
- Définire un ensemble fini de variante correspondant a un ensemble de
|
|
||||||
valeur pour un(des) paramètre(s) et executer un solveur sur
|
|
||||||
l'ensemble des variantes
|
|
||||||
- Pouvoir comparé des metrics sur un ensemble de résultats trop grand
|
|
||||||
pour une comparaisont à la mains
|
|
||||||
|
|
||||||
{{{pause}}}
|
|
||||||
|
|
||||||
**** COMMENT Développeur
|
|
||||||
:PROPERTIES:
|
|
||||||
:BEAMER_COL: 0.5
|
|
||||||
:BEAMER_ENV: block
|
|
||||||
:END:
|
|
||||||
|
|
||||||
- Un unique fichier
|
|
||||||
- Limité la duplication de donnée à sauvegarder
|
|
||||||
- Garder une bonne cohérence entre données et résultats
|
|
||||||
|
|
||||||
{{{pause}}}
|
|
||||||
|
|
||||||
*** Scénarios
|
|
||||||
:PROPERTIES:
|
|
||||||
:BEAMER_COL: 1
|
|
||||||
:BEAMER_ENV: definition
|
|
||||||
:END:
|
|
||||||
|
|
||||||
Un scénario peut être l'étude, une variate d'un scénarios ou un
|
|
||||||
ensemble fini de variantes d'un scénarios.
|
|
||||||
|
|
||||||
** Problématique -- Encore plus plus avancer
|
|
||||||
|
|
||||||
*** Utilisateur
|
|
||||||
|
|
||||||
- Définire un ensemble fini de variante correspondant a un ensemble de
|
|
||||||
valeur pour un(des) paramètre(s) et faire un nombre fini de tirage
|
|
||||||
aléatoire dans ces paramètre pour executer un solveur ces paramètres
|
|
||||||
- Pouvoir comparé des metrics sur un ensemble de résultats trop grand
|
|
||||||
pour une comparaisont à la mains
|
|
||||||
|
|
||||||
{{{pause}}}
|
|
||||||
|
|
||||||
*** Scénarios
|
|
||||||
:PROPERTIES:
|
|
||||||
:BEAMER_COL: 1
|
|
||||||
:BEAMER_ENV: definition
|
|
||||||
:END:
|
|
||||||
|
|
||||||
Un scénario peut être l'étude, une variate d'un scénarios ou un
|
|
||||||
ensemble fini ou un tirage aléatoire dans un ensemble de variantes
|
|
||||||
d'un scénarios.
|
|
||||||
|
|
||||||
** Problématique -- Développeur
|
|
||||||
|
|
||||||
*** Développeur
|
|
||||||
|
|
||||||
- Permetre a l'utilisateur de *garder une bonne cohérence entre
|
|
||||||
données et résultats* de façon claire
|
|
||||||
- Permetre a l'utilisateur de *géré les scénarios* de façon claire
|
|
||||||
(représentation graphique)
|
|
||||||
- Garder *un unique fichier* (=.pamhyr=)
|
|
||||||
- *Limité la duplication* de donnée à sauvegarder
|
|
||||||
- Permetre à l'utilisateur de garder les *résultats pertinant* sans
|
|
||||||
exploser la mémoire de l'ordinateur
|
|
||||||
|
|
||||||
* Proposition
|
|
||||||
|
|
||||||
** Proposition -- Concept
|
|
||||||
|
|
||||||
*** {{{no}}}
|
|
||||||
\vspace{-1.2cm}
|
|
||||||
**** Deux type de scénarios
|
|
||||||
:PROPERTIES:
|
|
||||||
:BEAMER_COL: 0.5
|
|
||||||
:BEAMER_ENV: block
|
|
||||||
:END:
|
|
||||||
|
|
||||||
- Scénarios simple
|
|
||||||
- Scénarios d'éxecution d'ensemble
|
|
||||||
|
|
||||||
{{{pause}}}
|
|
||||||
|
|
||||||
**** Scénarios simple
|
|
||||||
:PROPERTIES:
|
|
||||||
:BEAMER_COL: 0.5
|
|
||||||
:BEAMER_ENV: block
|
|
||||||
:END:
|
|
||||||
|
|
||||||
Est composer:
|
|
||||||
- d'un identifiant unique
|
|
||||||
- d'un nom (définie par l'utilisateur)
|
|
||||||
- d'une description (définie par l'utilisateur)
|
|
||||||
- d'un parent (définie par l'utilisateur)
|
|
||||||
|
|
||||||
{{{pause}}}
|
|
||||||
|
|
||||||
\vspace{-0.5cm}
|
|
||||||
|
|
||||||
*** Scénarios d'ensembles
|
|
||||||
|
|
||||||
**** {{{no}}}
|
|
||||||
:PROPERTIES:
|
|
||||||
:BEAMER_COL: 0.5
|
|
||||||
:BEAMER_ENV: block
|
|
||||||
:END:
|
|
||||||
\vspace{-1cm}
|
|
||||||
Est composer:
|
|
||||||
- d'un identifiant unique
|
|
||||||
- d'un nom (définie par l'utilisateur)
|
|
||||||
- d'une description (définie par l'utilisateur)
|
|
||||||
- d'un parent (définie par l'utilisateur)
|
|
||||||
- d'un ou plusieurs ensemble de valeur
|
|
||||||
|
|
||||||
**** {{{no}}}
|
|
||||||
:PROPERTIES:
|
|
||||||
:BEAMER_COL: 0.5
|
|
||||||
:BEAMER_ENV: block
|
|
||||||
:END:
|
|
||||||
\vspace{-0.5cm}
|
|
||||||
Un ensemble de valeurs est definie par:
|
|
||||||
- une valeurs de départ
|
|
||||||
- une valeurs de fin
|
|
||||||
- un pas de valeur
|
|
||||||
- un élément d'origine (élément définie dans l'étude)
|
|
||||||
- une fonctions (modification de l'élément à l'aide d'une valeur de
|
|
||||||
l'ensemble)
|
|
||||||
|
|
||||||
** Proposition -- Limitation
|
|
||||||
|
|
||||||
*** Scénarios simple
|
|
||||||
:PROPERTIES:
|
|
||||||
:BEAMER_COL: 0.5
|
|
||||||
:BEAMER_ENV: block
|
|
||||||
:END:
|
|
||||||
|
|
||||||
Si le scénarios est le *parent d'un autre scénarios*, il ne *peut plus
|
|
||||||
être modifier* sans risquer des comportement difficilement
|
|
||||||
comprehensible pour l'utilisateur.
|
|
||||||
|
|
||||||
{{{pause}}}
|
|
||||||
|
|
||||||
*** Scénarios d'ensembles
|
|
||||||
:PROPERTIES:
|
|
||||||
:BEAMER_COL: 0.5
|
|
||||||
:BEAMER_ENV: block
|
|
||||||
:END:
|
|
||||||
|
|
||||||
- Un scénarios d'ensemble *ne peut pas être parent* d'un autre
|
|
||||||
scénarios
|
|
||||||
- Le nombre d'exécution peut très rapidement grossir
|
|
||||||
|
|
||||||
** Proposition -- Affichage
|
|
||||||
|
|
||||||
#+name: graph-scenarios
|
|
||||||
#+header: :results drawer
|
|
||||||
#+header: :exports results
|
|
||||||
#+header: :post attr_wrap(width="7cm", data=*this*, name="graph-scenarios", caption="Exemple of scenarios graph", float="t")
|
|
||||||
#+begin_src dot :file "images/graph-scenarios.png" :cache no
|
|
||||||
digraph {
|
|
||||||
bgcolor="transparent";
|
|
||||||
node[colorscheme=pastel19,shape=ellipse,style="filled",fillcolor=9];
|
|
||||||
|
|
||||||
b[fillcolor=2];
|
|
||||||
|
|
||||||
aa[shape=box,fillcolor=2];
|
|
||||||
aa2[label="aa'", shape=box, fillcolor=2];
|
|
||||||
ad[shape=box, fillcolor=2];
|
|
||||||
re[shape=box, fillcolor=2];
|
|
||||||
rf[shape=box, fillcolor=2];
|
|
||||||
|
|
||||||
default -> a;
|
|
||||||
default -> b;
|
|
||||||
default -> c;
|
|
||||||
|
|
||||||
a -> aa;
|
|
||||||
a -> aa2;
|
|
||||||
a -> f -> rf;
|
|
||||||
c -> d -> ad;
|
|
||||||
c -> e -> re;
|
|
||||||
}
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
#+RESULTS[80ed2484b9cc71143f800ff23c10846fd04697c3]: graph-scenarios
|
|
||||||
:results:
|
|
||||||
#+CAPTION[]: Exemple of scenarios graph
|
|
||||||
#+NAME: graph-scenarios
|
|
||||||
#+ATTR_LATEX: :width 7cm :float t
|
|
||||||
[[file:images/graph-scenarios.png]]
|
|
||||||
:end:
|
|
||||||
|
|
||||||
* Solution technique
|
|
||||||
|
|
||||||
** Technique -- scénarios
|
|
||||||
|
|
||||||
#+begin_src sql
|
|
||||||
CREATE TABLE scenario(
|
|
||||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
||||||
name TEXT NOT NULL, -- Name of the scenario
|
|
||||||
description TEXT NOT NULL, -- Rich text description
|
|
||||||
parent_id INTEGER REFERENCES scenario(id) -- Recursive references
|
|
||||||
-- for parent scenario
|
|
||||||
)
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
** Technique -- Données -- Enregistrement
|
|
||||||
|
|
||||||
#+begin_src sql
|
|
||||||
-- Unique ID (Must be unused, to delete)
|
|
||||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
||||||
-- New ID (not unique)
|
|
||||||
pamhyr_id INTEGER NOT NULL,
|
|
||||||
-- (Optional) Flag for set there is no data in this scenario and
|
|
||||||
-- ignore parent scenario
|
|
||||||
dummy BOOLEAN NOT NULL,
|
|
||||||
-- Corresponding scenario
|
|
||||||
scenario_id INTEGER NOT NULL,
|
|
||||||
PRIMARY KEY(pamhyr_id, scenario_id),
|
|
||||||
FOREIGN KEY(scenario_id) REFERENCES scenario(id),
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
** Technique -- Données -- Chargement
|
|
||||||
|
|
||||||
#+begin_src python :python python3 :results output :noweb yes
|
|
||||||
def load(cls, db, current_scenario):
|
|
||||||
# Default case, end of recursion
|
|
||||||
if current_scenario is None:
|
|
||||||
return [] # Default value
|
|
||||||
|
|
||||||
table = db.execute(f"<MY SELECT> WHERE scenario_id = {current_scenario}")
|
|
||||||
|
|
||||||
# If no data for this scenario, recursion
|
|
||||||
if len(table) == 0:
|
|
||||||
parent_id = db.get_parent_id(current_scenario)
|
|
||||||
return cls.load(db, parent_id)
|
|
||||||
|
|
||||||
# Otherelse, parse data and return...
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
** Technique -- Données -- Modification
|
|
||||||
|
|
||||||
#+begin_src python :python python3 :results output :noweb yes
|
|
||||||
def set_modified(self, current_scenario):
|
|
||||||
self.scenario = current_scenario
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
#+begin_src python :python python3 :results output :noweb yes
|
|
||||||
def delete(self, current_scenario):
|
|
||||||
self.deleted = True
|
|
||||||
self.set_modified(current_scenario)
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
** Technique -- Exécution d'ensemble
|
|
||||||
|
|
||||||
#+begin_src sql
|
|
||||||
CREATE TABLE range_set(
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
start REAL NOT NULL, -- Range start
|
|
||||||
end REAL NOT NULL, -- Range end
|
|
||||||
step REAL NOT NULL, -- Range step
|
|
||||||
generator_name TEXT NOT NULL, -- Generator function name (ex:
|
|
||||||
-- 'minor_strickler')
|
|
||||||
original_data_id INTEGER, -- (optional) Ref to data who apply
|
|
||||||
-- this range (ex: a strickler id)
|
|
||||||
scenario_id INTEGER NOT NULL,
|
|
||||||
FOREIGN KEY(scenario_id) REFERENCES scenario(id)
|
|
||||||
)
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
* Sauvegarde des résultats
|
|
||||||
|
|
||||||
** Sauvegarde des résultats
|
|
||||||
|
|
||||||
*** Scénarios simple
|
|
||||||
|
|
||||||
Sauvegarde d'un seul résultats complet par scénarios (et par solver).
|
|
||||||
|
|
||||||
*** Scénarios d'ensemble
|
|
||||||
|
|
||||||
Sauvegarde de metrics récupéré sur un ensemble de résultats (pas de
|
|
||||||
sauvegarde de tous les résultats) ou certain résultats particuliers.
|
|
||||||
|
|
||||||
* Question & remarque
|
|
||||||
|
|
||||||
** Question & remarque
|
|
||||||
|
|
||||||
- Solution satisfaisante ?
|
|
||||||
- Cas d'usage intéressent non couvert ?
|
|
||||||
- Contre exemple ?
|
|
||||||
- Meilleur solution ?
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 70 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 72 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 42 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 196 KiB |
Binary file not shown.
@@ -1,10 +1,10 @@
|
|||||||
PyQt5==5.15.11
|
PyQt5==5.15.4
|
||||||
PyQt5-Qt5==5.15.2
|
PyQt5-Qt5==5.15.2
|
||||||
PyQt5-sip==12.18.0
|
PyQt5-sip==12.12.2
|
||||||
PyQtWebEngine==5.15.6
|
PyQtWebEngine==5.15.6
|
||||||
QScintilla>=2.14.1
|
QScintilla>=2.14.1
|
||||||
pyqtgraph>=0.12.1
|
pyqtgraph>=0.12.1
|
||||||
matplotlib==3.8.3
|
matplotlib>=3.7.1
|
||||||
numpy>=1.24.2
|
numpy>=1.24.2
|
||||||
colorama>=0.4.3
|
colorama>=0.4.3
|
||||||
pyinstaller>=5.11.0
|
pyinstaller>=5.11.0
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
guix shell --manifest=manifest.scm -- python3 ./src/pamhyr.py $@
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
;; manifest.scm -- Pamhyr2 Guix manifest
|
|
||||||
;; Copyright (C) 2025 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 <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
(specifications->manifest
|
|
||||||
(list "python" "python-platformdirs" "python-lxml"
|
|
||||||
"python-pyqt@5" "python-pyqt5-sip"
|
|
||||||
"python-pyqtgraph" "python-qscintilla"
|
|
||||||
"python-numpy@1" "python-shapely" "python-pyshp"
|
|
||||||
"python-matplotlib" "python-colorama"
|
|
||||||
"python-rasterio"))
|
|
||||||
+3
-3
@@ -1,10 +1,10 @@
|
|||||||
PyQt5==5.15.11
|
PyQt5==5.15.4
|
||||||
PyQt5-Qt5==5.15.2
|
PyQt5-Qt5==5.15.2
|
||||||
PyQt5-sip==12.18.0
|
PyQt5-sip==12.12.2
|
||||||
#PyQtWebEngine==5.15.6
|
#PyQtWebEngine==5.15.6
|
||||||
QScintilla>=2.14.1
|
QScintilla>=2.14.1
|
||||||
pyqtgraph>=0.12.1
|
pyqtgraph>=0.12.1
|
||||||
matplotlib==3.8.3
|
matplotlib>=3.7.1
|
||||||
numpy>=1.24.2
|
numpy>=1.24.2
|
||||||
colorama>=0.4.3
|
colorama>=0.4.3
|
||||||
pyinstaller>=5.11.0
|
pyinstaller>=5.11.0
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Adists.py -- Pamhyr study checkers
|
# Adists.py -- Pamhyr study checkers
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Checker.py -- Pamhyr abstract checker class
|
# Checker.py -- Pamhyr abstract checker class
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
# Mage.py -- Pamhyr MAGE checkers
|
# Mage.py -- Pamhyr MAGE checkers
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Study.py -- Pamhyr study checkers
|
# Study.py -- Pamhyr study checkers
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# AMeshingTool.py -- Pamhyr
|
# AMeshingTool.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
+75
-119
@@ -17,7 +17,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from numpy import sign, interp
|
from numpy import sign
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
from tools import logger_color_red, logger_color_reset, logger_exception
|
from tools import logger_color_red, logger_color_reset, logger_exception
|
||||||
@@ -40,32 +40,35 @@ class InternalMeshing(AMeshingTool):
|
|||||||
step: float = 50,
|
step: float = 50,
|
||||||
limites=[-1, -1],
|
limites=[-1, -1],
|
||||||
linear: bool = True):
|
linear: bool = True):
|
||||||
|
if reach is None or len(reach.profiles) == 0:
|
||||||
|
return reach
|
||||||
|
|
||||||
# pre process
|
if limites[0] > limites[1]:
|
||||||
|
lim = limites[1]
|
||||||
|
limites[1] = limites[0]
|
||||||
|
limites[0] = lim
|
||||||
|
elif limites[0] == limites[1]:
|
||||||
|
return reach
|
||||||
|
|
||||||
profiles = self.get_profiles(reach, limites)
|
self.st_to_m(reach, limites)
|
||||||
if profiles is None:
|
self.interpolate_transversal_step(reach,
|
||||||
return []
|
limites,
|
||||||
|
step)
|
||||||
|
self.purge_aligned_points(reach)
|
||||||
|
|
||||||
for p in profiles:
|
return reach
|
||||||
if len(p) < 1:
|
|
||||||
return []
|
|
||||||
|
|
||||||
guide_list = self.get_guide_list(reach, profiles)
|
def st_to_m(self, reach, limites):
|
||||||
if guide_list is None:
|
|
||||||
return []
|
|
||||||
|
|
||||||
# we never modify original profiles, we work on the copies
|
gl = reach.compute_guidelines()
|
||||||
m_profiles = self.st_to_m(profiles, guide_list)
|
profiles = reach.profiles[limites[0]:limites[1]+1]
|
||||||
new_profiles = self.interpolate_transversal_step(m_profiles, step)
|
|
||||||
|
|
||||||
for new_profiles2 in new_profiles:
|
# we make sure that the lines are in the left-to-right order
|
||||||
for p in new_profiles2:
|
guide_list = [
|
||||||
p.hard_purge_aligned_points()
|
x.name
|
||||||
|
for x in profiles[0].named_points()
|
||||||
return new_profiles
|
if x.name in gl[0]
|
||||||
|
]
|
||||||
def st_to_m(self, profiles, guide_list):
|
|
||||||
|
|
||||||
guide_list = ["un"] + guide_list + ["np"]
|
guide_list = ["un"] + guide_list + ["np"]
|
||||||
max_values = [0] * (len(guide_list) - 1)
|
max_values = [0] * (len(guide_list) - 1)
|
||||||
@@ -85,51 +88,54 @@ class InternalMeshing(AMeshingTool):
|
|||||||
profiles[isect+1],
|
profiles[isect+1],
|
||||||
guide_list[i],
|
guide_list[i],
|
||||||
guide_list[i+1])
|
guide_list[i+1])
|
||||||
|
|
||||||
for isect in reversed(range(0, max_values_index[i])):
|
for isect in reversed(range(0, max_values_index[i])):
|
||||||
self.compl_sect(profiles[isect+1],
|
self.compl_sect(profiles[isect+1],
|
||||||
profiles[isect],
|
profiles[isect],
|
||||||
guide_list[i],
|
guide_list[i],
|
||||||
guide_list[i+1])
|
guide_list[i+1])
|
||||||
return profiles
|
|
||||||
|
|
||||||
def interpolate_transversal_step(self,
|
def interpolate_transversal_step(self,
|
||||||
profiles,
|
reach,
|
||||||
|
limites,
|
||||||
step):
|
step):
|
||||||
# calcul number of intermediate profiles
|
# calcul number of intermediate profiles
|
||||||
np = []
|
np = []
|
||||||
for i in range(len(profiles)-1):
|
for i in range(limites[0], limites[1]):
|
||||||
np.append(int((profiles[i+1].rk -
|
np.append(int(abs(reach.profiles[i+1].rk -
|
||||||
profiles[i].rk) / step) - 1)
|
reach.profiles[i].rk) / step) - 1)
|
||||||
if np[-1] < 0:
|
if np[-1] < 0:
|
||||||
np[-1] = 0
|
np[-1] = 0
|
||||||
|
|
||||||
new_profiles = []
|
d = [] # ratios
|
||||||
for i in range(len(profiles)-1):
|
p = [] # new profiles
|
||||||
new_profiles2 = []
|
ptr = int(limites[0])
|
||||||
|
for i in range(limites[1] - limites[0]):
|
||||||
d = 1.0/float(np[i]+1)
|
d = 1.0/float(np[i]+1)
|
||||||
|
ptr0 = ptr
|
||||||
for j in range(np[i]):
|
for j in range(np[i]):
|
||||||
p = profiles[i].copy()
|
p = ProfileXYZ(reach=reach, status=reach._status)
|
||||||
# RATIO entre les deux sections initiales
|
# RATIO entre les deux sections initiales
|
||||||
dj = float(j+1)*d
|
dj = float(j+1)*d
|
||||||
pt1 = profiles[i].points
|
ptr += 1 # next profile, original
|
||||||
pt2 = profiles[i+1].points
|
for k in range(len(reach.profiles[ptr0].points)):
|
||||||
for k in range(len(pt1)):
|
p.add()
|
||||||
p.points[k].x = pt1[k].x + \
|
p.points[k].name = reach.profiles[ptr0].points[k].name
|
||||||
dj*(pt2[k].x -
|
p.points[k].x = reach.profiles[ptr0].points[k].x + \
|
||||||
pt1[k].x)
|
dj*(reach.profiles[ptr].points[k].x -
|
||||||
p.points[k].y = pt1[k].y + \
|
reach.profiles[ptr0].points[k].x)
|
||||||
dj*(pt2[k].y -
|
p.points[k].y = reach.profiles[ptr0].points[k].y + \
|
||||||
pt1[k].y)
|
dj*(reach.profiles[ptr].points[k].y -
|
||||||
p.points[k].z = pt1[k].z + \
|
reach.profiles[ptr0].points[k].y)
|
||||||
dj*(pt2[k].z -
|
p.points[k].z = reach.profiles[ptr0].points[k].z + \
|
||||||
pt1[k].z)
|
dj*(reach.profiles[ptr].points[k].z -
|
||||||
p.rk = profiles[i].rk + \
|
reach.profiles[ptr0].points[k].z)
|
||||||
dj*(profiles[i+1].rk -
|
p.rk = reach.profiles[ptr0].rk + \
|
||||||
profiles[i].rk)
|
dj*(reach.profiles[ptr].rk -
|
||||||
|
reach.profiles[ptr0].rk)
|
||||||
p.name = f'interpol{p.rk}'
|
p.name = f'interpol{p.rk}'
|
||||||
new_profiles2.append(p)
|
reach.insert_profile(ptr, p)
|
||||||
new_profiles.append(new_profiles2)
|
ptr += 1 # next profile, original
|
||||||
return new_profiles
|
|
||||||
|
|
||||||
def compl_sect(self, sect1, sect2, tag1, tag2):
|
def compl_sect(self, sect1, sect2, tag1, tag2):
|
||||||
# sect1: reference section
|
# sect1: reference section
|
||||||
@@ -149,7 +155,8 @@ class InternalMeshing(AMeshingTool):
|
|||||||
len2 = end2 - start2
|
len2 = end2 - start2
|
||||||
|
|
||||||
if len1 < len2:
|
if len1 < len2:
|
||||||
self.compl_sect(sect2, sect1, tag1, tag2)
|
self.compl_sect(sect2, sect1, start2, start1,
|
||||||
|
len2, len1, tag1, tag2)
|
||||||
return
|
return
|
||||||
elif len1 == len2:
|
elif len1 == len2:
|
||||||
return
|
return
|
||||||
@@ -168,6 +175,10 @@ class InternalMeshing(AMeshingTool):
|
|||||||
for i in range(len2):
|
for i in range(len2):
|
||||||
ltot2 += sect2.point(start2+i).dist(sect2.point(start2+i+1))
|
ltot2 += sect2.point(start2+i).dist(sect2.point(start2+i+1))
|
||||||
beta.append(ltot2)
|
beta.append(ltot2)
|
||||||
|
beta = list(map(lambda x: x/ltot2, beta)) # current ratios
|
||||||
|
for i in range(len1 - len2):
|
||||||
|
beta.append(1.0)
|
||||||
|
beta.append(0.0) # beta[-1]
|
||||||
if len2 < 1 or ltot2 < 0.0001:
|
if len2 < 1 or ltot2 < 0.0001:
|
||||||
# unique point (copy len1 times)
|
# unique point (copy len1 times)
|
||||||
# we are at an edge of the profile
|
# we are at an edge of the profile
|
||||||
@@ -179,10 +190,6 @@ class InternalMeshing(AMeshingTool):
|
|||||||
sect2.add_npoints(len1-len2)
|
sect2.add_npoints(len1-len2)
|
||||||
len2 = len1
|
len2 = len1
|
||||||
else: # regular case
|
else: # regular case
|
||||||
beta = list(map(lambda x: x/ltot2, beta)) # current ratios
|
|
||||||
for i in range(len1 - len2):
|
|
||||||
beta.append(1.0)
|
|
||||||
beta.append(0.0) # beta[-1]
|
|
||||||
# loop on the points to insert
|
# loop on the points to insert
|
||||||
for k in range(len1-len2):
|
for k in range(len1-len2):
|
||||||
trouve = False
|
trouve = False
|
||||||
@@ -243,11 +250,11 @@ class InternalMeshing(AMeshingTool):
|
|||||||
sect2.point(start2+len2).name = ''
|
sect2.point(start2+len2).name = ''
|
||||||
if tag1 != "un":
|
if tag1 != "un":
|
||||||
sect2.point(start2).name = tag1
|
sect2.point(start2).name = tag1
|
||||||
if tag1 != "np":
|
if tag2 != "np":
|
||||||
sect2.point(start2+len2).name = tag2
|
sect2.point(start2+len2).name = tag2
|
||||||
sect2.modified()
|
sect2.modified()
|
||||||
|
|
||||||
def update_rk(self, reach, begin_rk, end_rk,
|
def update_rk(self, reach,
|
||||||
step: float = 50,
|
step: float = 50,
|
||||||
limites=[-1, -1],
|
limites=[-1, -1],
|
||||||
origin=0,
|
origin=0,
|
||||||
@@ -258,40 +265,29 @@ class InternalMeshing(AMeshingTool):
|
|||||||
origin_value=0.0,
|
origin_value=0.0,
|
||||||
orientation=0):
|
orientation=0):
|
||||||
if reach is None or len(reach.profiles) == 0:
|
if reach is None or len(reach.profiles) == 0:
|
||||||
return []
|
return reach
|
||||||
|
|
||||||
nprof = reach.number_profiles
|
nprof = reach.number_profiles
|
||||||
if origin >= nprof or origin < 0:
|
if origin >= nprof or origin < 0:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"Origin outside reach"
|
f"Origin outside reach"
|
||||||
)
|
)
|
||||||
return reach.get_rk()
|
return reach
|
||||||
|
|
||||||
# orientation is the orintation of the bief:
|
# orientation is the orintation of the bief:
|
||||||
# 1: up -> downstream
|
# 1: up -> downstream
|
||||||
# 2: down -> upstream
|
# 2: down -> upstream
|
||||||
# else: keep current orientation
|
# else: keep current orientation
|
||||||
old_orientation = sign(reach.profiles[-1].rk - reach.profiles[0].rk)
|
|
||||||
if orientation == 1:
|
if orientation == 1:
|
||||||
sgn = 1.0
|
sgn = 1.0
|
||||||
elif orientation == 2:
|
elif orientation == 2:
|
||||||
sgn = -1.0
|
sgn = -1.0
|
||||||
else:
|
else:
|
||||||
sgn = old_orientation
|
sgn = sign(reach.profiles[-1].rk - reach.profiles[0].rk)
|
||||||
if abs(sgn) < 0.5:
|
if abs(sgn) < 0.5:
|
||||||
sgn = 1.0
|
sgn = 1.0
|
||||||
|
|
||||||
new_rk = [0.0]*nprof
|
reach.profiles[origin].rk = origin_value
|
||||||
new_rk[origin] = origin_value
|
|
||||||
|
|
||||||
frictions = reach._parent.frictions
|
|
||||||
|
|
||||||
old_begin_rk = list(map(lambda x: x * old_orientation,
|
|
||||||
begin_rk))
|
|
||||||
old_end_rk = list(map(lambda x: x * old_orientation,
|
|
||||||
end_rk))
|
|
||||||
old_rk = list(map(lambda x: x * old_orientation,
|
|
||||||
reach.get_rk()))
|
|
||||||
|
|
||||||
if origin < nprof - 1:
|
if origin < nprof - 1:
|
||||||
for i in range(origin+1, nprof):
|
for i in range(origin+1, nprof):
|
||||||
@@ -302,7 +298,8 @@ class InternalMeshing(AMeshingTool):
|
|||||||
d2 = reach.profiles[i-1].named_point(
|
d2 = reach.profiles[i-1].named_point(
|
||||||
directrices[1]
|
directrices[1]
|
||||||
).dist_2d(reach.profiles[i].named_point(directrices[1]))
|
).dist_2d(reach.profiles[i].named_point(directrices[1]))
|
||||||
new_rk[i] = new_rk[i-1] + (sgn * (d1 + d2) / 2)
|
reach.profiles[i].rk = reach.profiles[i-1].rk \
|
||||||
|
+ (sgn * (d1 + d2) / 2)
|
||||||
if origin > 0:
|
if origin > 0:
|
||||||
for i in reversed(range(0, origin)):
|
for i in reversed(range(0, origin)):
|
||||||
# 2D
|
# 2D
|
||||||
@@ -312,50 +309,9 @@ class InternalMeshing(AMeshingTool):
|
|||||||
d2 = reach.profiles[i+1].named_point(
|
d2 = reach.profiles[i+1].named_point(
|
||||||
directrices[1]
|
directrices[1]
|
||||||
).dist_2d(reach.profiles[i].named_point(directrices[1]))
|
).dist_2d(reach.profiles[i].named_point(directrices[1]))
|
||||||
new_rk[i] = new_rk[i+1] - (sgn * (d1 + d2) / 2)
|
reach.profiles[i].rk = reach.profiles[i+1].rk \
|
||||||
|
- (sgn * (d1 + d2) / 2)
|
||||||
|
|
||||||
new_begin_rk = interp(old_begin_rk, old_rk, new_rk)
|
def purge_aligned_points(self, reach):
|
||||||
new_end_rk = interp(old_end_rk, old_rk, new_rk)
|
for p in reach.profiles:
|
||||||
if abs(sgn - old_orientation) > 0.1: # orientation changed
|
p.purge_aligned_points()
|
||||||
return new_rk, new_end_rk, new_begin_rk
|
|
||||||
else:
|
|
||||||
return new_rk, new_begin_rk, new_end_rk
|
|
||||||
|
|
||||||
def get_guide_list(self, reach, profiles):
|
|
||||||
|
|
||||||
gl = reach.compute_guidelines()
|
|
||||||
|
|
||||||
# we make sure that the lines are in the left-to-right order
|
|
||||||
guide_list = [
|
|
||||||
[
|
|
||||||
x.name
|
|
||||||
for x in p.named_points()
|
|
||||||
if x.name in gl[0]
|
|
||||||
]
|
|
||||||
for p in profiles]
|
|
||||||
|
|
||||||
# we make sure the lines never cross
|
|
||||||
for gl in guide_list:
|
|
||||||
for i in range(len(guide_list[0])):
|
|
||||||
if gl[i] != guide_list[0][i]:
|
|
||||||
return None
|
|
||||||
|
|
||||||
return guide_list[0]
|
|
||||||
|
|
||||||
def get_profiles(self, reach, limites):
|
|
||||||
|
|
||||||
if reach is None or len(reach.profiles) == 0:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if limites[0] > limites[1]:
|
|
||||||
lim = limites[1]
|
|
||||||
limites[1] = limites[0]
|
|
||||||
limites[0] = lim
|
|
||||||
elif limites[0] == limites[1]:
|
|
||||||
return None
|
|
||||||
|
|
||||||
profiles = [reach.profile(i).copy()
|
|
||||||
for i in range(limites[0], limites[1]+1)
|
|
||||||
]
|
|
||||||
|
|
||||||
return profiles
|
|
||||||
+3
-7
@@ -1,5 +1,5 @@
|
|||||||
# Mage.py -- Pamhyr
|
# Mage.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -344,9 +344,7 @@ class MeshingWithMageMailleurTT(AMeshingTool):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
logger.info("Waiting ...")
|
|
||||||
proc.waitForFinished()
|
proc.waitForFinished()
|
||||||
logger.info(f"Waiting ... Done (status : {proc.exitCode()})")
|
|
||||||
|
|
||||||
if proc.exitCode() != 0:
|
if proc.exitCode() != 0:
|
||||||
logger.error(
|
logger.error(
|
||||||
@@ -355,7 +353,7 @@ class MeshingWithMageMailleurTT(AMeshingTool):
|
|||||||
f"{logger_color_reset()}"
|
f"{logger_color_reset()}"
|
||||||
)
|
)
|
||||||
outputs = proc.readAllStandardOutput()
|
outputs = proc.readAllStandardOutput()
|
||||||
logger.warning(outputs)
|
logger.debug(outputs)
|
||||||
|
|
||||||
errors = proc.readAllStandardError()
|
errors = proc.readAllStandardError()
|
||||||
logger.error(
|
logger.error(
|
||||||
@@ -421,9 +419,7 @@ class MeshingWithMageMailleurTT(AMeshingTool):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
logger.debug("Waiting ...")
|
|
||||||
proc.waitForFinished()
|
proc.waitForFinished()
|
||||||
logger.debug(f"Waiting ... Done (status : {proc.exitCode()})")
|
|
||||||
|
|
||||||
if proc.exitCode() != 0:
|
if proc.exitCode() != 0:
|
||||||
logger.error(
|
logger.error(
|
||||||
@@ -432,7 +428,7 @@ class MeshingWithMageMailleurTT(AMeshingTool):
|
|||||||
f"{logger_color_reset()}"
|
f"{logger_color_reset()}"
|
||||||
)
|
)
|
||||||
outputs = proc.readAllStandardOutput()
|
outputs = proc.readAllStandardOutput()
|
||||||
logger.warning(outputs)
|
logger.debug(outputs)
|
||||||
|
|
||||||
errors = proc.readAllStandardError()
|
errors = proc.readAllStandardError()
|
||||||
logger.error(
|
logger.error(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# __init__.py -- Pamhyr
|
# __init__.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# test_Model.py -- Pamhyr
|
# test_Model.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -21,6 +21,7 @@ import unittest
|
|||||||
import warnings
|
import warnings
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
|
from Model.Saved import SavedStatus
|
||||||
from Model.Study import Study
|
from Model.Study import Study
|
||||||
from Model.River import River
|
from Model.River import River
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# AddFile.py -- Pamhyr
|
# AddFile.py -- Pamhyr
|
||||||
# Copyright (C) 2024-2025 INRAE
|
# Copyright (C) 2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -22,25 +22,31 @@ from tools import trace, timer
|
|||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
from Model.Scenario import Scenario
|
|
||||||
|
|
||||||
|
|
||||||
class AddFile(SQLSubModel):
|
class AddFile(SQLSubModel):
|
||||||
_sub_classes = []
|
_sub_classes = []
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
def __init__(self, id: int = -1, enabled=True,
|
def __init__(self, id: int = -1, enabled=True,
|
||||||
name="", path="", text="",
|
name="", path="", text="",
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(AddFile, self).__init__(
|
super(AddFile, self).__init__()
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
if id == -1:
|
||||||
)
|
self.id = AddFile._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
self._status = status
|
||||||
|
|
||||||
self._enabled = enabled
|
self._enabled = enabled
|
||||||
self._name = f"File #{self._pamhyr_id}" if name == "" else name
|
self._name = f"File {self.id}" if name == "" else name
|
||||||
self._path = path
|
self._path = path
|
||||||
self._text = text
|
self._text = text
|
||||||
|
|
||||||
|
AddFile._id_cnt = max(self.id + 1, AddFile._id_cnt + 1)
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
value = None
|
value = None
|
||||||
|
|
||||||
@@ -65,7 +71,7 @@ class AddFile(SQLSubModel):
|
|||||||
elif key == "text":
|
elif key == "text":
|
||||||
self._text = value
|
self._text = value
|
||||||
|
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def enabled(self):
|
def enabled(self):
|
||||||
@@ -74,7 +80,7 @@ class AddFile(SQLSubModel):
|
|||||||
@enabled.setter
|
@enabled.setter
|
||||||
def enabled(self, enabled):
|
def enabled(self, enabled):
|
||||||
self._enabled = enabled
|
self._enabled = enabled
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def is_enabled(self):
|
def is_enabled(self):
|
||||||
return self._enabled
|
return self._enabled
|
||||||
@@ -86,7 +92,7 @@ class AddFile(SQLSubModel):
|
|||||||
@name.setter
|
@name.setter
|
||||||
def name(self, name):
|
def name(self, name):
|
||||||
self._name = name
|
self._name = name
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path(self):
|
def path(self):
|
||||||
@@ -95,7 +101,7 @@ class AddFile(SQLSubModel):
|
|||||||
@path.setter
|
@path.setter
|
||||||
def path(self, path):
|
def path(self, path):
|
||||||
self._path = path
|
self._path = path
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def text(self):
|
def text(self):
|
||||||
@@ -104,80 +110,38 @@ class AddFile(SQLSubModel):
|
|||||||
@text.setter
|
@text.setter
|
||||||
def text(self, text):
|
def text(self, text):
|
||||||
self._text = text
|
self._text = text
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE additional_files{ext} (
|
CREATE TABLE additional_files(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
enabled BOOLEAN NOT NULL,
|
enabled BOOLEAN NOT NULL,
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
path TEXT NOT NULL,
|
path TEXT NOT NULL,
|
||||||
text TEXT NOT NULL,
|
text TEXT NOT NULL
|
||||||
{Scenario.create_db_add_scenario()},
|
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
|
||||||
PRIMARY KEY(pamhyr_id, scenario)
|
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
release = int(release)
|
|
||||||
|
|
||||||
if major == minor == "0":
|
if major == minor == "0":
|
||||||
if release < 8:
|
if int(release) < 8:
|
||||||
cls._db_create(execute)
|
cls._db_create(execute)
|
||||||
return True
|
|
||||||
|
|
||||||
if 8 < release < 13:
|
|
||||||
cls._db_update_to_0_0_13(execute, data)
|
|
||||||
|
|
||||||
if major == "0" and minor == "1":
|
|
||||||
if release < 2:
|
|
||||||
execute(
|
|
||||||
"ALTER TABLE additional_files " +
|
|
||||||
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_0_13(cls, execute, data):
|
|
||||||
table = "additional_files"
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
execute(
|
|
||||||
"INSERT INTO additional_files_tmp " +
|
|
||||||
"(pamhyr_id, enabled, name, path, text, scenario) " +
|
|
||||||
"SELECT pamhyr_id, enabled, name, path, text, scenario " +
|
|
||||||
"FROM additional_files"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
new = []
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, enabled, deleted, name, path, text, scenario " +
|
"SELECT id, enabled, name, path, text " +
|
||||||
"FROM additional_files " +
|
"FROM additional_files"
|
||||||
f"WHERE scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for row in table:
|
for row in table:
|
||||||
@@ -185,45 +149,30 @@ class AddFile(SQLSubModel):
|
|||||||
|
|
||||||
id = next(it)
|
id = next(it)
|
||||||
enabled = (next(it) == 1)
|
enabled = (next(it) == 1)
|
||||||
deleted = (next(it) == 1)
|
|
||||||
name = next(it)
|
name = next(it)
|
||||||
path = next(it)
|
path = next(it)
|
||||||
text = next(it)
|
text = next(it)
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
f = cls(
|
f = cls(
|
||||||
id=id, enabled=enabled, name=name, path=path, text=text,
|
id=id, enabled=enabled, name=name, path=path, text=text,
|
||||||
status=data['status'], owner_scenario=owner_scenario
|
status=data['status']
|
||||||
)
|
)
|
||||||
if deleted:
|
|
||||||
f.set_as_deleted()
|
|
||||||
|
|
||||||
loaded.add(id)
|
|
||||||
new.append(f)
|
new.append(f)
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
if not self.must_be_saved():
|
sql = (
|
||||||
return True
|
|
||||||
|
|
||||||
execute(
|
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"additional_files(pamhyr_id, enabled, deleted, " +
|
"additional_files(id, enabled, name, path, text) " +
|
||||||
"name, path, text, scenario) " +
|
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self._pamhyr_id}, " +
|
f"{self.id}, {self._enabled}, " +
|
||||||
f"{self._db_format(self._enabled)}, " +
|
|
||||||
f"{self._db_format(self.is_deleted())}, " +
|
|
||||||
f"'{self._db_format(self._name)}', " +
|
f"'{self._db_format(self._name)}', " +
|
||||||
f"'{self._db_format(self._path)}', " +
|
f"'{self._db_format(self._path)}', " +
|
||||||
f"'{self._db_format(self._text)}', " +
|
f"'{self._db_format(self._text)}'" +
|
||||||
f"{self._status.scenario_id}" +
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# AddFileList.py -- Pamhyr
|
# AddFileList.py -- Pamhyr
|
||||||
# Copyright (C) 2024-2025 INRAE
|
# Copyright (C) 2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
|
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
from Model.Tools.PamhyrListExt import PamhyrModelList
|
from Model.Tools.PamhyrList import PamhyrModelList
|
||||||
from Model.AdditionalFile.AddFile import AddFile
|
from Model.AdditionalFile.AddFile import AddFile
|
||||||
|
|
||||||
|
|
||||||
@@ -38,10 +38,7 @@ class AddFileList(PamhyrModelList):
|
|||||||
ok = True
|
ok = True
|
||||||
|
|
||||||
# Delete previous data
|
# Delete previous data
|
||||||
execute(
|
execute("DELETE FROM additional_files")
|
||||||
"DELETE FROM additional_files " +
|
|
||||||
f"WHERE scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
for af in self._lst:
|
for af in self._lst:
|
||||||
ok &= af._db_save(execute, data)
|
ok &= af._db_save(execute, data)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# BoundaryCondition.py -- Pamhyr
|
# BoundaryCondition.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -27,193 +27,24 @@ from tools import (
|
|||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
from Model.Scenario import Scenario
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
class Data(SQLSubModel):
|
|
||||||
_sub_classes = []
|
|
||||||
|
|
||||||
def __init__(self, data0, data1,
|
|
||||||
id: int = -1,
|
|
||||||
types=[float, float],
|
|
||||||
status=None,
|
|
||||||
owner_scenario=-1):
|
|
||||||
super(Data, self).__init__(
|
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._types = types
|
|
||||||
self._data = [data0, data1]
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_create(cls, execute, ext=""):
|
|
||||||
execute(f"""
|
|
||||||
CREATE TABLE boundary_condition_data{ext}(
|
|
||||||
{cls.create_db_add_pamhyr_id()},
|
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
ind INTEGER NOT NULL,
|
|
||||||
data0 TEXT NOT NULL,
|
|
||||||
data1 TEXT NOT NULL,
|
|
||||||
bc INTEGER,
|
|
||||||
{Scenario.create_db_add_scenario()},
|
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
|
||||||
FOREIGN KEY(bc) REFERENCES boundary_condition(pamhyr_id),
|
|
||||||
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) < 1:
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
if major == "0" and minor == "1":
|
|
||||||
if int(release) < 2:
|
|
||||||
execute(
|
|
||||||
"ALTER TABLE boundary_condition_data " +
|
|
||||||
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
|
|
||||||
)
|
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "boundary_condition_data"
|
|
||||||
bcs = data['id2pid']['boundary_condition']
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(
|
|
||||||
execute, table,
|
|
||||||
data=data
|
|
||||||
)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
"(pamhyr_id, ind, data0, data1, bc, scenario) " +
|
|
||||||
"SELECT pamhyr_id, ind, data0, data1, bc, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
cls._db_update_to_0_2_0_set_bc_pid(execute, table, bcs)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_bc_pid(cls, execute, table, bcs):
|
|
||||||
els = execute(
|
|
||||||
f"SELECT pamhyr_id, bc FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in els:
|
|
||||||
it = iter(row)
|
|
||||||
pid = next(it)
|
|
||||||
bc_id = next(it)
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE {table} " +
|
|
||||||
f"SET bc = {bcs[bc_id]} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_load(cls, execute, data=None):
|
|
||||||
new = []
|
|
||||||
bc = data["bc"]
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
values = execute(
|
|
||||||
"SELECT pamhyr_id, deleted, ind, " +
|
|
||||||
"data0, data1, scenario " +
|
|
||||||
"FROM boundary_condition_data " +
|
|
||||||
f"WHERE bc = {bc._pamhyr_id} " +
|
|
||||||
f"AND 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)
|
|
||||||
deleted = (next(it) == 1)
|
|
||||||
ind = next(it)
|
|
||||||
data0 = bc._types[0](next(it))
|
|
||||||
data1 = bc._types[1](next(it))
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
nd = cls(
|
|
||||||
data0, data1,
|
|
||||||
id=pid,
|
|
||||||
types=bc._types,
|
|
||||||
status=data['status'],
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
if deleted:
|
|
||||||
nd.set_as_deleted()
|
|
||||||
|
|
||||||
loaded.add(pid)
|
|
||||||
new.append(nd)
|
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
|
||||||
if not self.must_be_saved():
|
|
||||||
return True
|
|
||||||
|
|
||||||
pid = self._pamhyr_id
|
|
||||||
ind = data["ind"]
|
|
||||||
data0 = self._db_format(str(self[0]))
|
|
||||||
data1 = self._db_format(str(self[1]))
|
|
||||||
bc = data["bc"]
|
|
||||||
|
|
||||||
execute(
|
|
||||||
"INSERT INTO " +
|
|
||||||
"boundary_condition_data(pamhyr_id, deleted, ind, " +
|
|
||||||
"data0, data1, bc, scenario) " +
|
|
||||||
f"VALUES (" +
|
|
||||||
f"{pid}, {self._db_format(self.is_deleted())}, " +
|
|
||||||
f"{ind}, '{self._db_format(data0)}', {self._db_format(data1)}, " +
|
|
||||||
f"{bc._pamhyr_id}, {self._status.scenario_id}" +
|
|
||||||
")"
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
|
||||||
return self._types[key](self._data[key])
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
self._data[key] = self._types[key](value)
|
|
||||||
|
|
||||||
|
|
||||||
class BoundaryCondition(SQLSubModel):
|
class BoundaryCondition(SQLSubModel):
|
||||||
_sub_classes = [Data]
|
_sub_classes = []
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
def __init__(self, id: int = -1,
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
name: str = "",
|
status=None):
|
||||||
status=None,
|
super(BoundaryCondition, self).__init__()
|
||||||
owner_scenario=-1):
|
|
||||||
super(BoundaryCondition, self).__init__(
|
self._status = status
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
if id == -1:
|
||||||
)
|
self.id = BoundaryCondition._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self._type = ""
|
self._type = ""
|
||||||
@@ -222,66 +53,38 @@ class BoundaryCondition(SQLSubModel):
|
|||||||
self._header = []
|
self._header = []
|
||||||
self._types = [float, float]
|
self._types = [float, float]
|
||||||
|
|
||||||
|
BoundaryCondition._id_cnt = max(BoundaryCondition._id_cnt + 1,
|
||||||
|
self.id + 1)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE boundary_condition{ext}(
|
CREATE TABLE boundary_condition(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
type TEXT NOT NULL,
|
type TEXT NOT NULL,
|
||||||
tab TEXT NOT NULL,
|
tab TEXT NOT NULL,
|
||||||
node INTEGER,
|
node INTEGER,
|
||||||
{Scenario.create_db_add_scenario()},
|
FOREIGN KEY(node) REFERENCES river_node(id)
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
|
||||||
FOREIGN KEY(node) REFERENCES river_node(pamhyr_id),
|
|
||||||
PRIMARY KEY(pamhyr_id, scenario)
|
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
if ext == "_tmp":
|
execute("""
|
||||||
return True
|
CREATE TABLE boundary_condition_data(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
ind INTEGER NOT NULL,
|
||||||
|
data0 TEXT NOT NULL,
|
||||||
|
data1 TEXT NOT NULL,
|
||||||
|
bc INTEGER,
|
||||||
|
FOREIGN KEY(bc) REFERENCES boundary_condition(id)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
return True
|
||||||
|
|
||||||
if major == minor == "0":
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
if major == "0" and minor == "1":
|
|
||||||
if int(release) < 2:
|
|
||||||
execute(
|
|
||||||
"ALTER TABLE friction " +
|
|
||||||
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
|
|
||||||
)
|
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "boundary_condition"
|
|
||||||
nodes = data['id2pid']['river_node']
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
# Copy table
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
f"(pamhyr_id, name, type, tab, node, scenario) " +
|
|
||||||
"SELECT pamhyr_id, name, type, tab, node, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
cls._db_update_to_0_2_0_set_node_pid(execute, table, nodes)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_ctor_from_type(cls, t):
|
def _get_ctor_from_type(cls, t):
|
||||||
@@ -308,117 +111,83 @@ class BoundaryCondition(SQLSubModel):
|
|||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
new = []
|
||||||
tab = data["tab"]
|
tab = data["tab"]
|
||||||
nodes = data["nodes"]
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, deleted, name, type, node, scenario " +
|
"SELECT id, name, type, node " +
|
||||||
"FROM boundary_condition " +
|
"FROM boundary_condition " +
|
||||||
f"WHERE tab = '{tab}' " +
|
f"WHERE tab = '{tab}'"
|
||||||
f"AND scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) "
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
t = row[2]
|
||||||
|
|
||||||
pid = next(it)
|
|
||||||
deleted = (next(it) == 1)
|
|
||||||
name = next(it)
|
|
||||||
t = next(it)
|
|
||||||
node = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
ctor = cls._get_ctor_from_type(t)
|
ctor = cls._get_ctor_from_type(t)
|
||||||
|
|
||||||
bc = ctor(
|
bc = ctor(
|
||||||
id=pid, name=name,
|
id=row[0],
|
||||||
status=data['status'],
|
name=row[1],
|
||||||
owner_scenario=owner_scenario
|
status=data['status']
|
||||||
)
|
)
|
||||||
if deleted:
|
|
||||||
bc.set_as_deleted()
|
|
||||||
|
|
||||||
bc.node = None
|
bc.node = None
|
||||||
if node != -1:
|
if row[3] != -1:
|
||||||
bc.node = next(filter(lambda n: n.id == node, nodes), None)
|
bc.node = next(filter(lambda n: n.id == row[3], data["nodes"]),
|
||||||
|
None)
|
||||||
|
|
||||||
data["bc"] = bc
|
values = execute(
|
||||||
bc._data = Data._db_load(execute, data=data)
|
"SELECT ind, data0, data1 FROM boundary_condition_data " +
|
||||||
|
f"WHERE bc = '{bc.id}'"
|
||||||
|
)
|
||||||
|
# Create dummy data list
|
||||||
|
for _ in values:
|
||||||
|
bc.add(0)
|
||||||
|
# Write data
|
||||||
|
for v in values:
|
||||||
|
ind = v[0]
|
||||||
|
data0 = bc._types[0](v[1])
|
||||||
|
data1 = bc._types[1](v[2])
|
||||||
|
# Replace data at pos ind
|
||||||
|
bc._data[ind] = (data0, data1)
|
||||||
|
|
||||||
loaded.add(pid)
|
|
||||||
new.append(bc)
|
new.append(bc)
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
if not self.must_be_saved():
|
|
||||||
return True
|
|
||||||
|
|
||||||
tab = data["tab"]
|
tab = data["tab"]
|
||||||
|
|
||||||
execute(
|
execute(f"DELETE FROM boundary_condition WHERE id = {self.id}")
|
||||||
"DELETE FROM boundary_condition_data " +
|
execute(f"DELETE FROM boundary_condition_data WHERE bc = {self.id}")
|
||||||
f"WHERE bc = {self._pamhyr_id} " +
|
|
||||||
f"AND scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
node = -1
|
node = -1
|
||||||
if self._node is not None:
|
if self._node is not None:
|
||||||
node = self._node.id
|
node = self._node.id
|
||||||
|
|
||||||
execute(
|
sql = (
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"boundary_condition(" +
|
"boundary_condition(id, name, type, tab, node) " +
|
||||||
"pamhyr_id, deleted, name, type, tab, node, scenario" +
|
|
||||||
") " +
|
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self._pamhyr_id}, {self._db_format(self.is_deleted())}, " +
|
f"{self.id}, '{self._db_format(self._name)}', " +
|
||||||
f"'{self._db_format(self._name)}', " +
|
f"'{self._db_format(self._type)}', '{tab}', {node}" +
|
||||||
f"'{self._db_format(self._type)}', '{tab}', {node}, " +
|
|
||||||
f"{self._status.scenario_id}" +
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
execute(sql)
|
||||||
data["bc"] = self
|
|
||||||
|
|
||||||
ind = 0
|
ind = 0
|
||||||
for d in self._data:
|
for d in self._data:
|
||||||
data["ind"] = ind
|
data0 = self._db_format(str(d[0]))
|
||||||
|
data1 = self._db_format(str(d[1]))
|
||||||
d._db_save(execute, data)
|
|
||||||
|
|
||||||
|
sql = (
|
||||||
|
"INSERT INTO " +
|
||||||
|
"boundary_condition_data(ind, data0, data1, bc) " +
|
||||||
|
f"VALUES ({ind}, '{data0}', {data1}, {self.id})"
|
||||||
|
)
|
||||||
|
execute(sql)
|
||||||
ind += 1
|
ind += 1
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _data_traversal(self,
|
|
||||||
predicate=lambda obj, data: True,
|
|
||||||
modifier=lambda obj, data: None,
|
|
||||||
data={}):
|
|
||||||
if predicate(self, data):
|
|
||||||
modifier(self, data)
|
|
||||||
|
|
||||||
for el in self._data:
|
|
||||||
el._data_traversal(predicate, modifier, data)
|
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(
|
return len(self._data)
|
||||||
list(
|
|
||||||
filter(
|
|
||||||
lambda el: not el.is_deleted(),
|
|
||||||
self._data
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def compatibility(cls):
|
def compatibility(cls):
|
||||||
@@ -443,14 +212,14 @@ class BoundaryCondition(SQLSubModel):
|
|||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
if self._name == "":
|
if self._name == "":
|
||||||
return f"B #{self.pamhyr_id}"
|
return f"B{self.id + 1}"
|
||||||
|
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@name.setter
|
@name.setter
|
||||||
def name(self, name):
|
def name(self, name):
|
||||||
self._name = name
|
self._name = name
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bctype(self):
|
def bctype(self):
|
||||||
@@ -463,7 +232,7 @@ class BoundaryCondition(SQLSubModel):
|
|||||||
@node.setter
|
@node.setter
|
||||||
def node(self, node):
|
def node(self, node):
|
||||||
self._node = node
|
self._node = node
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def has_node(self):
|
def has_node(self):
|
||||||
return self._node is not None
|
return self._node is not None
|
||||||
@@ -474,12 +243,7 @@ class BoundaryCondition(SQLSubModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def data(self):
|
def data(self):
|
||||||
return list(
|
return self._data.copy()
|
||||||
filter(
|
|
||||||
lambda el: not el.is_deleted(),
|
|
||||||
self._data
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_type_column(self, column):
|
def get_type_column(self, column):
|
||||||
if 0 <= column < 2:
|
if 0 <= column < 2:
|
||||||
@@ -513,79 +277,51 @@ class BoundaryCondition(SQLSubModel):
|
|||||||
new_0 = self._types[0](data[0].replace(",", "."))
|
new_0 = self._types[0](data[0].replace(",", "."))
|
||||||
new_1 = self._types[1](data[1].replace(",", "."))
|
new_1 = self._types[1](data[1].replace(",", "."))
|
||||||
|
|
||||||
return Data(
|
return (new_0, new_1)
|
||||||
new_0, new_1,
|
|
||||||
status=self._status
|
|
||||||
)
|
|
||||||
|
|
||||||
def add(self, index: int):
|
def add(self, index: int):
|
||||||
value = Data(
|
value = (self._default_0, self._default_1)
|
||||||
self._default_0, self._default_1,
|
|
||||||
status=self._status
|
|
||||||
)
|
|
||||||
self._data.insert(index, value)
|
self._data.insert(index, value)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def insert(self, index: int, value):
|
def insert(self, index: int, value):
|
||||||
if value in self._data:
|
self._data.insert(index, value)
|
||||||
value.set_as_not_deleted()
|
self._status.modified()
|
||||||
else:
|
|
||||||
self._data.insert(index, value)
|
|
||||||
|
|
||||||
self.modified()
|
def delete_i(self, indexes):
|
||||||
|
|
||||||
def hard_delete_i(self, indexes):
|
|
||||||
self._data = list(
|
self._data = list(
|
||||||
map(
|
map(
|
||||||
lambda e: e[1],
|
lambda e: e[1],
|
||||||
filter(
|
filter(
|
||||||
lambda e: e[0] not in indexes,
|
lambda e: e[0] not in indexes,
|
||||||
enumerate(self._data)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def delete_i(self, indexes):
|
|
||||||
list(
|
|
||||||
map(
|
|
||||||
lambda e: e[1].set_as_deleted(),
|
|
||||||
filter(
|
|
||||||
lambda e: e[0] in indexes,
|
|
||||||
enumerate(self.data)
|
enumerate(self.data)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def delete(self, els):
|
def delete(self, els):
|
||||||
for el in els:
|
self._data = list(
|
||||||
el.set_as_deleted()
|
filter(
|
||||||
|
lambda e: e not in els,
|
||||||
self.modified()
|
self.data
|
||||||
|
)
|
||||||
def undelete(self, els):
|
)
|
||||||
for el in els:
|
self._status.modified()
|
||||||
el.set_as_not_deleted()
|
|
||||||
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def sort(self, _reverse=False, key=None):
|
def sort(self, _reverse=False, key=None):
|
||||||
if key is None:
|
if key is None:
|
||||||
self._data.sort(reverse=_reverse)
|
self._data.sort(reverse=_reverse)
|
||||||
else:
|
else:
|
||||||
self._data.sort(reverse=_reverse, key=key)
|
self._data.sort(reverse=_reverse, key=key)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def index(self, bc):
|
def index(self, bc):
|
||||||
self._data.index(bc)
|
self._data.index(bc)
|
||||||
|
|
||||||
def get_i(self, index):
|
def get_i(self, index):
|
||||||
if len(self._data) == 0:
|
return self.data[index]
|
||||||
return None
|
|
||||||
|
|
||||||
return self._data[index]
|
|
||||||
|
|
||||||
def get_range(self, _range):
|
def get_range(self, _range):
|
||||||
lst = []
|
lst = []
|
||||||
@@ -594,10 +330,10 @@ class BoundaryCondition(SQLSubModel):
|
|||||||
return lst
|
return lst
|
||||||
|
|
||||||
def _set_i_c_v(self, index, column, value):
|
def _set_i_c_v(self, index, column, value):
|
||||||
v = self._data[index]
|
v = list(self._data[index])
|
||||||
v[column] = self._types[column](value)
|
v[column] = self._types[column](value)
|
||||||
self._data[index] = v
|
self._data[index] = tuple(v)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def set_i_0(self, index: int, value):
|
def set_i_0(self, index: int, value):
|
||||||
self._set_i_c_v(index, 0, value)
|
self._set_i_c_v(index, 0, value)
|
||||||
@@ -629,14 +365,14 @@ class BoundaryCondition(SQLSubModel):
|
|||||||
next = index - 1
|
next = index - 1
|
||||||
d = self._data
|
d = self._data
|
||||||
d[index], d[next] = d[next], d[index]
|
d[index], d[next] = d[next], d[index]
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def move_down(self, index):
|
def move_down(self, index):
|
||||||
if index >= 0:
|
if index >= 0:
|
||||||
prev = index + 1
|
prev = index + 1
|
||||||
d = self._data
|
d = self._data
|
||||||
d[index], d[prev] = d[prev], d[index]
|
d[index], d[prev] = d[prev], d[index]
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def reach(self, river):
|
def reach(self, river):
|
||||||
r = []
|
r = []
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# BoundaryConditionList.py -- Pamhyr
|
# BoundaryConditionList.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
from copy import copy
|
from copy import copy
|
||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
|
|
||||||
from Model.Tools.PamhyrListExt import PamhyrModelListWithTab
|
from Model.Tools.PamhyrList import PamhyrModelListWithTab
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
from Model.BoundaryCondition.BoundaryCondition import BoundaryCondition
|
from Model.BoundaryCondition.BoundaryCondition import BoundaryCondition
|
||||||
@@ -41,6 +41,9 @@ class BoundaryConditionList(PamhyrModelListWithTab):
|
|||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = cls(status=data['status'])
|
new = cls(status=data['status'])
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
data = {}
|
||||||
|
|
||||||
for tab in new._tabs:
|
for tab in new._tabs:
|
||||||
data["tab"] = tab
|
data["tab"] = tab
|
||||||
new._tabs[tab] = BoundaryCondition._db_load(
|
new._tabs[tab] = BoundaryCondition._db_load(
|
||||||
@@ -50,10 +53,10 @@ class BoundaryConditionList(PamhyrModelListWithTab):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute(
|
execute("DELETE FROM boundary_condition")
|
||||||
"DELETE FROM boundary_condition " +
|
|
||||||
f"WHERE scenario = {self._status.scenario_id}"
|
if data is None:
|
||||||
)
|
data = {}
|
||||||
|
|
||||||
for tab in self._tabs:
|
for tab in self._tabs:
|
||||||
data["tab"] = tab
|
data["tab"] = tab
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# BoundaryConditionTypes.py -- Pamhyr
|
# BoundaryConditionTypes.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -22,12 +22,8 @@ from Model.BoundaryCondition.BoundaryCondition import BoundaryCondition
|
|||||||
|
|
||||||
|
|
||||||
class NotDefined(BoundaryCondition):
|
class NotDefined(BoundaryCondition):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "", status=None):
|
||||||
status=None, owner_scenario=-1):
|
super(NotDefined, self).__init__(id=id, name=name, status=status)
|
||||||
super(NotDefined, self).__init__(
|
|
||||||
id=id, name=name, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._type = "ND"
|
self._type = "ND"
|
||||||
self._header = ["x", "y"]
|
self._header = ["x", "y"]
|
||||||
@@ -38,12 +34,9 @@ class NotDefined(BoundaryCondition):
|
|||||||
|
|
||||||
|
|
||||||
class PonctualContribution(BoundaryCondition):
|
class PonctualContribution(BoundaryCondition):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "", status=None):
|
||||||
status=None, owner_scenario=-1):
|
|
||||||
super(PonctualContribution, self).__init__(
|
super(PonctualContribution, self).__init__(
|
||||||
id=id, name=name, status=status,
|
id=id, name=name, status=status)
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._type = "PC"
|
self._type = "PC"
|
||||||
self._header = ["time", "discharge"]
|
self._header = ["time", "discharge"]
|
||||||
@@ -55,12 +48,8 @@ class PonctualContribution(BoundaryCondition):
|
|||||||
|
|
||||||
|
|
||||||
class TimeOverZ(BoundaryCondition):
|
class TimeOverZ(BoundaryCondition):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "", status=None):
|
||||||
status=None, owner_scenario=-1):
|
super(TimeOverZ, self).__init__(id=id, name=name, status=status)
|
||||||
super(TimeOverZ, self).__init__(
|
|
||||||
id=id, name=name, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._type = "TZ"
|
self._type = "TZ"
|
||||||
self._header = ["time", "z"]
|
self._header = ["time", "z"]
|
||||||
@@ -72,12 +61,9 @@ class TimeOverZ(BoundaryCondition):
|
|||||||
|
|
||||||
|
|
||||||
class TimeOverDischarge(BoundaryCondition):
|
class TimeOverDischarge(BoundaryCondition):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "", status=None):
|
||||||
status=None, owner_scenario=-1):
|
|
||||||
super(TimeOverDischarge, self).__init__(
|
super(TimeOverDischarge, self).__init__(
|
||||||
id=id, name=name, status=status,
|
id=id, name=name, status=status)
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._type = "TD"
|
self._type = "TD"
|
||||||
self._header = ["time", "discharge"]
|
self._header = ["time", "discharge"]
|
||||||
@@ -89,12 +75,8 @@ class TimeOverDischarge(BoundaryCondition):
|
|||||||
|
|
||||||
|
|
||||||
class ZOverDischarge(BoundaryCondition):
|
class ZOverDischarge(BoundaryCondition):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "", status=None):
|
||||||
status=None, owner_scenario=-1):
|
super(ZOverDischarge, self).__init__(id=id, name=name, status=status)
|
||||||
super(ZOverDischarge, self).__init__(
|
|
||||||
id=id, name=name, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._type = "ZD"
|
self._type = "ZD"
|
||||||
self._header = ["z", "discharge"]
|
self._header = ["z", "discharge"]
|
||||||
@@ -110,12 +92,8 @@ class ZOverDischarge(BoundaryCondition):
|
|||||||
|
|
||||||
|
|
||||||
class Solid(BoundaryCondition):
|
class Solid(BoundaryCondition):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "", status=None):
|
||||||
status=None, owner_scenario=-1):
|
super(Solid, self).__init__(id=id, name=name, status=status)
|
||||||
super(Solid, self).__init__(
|
|
||||||
id=id, name=name, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self.d50 = 0.002
|
self.d50 = 0.002
|
||||||
self.sigma = 1
|
self.sigma = 1
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# BoundaryConditionsAdisTS.py -- Pamhyr
|
# BoundaryConditionsAdisTS.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -27,193 +27,25 @@ from tools import (
|
|||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
from Model.Scenario import Scenario
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
class Data(SQLSubModel):
|
|
||||||
_sub_classes = []
|
|
||||||
|
|
||||||
def __init__(self,
|
|
||||||
data0, data1,
|
|
||||||
id: int = -1,
|
|
||||||
types=[float, float],
|
|
||||||
status=None,
|
|
||||||
owner_scenario=-1):
|
|
||||||
super(Data, self).__init__(
|
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._types = types
|
|
||||||
self._data = [data0, data1]
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_create(cls, execute, ext=""):
|
|
||||||
execute(f"""
|
|
||||||
CREATE TABLE boundary_condition_data_adists{ext}(
|
|
||||||
{cls.create_db_add_pamhyr_id()},
|
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
data0 TEXT NOT NULL,
|
|
||||||
data1 TEXT NOT NULL,
|
|
||||||
bca INTEGER,
|
|
||||||
{Scenario.create_db_add_scenario()},
|
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
|
||||||
FOREIGN KEY(bca) REFERENCES boundary_condition_adists(pamhyr_id)
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update(cls, execute, version, data=None):
|
|
||||||
major, minor, release = version.strip().split(".")
|
|
||||||
created = False
|
|
||||||
|
|
||||||
if major == "0" and int(minor) <= 1:
|
|
||||||
if int(release) < 7:
|
|
||||||
cls._db_create(execute)
|
|
||||||
created = True
|
|
||||||
|
|
||||||
if major == "0" and int(minor) < 2:
|
|
||||||
if not created:
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
if not created:
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "boundary_condition_data_adists"
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
"(pamhyr_id, data0, data1, bca, scenario) " +
|
|
||||||
"SELECT pamhyr_id, data0, data1, bc, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
cls._db_update_to_0_2_0_set_bca_pid(execute, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_bca_pid(cls, execute, data):
|
|
||||||
pid_bca = data["id2pid"]["boundary_condition_adists"]
|
|
||||||
els = execute(
|
|
||||||
"SELECT pamhyr_id, bca " +
|
|
||||||
"FROM boundary_condition_data_adists"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in els:
|
|
||||||
it = iter(row)
|
|
||||||
pid = next(it)
|
|
||||||
bca_id = next(it)
|
|
||||||
|
|
||||||
if bca_id == -1:
|
|
||||||
continue
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE boundary_condition_data_adists " +
|
|
||||||
f"SET bca = {pid_bca[bca_id]} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_load(cls, execute, data=None):
|
|
||||||
new = []
|
|
||||||
status = data['status']
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
bca = data["bca"]
|
|
||||||
|
|
||||||
table = execute(
|
|
||||||
"SELECT pamhyr_id, deleted, data0, data1, scenario FROM " +
|
|
||||||
"boundary_condition_data_adists " +
|
|
||||||
f"WHERE bca = {bca.id} " +
|
|
||||||
f"AND scenario = {scenario.id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if table is not None:
|
|
||||||
for row in table:
|
|
||||||
it = iter(row)
|
|
||||||
|
|
||||||
pid = next(it)
|
|
||||||
deleted = (next(it) == 1)
|
|
||||||
data0 = next(it)
|
|
||||||
data1 = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
bc = cls(
|
|
||||||
data0, data1,
|
|
||||||
id=pid,
|
|
||||||
status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
if deleted:
|
|
||||||
bc.set_as_deleted()
|
|
||||||
|
|
||||||
loaded.add(pid)
|
|
||||||
new.append(bc)
|
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
|
||||||
if not self.must_be_saved():
|
|
||||||
return True
|
|
||||||
|
|
||||||
bca = data["bca"]
|
|
||||||
|
|
||||||
data0 = self._db_format(self._data[0])
|
|
||||||
data1 = self._db_format(self._data[1])
|
|
||||||
|
|
||||||
execute(
|
|
||||||
"INSERT INTO " +
|
|
||||||
"boundary_condition_data_adists(" +
|
|
||||||
"pamhyr_id, data0, data1, bca, scenario) " +
|
|
||||||
f"VALUES ({self.id}, '{data0}', {data1}, {bca.id}, " +
|
|
||||||
f"{self._status.scenario_id})"
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
|
||||||
return self._types[key](self._data[key])
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
self._data[key] = self._types[key](value)
|
|
||||||
|
|
||||||
|
|
||||||
class BoundaryConditionAdisTS(SQLSubModel):
|
class BoundaryConditionAdisTS(SQLSubModel):
|
||||||
_sub_classes = [Data]
|
_sub_classes = []
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
def __init__(self, id: int = -1,
|
def __init__(self, id: int = -1,
|
||||||
pollutant: int = -1, status=None,
|
pollutant: int = -1, status=None):
|
||||||
owner_scenario=-1):
|
super(BoundaryConditionAdisTS, self).__init__()
|
||||||
super(BoundaryConditionAdisTS, self).__init__(
|
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._status = status
|
self._status = status
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = BoundaryConditionAdisTS._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
self._type = ""
|
self._type = ""
|
||||||
self._node = None
|
self._node = None
|
||||||
self._pollutant = pollutant
|
self._pollutant = pollutant
|
||||||
@@ -221,210 +53,117 @@ class BoundaryConditionAdisTS(SQLSubModel):
|
|||||||
self._header = []
|
self._header = []
|
||||||
self._types = [self.time_convert, float]
|
self._types = [self.time_convert, float]
|
||||||
|
|
||||||
|
BoundaryConditionAdisTS._id_cnt = max(
|
||||||
|
BoundaryConditionAdisTS._id_cnt + 1, self.id + 1)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE boundary_condition_adists{ext}(
|
CREATE TABLE boundary_condition_adists(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
pollutant INTEGER NOT NULL,
|
pollutant INTEGER NOT NULL,
|
||||||
type TEXT NOT NULL,
|
type TEXT NOT NULL,
|
||||||
node INTEGER,
|
node INTEGER,
|
||||||
{Scenario.create_db_add_scenario()},
|
FOREIGN KEY(pollutant) REFERENCES Pollutants(id),
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
FOREIGN KEY(node) REFERENCES river_node(id)
|
||||||
FOREIGN KEY(pollutant) REFERENCES Pollutants(pamhyr_id),
|
|
||||||
FOREIGN KEY(node) REFERENCES river_node(pamhyr_id)
|
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
if ext != "":
|
execute("""
|
||||||
return True
|
CREATE TABLE boundary_condition_data_adists(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
data0 TEXT NOT NULL,
|
||||||
|
data1 TEXT NOT NULL,
|
||||||
|
bc INTEGER,
|
||||||
|
FOREIGN KEY(bc) REFERENCES boundary_condition_adists(id)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
|
||||||
created = False
|
|
||||||
|
|
||||||
if major == "0" and int(minor) <= 1:
|
|
||||||
if int(release) < 7:
|
|
||||||
cls._db_create(execute)
|
|
||||||
created = True
|
|
||||||
|
|
||||||
if major == "0" and int(minor) < 2:
|
|
||||||
if not created:
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
if not created:
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "boundary_condition_adists"
|
|
||||||
nodes = data['id2pid']['river_node']
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
"(pamhyr_id, pollutant, type, node, scenario) " +
|
|
||||||
"SELECT pamhyr_id, pollutant, type, node, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
cls._db_update_to_0_2_0_set_node_pid(execute, table, nodes)
|
|
||||||
cls._db_update_to_0_2_0_set_pollutants_pid(execute, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_pollutants_pid(cls, execute, data):
|
|
||||||
pid_pol = data["id2pid"]["Pollutants"]
|
|
||||||
els = execute(
|
|
||||||
f"SELECT pamhyr_id, pollutant " +
|
|
||||||
"FROM boundary_condition_adists"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in els:
|
|
||||||
it = iter(row)
|
|
||||||
pid = next(it)
|
|
||||||
pol_id = next(it)
|
|
||||||
|
|
||||||
if pol_id == -1:
|
|
||||||
continue
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE boundary_condition_adists " +
|
|
||||||
f"SET pollutant = {pid_pol[pol_id]} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
new = []
|
||||||
status = data['status']
|
|
||||||
nodes = data['nodes']
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, deleted, pollutant, type, node, scenario " +
|
"SELECT id, pollutant, type, node " +
|
||||||
"FROM boundary_condition_adists " +
|
"FROM boundary_condition_adists"
|
||||||
f"WHERE scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) "
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if table is not None:
|
if table is not None:
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
|
||||||
|
|
||||||
pid = next(it)
|
|
||||||
deleted = (next(it) == 1)
|
|
||||||
pollutant = next(it)
|
|
||||||
bc_type = next(it)
|
|
||||||
node = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
bc = cls(
|
bc = cls(
|
||||||
id=pid,
|
id=row[0],
|
||||||
pollutant=pollutant,
|
pollutant=row[1],
|
||||||
status=status,
|
status=data['status']
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
if deleted:
|
|
||||||
bc.set_as_deleted()
|
|
||||||
|
|
||||||
bc.type = bc_type
|
bc.type = row[2]
|
||||||
|
|
||||||
bc.node = None
|
bc.node = None
|
||||||
if row[3] != -1:
|
if row[3] != -1:
|
||||||
tmp = next(
|
tmp = next(filter(
|
||||||
filter(
|
lambda n: n.id == row[3], data["nodes"]),
|
||||||
lambda n: n.id == node, nodes
|
None)
|
||||||
),
|
|
||||||
None
|
|
||||||
)
|
|
||||||
if tmp is not None:
|
if tmp is not None:
|
||||||
bc.node = tmp.id
|
bc.node = tmp.id
|
||||||
else:
|
else:
|
||||||
bc.node = -1
|
bc.node = -1
|
||||||
|
|
||||||
data["bca"] = bc
|
values = execute(
|
||||||
bc._data = Data._db_load(execute, data=data)
|
"SELECT data0, data1 FROM " +
|
||||||
|
"boundary_condition_data_adists " +
|
||||||
|
f"WHERE bc = '{bc.id}'"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Write data
|
||||||
|
for v in values:
|
||||||
|
data0 = bc._types[0](v[0])
|
||||||
|
data1 = bc._types[1](v[1])
|
||||||
|
# Replace data at pos ind
|
||||||
|
bc._data.append((data0, data1))
|
||||||
|
|
||||||
loaded.add(pid)
|
|
||||||
new.append(bc)
|
new.append(bc)
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
if not self.must_be_saved():
|
|
||||||
return True
|
|
||||||
|
|
||||||
execute(
|
execute(f"DELETE FROM boundary_condition_adists WHERE id = {self.id}")
|
||||||
"DELETE FROM boundary_condition_adists " +
|
execute(f"DELETE FROM boundary_condition_data_adists" +
|
||||||
f"WHERE pamhyr_id = {self.id} " +
|
f" WHERE bc = {self.id}")
|
||||||
f"AND scenario = {self._status.scenario_id} "
|
|
||||||
)
|
|
||||||
execute(
|
|
||||||
"DELETE FROM boundary_condition_data_adists " +
|
|
||||||
f"WHERE bca = {self.id} " +
|
|
||||||
f"AND scenario = {self._status.scenario_id} "
|
|
||||||
)
|
|
||||||
|
|
||||||
node = -1
|
node = -1
|
||||||
if self._node is not None:
|
if self._node is not None:
|
||||||
node = self._node
|
node = self._node
|
||||||
|
|
||||||
execute(
|
sql = (
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"boundary_condition_adists(" +
|
"boundary_condition_adists(id, pollutant, type, node) " +
|
||||||
"pamhyr_id, deleted, pollutant, type, " +
|
|
||||||
"node, scenario) " +
|
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, {self._db_format(self.is_deleted())}, " +
|
f"{self.id}, {self._pollutant}, " +
|
||||||
f"{self._pollutant}, " +
|
f"'{self._db_format(self._type)}', {node}" +
|
||||||
f"'{self._db_format(self._type)}', {node}, " +
|
|
||||||
f"{self._status.scenario_id}" +
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
data["bca"] = self
|
|
||||||
|
|
||||||
ind = 0
|
|
||||||
for d in self._data:
|
for d in self._data:
|
||||||
data["ind"] = ind
|
data0 = self._db_format(str(d[0]))
|
||||||
|
data1 = self._db_format(str(d[1]))
|
||||||
|
|
||||||
d._db_save(execute, data)
|
sql = (
|
||||||
|
"INSERT INTO " +
|
||||||
ind += 1
|
"boundary_condition_data_adists(data0, data1, bc) " +
|
||||||
|
f"VALUES ('{data0}', {data1}, {self.id})"
|
||||||
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _data_traversal(self,
|
|
||||||
predicate=lambda obj, data: True,
|
|
||||||
modifier=lambda obj, data: None,
|
|
||||||
data={}):
|
|
||||||
if predicate(self, data):
|
|
||||||
modifier(self, data)
|
|
||||||
|
|
||||||
for d in self._data:
|
|
||||||
d._data_traversal(predicate, modifier, data)
|
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self._data)
|
return len(self._data)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# BoundaryConditionsAdisTSList.py -- Pamhyr
|
# BoundaryConditionsAdisTSList.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
from copy import copy
|
from copy import copy
|
||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
|
|
||||||
from Model.Tools.PamhyrListExt import PamhyrModelList
|
from Model.Tools.PamhyrList import PamhyrModelList
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
from Model.BoundaryConditionsAdisTS.BoundaryConditionAdisTS \
|
from Model.BoundaryConditionsAdisTS.BoundaryConditionAdisTS \
|
||||||
@@ -45,20 +45,14 @@ class BoundaryConditionsAdisTSList(PamhyrModelList):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute(
|
execute("DELETE FROM boundary_condition_adists")
|
||||||
"DELETE FROM boundary_condition_adists " +
|
execute("DELETE FROM boundary_condition_data_adists")
|
||||||
f"WHERE scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
execute(
|
|
||||||
"DELETE FROM boundary_condition_data_adists " +
|
|
||||||
f"WHERE scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if data is None:
|
if data is None:
|
||||||
data = {}
|
data = {}
|
||||||
|
|
||||||
for bca in self._lst:
|
for bc in self._lst:
|
||||||
bca._db_save(execute, data=data)
|
bc._db_save(execute, data=data)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# D90AdisTS.py -- Pamhyr
|
# D90AdisTS.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -23,7 +23,6 @@ from tools import trace, timer, old_pamhyr_date_to_timestamp
|
|||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
from Model.Scenario import Scenario
|
|
||||||
|
|
||||||
from Model.D90AdisTS.D90AdisTSSpec import D90AdisTSSpec
|
from Model.D90AdisTS.D90AdisTSSpec import D90AdisTSSpec
|
||||||
|
|
||||||
@@ -34,162 +33,107 @@ class D90AdisTS(SQLSubModel):
|
|||||||
_sub_classes = [
|
_sub_classes = [
|
||||||
D90AdisTSSpec,
|
D90AdisTSSpec,
|
||||||
]
|
]
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
def __init__(self, id: int = -1, name: str = "default",
|
def __init__(self, id: int = -1, name: str = "default",
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(D90AdisTS, self).__init__(
|
super(D90AdisTS, self).__init__()
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._status = status
|
self._status = status
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = D90AdisTS._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self._d90 = None
|
self._d90 = None
|
||||||
self._enabled = True
|
self._enabled = True
|
||||||
self._data = []
|
self._data = []
|
||||||
|
|
||||||
@classmethod
|
D90AdisTS._id_cnt = max(
|
||||||
def _db_create(cls, execute, ext=""):
|
D90AdisTS._id_cnt + 1,
|
||||||
execute(f"""
|
self.id + 1
|
||||||
CREATE TABLE d90_adists{ext}(
|
)
|
||||||
{cls.create_db_add_pamhyr_id()},
|
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
d90 REAL NOT NULL,
|
|
||||||
enabled BOOLEAN NOT NULL,
|
|
||||||
{Scenario.create_db_add_scenario()},
|
|
||||||
{Scenario.create_db_add_scenario_fk()}
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
|
|
||||||
if ext != "":
|
@classmethod
|
||||||
return True
|
def _db_create(cls, execute):
|
||||||
|
execute("""
|
||||||
|
CREATE TABLE d90_adists(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
d90 REAL NOT NULL,
|
||||||
|
enabled BOOLEAN NOT NULL
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
created = False
|
if major == 0 and minor < 1:
|
||||||
|
|
||||||
if major == "0" and int(minor) <= 1:
|
|
||||||
if int(release) < 6:
|
if int(release) < 6:
|
||||||
cls._db_create(execute)
|
cls._db_create(execute)
|
||||||
created = True
|
|
||||||
|
|
||||||
if major == "0" and int(minor) < 2:
|
|
||||||
if not created:
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
if not created:
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "d90_adists"
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
"(pamhyr_id, name, d90, enabled, scenario) " +
|
|
||||||
"SELECT pamhyr_id, name, d90, enabled, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
new = []
|
||||||
status = data['status']
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, deleted, name, d90, enabled, scenario " +
|
"SELECT id, name, d90, enabled " +
|
||||||
"FROM d90_adists " +
|
"FROM d90_adists"
|
||||||
f"WHERE scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) "
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if table is not None:
|
if table is not None:
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
d90_id = row[0]
|
||||||
|
name = row[1]
|
||||||
|
d90 = row[2]
|
||||||
|
enabled = (row[3] == 1)
|
||||||
|
|
||||||
pid = next(it)
|
D90 = cls(
|
||||||
deleted = (next(it) == 1)
|
id=d90_id,
|
||||||
name = next(it)
|
|
||||||
value_d90 = next(it)
|
|
||||||
enabled = (next(it) == 1)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
d90 = cls(
|
|
||||||
id=pid,
|
|
||||||
name=name,
|
name=name,
|
||||||
status=status,
|
status=data['status']
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
if deleted:
|
|
||||||
d90.set_as_deleted()
|
|
||||||
|
|
||||||
d90.d90 = value_d90
|
D90.d90 = d90
|
||||||
d90.enabled = enabled
|
D90.enabled = enabled
|
||||||
|
|
||||||
data['d90_default_id'] = pid
|
data['d90_default_id'] = d90_id
|
||||||
d90._data = D90AdisTSSpec._db_load(execute, data)
|
D90._data = D90AdisTSSpec._db_load(execute, data)
|
||||||
|
|
||||||
loaded.add(pid)
|
new.append(D90)
|
||||||
new.append(d90)
|
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
if not self.must_be_saved():
|
execute(f"DELETE FROM d90_adists WHERE id = {self.id}")
|
||||||
return True
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"DELETE FROM d90_adists " +
|
|
||||||
f"WHERE pamhyr_id = {self.id} " +
|
|
||||||
f"AND scenario = {self._status.scenario_id} "
|
|
||||||
)
|
|
||||||
|
|
||||||
d90 = -1.
|
d90 = -1.
|
||||||
if self.d90 is not None:
|
if self.d90 is not None:
|
||||||
d90 = self.d90
|
d90 = self.d90
|
||||||
|
|
||||||
execute(
|
sql = (
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"d90_adists(" +
|
"d90_adists(" +
|
||||||
"pamhyr_id, deleted, " +
|
"id, name, d90, enabled" +
|
||||||
"name, d90, enabled, scenario" +
|
|
||||||
") " +
|
") " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, {self._db_format(self.is_deleted())}, " +
|
f"{self.id}, '{self._db_format(self._name)}', " +
|
||||||
f"'{self._db_format(self._name)}', " +
|
f"{d90}, {self._enabled}" +
|
||||||
f"{d90}, {self._enabled}, {self._status.scenario_id}" +
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
data['d90_default_id'] = self.id
|
data['d90_default_id'] = self.id
|
||||||
execute(
|
execute(
|
||||||
"DELETE FROM d90_adists_spec " +
|
"DELETE FROM d90_spec " +
|
||||||
f"WHERE d90_default = {self.id} " +
|
f"WHERE d90_default = {self.id}"
|
||||||
f"AND scenario = {self._status.scenario_id} "
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for d90_spec in self._data:
|
for d90_spec in self._data:
|
||||||
@@ -197,16 +141,6 @@ class D90AdisTS(SQLSubModel):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _data_traversal(self,
|
|
||||||
predicate=lambda obj, data: True,
|
|
||||||
modifier=lambda obj, data: None,
|
|
||||||
data={}):
|
|
||||||
if predicate(self, data):
|
|
||||||
modifier(self, data)
|
|
||||||
|
|
||||||
for d in self._data:
|
|
||||||
d._data_traversal(predicate, modifier, data)
|
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self._data)
|
return len(self._data)
|
||||||
|
|
||||||
@@ -217,7 +151,7 @@ class D90AdisTS(SQLSubModel):
|
|||||||
@name.setter
|
@name.setter
|
||||||
def name(self, name):
|
def name(self, name):
|
||||||
self._name = name
|
self._name = name
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def d90(self):
|
def d90(self):
|
||||||
@@ -226,7 +160,7 @@ class D90AdisTS(SQLSubModel):
|
|||||||
@d90.setter
|
@d90.setter
|
||||||
def d90(self, d90):
|
def d90(self, d90):
|
||||||
self._d90 = d90
|
self._d90 = d90
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def enabled(self):
|
def enabled(self):
|
||||||
@@ -235,45 +169,28 @@ class D90AdisTS(SQLSubModel):
|
|||||||
@enabled.setter
|
@enabled.setter
|
||||||
def enabled(self, enabled):
|
def enabled(self, enabled):
|
||||||
self._enabled = enabled
|
self._enabled = enabled
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def new(self, index):
|
def new(self, index):
|
||||||
n = D90AdisTSSpec(status=self._status)
|
n = D90AdisTSSpec(status=self._status)
|
||||||
self._data.insert(index, n)
|
self._data.insert(index, n)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
return n
|
return n
|
||||||
|
|
||||||
def delete(self, data):
|
def delete(self, data):
|
||||||
list(
|
self._data = list(
|
||||||
map(
|
filter(
|
||||||
lambda x: x.set_as_deleted(),
|
lambda x: x not in data,
|
||||||
data
|
self._data
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def delete_i(self, indexes):
|
def delete_i(self, indexes):
|
||||||
list(
|
for ind in indexes:
|
||||||
map(
|
del self._data[ind]
|
||||||
lambda e: e[1].set_as_deleted(),
|
self._status.modified()
|
||||||
filter(
|
|
||||||
lambda e: e[0] in indexes,
|
|
||||||
enumerate(self._data)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def insert(self, index, data):
|
def insert(self, index, data):
|
||||||
if data in self._data:
|
self._data.insert(index, data)
|
||||||
self.undelete([data])
|
self._status.modified()
|
||||||
else:
|
|
||||||
self._data.insert(index, data)
|
|
||||||
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def undelete(self, lst):
|
|
||||||
for x in lst:
|
|
||||||
x.set_as_not_deleted()
|
|
||||||
|
|
||||||
self.modified()
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# D90AdisTSList.py -- Pamhyr
|
# D90AdisTSList.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
from copy import copy
|
from copy import copy
|
||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
|
|
||||||
from Model.Tools.PamhyrListExt import PamhyrModelList
|
from Model.Tools.PamhyrList import PamhyrModelList
|
||||||
from Model.D90AdisTS.D90AdisTS import D90AdisTS
|
from Model.D90AdisTS.D90AdisTS import D90AdisTS
|
||||||
|
|
||||||
|
|
||||||
@@ -42,10 +42,7 @@ class D90AdisTSList(PamhyrModelList):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute(
|
execute("DELETE FROM d90_adists")
|
||||||
"DELETE FROM d90_adists " +
|
|
||||||
f"WHERE scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if data is None:
|
if data is None:
|
||||||
data = {}
|
data = {}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# D90AdisTSSpec.py -- Pamhyr
|
# D90AdisTSSpec.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -22,21 +22,25 @@ from tools import trace, timer
|
|||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
from Model.Scenario import Scenario
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
class D90AdisTSSpec(SQLSubModel):
|
class D90AdisTSSpec(SQLSubModel):
|
||||||
_sub_classes = []
|
_sub_classes = [
|
||||||
|
]
|
||||||
_id_cnt = 0
|
_id_cnt = 0
|
||||||
|
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(D90AdisTSSpec, self).__init__(
|
super(D90AdisTSSpec, self).__init__()
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
self._status = status
|
||||||
)
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = D90AdisTSSpec._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
self._name_section = name
|
self._name_section = name
|
||||||
self._reach = None
|
self._reach = None
|
||||||
@@ -45,129 +49,62 @@ class D90AdisTSSpec(SQLSubModel):
|
|||||||
self._d90 = None
|
self._d90 = None
|
||||||
self._enabled = True
|
self._enabled = True
|
||||||
|
|
||||||
|
D90AdisTSSpec._id_cnt = max(D90AdisTSSpec._id_cnt + 1, self.id + 1)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE d90_adists_spec{ext}(
|
CREATE TABLE d90_spec(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
d90_default INTEGER NOT NULL,
|
||||||
d90_default INTEGER NOT NULL,
|
name TEXT NOT NULL,
|
||||||
name TEXT NOT NULL,
|
reach INTEGER NOT NULL,
|
||||||
reach INTEGER NOT NULL,
|
start_rk REAL NOT NULL,
|
||||||
start_rk REAL NOT NULL,
|
end_rk REAL NOT NULL,
|
||||||
end_rk REAL NOT NULL,
|
d90 REAL NOT NULL,
|
||||||
d90 REAL NOT NULL,
|
enabled BOOLEAN NOT NULL,
|
||||||
enabled BOOLEAN NOT NULL,
|
FOREIGN KEY(d90_default) REFERENCES d90_adists(id),
|
||||||
{Scenario.create_db_add_scenario()},
|
FOREIGN KEY(reach) REFERENCES river_reach(id)
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
)
|
||||||
FOREIGN KEY(d90_default) REFERENCES d90_adists(pamhyr_id),
|
""")
|
||||||
FOREIGN KEY(reach) REFERENCES river_reach(pamhyr_id)
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
created = False
|
|
||||||
|
|
||||||
if major == 0 and minor < 1:
|
if major == 0 and minor < 1:
|
||||||
if int(release) < 6:
|
if int(release) < 6:
|
||||||
cls._db_create(execute)
|
cls._db_create(execute)
|
||||||
created = True
|
|
||||||
|
|
||||||
if major == "0" and int(minor) < 2:
|
|
||||||
if not created:
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "d90_spec"
|
|
||||||
table_new = "d90_adists_spec"
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table_new}_tmp " +
|
|
||||||
"(pamhyr_id, name, d90, enabled, scenario) " +
|
|
||||||
"SELECT pamhyr_id, name, d90, enabled, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table_new}_tmp RENAME TO {table_new}")
|
|
||||||
|
|
||||||
cls._db_update_to_0_2_0_set_d90_pid(execute, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_d90_pid(cls, execute, data):
|
|
||||||
pid_d90 = data["id2pid"]["d90_adists"]
|
|
||||||
els = execute(
|
|
||||||
f"SELECT pamhyr_id, d90 FROM d90_adists_spec"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in els:
|
|
||||||
it = iter(row)
|
|
||||||
pid = next(it)
|
|
||||||
d90_id = next(it)
|
|
||||||
|
|
||||||
if d90_id == -1:
|
|
||||||
continue
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE d90_adists_spec" +
|
|
||||||
f"SET d90 = {pid_d90[d90_id]} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
new = []
|
||||||
status = data['status']
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, deleted, " +
|
"SELECT id, d90_default, name, reach, start_rk, end_rk, " +
|
||||||
f"name, reach, start_rk, end_rk, " +
|
"d90, enabled " +
|
||||||
"d90, enabled, scenario " +
|
"FROM d90_spec " +
|
||||||
"FROM d90_adists_spec " +
|
f"WHERE d90_default = {data['d90_default_id']} "
|
||||||
f"WHERE d90_default = {data['d90_default_id']} " +
|
|
||||||
f"AND scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) "
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if table is not None:
|
if table is not None:
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
id = row[0]
|
||||||
|
name = row[2]
|
||||||
pid = next(it)
|
reach = row[3]
|
||||||
deleted = (next(it) == 1)
|
start_rk = row[4]
|
||||||
name = next(it)
|
end_rk = row[5]
|
||||||
reach = next(it)
|
d90 = row[6]
|
||||||
start_rk = next(it)
|
enabled = (row[7] == 1)
|
||||||
end_rk = next(it)
|
|
||||||
d90 = next(it)
|
|
||||||
enabled = (next(it) == 1)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
new_spec = cls(
|
new_spec = cls(
|
||||||
id=pid,
|
id=id,
|
||||||
name=name,
|
name=name,
|
||||||
status=status,
|
status=data['status']
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
if deleted:
|
|
||||||
new_spec.is_deleted()
|
|
||||||
|
|
||||||
new_spec.reach = reach
|
new_spec.reach = reach
|
||||||
new_spec.start_rk = start_rk
|
new_spec.start_rk = start_rk
|
||||||
@@ -175,36 +112,29 @@ class D90AdisTSSpec(SQLSubModel):
|
|||||||
new_spec.d90 = d90
|
new_spec.d90 = d90
|
||||||
new_spec.enabled = enabled
|
new_spec.enabled = enabled
|
||||||
|
|
||||||
loaded.add(pid)
|
|
||||||
new.append(new_spec)
|
new.append(new_spec)
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
if not self.must_be_saved():
|
|
||||||
return True
|
|
||||||
|
|
||||||
d90_default = data['d90_default_id']
|
d90_default = data['d90_default_id']
|
||||||
|
|
||||||
execute(
|
sql = (
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"d90_adists_spec(pamhyr_id, deleted, " +
|
"d90_spec(id, d90_default, name, reach, " +
|
||||||
"d90_default, name, reach, " +
|
"start_rk, end_rk, d90, enabled) " +
|
||||||
"start_rk, end_rk, d90, enabled, scenario) " +
|
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, {self._db_format(self.is_deleted())}" +
|
f"{self.id}, " +
|
||||||
f"{d90_default}, " +
|
f"{d90_default}, " +
|
||||||
f"'{self._db_format(self._name_section)}', " +
|
f"'{self._db_format(self._name_section)}', " +
|
||||||
f"{self._reach}, " +
|
f"{self._reach}, " +
|
||||||
f"{self._start_rk}, {self._end_rk}, " +
|
f"{self._start_rk}, " +
|
||||||
f"{self._d90}, {self._enabled}" +
|
f"{self._end_rk}, " +
|
||||||
f"{self._status.scenario_id}" +
|
f"{self._d90}, " +
|
||||||
|
f"{self._enabled}" +
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -215,7 +145,7 @@ class D90AdisTSSpec(SQLSubModel):
|
|||||||
@name.setter
|
@name.setter
|
||||||
def name(self, name):
|
def name(self, name):
|
||||||
self._name_section = name
|
self._name_section = name
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def reach(self):
|
def reach(self):
|
||||||
@@ -224,7 +154,7 @@ class D90AdisTSSpec(SQLSubModel):
|
|||||||
@reach.setter
|
@reach.setter
|
||||||
def reach(self, reach):
|
def reach(self, reach):
|
||||||
self._reach = reach
|
self._reach = reach
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def start_rk(self):
|
def start_rk(self):
|
||||||
@@ -233,7 +163,7 @@ class D90AdisTSSpec(SQLSubModel):
|
|||||||
@start_rk.setter
|
@start_rk.setter
|
||||||
def start_rk(self, start_rk):
|
def start_rk(self, start_rk):
|
||||||
self._start_rk = start_rk
|
self._start_rk = start_rk
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def end_rk(self):
|
def end_rk(self):
|
||||||
@@ -242,7 +172,7 @@ class D90AdisTSSpec(SQLSubModel):
|
|||||||
@end_rk.setter
|
@end_rk.setter
|
||||||
def end_rk(self, end_rk):
|
def end_rk(self, end_rk):
|
||||||
self._end_rk = end_rk
|
self._end_rk = end_rk
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def d90(self):
|
def d90(self):
|
||||||
@@ -251,7 +181,7 @@ class D90AdisTSSpec(SQLSubModel):
|
|||||||
@d90.setter
|
@d90.setter
|
||||||
def d90(self, d90):
|
def d90(self, d90):
|
||||||
self._d90 = d90
|
self._d90 = d90
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def enabled(self):
|
def enabled(self):
|
||||||
@@ -260,4 +190,4 @@ class D90AdisTSSpec(SQLSubModel):
|
|||||||
@enabled.setter
|
@enabled.setter
|
||||||
def enabled(self, enabled):
|
def enabled(self, enabled):
|
||||||
self._enabled = enabled
|
self._enabled = enabled
|
||||||
self.modified()
|
self._status.modified()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# DIFAdisTS.py -- Pamhyr
|
# DIFAdisTS.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -23,7 +23,6 @@ from tools import trace, timer, old_pamhyr_date_to_timestamp
|
|||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
from Model.Scenario import Scenario
|
|
||||||
|
|
||||||
from Model.DIFAdisTS.DIFAdisTSSpec import DIFAdisTSSpec
|
from Model.DIFAdisTS.DIFAdisTSSpec import DIFAdisTSSpec
|
||||||
|
|
||||||
@@ -34,16 +33,19 @@ class DIFAdisTS(SQLSubModel):
|
|||||||
_sub_classes = [
|
_sub_classes = [
|
||||||
DIFAdisTSSpec,
|
DIFAdisTSSpec,
|
||||||
]
|
]
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
def __init__(self, id: int = -1, name: str = "default",
|
def __init__(self, id: int = -1, name: str = "default",
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(DIFAdisTS, self).__init__(
|
super(DIFAdisTS, self).__init__()
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._status = status
|
self._status = status
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = DIFAdisTS._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self._method = None
|
self._method = None
|
||||||
self._dif = None
|
self._dif = None
|
||||||
@@ -53,129 +55,76 @@ class DIFAdisTS(SQLSubModel):
|
|||||||
self._types = ["iwasa", "fisher", "elder", "constante", "generique"]
|
self._types = ["iwasa", "fisher", "elder", "constante", "generique"]
|
||||||
self._data = []
|
self._data = []
|
||||||
|
|
||||||
@classmethod
|
DIFAdisTS._id_cnt = max(
|
||||||
def _db_create(cls, execute, ext=""):
|
DIFAdisTS._id_cnt + 1,
|
||||||
execute(f"""
|
self.id + 1
|
||||||
CREATE TABLE dif_adists{ext}(
|
)
|
||||||
{cls.create_db_add_pamhyr_id()},
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
method TEXT NOT NULL,
|
|
||||||
dif REAL NOT NULL,
|
|
||||||
b REAL,
|
|
||||||
c REAL,
|
|
||||||
enabled BOOLEAN NOT NULL,
|
|
||||||
{Scenario.create_db_add_scenario()},
|
|
||||||
{Scenario.create_db_add_scenario_fk()}
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
|
|
||||||
if ext != "":
|
@classmethod
|
||||||
return True
|
def _db_create(cls, execute):
|
||||||
|
execute("""
|
||||||
|
CREATE TABLE dif_adists(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
method TEXT NOT NULL,
|
||||||
|
dif REAL NOT NULL,
|
||||||
|
b REAL,
|
||||||
|
c REAL,
|
||||||
|
enabled BOOLEAN NOT NULL
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
created = False
|
if major == 0 and minor < 1:
|
||||||
|
|
||||||
if major == "0" and int(minor) <= 1:
|
|
||||||
if int(release) < 6:
|
if int(release) < 6:
|
||||||
cls._db_create(execute)
|
cls._db_create(execute)
|
||||||
created = True
|
|
||||||
|
|
||||||
if major == "0" and int(minor) < 2:
|
|
||||||
if not created:
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
if not created:
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "dif_adists"
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
"(pamhyr_id, name, method, dif, b, c, enabled, scenario) " +
|
|
||||||
"SELECT pamhyr_id, name, method, dif, b, c, enabled, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
new = []
|
||||||
status = data['status']
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, name, method, dif, b, c, enabled, scenario " +
|
"SELECT id, name, method, dif, b, c, enabled " +
|
||||||
"FROM dif_adists " +
|
"FROM dif_adists"
|
||||||
f"WHERE scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) "
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if table is not None:
|
if table is not None:
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
dif_id = row[0]
|
||||||
|
name = row[1]
|
||||||
|
method = row[2]
|
||||||
|
dif = row[3]
|
||||||
|
b = row[4]
|
||||||
|
c = row[5]
|
||||||
|
enabled = (row[6] == 1)
|
||||||
|
|
||||||
pid = next(it)
|
DIF = cls(
|
||||||
name = next(it)
|
id=dif_id,
|
||||||
method = next(it)
|
|
||||||
dif = next(it)
|
|
||||||
b = next(it)
|
|
||||||
c = next(it)
|
|
||||||
enabled = (next(it) == 1)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
dif = cls(
|
|
||||||
id=pid,
|
|
||||||
name=name,
|
name=name,
|
||||||
status=status,
|
status=data['status']
|
||||||
owner_scenario=owner_scenario,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
dif.method = method
|
DIF.method = method
|
||||||
dif.dif = dif
|
DIF.dif = dif
|
||||||
dif.b = b
|
DIF.b = b
|
||||||
dif.c = c
|
DIF.c = c
|
||||||
dif.enabled = enabled
|
DIF.enabled = enabled
|
||||||
|
|
||||||
data['dif_default_id'] = pid
|
data['dif_default_id'] = dif_id
|
||||||
dif._data = DIFAdisTSSpec._db_load(execute, data)
|
DIF._data = DIFAdisTSSpec._db_load(execute, data)
|
||||||
|
|
||||||
loaded.add(pid)
|
new.append(DIF)
|
||||||
new.append(dif)
|
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
if not self.must_be_saved():
|
execute(f"DELETE FROM dif_adists WHERE id = {self.id}")
|
||||||
return True
|
|
||||||
|
|
||||||
execute(
|
|
||||||
"DELETE FROM dif_adists " +
|
|
||||||
f"WHERE pamhyr_id = {self.id} " +
|
|
||||||
f"AND scenario = {self._status.scenario_id} "
|
|
||||||
)
|
|
||||||
|
|
||||||
method = ""
|
method = ""
|
||||||
if self.method is not None:
|
if self.method is not None:
|
||||||
@@ -196,13 +145,12 @@ class DIFAdisTS(SQLSubModel):
|
|||||||
sql = (
|
sql = (
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"dif_adists(" +
|
"dif_adists(" +
|
||||||
"pamhyr_id, name, method, dif, b, c, enabled, scenario" +
|
"id, name, method, dif, b, c, enabled" +
|
||||||
") " +
|
") " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, '{self._db_format(self._name)}', " +
|
f"{self.id}, '{self._db_format(self._name)}', " +
|
||||||
f"'{self._db_format(self._method)}', " +
|
f"'{self._db_format(self._method)}', " +
|
||||||
f"{dif.id}, {b}, {c}, {self._enabled}, " +
|
f"{dif}, {b}, {c}, {self._enabled}" +
|
||||||
f"{self._status.scenario_id}" +
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -210,9 +158,8 @@ class DIFAdisTS(SQLSubModel):
|
|||||||
|
|
||||||
data['dif_default_id'] = self.id
|
data['dif_default_id'] = self.id
|
||||||
execute(
|
execute(
|
||||||
"DELETE FROM dif_adists_spec " +
|
"DELETE FROM dif_spec " +
|
||||||
f"WHERE dif_default = {self.id} " +
|
f"WHERE dif_default = {self.id}"
|
||||||
f"AND scenario = {self._status.scenario_id} "
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for dif_spec in self._data:
|
for dif_spec in self._data:
|
||||||
@@ -220,16 +167,6 @@ class DIFAdisTS(SQLSubModel):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _data_traversal(self,
|
|
||||||
predicate=lambda obj, data: True,
|
|
||||||
modifier=lambda obj, data: None,
|
|
||||||
data={}):
|
|
||||||
if predicate(self, data):
|
|
||||||
modifier(self, data)
|
|
||||||
|
|
||||||
for d in self._data:
|
|
||||||
d._data_traversal(predicate, modifier, data)
|
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self._data)
|
return len(self._data)
|
||||||
|
|
||||||
@@ -240,7 +177,7 @@ class DIFAdisTS(SQLSubModel):
|
|||||||
@name.setter
|
@name.setter
|
||||||
def name(self, name):
|
def name(self, name):
|
||||||
self._name = name
|
self._name = name
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def method(self):
|
def method(self):
|
||||||
@@ -249,7 +186,7 @@ class DIFAdisTS(SQLSubModel):
|
|||||||
@method.setter
|
@method.setter
|
||||||
def method(self, method):
|
def method(self, method):
|
||||||
self._method = method
|
self._method = method
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def types(self):
|
def types(self):
|
||||||
@@ -262,7 +199,7 @@ class DIFAdisTS(SQLSubModel):
|
|||||||
@dif.setter
|
@dif.setter
|
||||||
def dif(self, dif):
|
def dif(self, dif):
|
||||||
self._dif = dif
|
self._dif = dif
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def b(self):
|
def b(self):
|
||||||
@@ -271,7 +208,7 @@ class DIFAdisTS(SQLSubModel):
|
|||||||
@b.setter
|
@b.setter
|
||||||
def b(self, b):
|
def b(self, b):
|
||||||
self._b = b
|
self._b = b
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def c(self):
|
def c(self):
|
||||||
@@ -280,7 +217,7 @@ class DIFAdisTS(SQLSubModel):
|
|||||||
@c.setter
|
@c.setter
|
||||||
def c(self, c):
|
def c(self, c):
|
||||||
self._c = c
|
self._c = c
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def enabled(self):
|
def enabled(self):
|
||||||
@@ -289,45 +226,28 @@ class DIFAdisTS(SQLSubModel):
|
|||||||
@enabled.setter
|
@enabled.setter
|
||||||
def enabled(self, enabled):
|
def enabled(self, enabled):
|
||||||
self._enabled = enabled
|
self._enabled = enabled
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def new(self, index):
|
def new(self, index):
|
||||||
n = DIFAdisTSSpec(status=self._status)
|
n = DIFAdisTSSpec(status=self._status)
|
||||||
self._data.insert(index, n)
|
self._data.insert(index, n)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
return n
|
return n
|
||||||
|
|
||||||
def delete(self, data):
|
def delete(self, data):
|
||||||
list(
|
self._data = list(
|
||||||
map(
|
filter(
|
||||||
lambda x: x.set_as_deleted(),
|
lambda x: x not in data,
|
||||||
data
|
self._data
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def delete_i(self, indexes):
|
def delete_i(self, indexes):
|
||||||
list(
|
for ind in indexes:
|
||||||
map(
|
del self._data[ind]
|
||||||
lambda e: e[1].set_as_deleted(),
|
self._status.modified()
|
||||||
filter(
|
|
||||||
lambda e: e[0] in indexes,
|
|
||||||
enumerate(self._data)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def insert(self, index, data):
|
def insert(self, index, data):
|
||||||
if data in self._data:
|
self._data.insert(index, data)
|
||||||
self.undelete([data])
|
self._status.modified()
|
||||||
else:
|
|
||||||
self._data.insert(index, data)
|
|
||||||
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def undelete(self, lst):
|
|
||||||
for x in lst:
|
|
||||||
x.set_as_not_deleted()
|
|
||||||
|
|
||||||
self.modified()
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# DIFAdisTSList.py -- Pamhyr
|
# DIFAdisTSList.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
from copy import copy
|
from copy import copy
|
||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
|
|
||||||
from Model.Tools.PamhyrListExt import PamhyrModelList
|
from Model.Tools.PamhyrList import PamhyrModelList
|
||||||
from Model.DIFAdisTS.DIFAdisTS import DIFAdisTS
|
from Model.DIFAdisTS.DIFAdisTS import DIFAdisTS
|
||||||
|
|
||||||
|
|
||||||
@@ -42,10 +42,7 @@ class DIFAdisTSList(PamhyrModelList):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute(
|
execute("DELETE FROM dif_adists")
|
||||||
"DELETE FROM dif_adists " +
|
|
||||||
f"WHERE scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if data is None:
|
if data is None:
|
||||||
data = {}
|
data = {}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# DIFAdisTSSpec.py -- Pamhyr
|
# DIFAdisTSSpec.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -22,7 +22,6 @@ from tools import trace, timer
|
|||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
from Model.Scenario import Scenario
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
@@ -52,140 +51,66 @@ class DIFAdisTSSpec(SQLSubModel):
|
|||||||
self._c = None
|
self._c = None
|
||||||
self._enabled = True
|
self._enabled = True
|
||||||
|
|
||||||
DIFAdisTSSpec._id_cnt = max(DIFAdisTSSpec._id_cnt + 1, self.id)
|
DIFAdisTSSpec._id_cnt = max(DIFAdisTSSpec._id_cnt + 1, self.id + 1)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE dif_adists_spec{ext}(
|
CREATE TABLE dif_spec(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
dif_default INTEGER NOT NULL,
|
||||||
dif_default INTEGER NOT NULL,
|
method TEXT NOT NULL,
|
||||||
method TEXT NOT NULL,
|
reach INTEGER NOT NULL,
|
||||||
reach INTEGER NOT NULL,
|
start_rk REAL NOT NULL,
|
||||||
start_rk REAL NOT NULL,
|
end_rk REAL NOT NULL,
|
||||||
end_rk REAL NOT NULL,
|
dif REAL NOT NULL,
|
||||||
dif REAL NOT NULL,
|
b REAL,
|
||||||
b REAL,
|
c REAL,
|
||||||
c REAL,
|
enabled BOOLEAN NOT NULL,
|
||||||
enabled BOOLEAN NOT NULL,
|
FOREIGN KEY(dif_default) REFERENCES dif_adists(id),
|
||||||
{Scenario.create_db_add_scenario()},
|
FOREIGN KEY(reach) REFERENCES river_reach(id)
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
)
|
||||||
FOREIGN KEY(dif_default) REFERENCES dif_adists(id),
|
""")
|
||||||
FOREIGN KEY(reach) REFERENCES river_reach(id)
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
created = False
|
|
||||||
|
|
||||||
if major == 0 and minor < 1:
|
if major == 0 and minor < 1:
|
||||||
if int(release) < 6:
|
if int(release) < 6:
|
||||||
cls._db_create(execute)
|
cls._db_create(execute)
|
||||||
created = True
|
|
||||||
|
|
||||||
if major == "0" and int(minor) < 2:
|
|
||||||
if not created:
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "dif_spec"
|
|
||||||
table_new = "dif_adists_spec"
|
|
||||||
reachs = data['id2pid']['river_reach']
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table_new}_tmp " +
|
|
||||||
"(pamhyr_id, dif_default, " +
|
|
||||||
"method, reach, start_rk, end_rk, " +
|
|
||||||
"dif, b, c, scenario) " +
|
|
||||||
"SELECT pamhyr_id, dif_default, " +
|
|
||||||
"method, reach, start_rk, end_rk, " +
|
|
||||||
"dif, b, c, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table_new}_tmp RENAME TO {table_new}")
|
|
||||||
|
|
||||||
cls._db_update_to_0_2_0_set_reach_pid(execute, table_new, reachs)
|
|
||||||
cls._db_update_to_0_2_0_set_dif_pid(execute, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_dif_pid(cls, execute, data):
|
|
||||||
pid_dif = data["id2pid"]["dif_adists"]
|
|
||||||
els = execute(
|
|
||||||
"SELECT pamhyr_id, dif_default FROM dif_adists_spec"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in els:
|
|
||||||
it = iter(row)
|
|
||||||
pid = next(it)
|
|
||||||
dif_id = next(it)
|
|
||||||
|
|
||||||
if dif_id == -1:
|
|
||||||
continue
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE dif_adists_spec" +
|
|
||||||
f"SET dif_default = {pid_dif[dif_id]} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
new = []
|
||||||
status = data['status']
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, deleted, method, reach, start_rk, end_rk, " +
|
"SELECT id, dif_default, method, reach, start_rk, end_rk, " +
|
||||||
"dif, b, c, enabled, scenario " +
|
"dif, b, c, enabled " +
|
||||||
"FROM dif_adists_spec " +
|
"FROM dif_spec " +
|
||||||
f"WHERE dif_default = {data['dif_default_id']} " +
|
f"WHERE dif_default = {data['dif_default_id']} "
|
||||||
f"AND scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) "
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if table is not None:
|
if table is not None:
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
id = row[0]
|
||||||
|
method = row[2]
|
||||||
id = next(it)
|
reach = row[3]
|
||||||
deleted = (next(it) == 1)
|
start_rk = row[4]
|
||||||
method = next(it)
|
end_rk = row[5]
|
||||||
reach = next(it)
|
dif = row[6]
|
||||||
start_rk = next(it)
|
b = row[7]
|
||||||
end_rk = next(it)
|
c = row[8]
|
||||||
dif = next(it)
|
enabled = (row[9] == 1)
|
||||||
b = next(it)
|
|
||||||
c = next(it)
|
|
||||||
enabled = (next(it) == 1)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
new_spec = cls(
|
new_spec = cls(
|
||||||
id=id,
|
id=id,
|
||||||
method=method,
|
method=method,
|
||||||
status=status,
|
status=data['status']
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
if deleted:
|
|
||||||
new_spec.set_as_deleted()
|
|
||||||
|
|
||||||
new_spec.reach = reach
|
new_spec.reach = reach
|
||||||
new_spec.start_rk = start_rk
|
new_spec.start_rk = start_rk
|
||||||
@@ -195,37 +120,31 @@ class DIFAdisTSSpec(SQLSubModel):
|
|||||||
new_spec.c = c
|
new_spec.c = c
|
||||||
new_spec.enabled = enabled
|
new_spec.enabled = enabled
|
||||||
|
|
||||||
loaded.add(pid)
|
|
||||||
new.append(new_spec)
|
new.append(new_spec)
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
if not self.must_be_saved():
|
|
||||||
return True
|
|
||||||
|
|
||||||
dif_default = data['dif_default_id']
|
dif_default = data['dif_default_id']
|
||||||
|
|
||||||
execute(
|
sql = (
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"dif_adists_spec(pamhyr_id, deleted, " +
|
"dif_spec(id, dif_default, method, reach, " +
|
||||||
"dif_default, method, reach, " +
|
"start_rk, end_rk, dif, b, c, enabled) " +
|
||||||
"start_rk, end_rk, dif, b, c, enabled, scenario) " +
|
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, {self._db_format(self.is_deleted())}" +
|
f"{self.id}, " +
|
||||||
f"{dif_default}, " +
|
f"{dif_default}, " +
|
||||||
f"'{self._db_format(self._method)}', " +
|
f"'{self._db_format(self._method)}', " +
|
||||||
f"{self._reach}, " +
|
f"{self._reach}, " +
|
||||||
f"{self._start_rk}, {self._end_rk}, " +
|
f"{self._start_rk}, " +
|
||||||
f"{self._dif}, {self._b}, {self._c}, " +
|
f"{self._end_rk}, " +
|
||||||
|
f"{self._dif}, " +
|
||||||
|
f"{self._b}, " +
|
||||||
|
f"{self._c}, " +
|
||||||
f"{self._enabled}" +
|
f"{self._enabled}" +
|
||||||
f"{self._status.scenario_id}" +
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
# Except.py -- Pamhyr model exceptions
|
# Except.py -- Pamhyr model exceptions
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
+87
-190
@@ -1,5 +1,5 @@
|
|||||||
# Friction.py -- Pamhyr
|
# Friction.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -21,7 +21,6 @@ import logging
|
|||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Scenario import Scenario
|
|
||||||
|
|
||||||
from numpy import interp
|
from numpy import interp
|
||||||
|
|
||||||
@@ -29,49 +28,49 @@ logger = logging.getLogger()
|
|||||||
|
|
||||||
|
|
||||||
class Friction(SQLSubModel):
|
class Friction(SQLSubModel):
|
||||||
def __init__(self, id: int = -1,
|
_id_cnt = 0
|
||||||
reach=None,
|
|
||||||
status=None,
|
|
||||||
owner_scenario=-1):
|
|
||||||
super(Friction, self).__init__(
|
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._reach = reach
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
|
status=None):
|
||||||
|
super(Friction, self).__init__()
|
||||||
|
|
||||||
|
self._status = status
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = Friction._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
Friction._id_cnt = max(self.id + 1, Friction._id_cnt + 1)
|
||||||
|
|
||||||
|
self._name = name
|
||||||
|
self._edge = None
|
||||||
self._begin_rk = 0.0
|
self._begin_rk = 0.0
|
||||||
self._end_rk = 0.0
|
self._end_rk = 0.0
|
||||||
self._begin_strickler = None
|
self._begin_strickler = None
|
||||||
self._end_strickler = None
|
self._end_strickler = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE friction{ext} (
|
CREATE TABLE friction(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
ind INTEGER NOT NULL,
|
ind INTEGER NOT NULL,
|
||||||
begin_rk REAL NOT NULL,
|
begin_rk REAL NOT NULL,
|
||||||
end_rk REAL NOT NULL,
|
end_rk REAL NOT NULL,
|
||||||
reach INTEGER NOT NULL,
|
reach INTEGER NOT NULL,
|
||||||
begin_strickler INTEGER NOT NULL,
|
begin_strickler INTEGER NOT NULL,
|
||||||
end_strickler INTEGER NOT NULL,
|
end_strickler INTEGER NOT NULL,
|
||||||
{Scenario.create_db_add_scenario()},
|
FOREIGN KEY(reach) REFERENCES river_reach(id),
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
FOREIGN KEY(begin_strickler) REFERENCES stricklers(id),
|
||||||
FOREIGN KEY(reach) REFERENCES river_reach(pamhyr_id),
|
FOREIGN KEY(end_strickler) REFERENCES stricklers(id)
|
||||||
FOREIGN KEY(begin_strickler) REFERENCES stricklers(pamhyr_id),
|
|
||||||
FOREIGN KEY(end_strickler) REFERENCES stricklers(pamhyr_id),
|
|
||||||
PRIMARY KEY(pamhyr_id, scenario)
|
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
if ext != "":
|
return True
|
||||||
return True
|
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
if major == minor == "0":
|
if major == minor == "0":
|
||||||
if int(release) < 11:
|
if int(release) < 11:
|
||||||
@@ -79,164 +78,49 @@ class Friction(SQLSubModel):
|
|||||||
"RENAME COLUMN begin_kp TO begin_rk")
|
"RENAME COLUMN begin_kp TO begin_rk")
|
||||||
execute("ALTER TABLE friction RENAME COLUMN end_kp TO end_rk")
|
execute("ALTER TABLE friction RENAME COLUMN end_kp TO end_rk")
|
||||||
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
return True
|
||||||
|
|
||||||
if major == "0" and minor == "1":
|
|
||||||
if int(release) < 2:
|
|
||||||
execute(
|
|
||||||
"ALTER TABLE friction " +
|
|
||||||
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
|
|
||||||
)
|
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "friction"
|
|
||||||
reachs = data['id2pid']['river_reach']
|
|
||||||
stricklers = data['id2pid']['stricklers']
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
"(pamhyr_id, ind, begin_rk, end_rk, reach, " +
|
|
||||||
"begin_strickler, end_strickler, scenario) " +
|
|
||||||
"SELECT pamhyr_id, ind, begin_rk, end_rk, reach, " +
|
|
||||||
"begin_strickler, end_strickler, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
cls._db_update_to_0_2_0_set_reach_pid(execute, table, reachs)
|
|
||||||
cls._db_update_to_0_2_0_set_stricklers_pid(execute, table, stricklers)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_stricklers_pid(cls, execute, table,
|
|
||||||
stricklers):
|
|
||||||
els = execute(
|
|
||||||
f"SELECT pamhyr_id, begin_strickler, end_strickler FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in els:
|
|
||||||
it = iter(row)
|
|
||||||
pid = next(it)
|
|
||||||
b_s_id = next(it)
|
|
||||||
e_s_id = next(it)
|
|
||||||
|
|
||||||
b_s = stricklers[b_s_id] if b_s_id != -1 else -1
|
|
||||||
e_s = stricklers[e_s_id] if e_s_id != -1 else -1
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE {table} " +
|
|
||||||
f"SET begin_strickler = {b_s}, " +
|
|
||||||
f"end_strickler = {e_s} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
new = []
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
reach = data["reach"]
|
reach = data["reach"]
|
||||||
|
status = data["status"]
|
||||||
stricklers = data["stricklers"].stricklers
|
stricklers = data["stricklers"].stricklers
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, deleted, begin_rk, end_rk, " +
|
"SELECT id, ind, begin_rk, end_rk, " +
|
||||||
"begin_strickler, end_strickler, scenario " +
|
"begin_strickler, end_strickler " +
|
||||||
"FROM friction " +
|
f"FROM friction WHERE reach = {reach.id}"
|
||||||
f"WHERE reach = {reach.pamhyr_id} " +
|
|
||||||
f"AND scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) " +
|
|
||||||
"ORDER BY ind ASC"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
id = row[0]
|
||||||
|
ind = row[1]
|
||||||
pid = next(it)
|
|
||||||
deleted = (next(it) == 1)
|
|
||||||
begin_rk = next(it)
|
|
||||||
end_rk = next(it)
|
|
||||||
begin_strickler_pid = int(next(it))
|
|
||||||
end_strickler_pid = int(next(it))
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
# Get stricklers
|
# Get stricklers
|
||||||
begin_strickler = None
|
if int(row[4]) == -1:
|
||||||
if begin_strickler_pid != -1:
|
bs = None
|
||||||
begin_strickler = next(
|
else:
|
||||||
filter(
|
bs = next(filter(lambda s: s.id == row[4], stricklers))
|
||||||
lambda s: s.id == begin_strickler_pid,
|
|
||||||
stricklers
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
end_strickler = None
|
if int(row[5]) == -1:
|
||||||
if end_strickler_pid != -1:
|
es = None
|
||||||
end_strickler = next(
|
else:
|
||||||
filter(
|
es = next(filter(lambda s: s.id == row[5], stricklers))
|
||||||
lambda s: s.id == begin_strickler_pid,
|
|
||||||
stricklers
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
new_friction = cls(
|
# Create friction
|
||||||
id=pid, status=data['status'],
|
sec = cls(status=status)
|
||||||
owner_scenario=owner_scenario
|
sec.edge = reach
|
||||||
)
|
sec.begin_rk = row[2]
|
||||||
if deleted:
|
sec.end_rk = row[3]
|
||||||
f.set_as_deleted()
|
sec.begin_strickler = bs
|
||||||
|
sec.end_strickler = es
|
||||||
|
|
||||||
new_friction.reach = reach
|
new.append((ind, sec))
|
||||||
new_friction.begin_rk = begin_rk
|
|
||||||
new_friction.end_rk = end_rk
|
|
||||||
new_friction.begin_strickler = begin_strickler
|
|
||||||
new_friction.end_strickler = begin_strickler
|
|
||||||
|
|
||||||
loaded.add(pid)
|
|
||||||
new.append(new_friction)
|
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def cloned_for(self, reach):
|
|
||||||
new = Friction(reach=reach, status=self._status)
|
|
||||||
|
|
||||||
new.reach = reach
|
|
||||||
new.begin_rk = self._begin_rk
|
|
||||||
new.end_rk = self._end_rk
|
|
||||||
new.begin_strickler = self._begin_strickler
|
|
||||||
new.end_strickler = self._begin_strickler
|
|
||||||
|
|
||||||
return new
|
|
||||||
|
|
||||||
def must_be_saved(self):
|
|
||||||
ssi = self._status.scenario_id
|
|
||||||
|
|
||||||
return (
|
|
||||||
self._begin_strickler._owner_scenario == ssi
|
|
||||||
# or self._end_strickler._owner_scenario == ssi
|
|
||||||
or super(Friction, self).must_be_saved()
|
|
||||||
)
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
if not self.must_be_saved():
|
|
||||||
return True
|
|
||||||
|
|
||||||
ind = data["ind"]
|
ind = data["ind"]
|
||||||
|
|
||||||
b_s_id = -1
|
b_s_id = -1
|
||||||
@@ -249,34 +133,47 @@ class Friction(SQLSubModel):
|
|||||||
|
|
||||||
execute(
|
execute(
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"friction(pamhyr_id, deleted, ind, begin_rk, end_rk, " +
|
"friction(id, ind, begin_rk, end_rk, " +
|
||||||
"reach, begin_strickler, end_strickler, scenario) " +
|
"reach, begin_strickler, end_strickler) " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, {self._db_format(self.is_deleted())}, " +
|
f"{self.id}, {ind}, " +
|
||||||
f"{ind}, {self._begin_rk}, {self._end_rk}, " +
|
f"{self._begin_rk}, {self._end_rk}, " +
|
||||||
f"{self._reach.id}, {b_s_id}, {e_s_id}, " +
|
f"{self._edge.id}, " +
|
||||||
f"{self._status.scenario_id}" +
|
f"{b_s_id}, {e_s_id}" +
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def reach(self):
|
def name(self):
|
||||||
return self._reach
|
return self._name
|
||||||
|
|
||||||
@reach.setter
|
@name.setter
|
||||||
def reach(self, reach):
|
def name(self, name):
|
||||||
self._reach = reach
|
self._name = name
|
||||||
if (reach is not None and
|
|
||||||
self._begin_rk == 0.0 and
|
|
||||||
self._end_rk == 0.0):
|
|
||||||
self._begin_rk = self._reach.reach.get_rk_min()
|
|
||||||
self._end_rk = self._reach.reach.get_rk_max()
|
|
||||||
self._status.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def has_reach(self):
|
@property
|
||||||
return self._reach is not None
|
def edge(self):
|
||||||
|
return self._edge
|
||||||
|
|
||||||
|
@property
|
||||||
|
def reach(self):
|
||||||
|
return self._edge
|
||||||
|
|
||||||
|
@edge.setter
|
||||||
|
def edge(self, edge):
|
||||||
|
self._edge = edge
|
||||||
|
if (edge is not None and
|
||||||
|
self._begin_rk == 0.0 and
|
||||||
|
self._end_rk == 0.0):
|
||||||
|
self._begin_rk = self._edge.reach.get_rk_min()
|
||||||
|
self._end_rk = self._edge.reach.get_rk_max()
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
|
def has_edge(self):
|
||||||
|
return self._edge is not None
|
||||||
|
|
||||||
def has_coefficient(self):
|
def has_coefficient(self):
|
||||||
return (
|
return (
|
||||||
@@ -284,7 +181,7 @@ class Friction(SQLSubModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def is_full_defined(self):
|
def is_full_defined(self):
|
||||||
return self.has_reach() and self.has_coefficient()
|
return self.has_edge() and self.has_coefficient()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def begin_rk(self):
|
def begin_rk(self):
|
||||||
@@ -292,11 +189,11 @@ class Friction(SQLSubModel):
|
|||||||
|
|
||||||
@begin_rk.setter
|
@begin_rk.setter
|
||||||
def begin_rk(self, begin_rk):
|
def begin_rk(self, begin_rk):
|
||||||
if self._reach is None:
|
if self._edge is None:
|
||||||
self._begin_rk = begin_rk
|
self._begin_rk = begin_rk
|
||||||
else:
|
else:
|
||||||
_min = self._reach.reach.get_rk_min()
|
_min = self._edge.reach.get_rk_min()
|
||||||
_max = self._reach.reach.get_rk_max()
|
_max = self._edge.reach.get_rk_max()
|
||||||
|
|
||||||
if _min <= begin_rk <= _max:
|
if _min <= begin_rk <= _max:
|
||||||
self._begin_rk = begin_rk
|
self._begin_rk = begin_rk
|
||||||
@@ -309,11 +206,11 @@ class Friction(SQLSubModel):
|
|||||||
|
|
||||||
@end_rk.setter
|
@end_rk.setter
|
||||||
def end_rk(self, end_rk):
|
def end_rk(self, end_rk):
|
||||||
if self._reach is None:
|
if self._edge is None:
|
||||||
self._end_rk = end_rk
|
self._end_rk = end_rk
|
||||||
else:
|
else:
|
||||||
_min = self._reach.reach.get_rk_min()
|
_min = self._edge.reach.get_rk_min()
|
||||||
_max = self._reach.reach.get_rk_max()
|
_max = self._edge.reach.get_rk_max()
|
||||||
|
|
||||||
if _min <= end_rk <= _max:
|
if _min <= end_rk <= _max:
|
||||||
self._end_rk = end_rk
|
self._end_rk = end_rk
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# FrictionList.py -- Pamhyr
|
# FrictionList.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -20,10 +20,9 @@ import logging
|
|||||||
|
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
from functools import reduce
|
|
||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Tools.PamhyrListExt import PamhyrModelList
|
from Model.Tools.PamhyrList import PamhyrModelList
|
||||||
|
|
||||||
from Model.Friction.Friction import Friction
|
from Model.Friction.Friction import Friction
|
||||||
|
|
||||||
@@ -40,30 +39,30 @@ class FrictionList(PamhyrModelList):
|
|||||||
execute("ALTER TABLE `section` RENAME TO `friction`")
|
execute("ALTER TABLE `section` RENAME TO `friction`")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
if version == "0.0.0":
|
if version == "0.0.0":
|
||||||
|
logger.info(f"Update friction TABLE from {version}")
|
||||||
cls._db_update_0_0_1(execute, version)
|
cls._db_update_0_0_1(execute, version)
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
return cls._update_submodel(execute, version)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = cls(status=data['status'])
|
new = cls(status=data['status'])
|
||||||
|
|
||||||
new._lst = Friction._db_load(
|
ilst = Friction._db_load(
|
||||||
execute, data
|
execute, data
|
||||||
)
|
)
|
||||||
|
|
||||||
|
new._lst = list(map(lambda x: x[1], sorted(ilst)))
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
frictions = self.lst
|
frictions = self.lst
|
||||||
|
|
||||||
reach = data["reach"]
|
reach = data["reach"]
|
||||||
execute(
|
execute(f"DELETE FROM friction WHERE reach = {reach.id}")
|
||||||
f"DELETE FROM friction WHERE reach = {reach.id} " +
|
|
||||||
f"AND scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
ok = True
|
ok = True
|
||||||
ind = 0
|
ind = 0
|
||||||
@@ -87,38 +86,19 @@ class FrictionList(PamhyrModelList):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lst(self):
|
||||||
|
if not isinstance(self._lst, list):
|
||||||
|
self._lst = self._get_frictions_list()
|
||||||
|
|
||||||
|
return self._lst
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def frictions(self):
|
def frictions(self):
|
||||||
return self.lst
|
return self.lst
|
||||||
|
|
||||||
def new(self, reach, index):
|
def new(self, index):
|
||||||
n = Friction(reach=reach, status=self._status)
|
n = Friction(status=self._status)
|
||||||
self._lst.insert(index, n)
|
self._lst.insert(index, n)
|
||||||
self._status.modified()
|
self._status.modified()
|
||||||
return n
|
return n
|
||||||
|
|
||||||
def _splited_for(self, reach):
|
|
||||||
new = FrictionList(status=self._status)
|
|
||||||
|
|
||||||
rk_min, rk_max = reduce(
|
|
||||||
lambda mm, p: (
|
|
||||||
min(mm[0], p.rk),
|
|
||||||
max(mm[1], p.rk)
|
|
||||||
),
|
|
||||||
reach.reach.profiles,
|
|
||||||
((999 ** 100), -(999 ** 100))
|
|
||||||
)
|
|
||||||
|
|
||||||
frictions = list(
|
|
||||||
filter(
|
|
||||||
lambda f: ((rk_min <= f.begin_rk <= rk_max) or
|
|
||||||
(rk_min <= f.end_rk <= rk_max)),
|
|
||||||
self._lst))
|
|
||||||
|
|
||||||
for ind, friction in enumerate(frictions):
|
|
||||||
n = friction.cloned_for(reach)
|
|
||||||
|
|
||||||
new._lst.insert(ind, n)
|
|
||||||
new._status.modified()
|
|
||||||
|
|
||||||
return new
|
|
||||||
@@ -1,368 +0,0 @@
|
|||||||
# GeoTIFF.py -- Pamhyr
|
|
||||||
# Copyright (C) 2024-2025 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 <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import os
|
|
||||||
import struct
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from functools import reduce
|
|
||||||
|
|
||||||
from tools import trace, timer
|
|
||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
|
||||||
from Model.Except import NotImplementedMethodeError
|
|
||||||
from Model.Scenario import Scenario
|
|
||||||
|
|
||||||
try:
|
|
||||||
import rasterio
|
|
||||||
import rasterio.control
|
|
||||||
import rasterio.crs
|
|
||||||
import rasterio.sample
|
|
||||||
import rasterio.vrt
|
|
||||||
import rasterio._features
|
|
||||||
|
|
||||||
from rasterio.io import MemoryFile
|
|
||||||
_rasterio_loaded = True
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Module 'rasterio' is not available: {e}")
|
|
||||||
_rasterio_loaded = False
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
|
||||||
|
|
||||||
|
|
||||||
class GeoTIFF(SQLSubModel):
|
|
||||||
_sub_classes = []
|
|
||||||
|
|
||||||
def __init__(self, id: int = -1, enabled=True,
|
|
||||||
name="", description="",
|
|
||||||
path="", coordinates=None,
|
|
||||||
status=None, owner_scenario=-1):
|
|
||||||
super(GeoTIFF, self).__init__(
|
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._enabled = enabled
|
|
||||||
self._name = f"GeoTIFF #{self._pamhyr_id}" if name == "" else name
|
|
||||||
self._description = description
|
|
||||||
|
|
||||||
self._file_bytes = b''
|
|
||||||
if coordinates is None:
|
|
||||||
self._coordinates = {
|
|
||||||
"bottom": 0.0,
|
|
||||||
"top": 0.0,
|
|
||||||
"left": 0.0,
|
|
||||||
"right": 0.0,
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
self._coordinates = coordinates
|
|
||||||
self._file_name = ""
|
|
||||||
|
|
||||||
if path != "":
|
|
||||||
self.read_file(path)
|
|
||||||
|
|
||||||
self._memfile = None
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
|
||||||
value = None
|
|
||||||
|
|
||||||
if key == "enabled":
|
|
||||||
value = self._enabled
|
|
||||||
elif key == "name":
|
|
||||||
value = self.name
|
|
||||||
elif key == "description":
|
|
||||||
value = self.description
|
|
||||||
elif key == "file_name":
|
|
||||||
value = self.file_name
|
|
||||||
elif key == "coordinates":
|
|
||||||
value = self.coordinates
|
|
||||||
elif key == "coordinates_bottom":
|
|
||||||
value = self.coordinates["bottom"]
|
|
||||||
elif key == "coordinates_top":
|
|
||||||
value = self.coordinates["top"]
|
|
||||||
elif key == "coordinates_left":
|
|
||||||
value = self.coordinates["left"]
|
|
||||||
elif key == "coordinates_right":
|
|
||||||
value = self.coordinates["right"]
|
|
||||||
elif key == "memfile":
|
|
||||||
value = self.memfile
|
|
||||||
|
|
||||||
return value
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
if key == "enabled":
|
|
||||||
self.enabled = value
|
|
||||||
elif key == "name":
|
|
||||||
self.name = value
|
|
||||||
elif key == "description":
|
|
||||||
self.description = value
|
|
||||||
elif key == "file_name":
|
|
||||||
if self._file_name != value:
|
|
||||||
self.read_file(value)
|
|
||||||
elif key == "coordinates":
|
|
||||||
self.coordinates = value
|
|
||||||
elif key == "coordinates_bottom" or key == "bottom":
|
|
||||||
self.coordinates["bottom"] = value
|
|
||||||
elif key == "coordinates_top" or key == "top":
|
|
||||||
self.coordinates["top"] = value
|
|
||||||
elif key == "coordinates_left" or key == "left":
|
|
||||||
self.coordinates["left"] = value
|
|
||||||
elif key == "coordinates_right" or key == "right":
|
|
||||||
self.coordinates["right"] = value
|
|
||||||
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def enabled(self):
|
|
||||||
return self._enabled
|
|
||||||
|
|
||||||
@enabled.setter
|
|
||||||
def enabled(self, enabled):
|
|
||||||
self._enabled = enabled
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def is_enabled(self):
|
|
||||||
return self._enabled
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
return self._name
|
|
||||||
|
|
||||||
@name.setter
|
|
||||||
def name(self, name):
|
|
||||||
self._name = name
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def description(self):
|
|
||||||
return self._description
|
|
||||||
|
|
||||||
@description.setter
|
|
||||||
def description(self, description):
|
|
||||||
self._description = description
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def file_name(self):
|
|
||||||
return self._file_name
|
|
||||||
|
|
||||||
@file_name.setter
|
|
||||||
def file_name(self, file_name):
|
|
||||||
self._file_name = file_name
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def coordinates(self):
|
|
||||||
return self._coordinates
|
|
||||||
|
|
||||||
@coordinates.setter
|
|
||||||
def coordinates(self, coordinates):
|
|
||||||
self._coordinates = coordinates
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def coord_bottom(self):
|
|
||||||
if self._coordinates is None:
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
return self._coordinates["bottom"]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def coord_top(self):
|
|
||||||
if self._coordinates is None:
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
return self._coordinates["top"]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def coord_left(self):
|
|
||||||
if self._coordinates is None:
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
return self._coordinates["left"]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def coord_right(self):
|
|
||||||
if self._coordinates is None:
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
return self._coordinates["right"]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def memfile(self):
|
|
||||||
if not _rasterio_loaded:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if self._file_bytes == b'':
|
|
||||||
return None
|
|
||||||
|
|
||||||
if self._memfile is None:
|
|
||||||
self._memfile = MemoryFile()
|
|
||||||
self._memfile.write(self._file_bytes)
|
|
||||||
|
|
||||||
return self._memfile
|
|
||||||
|
|
||||||
def read_file(self, path):
|
|
||||||
logger.debug(f"Read GeoTIFF file at : '{path}'")
|
|
||||||
|
|
||||||
self._file_name = path
|
|
||||||
self._file_bytes = b''
|
|
||||||
self._memfile = None
|
|
||||||
|
|
||||||
nbytes = 0
|
|
||||||
|
|
||||||
with open(path, "rb") as f:
|
|
||||||
while True:
|
|
||||||
data = f.read(4096)
|
|
||||||
if not data:
|
|
||||||
break
|
|
||||||
|
|
||||||
nbytes += len(data)
|
|
||||||
self._file_bytes += data
|
|
||||||
|
|
||||||
logger.debug(f"Read GeoTIFF: {nbytes} bytes readed")
|
|
||||||
|
|
||||||
def write_file(self, path):
|
|
||||||
with open(path, "w+b") as f:
|
|
||||||
f.write(self._file_bytes)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_create(cls, execute, ext=""):
|
|
||||||
execute(f"""
|
|
||||||
CREATE TABLE geotiff{ext} (
|
|
||||||
{cls.create_db_add_pamhyr_id()},
|
|
||||||
enabled BOOLEAN NOT NULL,
|
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
description TEXT NOT NULL,
|
|
||||||
file_name TEXT NOT NULL,
|
|
||||||
file_bytes BLOB NOT NULL,
|
|
||||||
coordinates_bottom REAL NOT NULL,
|
|
||||||
coordinates_top REAL NOT NULL,
|
|
||||||
coordinates_left REAL NOT NULL,
|
|
||||||
coordinates_right REAL 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)
|
|
||||||
|
|
||||||
if major == "0" and int(minor) == 2:
|
|
||||||
if int(release) < 3:
|
|
||||||
cls._db_create(execute)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_load(cls, execute, data=None):
|
|
||||||
new = []
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
|
||||||
"SELECT pamhyr_id, enabled, deleted, " +
|
|
||||||
"name, description, file_name, file_bytes, " +
|
|
||||||
"coordinates_bottom, coordinates_top, " +
|
|
||||||
"coordinates_left, coordinates_right, " +
|
|
||||||
"scenario " +
|
|
||||||
"FROM geotiff " +
|
|
||||||
f"WHERE scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})"
|
|
||||||
)
|
|
||||||
|
|
||||||
if table is not None:
|
|
||||||
for row in table:
|
|
||||||
it = iter(row)
|
|
||||||
|
|
||||||
id = next(it)
|
|
||||||
enabled = (next(it) == 1)
|
|
||||||
deleted = (next(it) == 1)
|
|
||||||
name = next(it)
|
|
||||||
description = next(it)
|
|
||||||
file_name = next(it)
|
|
||||||
file_bytes = next(it)
|
|
||||||
coordinates_bottom = next(it)
|
|
||||||
coordinates_top = next(it)
|
|
||||||
coordinates_left = next(it)
|
|
||||||
coordinates_right = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
f = cls(
|
|
||||||
id=id, enabled=enabled, name=name,
|
|
||||||
description=description, coordinates={
|
|
||||||
"bottom": coordinates_bottom,
|
|
||||||
"top": coordinates_top,
|
|
||||||
"left": coordinates_left,
|
|
||||||
"right": coordinates_right,
|
|
||||||
},
|
|
||||||
status=data['status'],
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
if deleted:
|
|
||||||
f.set_as_deleted()
|
|
||||||
|
|
||||||
f._file_bytes = file_bytes
|
|
||||||
|
|
||||||
loaded.add(id)
|
|
||||||
new.append(f)
|
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
|
||||||
if not self.must_be_saved():
|
|
||||||
return True
|
|
||||||
|
|
||||||
execute(
|
|
||||||
"INSERT INTO geotiff (" +
|
|
||||||
"pamhyr_id, enabled, deleted, " +
|
|
||||||
"name, description, file_name, file_bytes, " +
|
|
||||||
"coordinates_bottom, coordinates_top, " +
|
|
||||||
"coordinates_left, coordinates_right, " +
|
|
||||||
"scenario) " +
|
|
||||||
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",
|
|
||||||
self._pamhyr_id,
|
|
||||||
self._enabled,
|
|
||||||
self.is_deleted(),
|
|
||||||
self.name,
|
|
||||||
self.description,
|
|
||||||
self.file_name,
|
|
||||||
self._file_bytes,
|
|
||||||
self.coordinates['bottom'],
|
|
||||||
self.coordinates['top'],
|
|
||||||
self.coordinates['left'],
|
|
||||||
self.coordinates['right'],
|
|
||||||
self._status.scenario_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
# GeoTIFFList.py -- Pamhyr
|
|
||||||
# Copyright (C) 2024-2025 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 <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from tools import trace, timer
|
|
||||||
|
|
||||||
from Model.Except import NotImplementedMethodeError
|
|
||||||
from Model.Tools.PamhyrListExt import PamhyrModelList
|
|
||||||
from Model.GeoTIFF.GeoTIFF import GeoTIFF
|
|
||||||
|
|
||||||
|
|
||||||
class GeoTIFFList(PamhyrModelList):
|
|
||||||
_sub_classes = [GeoTIFF]
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_load(cls, execute, data=None):
|
|
||||||
new = cls(status=data["status"])
|
|
||||||
|
|
||||||
new._lst = GeoTIFF._db_load(execute, data)
|
|
||||||
|
|
||||||
return new
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
|
||||||
ok = True
|
|
||||||
|
|
||||||
# Delete previous data
|
|
||||||
execute(
|
|
||||||
"DELETE FROM geotiff " +
|
|
||||||
f"WHERE scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
for gt in self._lst:
|
|
||||||
ok &= gt._db_save(execute, data)
|
|
||||||
|
|
||||||
return ok
|
|
||||||
|
|
||||||
@property
|
|
||||||
def files(self):
|
|
||||||
return self.lst
|
|
||||||
|
|
||||||
def new(self, index):
|
|
||||||
n = GeoTIFF(status=self._status)
|
|
||||||
self.insert(index, n)
|
|
||||||
self._status.modified()
|
|
||||||
return n
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# Point.py -- Pamhyr
|
# Point.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,18 +16,14 @@
|
|||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
|
|
||||||
class Point(SQLSubModel):
|
class Point(object):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, name: str = "", profile=None, status=None):
|
||||||
profile=None, status=None,
|
super(Point, self).__init__()
|
||||||
owner_scenario=-1):
|
|
||||||
super(Point, self).__init__(
|
self._status = status
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self._profile = profile
|
self._profile = profile
|
||||||
@@ -47,8 +43,6 @@ class Point(SQLSubModel):
|
|||||||
else:
|
else:
|
||||||
self._sl = sl
|
self._sl = sl
|
||||||
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
return self._name
|
return self._name
|
||||||
@@ -56,9 +50,9 @@ class Point(SQLSubModel):
|
|||||||
@name.setter
|
@name.setter
|
||||||
def name(self, name):
|
def name(self, name):
|
||||||
self._name = name
|
self._name = name
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def is_named(self):
|
def point_is_named(self):
|
||||||
"""
|
"""
|
||||||
Returns:
|
Returns:
|
||||||
True if the point is named.
|
True if the point is named.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# PointXY.py -- Pamhyr
|
# PointXY.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
+50
-206
@@ -1,5 +1,5 @@
|
|||||||
# PointXYZ.py -- Pamhyr
|
# PointXYZ.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,40 +16,30 @@
|
|||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from math import dist
|
from math import dist
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Scenario import Scenario
|
|
||||||
from Model.Geometry.Point import Point
|
from Model.Geometry.Point import Point
|
||||||
|
|
||||||
logger = logging.getLogger()
|
|
||||||
|
|
||||||
|
class PointXYZ(Point, SQLSubModel):
|
||||||
class PointXYZ(Point):
|
|
||||||
_sub_classes = []
|
_sub_classes = []
|
||||||
|
|
||||||
def __init__(self, id: int = -1,
|
def __init__(self, x: float = 0.0, y: float = 0.0, z: float = 0.0,
|
||||||
x: float = 0.0, y: float = 0.0, z: float = 0.0,
|
name: str = "", profile=None, status=None):
|
||||||
name: str = "", profile=None,
|
|
||||||
status=None, owner_scenario=-1):
|
|
||||||
super(PointXYZ, self).__init__(
|
super(PointXYZ, self).__init__(
|
||||||
id=id, name=name, profile=profile, status=status,
|
name=name, profile=profile, status=status)
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._x = float(x)
|
self._x = float(x)
|
||||||
self._y = float(y)
|
self._y = float(y)
|
||||||
self._z = float(z)
|
self._z = float(z)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE geometry_pointXYZ{ext} (
|
CREATE TABLE geometry_pointXYZ(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
ind INTEGER NOT NULL,
|
ind INTEGER NOT NULL,
|
||||||
name TEXT,
|
name TEXT,
|
||||||
x INTEGER NOT NULL,
|
x INTEGER NOT NULL,
|
||||||
@@ -57,19 +47,17 @@ class PointXYZ(Point):
|
|||||||
z INTEGER NOT NULL,
|
z INTEGER NOT NULL,
|
||||||
profile INTEGER NOT NULL,
|
profile INTEGER NOT NULL,
|
||||||
sl INTEGER,
|
sl INTEGER,
|
||||||
{Scenario.create_db_add_scenario()},
|
FOREIGN KEY(profile) REFERENCES geometry_profileXYZ(id),
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
FOREIGN KEY(sl) REFERENCES sedimentary_layer(id)
|
||||||
FOREIGN KEY(profile)
|
|
||||||
REFERENCES geometry_profileXYZ(pamhyr_id),
|
|
||||||
FOREIGN KEY(sl) REFERENCES sedimentary_layer(pamhyr_id),
|
|
||||||
PRIMARY KEY(pamhyr_id, scenario)
|
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
|
cls._update_submodel(execute, version)
|
||||||
|
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
if major == minor == "0":
|
if major == minor == "0":
|
||||||
if int(release) < 2:
|
if int(release) < 2:
|
||||||
@@ -80,174 +68,63 @@ class PointXYZ(Point):
|
|||||||
REFERENCES sedimentary_layer(id)
|
REFERENCES sedimentary_layer(id)
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
return True
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
if major == "0" and minor == "1":
|
|
||||||
if int(release) < 2:
|
|
||||||
execute(
|
|
||||||
"ALTER TABLE geometry_pointXYZ " +
|
|
||||||
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
|
|
||||||
)
|
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "geometry_pointXYZ"
|
|
||||||
id2pid = data['id2pid']
|
|
||||||
profiles = id2pid['geometry_profileXYZ']
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
"(pamhyr_id, ind, name, x, y, z, profile, sl, scenario) " +
|
|
||||||
"SELECT pamhyr_id, ind, name, x, y, z, profile, sl, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
cls._db_update_to_0_2_0_set_profile_pid(execute, table, profiles)
|
|
||||||
|
|
||||||
if 'sedimentary_layer' in id2pid:
|
|
||||||
sl = id2pid['sedimentary_layer']
|
|
||||||
cls._db_update_to_0_2_0_set_sl_pid(execute, table, sl)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_profile_pid(cls, execute, table, profiles):
|
|
||||||
els = execute(
|
|
||||||
f"SELECT pamhyr_id, profile FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in els:
|
|
||||||
it = iter(row)
|
|
||||||
pid = next(it)
|
|
||||||
profile_id = next(it)
|
|
||||||
|
|
||||||
if profile_id not in profiles:
|
|
||||||
logger.warning(
|
|
||||||
f"This point as no associated profile ({profile_id})"
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE {table} " +
|
|
||||||
f"SET profile = {profiles[profile_id]} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_sl_pid(cls, execute, table, sl):
|
|
||||||
els = execute(
|
|
||||||
f"SELECT pamhyr_id, sl FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in els:
|
|
||||||
it = iter(row)
|
|
||||||
pid = next(it)
|
|
||||||
sl_id = next(it)
|
|
||||||
|
|
||||||
if sl_id == -1:
|
|
||||||
continue
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE {table} " +
|
|
||||||
f"SET sl = {sl[sl_id]} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
|
||||||
|
|
||||||
status = data["status"]
|
status = data["status"]
|
||||||
profile = data["profile"]
|
profile = data["profile"]
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, ind, deleted, " +
|
"SELECT ind, name, x, y, z, sl " +
|
||||||
"name, x, y, z, sl, scenario " +
|
|
||||||
"FROM geometry_pointXYZ " +
|
"FROM geometry_pointXYZ " +
|
||||||
f"WHERE profile = {profile.pamhyr_id} " +
|
f"WHERE profile = {profile.id}"
|
||||||
f"AND scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) " +
|
|
||||||
"ORDER BY ind ASC"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Fill points list with new point
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
ind = row[0]
|
||||||
|
name = row[1]
|
||||||
|
x = row[2]
|
||||||
|
y = row[3]
|
||||||
|
z = row[4]
|
||||||
|
sl = row[5]
|
||||||
|
|
||||||
pid = next(it)
|
new = cls(
|
||||||
ind = next(it)
|
|
||||||
deleted = (next(it) == 1)
|
|
||||||
name = next(it)
|
|
||||||
x = next(it)
|
|
||||||
y = next(it)
|
|
||||||
z = next(it)
|
|
||||||
sl = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
point = cls(
|
|
||||||
id=pid,
|
|
||||||
name=name,
|
name=name,
|
||||||
x=x, y=y, z=z,
|
x=x, y=y, z=z,
|
||||||
profile=profile,
|
profile=profile,
|
||||||
status=status,
|
status=status
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
if deleted:
|
|
||||||
point.set_as_deleted()
|
|
||||||
|
|
||||||
if sl == -1 or sl is None:
|
if sl == -1 or sl is None:
|
||||||
point._sl = None
|
new._sl = None
|
||||||
else:
|
else:
|
||||||
point._sl = next(
|
new._sl = next(
|
||||||
filter(
|
filter(
|
||||||
lambda s: s.pamhyr_id == sl,
|
lambda s: s.id == sl,
|
||||||
data["sediment_layers_list"].sediment_layers
|
data["sediment_layers_list"].sediment_layers
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
loaded.add(pid)
|
yield ind, new
|
||||||
new.append((ind, point))
|
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
if not self.must_be_saved():
|
|
||||||
return True
|
|
||||||
|
|
||||||
profile = data["profile"]
|
profile = data["profile"]
|
||||||
ind = data["ind"]
|
ind = data["ind"]
|
||||||
|
|
||||||
sl = self._sl.pamhyr_id if self._sl is not None else -1
|
sl = self._sl.id if self._sl is not None else -1
|
||||||
|
|
||||||
execute(
|
sql = (
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"geometry_pointXYZ(pamhyr_id, deleted, ind, name, " +
|
"geometry_pointXYZ(ind, name, x, y, z, profile, sl) " +
|
||||||
"x, y, z, profile, sl, scenario) " +
|
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.pamhyr_id}, {self._db_format(self.is_deleted())}, " +
|
|
||||||
f"{ind}, '{self._db_format(self._name)}', " +
|
f"{ind}, '{self._db_format(self._name)}', " +
|
||||||
f"{self.x}, {self.y}, {self.z}, " +
|
f"{self.x}, {self.y}, {self.z}, " +
|
||||||
f"{profile.pamhyr_id}, {sl}, " +
|
f"{profile.id}, {sl}" +
|
||||||
f"{self._status.scenario_id}" +
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -273,44 +150,9 @@ class PointXYZ(Point):
|
|||||||
|
|
||||||
return point
|
return point
|
||||||
|
|
||||||
def copy(self):
|
|
||||||
new_p = PointXYZ(
|
|
||||||
id=-1,
|
|
||||||
name=self.name,
|
|
||||||
x=self.x, y=self.y, z=self.z,
|
|
||||||
profile=self._profile,
|
|
||||||
status=self._status
|
|
||||||
)
|
|
||||||
if self.is_deleted():
|
|
||||||
new_p.set_as_deleted()
|
|
||||||
|
|
||||||
new_p._sl = self.sl
|
|
||||||
|
|
||||||
new_p.modified()
|
|
||||||
return new_p
|
|
||||||
|
|
||||||
def cloned_for(self, new_profile):
|
|
||||||
new_p = PointXYZ(
|
|
||||||
id=-1,
|
|
||||||
name=self.name,
|
|
||||||
x=self.x, y=self.y, z=self.z,
|
|
||||||
profile=new_profile,
|
|
||||||
status=self._status
|
|
||||||
)
|
|
||||||
if self.is_deleted():
|
|
||||||
new_p.set_as_deleted()
|
|
||||||
|
|
||||||
new_p._sl = self.sl
|
|
||||||
|
|
||||||
new_p.modified()
|
|
||||||
return new_p
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"({self._x}, {self._y}, {self._z}, {self._name})"
|
return f"({self._x}, {self._y}, {self._z}, {self._name})"
|
||||||
|
|
||||||
def __gt__(self, other):
|
|
||||||
return self.y > other.y
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def x(self):
|
def x(self):
|
||||||
return self._x
|
return self._x
|
||||||
@@ -318,7 +160,7 @@ class PointXYZ(Point):
|
|||||||
@x.setter
|
@x.setter
|
||||||
def x(self, value):
|
def x(self, value):
|
||||||
self._x = float(value)
|
self._x = float(value)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def y(self):
|
def y(self):
|
||||||
@@ -327,7 +169,7 @@ class PointXYZ(Point):
|
|||||||
@y.setter
|
@y.setter
|
||||||
def y(self, value):
|
def y(self, value):
|
||||||
self._y = float(value)
|
self._y = float(value)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def z(self):
|
def z(self):
|
||||||
@@ -336,10 +178,7 @@ class PointXYZ(Point):
|
|||||||
@z.setter
|
@z.setter
|
||||||
def z(self, value):
|
def z(self, value):
|
||||||
self._z = float(value)
|
self._z = float(value)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def get_coordinate(self):
|
|
||||||
return (self._x, self._y, self._z)
|
|
||||||
|
|
||||||
def is_nan(self):
|
def is_nan(self):
|
||||||
"""
|
"""
|
||||||
@@ -387,13 +226,8 @@ class PointXYZ(Point):
|
|||||||
a = PointXYZ.distance(p1, p2)
|
a = PointXYZ.distance(p1, p2)
|
||||||
b = PointXYZ.distance(p2, p3)
|
b = PointXYZ.distance(p2, p3)
|
||||||
c = PointXYZ.distance(p3, p1)
|
c = PointXYZ.distance(p3, p1)
|
||||||
|
s = (a + b + c) / 2
|
||||||
s = float((a + b + c) / 2)
|
return (s*(s-a) * (s-b)*(s-c)) ** 0.5
|
||||||
res = abs(
|
|
||||||
s * (s - a) * (s - b) * (s - c)
|
|
||||||
) ** 0.5
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
def dist_from_seg(self, p1, p2):
|
def dist_from_seg(self, p1, p2):
|
||||||
a2 = pow(PointXYZ.distance(self, p1), 2)
|
a2 = pow(PointXYZ.distance(self, p1), 2)
|
||||||
@@ -406,3 +240,13 @@ class PointXYZ(Point):
|
|||||||
d = np.sqrt(abs(a2 - pow((c2 - b2 + a2) / (2*np.sqrt(c2)), 2)))
|
d = np.sqrt(abs(a2 - pow((c2 - b2 + a2) / (2*np.sqrt(c2)), 2)))
|
||||||
|
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
p = PointXYZ(self.x,
|
||||||
|
self.y,
|
||||||
|
self.z,
|
||||||
|
self.name,
|
||||||
|
self._profile,
|
||||||
|
self._status)
|
||||||
|
p.sl = self.sl
|
||||||
|
return p
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# Profile.py -- Pamhyr
|
# Profile.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -28,15 +28,23 @@ logger = logging.getLogger()
|
|||||||
|
|
||||||
|
|
||||||
class Profile(object):
|
class Profile(object):
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
def __init__(self, id: int = -1, num: int = 0,
|
def __init__(self, id: int = -1, num: int = 0,
|
||||||
rk: float = 0.0, name: str = "",
|
rk: float = 0.0, name: str = "",
|
||||||
code1: int = 0, code2: int = 0,
|
code1: int = 0, code2: int = 0,
|
||||||
_type: str = "", reach=None,
|
_type: str = "", reach=None,
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(Profile, self).__init__(
|
super(Profile, self).__init__()
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
self._status = status
|
||||||
)
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = Profile._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
Profile._id_cnt = max(self.id + 1, Profile._id_cnt + 1)
|
||||||
|
|
||||||
self._num = int(num)
|
self._num = int(num)
|
||||||
self._code1 = int(code1)
|
self._code1 = int(code1)
|
||||||
@@ -58,19 +66,24 @@ class Profile(object):
|
|||||||
return len(self.points)
|
return len(self.points)
|
||||||
|
|
||||||
def _get_points_list(self):
|
def _get_points_list(self):
|
||||||
return list(self._points)
|
# Points list generator is type (int, Point) with the first
|
||||||
|
# element the index of the Point in list
|
||||||
|
return list(
|
||||||
|
map(
|
||||||
|
lambda p: p[1],
|
||||||
|
sorted(
|
||||||
|
self._points,
|
||||||
|
key=lambda p: p[0]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def points(self):
|
def points(self):
|
||||||
if not isinstance(self._points, list):
|
if not isinstance(self._points, list):
|
||||||
self._points = self._get_points_list()
|
self._points = self._get_points_list()
|
||||||
|
|
||||||
return list(
|
return self._points
|
||||||
filter(
|
|
||||||
lambda p: not p.is_deleted(),
|
|
||||||
self._points
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def point(self, index):
|
def point(self, index):
|
||||||
return self.points[index]
|
return self.points[index]
|
||||||
@@ -80,9 +93,6 @@ class Profile(object):
|
|||||||
return self.points[0]
|
return self.points[0]
|
||||||
elif name == "np":
|
elif name == "np":
|
||||||
return self.points[-1]
|
return self.points[-1]
|
||||||
elif name == "talweg":
|
|
||||||
zm = self.z_min()
|
|
||||||
return next((p for p in self.points if p.z == zm), None)
|
|
||||||
else:
|
else:
|
||||||
return next((p for p in self.points if p.name == name), None)
|
return next((p for p in self.points if p.name == name), None)
|
||||||
|
|
||||||
@@ -91,12 +101,6 @@ class Profile(object):
|
|||||||
return 0
|
return 0
|
||||||
elif name == "np":
|
elif name == "np":
|
||||||
return len(self.points) - 1
|
return len(self.points) - 1
|
||||||
elif name == "talweg":
|
|
||||||
zm = self.z_min()
|
|
||||||
return next(
|
|
||||||
(p for p in enumerate(self.points) if p[1].z == zm),
|
|
||||||
None
|
|
||||||
)[0]
|
|
||||||
else:
|
else:
|
||||||
return next(
|
return next(
|
||||||
(p for p in enumerate(self.points) if p[1].name == name),
|
(p for p in enumerate(self.points) if p[1].name == name),
|
||||||
@@ -118,7 +122,7 @@ class Profile(object):
|
|||||||
@num.setter
|
@num.setter
|
||||||
def num(self, value: int):
|
def num(self, value: int):
|
||||||
self._num = int(value)
|
self._num = int(value)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def code1(self):
|
def code1(self):
|
||||||
@@ -131,7 +135,7 @@ class Profile(object):
|
|||||||
@code1.setter
|
@code1.setter
|
||||||
def code1(self, value: int):
|
def code1(self, value: int):
|
||||||
self._code1 = int(value)
|
self._code1 = int(value)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def code2(self):
|
def code2(self):
|
||||||
@@ -144,7 +148,7 @@ class Profile(object):
|
|||||||
@code2.setter
|
@code2.setter
|
||||||
def code2(self, value: int):
|
def code2(self, value: int):
|
||||||
self._code2 = int(value)
|
self._code2 = int(value)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def nb_points(self):
|
def nb_points(self):
|
||||||
@@ -161,7 +165,7 @@ class Profile(object):
|
|||||||
@rk.setter
|
@rk.setter
|
||||||
def rk(self, value: float):
|
def rk(self, value: float):
|
||||||
self._rk = float(value)
|
self._rk = float(value)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
@@ -174,7 +178,7 @@ class Profile(object):
|
|||||||
@name.setter
|
@name.setter
|
||||||
def name(self, value: str):
|
def name(self, value: str):
|
||||||
self._name = value.strip()
|
self._name = value.strip()
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sl(self):
|
def sl(self):
|
||||||
@@ -187,7 +191,7 @@ class Profile(object):
|
|||||||
@sl.setter
|
@sl.setter
|
||||||
def sl(self, value):
|
def sl(self, value):
|
||||||
self._sl = value
|
self._sl = value
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def profile_type(self):
|
def profile_type(self):
|
||||||
@@ -200,7 +204,7 @@ class Profile(object):
|
|||||||
@profile_type.setter
|
@profile_type.setter
|
||||||
def profile_type(self, value: str):
|
def profile_type(self, value: str):
|
||||||
self._profile_type = value
|
self._profile_type = value
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def point(self, i: int):
|
def point(self, i: int):
|
||||||
if i < len(self.points):
|
if i < len(self.points):
|
||||||
@@ -215,7 +219,7 @@ class Profile(object):
|
|||||||
The list of named point
|
The list of named point
|
||||||
"""
|
"""
|
||||||
return [point for point in self.points
|
return [point for point in self.points
|
||||||
if point.is_named()]
|
if point.point_is_named()]
|
||||||
|
|
||||||
def insert_point(self, index: int, point: Point):
|
def insert_point(self, index: int, point: Point):
|
||||||
"""Insert point at index.
|
"""Insert point at index.
|
||||||
@@ -227,25 +231,21 @@ class Profile(object):
|
|||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
"""
|
"""
|
||||||
if point in self._points:
|
self.points.insert(index, point)
|
||||||
point.set_as_not_deleted()
|
self._status.modified()
|
||||||
else:
|
|
||||||
self._points.insert(index, point)
|
|
||||||
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def delete_i(self, indexes: list):
|
def delete_i(self, indexes: list):
|
||||||
list(
|
self._points = list(
|
||||||
map(
|
map(
|
||||||
lambda e: e[1].set_as_deleted(),
|
lambda e: e[1],
|
||||||
filter(
|
filter(
|
||||||
lambda e: e[0] in indexes,
|
lambda e: e[0] not in indexes,
|
||||||
enumerate(self.points)
|
enumerate(self.points)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def delete_points(self, points):
|
def delete_points(self, points):
|
||||||
"""Delete some elements in profile list
|
"""Delete some elements in profile list
|
||||||
@@ -256,31 +256,31 @@ class Profile(object):
|
|||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
"""
|
"""
|
||||||
list(
|
self._points = list(
|
||||||
map(
|
filter(
|
||||||
lambda p: p.set_as_deleted(),
|
lambda p: p not in points,
|
||||||
points
|
self.points
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
# Move
|
# Move
|
||||||
|
|
||||||
def move_up_point(self, index: int):
|
def move_up_point(self, index: int):
|
||||||
if index < len(self._points):
|
if index < len(self.points):
|
||||||
next = index - 1
|
next = index - 1
|
||||||
|
|
||||||
p = self._points
|
p = self.points
|
||||||
p[index], p[next] = p[next], p[index]
|
p[index], p[next] = p[next], p[index]
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def move_down_point(self, index: int):
|
def move_down_point(self, index: int):
|
||||||
if index >= 0:
|
if index >= 0:
|
||||||
prev = index + 1
|
prev = index + 1
|
||||||
|
|
||||||
p = self._points
|
p = self.points
|
||||||
p[index], p[prev] = p[prev], p[index]
|
p[index], p[prev] = p[prev], p[index]
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
# Sort
|
# Sort
|
||||||
|
|
||||||
@@ -293,32 +293,32 @@ class Profile(object):
|
|||||||
def predicate(p): return p.z
|
def predicate(p): return p.z
|
||||||
|
|
||||||
self._points = sorted(
|
self._points = sorted(
|
||||||
self._points,
|
self.points,
|
||||||
key=predicate,
|
key=predicate,
|
||||||
reverse=is_reversed
|
reverse=is_reversed
|
||||||
)
|
)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@timer
|
@timer
|
||||||
def sort_with_indexes(self, indexes: list):
|
def sort_with_indexes(self, indexes: list):
|
||||||
if len(self._points) != len(indexes):
|
if len(self.points) != len(indexes):
|
||||||
logger.critical("Indexes list do not correspond to point list")
|
logger.critical("Indexes list do not correspond to point list")
|
||||||
|
|
||||||
self._points = list(
|
self._points = list(
|
||||||
map(
|
map(
|
||||||
lambda x: x[1],
|
lambda x: x[1],
|
||||||
sorted(
|
sorted(
|
||||||
enumerate(self._points),
|
enumerate(self.points),
|
||||||
key=lambda x: indexes[x[0]]
|
key=lambda x: indexes[x[0]]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@timer
|
@timer
|
||||||
def reverse(self):
|
def reverse(self):
|
||||||
self._points.reverse()
|
self._points.reverse()
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
# Sediment Layers
|
# Sediment Layers
|
||||||
|
|
||||||
|
|||||||
+126
-340
@@ -1,5 +1,5 @@
|
|||||||
# ProfileXYZ.py -- Pamhyr
|
# ProfileXYZ.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,20 +16,17 @@
|
|||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import queue
|
|
||||||
import random
|
|
||||||
import logging
|
import logging
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from typing import List
|
from typing import List
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from tools import timer, flatten
|
from tools import timer
|
||||||
from shapely import geometry
|
from shapely import geometry
|
||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Except import ClipboardFormatError
|
from Model.Except import ClipboardFormatError
|
||||||
from Model.Scenario import Scenario
|
|
||||||
from Model.Geometry.Profile import Profile
|
from Model.Geometry.Profile import Profile
|
||||||
from Model.Geometry.PointXYZ import PointXYZ
|
from Model.Geometry.PointXYZ import PointXYZ
|
||||||
from Model.Geometry.Vector_1d import Vector1d
|
from Model.Geometry.Vector_1d import Vector1d
|
||||||
@@ -51,14 +48,13 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
id: int = -1,
|
id: int = -1,
|
||||||
ind: int = -1,
|
|
||||||
name: str = "",
|
name: str = "",
|
||||||
rk: float = 0.,
|
rk: float = 0.,
|
||||||
reach=None,
|
reach=None,
|
||||||
num=0,
|
num=0,
|
||||||
nb_point: int = 0,
|
nb_point: int = 0,
|
||||||
code1: int = 0, code2: int = 0,
|
code1: int = 0, code2: int = 0,
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
"""ProfileXYZ constructor
|
"""ProfileXYZ constructor
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -73,16 +69,15 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
"""
|
"""
|
||||||
super(ProfileXYZ, self).__init__(
|
super(ProfileXYZ, self).__init__(
|
||||||
id=id,
|
id=id,
|
||||||
|
num=num,
|
||||||
name=name,
|
name=name,
|
||||||
num=num, rk=rk,
|
rk=rk,
|
||||||
code1=code1, code2=code2,
|
code1=code1, code2=code2,
|
||||||
_type="XYZ",
|
_type="XYZ",
|
||||||
reach=reach,
|
reach=reach,
|
||||||
status=status,
|
status=status,
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self._db_ind = ind
|
|
||||||
self.tab = Tabulation([], [], [])
|
self.tab = Tabulation([], [], [])
|
||||||
self.tab_up_to_date = False
|
self.tab_up_to_date = False
|
||||||
self.time_z = 0.0
|
self.time_z = 0.0
|
||||||
@@ -92,11 +87,10 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
self.station_up_to_date = False
|
self.station_up_to_date = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE geometry_profileXYZ{ext} (
|
CREATE TABLE geometry_profileXYZ(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
ind INTEGER NOT NULL,
|
ind INTEGER NOT NULL,
|
||||||
name TEXT,
|
name TEXT,
|
||||||
reach INTEGER NOT NULL,
|
reach INTEGER NOT NULL,
|
||||||
@@ -105,21 +99,15 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
code1 INTEGER NOT NULL,
|
code1 INTEGER NOT NULL,
|
||||||
code2 INTEGER NOT NULL,
|
code2 INTEGER NOT NULL,
|
||||||
sl INTEGER,
|
sl INTEGER,
|
||||||
{Scenario.create_db_add_scenario()},
|
FOREIGN KEY(reach) REFERENCES river_reach(id),
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
FOREIGN KEY(sl) REFERENCES sedimentary_layer(id)
|
||||||
FOREIGN KEY(reach) REFERENCES river_reach(pamhyr_id),
|
|
||||||
FOREIGN KEY(sl) REFERENCES sedimentary_layer(pamhyr_id),
|
|
||||||
PRIMARY KEY(pamhyr_id, scenario)
|
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
if ext == "_tmp":
|
|
||||||
return True
|
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
if major == minor == "0":
|
if major == minor == "0":
|
||||||
rl = int(release)
|
rl = int(release)
|
||||||
@@ -140,215 +128,83 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
return cls._update_submodel(execute, version)
|
||||||
|
|
||||||
if major == "0" and minor == "1":
|
|
||||||
if int(release) < 2:
|
|
||||||
execute(
|
|
||||||
"ALTER TABLE geometry_profileXYZ " +
|
|
||||||
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
|
|
||||||
)
|
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "geometry_profileXYZ"
|
|
||||||
id2pid = data['id2pid']
|
|
||||||
reachs = id2pid['river_reach']
|
|
||||||
|
|
||||||
cls._db_update_cleanup_points(execute)
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
"(pamhyr_id, ind, name, reach, rk, " +
|
|
||||||
"num, code1, code2, sl, scenario) " +
|
|
||||||
"SELECT pamhyr_id, ind, name, reach, rk, " +
|
|
||||||
"num, code1, code2, sl, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
cls._db_update_to_0_2_0_set_reach_pid(execute, table, reachs)
|
|
||||||
|
|
||||||
if 'sedimentary_layer' in id2pid:
|
|
||||||
sl = id2pid['sedimentary_layer']
|
|
||||||
cls._db_update_to_0_2_0_set_sl_pid(execute, table, sl)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_cleanup_points(cls, execute):
|
|
||||||
profiles = set(
|
|
||||||
map(
|
|
||||||
str,
|
|
||||||
flatten(
|
|
||||||
execute(
|
|
||||||
"SELECT id FROM geometry_profileXYZ"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(
|
|
||||||
"DELETE FROM geometry_pointXYZ " +
|
|
||||||
f"WHERE profile NOT IN ({', '.join(profiles)})"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_sl_pid(cls, execute, table, sl):
|
|
||||||
els = execute(
|
|
||||||
f"SELECT pamhyr_id, sl FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in els:
|
|
||||||
it = iter(row)
|
|
||||||
pid = next(it)
|
|
||||||
sl_id = next(it)
|
|
||||||
|
|
||||||
if sl_id == -1:
|
|
||||||
continue
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE {table} " +
|
|
||||||
f"SET sl = {sl[sl_id]} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
profiles = []
|
||||||
|
|
||||||
status = data["status"]
|
status = data["status"]
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
reach = data["reach"]
|
reach = data["reach"]
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, ind, deleted, name, rk, num, " +
|
"SELECT id, ind, name, rk, num, code1, code2, sl " +
|
||||||
"code1, code2, sl, scenario " +
|
|
||||||
"FROM geometry_profileXYZ " +
|
"FROM geometry_profileXYZ " +
|
||||||
f"WHERE reach = {reach.id} " +
|
f"WHERE reach = {reach.id}"
|
||||||
f"AND scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) " +
|
|
||||||
"ORDER BY ind ASC"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
id = row[0]
|
||||||
|
ind = row[1]
|
||||||
|
name = row[2]
|
||||||
|
rk = row[3]
|
||||||
|
num = row[5]
|
||||||
|
code1 = row[5]
|
||||||
|
code2 = row[6]
|
||||||
|
sl = row[7]
|
||||||
|
|
||||||
pid = next(it)
|
new = cls(
|
||||||
ind = next(it)
|
id=id, num=num,
|
||||||
deleted = (next(it) == 1)
|
|
||||||
name = next(it)
|
|
||||||
rk = next(it)
|
|
||||||
num = next(it)
|
|
||||||
code1 = next(it)
|
|
||||||
code2 = next(it)
|
|
||||||
sl = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
profile = cls(
|
|
||||||
id=pid, ind=ind, num=num,
|
|
||||||
name=name, rk=rk,
|
name=name, rk=rk,
|
||||||
code1=code1, code2=code2,
|
code1=code1, code2=code2,
|
||||||
reach=reach,
|
reach=reach,
|
||||||
status=status,
|
status=status
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
if deleted:
|
|
||||||
profile.set_as_deleted()
|
|
||||||
|
|
||||||
if sl == -1 or sl is None:
|
if sl == -1 or sl is None:
|
||||||
profile._sl = None
|
new._sl = None
|
||||||
else:
|
else:
|
||||||
profile._sl = next(
|
new._sl = next(
|
||||||
filter(
|
filter(
|
||||||
lambda s: s.id == sl,
|
lambda s: s.id == sl,
|
||||||
data["sediment_layers_list"].sediment_layers
|
data["sediment_layers_list"].sediment_layers
|
||||||
),
|
), None)
|
||||||
None
|
|
||||||
)
|
|
||||||
|
|
||||||
loaded_save = data['loaded_pid']
|
data["profile"] = new
|
||||||
data['loaded_pid'] = set()
|
new._points = PointXYZ._db_load(execute, data.copy())
|
||||||
data["profile"] = profile
|
|
||||||
|
|
||||||
profile._points = list(
|
yield ind, new
|
||||||
map(
|
|
||||||
lambda ip: ip[1],
|
|
||||||
sorted(
|
|
||||||
PointXYZ._db_load(execute, data.copy()),
|
|
||||||
key=lambda ip: ip[0]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
data['loaded_pid'] = loaded_save
|
|
||||||
|
|
||||||
loaded.add(pid)
|
|
||||||
new.append(profile)
|
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
if not self.must_be_saved():
|
|
||||||
return True
|
|
||||||
|
|
||||||
ok = True
|
ok = True
|
||||||
ind = data["ind"]
|
ind = data["ind"]
|
||||||
|
|
||||||
sl = self._sl.pamhyr_id if self._sl is not None else -1
|
sl = self._sl.id if self._sl is not None else -1
|
||||||
|
|
||||||
execute(
|
sql = (
|
||||||
"INSERT OR REPLACE INTO " +
|
"INSERT OR REPLACE INTO " +
|
||||||
"geometry_profileXYZ(pamhyr_id, deleted, ind, name, reach, " +
|
"geometry_profileXYZ(id, ind, name, reach, " +
|
||||||
"rk, num, code1, code2, sl, scenario) " +
|
"rk, num, code1, code2, sl) " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.pamhyr_id}, {self._db_format(self.is_deleted())}, " +
|
f"{self.id}, {ind}, '{self._db_format(self._name)}', " +
|
||||||
f"{ind}, '{self._db_format(self._name)}', " +
|
f"{self.reach.id}, {self.rk}, {self.num}, " +
|
||||||
f"{self.reach.pamhyr_id}, {self.rk}, {self.num}, " +
|
f"{self.code1}, {self.code1}, {sl}" +
|
||||||
f"{self.code1}, {self.code1}, {sl}, {self._status.scenario_id}" +
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
|
points = self.points
|
||||||
|
|
||||||
data["profile"] = self
|
data["profile"] = self
|
||||||
execute(
|
execute(f"DELETE FROM geometry_pointXYZ WHERE profile = {self.id}")
|
||||||
"DELETE FROM geometry_pointXYZ " +
|
|
||||||
f"WHERE profile = {self.pamhyr_id} " +
|
|
||||||
f"AND scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
ind = 0
|
ind = 0
|
||||||
for point in self._points:
|
for point in points:
|
||||||
data["ind"] = ind
|
data["ind"] = ind
|
||||||
ok &= point._db_save(execute, data)
|
ok &= point._db_save(execute, data)
|
||||||
ind += 1
|
ind += 1
|
||||||
|
|
||||||
return ok
|
return ok
|
||||||
|
|
||||||
def _data_traversal(self,
|
|
||||||
predicate=lambda obj, data: True,
|
|
||||||
modifier=lambda obj, data: None,
|
|
||||||
data={}):
|
|
||||||
if predicate(self, data):
|
|
||||||
modifier(self, data)
|
|
||||||
|
|
||||||
for p in self._points:
|
|
||||||
p._data_traversal(predicate, modifier, data)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_data(cls, header, data):
|
def from_data(cls, header, data):
|
||||||
profile = None
|
profile = None
|
||||||
@@ -381,47 +237,6 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
|
|
||||||
return profile
|
return profile
|
||||||
|
|
||||||
def copy(self):
|
|
||||||
new_p = ProfileXYZ(
|
|
||||||
id=-1,
|
|
||||||
name=self.name,
|
|
||||||
rk=self.rk,
|
|
||||||
reach=self.reach,
|
|
||||||
status=self._status
|
|
||||||
)
|
|
||||||
if self.is_deleted():
|
|
||||||
new_p.set_as_deleted()
|
|
||||||
|
|
||||||
new_p._sl = self.sl
|
|
||||||
|
|
||||||
for point in self._points:
|
|
||||||
p = point.copy()
|
|
||||||
print(p)
|
|
||||||
new_p._points.append(p)
|
|
||||||
|
|
||||||
new_p.modified()
|
|
||||||
return new_p
|
|
||||||
|
|
||||||
def cloned_for(self, new_reach):
|
|
||||||
new_p = ProfileXYZ(
|
|
||||||
id=-1,
|
|
||||||
name=self.name,
|
|
||||||
rk=self.rk,
|
|
||||||
reach=new_reach,
|
|
||||||
status=self._status
|
|
||||||
)
|
|
||||||
if self.is_deleted():
|
|
||||||
new_p.set_as_deleted()
|
|
||||||
|
|
||||||
new_p._sl = self.sl
|
|
||||||
|
|
||||||
for point in self._points:
|
|
||||||
p = point.cloned_for(new_p)
|
|
||||||
new_p._points.append(p)
|
|
||||||
|
|
||||||
new_p.modified()
|
|
||||||
return new_p
|
|
||||||
|
|
||||||
def point_from_data(self, header, data):
|
def point_from_data(self, header, data):
|
||||||
def float_format(s: str):
|
def float_format(s: str):
|
||||||
return float(
|
return float(
|
||||||
@@ -462,12 +277,6 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
|
|
||||||
return name
|
return name
|
||||||
|
|
||||||
def modified(self):
|
|
||||||
super(ProfileXYZ, self).modified()
|
|
||||||
|
|
||||||
self.tab_up_to_date = False
|
|
||||||
self.station_up_to_date = False
|
|
||||||
|
|
||||||
def x(self):
|
def x(self):
|
||||||
return [point.x for point in self.points]
|
return [point.x for point in self.points]
|
||||||
|
|
||||||
@@ -508,20 +317,11 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
Nothing.
|
Nothing.
|
||||||
"""
|
"""
|
||||||
for point in list_points:
|
for point in list_points:
|
||||||
named_args = {
|
pt = PointXYZ(*point, profile=self, status=self._status)
|
||||||
"x": point[0],
|
self.points.append(pt)
|
||||||
"y": point[1],
|
self._status.modified()
|
||||||
"z": point[2],
|
self.tab_up_to_date = False
|
||||||
"name": point[3],
|
self.station_up_to_date = False
|
||||||
}
|
|
||||||
|
|
||||||
pt = PointXYZ(
|
|
||||||
id=-1, **named_args, profile=self,
|
|
||||||
status=self._status
|
|
||||||
)
|
|
||||||
self._points.append(pt)
|
|
||||||
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def get_point_i(self, index: int) -> PointXYZ:
|
def get_point_i(self, index: int) -> PointXYZ:
|
||||||
"""Get point at index.
|
"""Get point at index.
|
||||||
@@ -580,13 +380,11 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
"""
|
"""
|
||||||
point_xyz = PointXYZ(
|
point_xyz = PointXYZ(0., 0., 0., profile=self, status=self._status)
|
||||||
x=0., y=0., z=0., profile=self,
|
|
||||||
status=self._status
|
|
||||||
)
|
|
||||||
self.points.append(point_xyz)
|
self.points.append(point_xyz)
|
||||||
|
self._status.modified()
|
||||||
self.modified()
|
self.tab_up_to_date = False
|
||||||
|
self.station_up_to_date = False
|
||||||
|
|
||||||
def insert(self, index: int):
|
def insert(self, index: int):
|
||||||
"""Insert a new point at index.
|
"""Insert a new point at index.
|
||||||
@@ -597,16 +395,11 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
Returns:
|
Returns:
|
||||||
The new point.
|
The new point.
|
||||||
"""
|
"""
|
||||||
x, y, z = (0., 0., 0.)
|
point = PointXYZ(0., 0., 0., profile=self, status=self._status)
|
||||||
|
self.points.insert(index, point)
|
||||||
if 0 < len(self._points) >= index:
|
self._status.modified()
|
||||||
x, y, z = self._points[index - 1].get_coordinate()
|
self.tab_up_to_date = False
|
||||||
|
self.station_up_to_date = False
|
||||||
point = PointXYZ(x=x, y=y, z=z, profile=self, status=self._status)
|
|
||||||
self._points.insert(index, point)
|
|
||||||
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
return point
|
return point
|
||||||
|
|
||||||
def filter_isnan(self, lst):
|
def filter_isnan(self, lst):
|
||||||
@@ -749,8 +542,8 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
if zz[i] >= z and zz[i+1] < z:
|
if zz[i] >= z and zz[i+1] < z:
|
||||||
y = np.interp(
|
y = np.interp(
|
||||||
z,
|
z,
|
||||||
[zz[i+1], zz[i]],
|
[zz[i], zz[i+1]],
|
||||||
[station[i+1], station[i]]
|
[station[i], station[i+1]]
|
||||||
)
|
)
|
||||||
line.append([y, z])
|
line.append([y, z])
|
||||||
|
|
||||||
@@ -839,8 +632,11 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
|
|
||||||
for i in range(self.number_points-1):
|
for i in range(self.number_points-1):
|
||||||
if zz[i] > z and zz[i+1] <= z:
|
if zz[i] > z and zz[i+1] <= z:
|
||||||
fact = (z - zz[i]) / (zz[i+1] - zz[i])
|
y = np.interp(
|
||||||
y = station[i] + fact * (station[i+1] - station[i])
|
z,
|
||||||
|
[zz[i], zz[i+1]],
|
||||||
|
[station[i], station[i+1]]
|
||||||
|
)
|
||||||
start.append(y)
|
start.append(y)
|
||||||
|
|
||||||
end = []
|
end = []
|
||||||
@@ -849,8 +645,11 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
|
|
||||||
for i in reversed(range(self.number_points-1)):
|
for i in reversed(range(self.number_points-1)):
|
||||||
if zz[i] <= z and zz[i+1] > z:
|
if zz[i] <= z and zz[i+1] > z:
|
||||||
fact = (z - zz[i]) / (zz[i+1] - zz[i])
|
y = np.interp(
|
||||||
y = station[i] + fact * (station[i+1] - station[i])
|
z,
|
||||||
|
[zz[i], zz[i+1]],
|
||||||
|
[station[i], station[i+1]]
|
||||||
|
)
|
||||||
end.append(y)
|
end.append(y)
|
||||||
|
|
||||||
if len(start) != len(end):
|
if len(start) != len(end):
|
||||||
@@ -881,31 +680,33 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
|
|
||||||
# Interpolate points at river left side
|
# Interpolate points at river left side
|
||||||
if (i_left > 0):
|
if (i_left > 0):
|
||||||
if abs(self.point(i_left - 1).z - self.point(i_left).z) < 1e-20:
|
x = np.interp(
|
||||||
pt_left = self.point(i_left)
|
z,
|
||||||
else:
|
[self.point(i_left).z, self.point(i_left - 1).z],
|
||||||
fact = (z - self.point(i_left).z) / \
|
[self.point(i_left).x, self.point(i_left - 1).x]
|
||||||
(self.point(i_left - 1).z - self.point(i_left).z)
|
)
|
||||||
x = self.point(i_left).x + fact * \
|
y = np.interp(
|
||||||
(self.point(i_left - 1).x - self.point(i_left).x)
|
z,
|
||||||
y = self.point(i_left).y + fact * \
|
[self.point(i_left).z, self.point(i_left - 1).z],
|
||||||
(self.point(i_left - 1).y - self.point(i_left).y)
|
[self.point(i_left).y, self.point(i_left - 1).y]
|
||||||
pt_left = PointXYZ(x=x, y=y, z=z, name="wl_left")
|
)
|
||||||
|
pt_left = PointXYZ(x, y, z, name="wl_left")
|
||||||
else:
|
else:
|
||||||
pt_left = self.point(0)
|
pt_left = self.point(0)
|
||||||
|
|
||||||
# Interpolate points at river right side
|
# Interpolate points at river right side
|
||||||
if (i_right < self.number_points - 1):
|
if (i_right < self.number_points - 1):
|
||||||
if abs(self.point(i_right + 1).z - self.point(i_right).z) < 1e-20:
|
x = np.interp(
|
||||||
pt_right = self.point(i_right)
|
z,
|
||||||
else:
|
[self.point(i_right).z, self.point(i_right + 1).z],
|
||||||
fact = (z - self.point(i_right).z) / \
|
[self.point(i_right).x, self.point(i_right + 1).x]
|
||||||
(self.point(i_right + 1).z - self.point(i_right).z)
|
)
|
||||||
x = self.point(i_right).x + fact * \
|
y = np.interp(
|
||||||
(self.point(i_right + 1).x - self.point(i_right).x)
|
z,
|
||||||
y = self.point(i_right).y + fact * \
|
[self.point(i_right).z, self.point(i_right + 1).z],
|
||||||
(self.point(i_right + 1).y - self.point(i_right).y)
|
[self.point(i_right).y, self.point(i_right + 1).y]
|
||||||
pt_right = PointXYZ(x=x, y=y, z=z, name="wl_right")
|
)
|
||||||
|
pt_right = PointXYZ(x, y, z, name="wl_right")
|
||||||
else:
|
else:
|
||||||
pt_right = self.point(self.number_points - 1)
|
pt_right = self.point(self.number_points - 1)
|
||||||
|
|
||||||
@@ -953,13 +754,13 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
last_point_not_nan = self._last_point_not_nan(points)
|
last_point_not_nan = self._last_point_not_nan(points)
|
||||||
|
|
||||||
for index, point in enumerate(points):
|
for index, point in enumerate(points):
|
||||||
if point.is_named():
|
if point.point_is_named():
|
||||||
index_first_named_point = index
|
index_first_named_point = index
|
||||||
first_named_point = point
|
first_named_point = point
|
||||||
break
|
break
|
||||||
|
|
||||||
for point in reversed(points):
|
for point in reversed(points):
|
||||||
if point.is_named():
|
if point.point_is_named():
|
||||||
last_named_point = point
|
last_named_point = point
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -1030,56 +831,54 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
"""
|
"""
|
||||||
Remove points to keep at most np_purge points.
|
Remove points to keep at most np_purge points.
|
||||||
"""
|
"""
|
||||||
if self.nb_points <= np_purge:
|
if (self.nb_points <= np_purge):
|
||||||
return
|
return
|
||||||
|
|
||||||
nb_named = 0 # we consider the first and last point as named
|
nb_named = 2 # we consider the first and last point as named
|
||||||
area = []
|
area = [0.0]
|
||||||
|
|
||||||
for i, p in enumerate(self.points):
|
for i in range(1, self.nb_points-1):
|
||||||
if p.is_named() or i == 0 or i == self.nb_points - 1:
|
if self.point(i).point_is_named():
|
||||||
area.append((9999999.999, p))
|
area.append(9999999.999)
|
||||||
nb_named += 1
|
nb_named += 1
|
||||||
else:
|
else:
|
||||||
area.append((
|
area.append(
|
||||||
PointXYZ.areatriangle3d(
|
PointXYZ.areatriangle3d(
|
||||||
self.point(i-1),
|
self.point(i-1),
|
||||||
p,
|
self.point(i),
|
||||||
self.point(i+1)
|
self.point(i+1))
|
||||||
),
|
)
|
||||||
p
|
|
||||||
))
|
area.append(0.0)
|
||||||
|
|
||||||
while self.nb_points > max(np_purge, nb_named):
|
while self.nb_points > max(np_purge, nb_named):
|
||||||
ind, (min_area, p) = min(
|
to_rm = np.argmin(area[1:self.nb_points - 1]) + 1
|
||||||
enumerate(area),
|
|
||||||
key=lambda t: t[1][0]
|
|
||||||
)
|
|
||||||
|
|
||||||
p.set_as_deleted()
|
self.delete_i([to_rm])
|
||||||
area.pop(ind)
|
area.pop(to_rm)
|
||||||
|
|
||||||
for i in [ind-1, ind]:
|
for i in [to_rm-1, to_rm]:
|
||||||
p = self.point(i)
|
if (i == 0):
|
||||||
|
continue
|
||||||
|
|
||||||
if p.is_named() or i == 0 or i == self.nb_points - 1:
|
if (i == self.nb_points - 1):
|
||||||
area[i] = (9999999.999, p)
|
continue
|
||||||
|
|
||||||
|
if self.point(i).point_is_named():
|
||||||
|
area[i] = 9999999.999
|
||||||
else:
|
else:
|
||||||
area[i] = (
|
area[i] = PointXYZ.areatriangle3d(
|
||||||
PointXYZ.areatriangle3d(
|
self.point(i-1),
|
||||||
self.point(i-1), p, self.point(i+1)
|
self.point(i),
|
||||||
),
|
self.point(i+1)
|
||||||
p
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def shift(self, x, y, z):
|
def shift(self, x, y, z):
|
||||||
for p in self._points:
|
for p in self.points:
|
||||||
p.x = p.x + x
|
p.x = p.x + x
|
||||||
p.y = p.y + y
|
p.y = p.y + y
|
||||||
p.z = p.z + z
|
p.z = p.z + z
|
||||||
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def modified(self):
|
def modified(self):
|
||||||
self.tab_up_to_date = False
|
self.tab_up_to_date = False
|
||||||
self.station_up_to_date = False
|
self.station_up_to_date = False
|
||||||
@@ -1104,7 +903,9 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
p = ProfileXYZ(name=self.name,
|
p = ProfileXYZ(name=self.name,
|
||||||
rk=self.rk,
|
rk=self.rk,
|
||||||
reach=self.reach,
|
reach=self.reach,
|
||||||
status=self._status)
|
nb_point=0,
|
||||||
|
code1=0, code2=0,
|
||||||
|
status=self.reach._status)
|
||||||
for i, k in enumerate(self.points):
|
for i, k in enumerate(self.points):
|
||||||
p.insert_point(i, k.copy())
|
p.insert_point(i, k.copy())
|
||||||
|
|
||||||
@@ -1122,18 +923,3 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
align = True
|
align = True
|
||||||
self.delete_i([i])
|
self.delete_i([i])
|
||||||
break
|
break
|
||||||
|
|
||||||
def hard_purge_aligned_points(self):
|
|
||||||
align = True
|
|
||||||
points = self.points
|
|
||||||
while (align):
|
|
||||||
align = False
|
|
||||||
for i in range(1, len(points) - 1):
|
|
||||||
d = points[i].dist_from_seg(points[i-1],
|
|
||||||
points[i+1])
|
|
||||||
if d < 0.00001 and points[i].name == "":
|
|
||||||
align = True
|
|
||||||
points.pop(i)
|
|
||||||
break
|
|
||||||
self._points = points
|
|
||||||
self.modified()
|
|
||||||
+84
-146
@@ -1,5 +1,5 @@
|
|||||||
# Reach.py -- Pamhyr
|
# Reach.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -45,6 +45,7 @@ class Reach(SQLSubModel):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, status=None, parent=None):
|
def __init__(self, status=None, parent=None):
|
||||||
|
self.id = parent.id if parent is not None else 0
|
||||||
self._status = status
|
self._status = status
|
||||||
self._parent = parent
|
self._parent = parent
|
||||||
self._profiles: List[Profile] = []
|
self._profiles: List[Profile] = []
|
||||||
@@ -52,59 +53,41 @@ class Reach(SQLSubModel):
|
|||||||
self._guidelines_is_valid = False
|
self._guidelines_is_valid = False
|
||||||
self._guidelines = {}
|
self._guidelines = {}
|
||||||
|
|
||||||
@property
|
|
||||||
def pamhyr_id(self):
|
|
||||||
return self._parent.pamhyr_id
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _pamhyr_id(self):
|
|
||||||
return self._parent.pamhyr_id
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute):
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
return cls._update_submodel(execute, version, data)
|
return cls._update_submodel(execute, version)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = cls(status=data["status"], parent=data["reach"])
|
new = cls(status=data["status"], parent=data["reach"])
|
||||||
|
|
||||||
new._profiles = sorted(
|
new._profiles = ProfileXYZ._db_load(
|
||||||
ProfileXYZ._db_load(
|
execute,
|
||||||
execute,
|
data=data.copy()
|
||||||
data=data.copy()
|
|
||||||
),
|
|
||||||
key=lambda p: p._db_ind
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
ok = True
|
profiles = self.profiles
|
||||||
|
|
||||||
execute(
|
# Delete old data
|
||||||
"DELETE FROM geometry_profileXYZ " +
|
execute(f"DELETE FROM geometry_profileXYZ WHERE reach = {self.id}")
|
||||||
f"WHERE reach = {self.pamhyr_id} " +
|
|
||||||
f"AND scenario = {self._status.scenario_id}"
|
if data is None:
|
||||||
)
|
data = {}
|
||||||
|
|
||||||
ind = 0
|
ind = 0
|
||||||
for profile in self._profiles:
|
for profile in profiles:
|
||||||
data["ind"] = ind
|
data["ind"] = ind
|
||||||
ok &= profile._db_save(execute, data)
|
profile._db_save(execute, data)
|
||||||
ind += 1
|
ind += 1
|
||||||
|
|
||||||
return ok
|
return True
|
||||||
|
|
||||||
def _data_traversal(self,
|
|
||||||
predicate=lambda obj, data: True,
|
|
||||||
modifier=lambda obj, data: None,
|
|
||||||
data={}):
|
|
||||||
for p in self._profiles:
|
|
||||||
p._data_traversal(predicate, modifier, data)
|
|
||||||
|
|
||||||
def profile(self, i):
|
def profile(self, i):
|
||||||
"""Returns profile at index i
|
"""Returns profile at index i
|
||||||
@@ -128,18 +111,31 @@ class Reach(SQLSubModel):
|
|||||||
return self._parent.name
|
return self._parent.name
|
||||||
|
|
||||||
def _get_profiles_list(self):
|
def _get_profiles_list(self):
|
||||||
|
# Profiles list generator is type (int, Point) with the first
|
||||||
|
# element the index of the Point in list
|
||||||
logger.debug(f"Load profiles from reach {self.name}")
|
logger.debug(f"Load profiles from reach {self.name}")
|
||||||
return list(self._profiles)
|
return list(
|
||||||
|
map(
|
||||||
|
lambda p: p[1],
|
||||||
|
sorted(
|
||||||
|
self._profiles,
|
||||||
|
key=lambda p: p[0]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self.profiles)
|
if not isinstance(self._profiles, list):
|
||||||
|
self._profiles = self._get_profiles_list()
|
||||||
|
|
||||||
|
return len(self._profiles)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def profiles(self):
|
def profiles(self):
|
||||||
if not isinstance(self._profiles, list):
|
if not isinstance(self._profiles, list):
|
||||||
self._profiles = self._get_profiles_list()
|
self._profiles = self._get_profiles_list()
|
||||||
|
|
||||||
return list(filter(lambda p: not p.is_deleted(), self._profiles))
|
return self._profiles
|
||||||
|
|
||||||
@profiles.setter
|
@profiles.setter
|
||||||
def profiles(self, profiles):
|
def profiles(self, profiles):
|
||||||
@@ -167,6 +163,15 @@ class Reach(SQLSubModel):
|
|||||||
"""
|
"""
|
||||||
return self.profiles
|
return self.profiles
|
||||||
|
|
||||||
|
def _update_profile_numbers(self):
|
||||||
|
"""Update profiles index
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Nothing.
|
||||||
|
"""
|
||||||
|
for ind, profile in enumerate(self.get_geometry()):
|
||||||
|
profile.num = ind + 1
|
||||||
|
|
||||||
def insert(self, index: int):
|
def insert(self, index: int):
|
||||||
"""Insert new profile in list
|
"""Insert new profile in list
|
||||||
|
|
||||||
@@ -178,9 +183,10 @@ class Reach(SQLSubModel):
|
|||||||
"""
|
"""
|
||||||
profile = ProfileXYZ(reach=self, status=self._status)
|
profile = ProfileXYZ(reach=self, status=self._status)
|
||||||
|
|
||||||
self._profiles.insert(index, profile)
|
self.profiles.insert(index, profile)
|
||||||
|
self._update_profile_numbers()
|
||||||
|
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
return profile
|
return profile
|
||||||
|
|
||||||
@@ -193,16 +199,9 @@ class Reach(SQLSubModel):
|
|||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
"""
|
"""
|
||||||
if profile in self._profiles:
|
self.profiles.insert(index, profile)
|
||||||
self.undelete([profile])
|
self._update_profile_numbers()
|
||||||
profile.num = index
|
self._status.modified()
|
||||||
else:
|
|
||||||
gi = self.get_global_profil_index(index)
|
|
||||||
profile.num = gi
|
|
||||||
print(f"gi = {gi}")
|
|
||||||
self._profiles.insert(gi, profile)
|
|
||||||
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def delete(self, indexes):
|
def delete(self, indexes):
|
||||||
"""Delete some elements in profile list
|
"""Delete some elements in profile list
|
||||||
@@ -213,38 +212,24 @@ class Reach(SQLSubModel):
|
|||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
"""
|
"""
|
||||||
gi = [self.get_global_profil_index(i) for i in indexes]
|
profiles = set(
|
||||||
list(
|
|
||||||
map(
|
map(
|
||||||
lambda p: p[1].set_as_deleted(),
|
lambda e: e[1],
|
||||||
filter(
|
filter(
|
||||||
lambda e: e[0] in gi,
|
lambda e: e[0] in indexes,
|
||||||
enumerate(self._profiles)
|
enumerate(self.profiles)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def hard_delete(self, indexes):
|
|
||||||
"""Delete some elements in profile list
|
|
||||||
|
|
||||||
Args:
|
|
||||||
indexes: The list of index to delete
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Nothing.
|
|
||||||
"""
|
|
||||||
gi = [self.get_global_profil_index(i) for i in indexes]
|
|
||||||
self._profiles = list(
|
self._profiles = list(
|
||||||
map(
|
filter(
|
||||||
lambda p: p[1],
|
lambda p: p not in profiles,
|
||||||
filter(
|
self.profiles
|
||||||
lambda e: e[0] not in gi,
|
|
||||||
enumerate(self._profiles)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.modified()
|
self._update_profile_numbers()
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
def delete_profiles(self, profiles):
|
def delete_profiles(self, profiles):
|
||||||
"""Delete some elements in profile list
|
"""Delete some elements in profile list
|
||||||
@@ -255,41 +240,35 @@ class Reach(SQLSubModel):
|
|||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
"""
|
"""
|
||||||
list(
|
self._profiles = list(
|
||||||
map(
|
filter(
|
||||||
lambda p: p.set_as_deleted(),
|
lambda p: p not in profiles,
|
||||||
profiles
|
self.profiles
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.modified()
|
self._update_profile_numbers()
|
||||||
|
self._status.modified()
|
||||||
def undelete(self, lst):
|
|
||||||
for el in lst:
|
|
||||||
el.set_as_not_deleted()
|
|
||||||
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def purge(self):
|
def purge(self):
|
||||||
for el in self._profiles:
|
self._profiles = []
|
||||||
el.set_as_deleted()
|
self._update_profile_numbers()
|
||||||
|
self._status.modified()
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def move_up_profile(self, index: int):
|
def move_up_profile(self, index: int):
|
||||||
if index < len(self._profiles):
|
if index < len(self.profiles):
|
||||||
next = index - 1
|
next = index - 1
|
||||||
|
|
||||||
p = self._profiles
|
p = self.profiles
|
||||||
p[index], p[next] = p[next], p[index]
|
p[index], p[next] = p[next], p[index]
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def move_down_profile(self, index: int):
|
def move_down_profile(self, index: int):
|
||||||
if index >= 0:
|
if index >= 0:
|
||||||
prev = index + 1
|
prev = index + 1
|
||||||
|
|
||||||
p = self._profiles
|
p = self.profiles
|
||||||
p[index], p[prev] = p[prev], p[index]
|
p[index], p[prev] = p[prev], p[index]
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def get_x(self):
|
def get_x(self):
|
||||||
return list(
|
return list(
|
||||||
@@ -517,7 +496,7 @@ class Reach(SQLSubModel):
|
|||||||
self._compute_guidelines_cache(guide_set, named_points,
|
self._compute_guidelines_cache(guide_set, named_points,
|
||||||
complete, incomplete)
|
complete, incomplete)
|
||||||
|
|
||||||
self.modified()
|
self._status.modified()
|
||||||
return (complete, incomplete)
|
return (complete, incomplete)
|
||||||
|
|
||||||
def _map_guidelines_points(self, func, full=False):
|
def _map_guidelines_points(self, func, full=False):
|
||||||
@@ -558,28 +537,28 @@ class Reach(SQLSubModel):
|
|||||||
|
|
||||||
@timer
|
@timer
|
||||||
def sort(self, is_reversed: bool = False):
|
def sort(self, is_reversed: bool = False):
|
||||||
self._profiles = sorted(
|
self.profiles = sorted(
|
||||||
self._profiles,
|
self.profiles,
|
||||||
key=lambda profile: profile.rk,
|
key=lambda profile: profile.rk,
|
||||||
reverse=is_reversed
|
reverse=is_reversed
|
||||||
)
|
)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@timer
|
@timer
|
||||||
def sort_with_indexes(self, indexes: list):
|
def sort_with_indexes(self, indexes: list):
|
||||||
if len(self._profiles) != len(indexes):
|
if len(self.profiles) != len(indexes):
|
||||||
logger.critical("Indexes list do not correspond to profile list")
|
logger.critical("Indexes list do not correspond to profile list")
|
||||||
|
|
||||||
self._profiles = list(
|
self.profiles = list(
|
||||||
map(
|
map(
|
||||||
lambda x: x[1],
|
lambda x: x[1],
|
||||||
sorted(
|
sorted(
|
||||||
enumerate(self._profiles),
|
enumerate(self.profiles),
|
||||||
key=lambda x: indexes[x[0]]
|
key=lambda x: indexes[x[0]]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
# Import/Export
|
# Import/Export
|
||||||
|
|
||||||
@@ -635,7 +614,8 @@ class Reach(SQLSubModel):
|
|||||||
|
|
||||||
profiles.append(prof)
|
profiles.append(prof)
|
||||||
|
|
||||||
self._profiles = profiles + self._profiles
|
self.profiles = profiles + self.profiles
|
||||||
|
self._update_profile_numbers()
|
||||||
self._recompute_rk()
|
self._recompute_rk()
|
||||||
|
|
||||||
return profiles
|
return profiles
|
||||||
@@ -653,8 +633,6 @@ class Reach(SQLSubModel):
|
|||||||
"""
|
"""
|
||||||
imported_profiles = []
|
imported_profiles = []
|
||||||
|
|
||||||
logger.info(f"import reach from {file_path_name}")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
list_profile, list_header = self.read_file_st(str(file_path_name))
|
list_profile, list_header = self.read_file_st(str(file_path_name))
|
||||||
profile_header = ["num", "code1", "code2",
|
profile_header = ["num", "code1", "code2",
|
||||||
@@ -667,15 +645,12 @@ class Reach(SQLSubModel):
|
|||||||
d[profile_header[i]] = data
|
d[profile_header[i]] = data
|
||||||
|
|
||||||
prof = ProfileXYZ(
|
prof = ProfileXYZ(
|
||||||
id=-1,
|
**d, reach=self, status=self._status
|
||||||
**d,
|
|
||||||
reach=self,
|
|
||||||
status=self._status
|
|
||||||
)
|
)
|
||||||
prof.import_points(profile)
|
prof.import_points(profile)
|
||||||
|
|
||||||
imported_profiles.append(prof)
|
imported_profiles.append(prof)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
exception_message_box(e)
|
exception_message_box(e)
|
||||||
@@ -683,8 +658,9 @@ class Reach(SQLSubModel):
|
|||||||
logger.error(e)
|
logger.error(e)
|
||||||
e.alert()
|
e.alert()
|
||||||
finally:
|
finally:
|
||||||
self._profiles = self._profiles + imported_profiles
|
self.profiles = imported_profiles + self.profiles
|
||||||
|
|
||||||
|
self._update_profile_numbers()
|
||||||
return imported_profiles
|
return imported_profiles
|
||||||
|
|
||||||
@timer
|
@timer
|
||||||
@@ -918,41 +894,3 @@ class Reach(SQLSubModel):
|
|||||||
|
|
||||||
profile.rk = previous.rk + dist
|
profile.rk = previous.rk + dist
|
||||||
previous = profile
|
previous = profile
|
||||||
|
|
||||||
def _split(self, profile, reach1, reach2):
|
|
||||||
if profile is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
nr1 = Reach(status=self._status, parent=reach1)
|
|
||||||
nr2 = Reach(status=self._status, parent=reach2)
|
|
||||||
|
|
||||||
ind = self.profiles.index(profile)
|
|
||||||
|
|
||||||
nr1_profiles = self.profiles[:ind+1]
|
|
||||||
nr2_profiles = self.profiles[ind:]
|
|
||||||
|
|
||||||
nr1._profiles = list(map(lambda p: p.cloned_for(nr1), nr1_profiles))
|
|
||||||
nr2._profiles = list(map(lambda p: p.cloned_for(nr2), nr2_profiles))
|
|
||||||
|
|
||||||
return nr1, nr2
|
|
||||||
|
|
||||||
def get_local_profil_index(self, index):
|
|
||||||
local_index = -1
|
|
||||||
for i, p in enumerate(self._profiles):
|
|
||||||
if not p.is_deleted():
|
|
||||||
local_index += 1
|
|
||||||
if i == index:
|
|
||||||
break
|
|
||||||
|
|
||||||
return local_index
|
|
||||||
|
|
||||||
def get_global_profil_index(self, index):
|
|
||||||
local_index = -1
|
|
||||||
for i, p in enumerate(self._profiles):
|
|
||||||
if not p.is_deleted():
|
|
||||||
local_index += 1
|
|
||||||
if local_index == index:
|
|
||||||
global_index = i
|
|
||||||
break
|
|
||||||
|
|
||||||
return global_index
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# Vector_1d.py -- Pamhyr
|
# Vector_1d.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# HydraulicStructures.py -- Pamhyr
|
# HydraulicStructures.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -21,7 +21,6 @@ import logging
|
|||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Scenario import Scenario
|
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
from Model.HydraulicStructures.Basic.Value import (
|
from Model.HydraulicStructures.Basic.Value import (
|
||||||
@@ -35,97 +34,49 @@ class BasicHS(SQLSubModel):
|
|||||||
_sub_classes = [
|
_sub_classes = [
|
||||||
BHSValue,
|
BHSValue,
|
||||||
]
|
]
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(BasicHS, self).__init__(
|
super(BasicHS, self).__init__()
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
self._status = status
|
||||||
)
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = BasicHS._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self._type = ""
|
self._type = ""
|
||||||
self._enabled = True
|
self._enabled = True
|
||||||
self._data = []
|
self._data = []
|
||||||
|
|
||||||
|
BasicHS._id_cnt = max(BasicHS._id_cnt + 1, self.id + 1)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE hydraulic_structures_basic{ext} (
|
CREATE TABLE hydraulic_structures_basic(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
type TEXT NOT NULL,
|
type TEXT NOT NULL,
|
||||||
enabled BOOLEAN NOT NULL,
|
enabled BOOLEAN NOT NULL,
|
||||||
hs INTEGER,
|
hs INTEGER,
|
||||||
{Scenario.create_db_add_scenario()},
|
FOREIGN KEY(hs) REFERENCES hydraulic_structures(id)
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
|
||||||
FOREIGN KEY(hs) REFERENCES hydraulic_structures(pamhyr_id),
|
|
||||||
PRIMARY KEY(pamhyr_id, scenario)
|
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
if ext == "_tmp":
|
|
||||||
return True
|
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
if major == minor == "0":
|
if major == minor == "0":
|
||||||
if int(release) < 6:
|
if int(release) < 6:
|
||||||
cls._db_create(execute)
|
cls._db_create(execute)
|
||||||
return True
|
|
||||||
else:
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
if major == "0" and minor == "1":
|
return True
|
||||||
if int(release) < 2:
|
|
||||||
execute(
|
|
||||||
"ALTER TABLE hydraulic_structures_basic " +
|
|
||||||
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
|
|
||||||
)
|
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "hydraulic_structures_basic"
|
|
||||||
hs = data['id2pid']['hydraulic_structures']
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
cls._db_update_to_0_2_0_set_hs_pid(execute, table, hs)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
"(pamhyr_id, name, type, enabled, hs, scenario) " +
|
|
||||||
"SELECT pamhyr_id, name, type, enabled, hs, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_hs_pid(cls, execute, table, hs):
|
|
||||||
els = execute(
|
|
||||||
f"SELECT pamhyr_id, hs FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in els:
|
|
||||||
it = iter(row)
|
|
||||||
pid = next(it)
|
|
||||||
hs_id = next(it)
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE {table} " +
|
|
||||||
f"SET hs = {hs[hs_id]} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_ctor_from_type(cls, t):
|
def _get_ctor_from_type(cls, t):
|
||||||
@@ -143,85 +94,58 @@ class BasicHS(SQLSubModel):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
new = []
|
||||||
status = data["status"]
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return []
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, deleted, name, type, enabled, hs, scenario " +
|
"SELECT id, name, type, enabled, hs " +
|
||||||
"FROM hydraulic_structures_basic " +
|
"FROM hydraulic_structures_basic " +
|
||||||
f"WHERE hs = {data['hs_id']} " +
|
f"WHERE hs = {data['hs_id']} "
|
||||||
f"AND scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
bhs_id = row[0]
|
||||||
|
name = row[1]
|
||||||
bhs_pid = next(it)
|
type = row[2]
|
||||||
deleted = (next(it) == 1)
|
enabled = (row[3] == 1)
|
||||||
name = next(it)
|
hs_id = row[4]
|
||||||
type = next(it)
|
|
||||||
enabled = (next(it) == 1)
|
|
||||||
hs_id = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
ctor = cls._get_ctor_from_type(type)
|
ctor = cls._get_ctor_from_type(type)
|
||||||
bhs = ctor(
|
bhs = ctor(
|
||||||
id=bhs_pid, name=name,
|
id=bhs_id,
|
||||||
status=status, owner_scenario=owner_scenario
|
name=name,
|
||||||
|
status=data['status']
|
||||||
)
|
)
|
||||||
if deleted:
|
|
||||||
bhs.set_as_deleted()
|
|
||||||
|
|
||||||
bhs.enabled = enabled
|
bhs.enabled = enabled
|
||||||
|
|
||||||
data['bhs_id'] = bhs_pid
|
data['bhs_id'] = bhs_id
|
||||||
bhs._data = BHSValue._db_load(
|
bhs._data = BHSValue._db_load(
|
||||||
execute, data
|
execute, data
|
||||||
)
|
)
|
||||||
print(f"{bhs_pid} : {deleted}")
|
|
||||||
if deleted:
|
|
||||||
bhs.set_as_deleted()
|
|
||||||
|
|
||||||
new.append(bhs)
|
new.append(bhs)
|
||||||
loaded.add(bhs_pid)
|
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
if not self.must_be_saved():
|
|
||||||
return True
|
|
||||||
|
|
||||||
hs_id = data['hs_id']
|
hs_id = data['hs_id']
|
||||||
|
|
||||||
execute(
|
sql = (
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"hydraulic_structures_basic(pamhyr_id, deleted, " +
|
"hydraulic_structures_basic(id, name, type, enabled, hs) " +
|
||||||
"name, type, enabled, hs, scenario) " +
|
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.pamhyr_id}, " +
|
f"{self.id}, " +
|
||||||
f"{self._db_format(self.is_deleted())}, " +
|
|
||||||
f"'{self._db_format(self._name)}', " +
|
f"'{self._db_format(self._name)}', " +
|
||||||
f"'{self._db_format(self._type)}', " +
|
f"'{self._db_format(self._type)}', " +
|
||||||
f"{self._db_format(self.enabled)}, " +
|
f"{self._db_format(self.enabled)}, " +
|
||||||
f"{hs_id}, " +
|
f"{hs_id} " +
|
||||||
f"{self._status.scenario_id}"
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
data['bhs_id'] = self.pamhyr_id
|
data['bhs_id'] = self.id
|
||||||
execute(
|
execute(
|
||||||
"DELETE FROM hydraulic_structures_basic_value " +
|
"DELETE FROM hydraulic_structures_basic_value " +
|
||||||
f"WHERE bhs = {self.pamhyr_id} " +
|
f"WHERE bhs = {self.id}"
|
||||||
f"AND scenario = {self._status.scenario_id}"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for values in self._data:
|
for values in self._data:
|
||||||
@@ -229,30 +153,20 @@ class BasicHS(SQLSubModel):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _data_traversal(self,
|
|
||||||
predicate=lambda obj, data: True,
|
|
||||||
modifier=lambda obj, data: None,
|
|
||||||
data={}):
|
|
||||||
if predicate(self, data):
|
|
||||||
modifier(self, data)
|
|
||||||
|
|
||||||
for d in self._data:
|
|
||||||
d._data_traversal(predicate, modifier, data)
|
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self._data)
|
return len(self._data)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
if self._name == "":
|
if self._name == "":
|
||||||
return f"{self._type} #{self.pamhyr_id}"
|
return f"{self._type}{self.id + 1}"
|
||||||
|
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@name.setter
|
@name.setter
|
||||||
def name(self, name):
|
def name(self, name):
|
||||||
self._name = name
|
self._name = name
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self):
|
def type(self):
|
||||||
@@ -261,7 +175,7 @@ class BasicHS(SQLSubModel):
|
|||||||
@type.setter
|
@type.setter
|
||||||
def type(self, type):
|
def type(self, type):
|
||||||
self._type = type
|
self._type = type
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def enabled(self):
|
def enabled(self):
|
||||||
@@ -270,7 +184,7 @@ class BasicHS(SQLSubModel):
|
|||||||
@enabled.setter
|
@enabled.setter
|
||||||
def enabled(self, enabled):
|
def enabled(self, enabled):
|
||||||
self._enabled = enabled
|
self._enabled = enabled
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def parameters(self):
|
def parameters(self):
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Types.py -- Pamhyr
|
# Types.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -27,11 +27,10 @@ from Model.HydraulicStructures.Basic.Value import (
|
|||||||
|
|
||||||
class NotDefined(BasicHS):
|
class NotDefined(BasicHS):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(NotDefined, self).__init__(
|
super(NotDefined, self).__init__(
|
||||||
id=id, name=name,
|
id=id, name=name,
|
||||||
status=status,
|
status=status
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self._type = "ND"
|
self._type = "ND"
|
||||||
@@ -40,208 +39,197 @@ class NotDefined(BasicHS):
|
|||||||
|
|
||||||
class DischargeWeir(BasicHS):
|
class DischargeWeir(BasicHS):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(DischargeWeir, self).__init__(
|
super(DischargeWeir, self).__init__(
|
||||||
id=id, name=name,
|
id=id, name=name,
|
||||||
status=status,
|
status=status
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self._type = "S1"
|
self._type = "S1"
|
||||||
self._data = [
|
self._data = [
|
||||||
BHSValue(-1, "width", float, 1.0, status=status),
|
BHSValue("width", float, 1.0, status=status),
|
||||||
BHSValue(-1, "elevation", float, 1.0, status=status),
|
BHSValue("elevation", float, 1.0, status=status),
|
||||||
BHSValue(-1, "discharge_coefficient", float, 0.4, status=status),
|
BHSValue("discharge_coefficient", float, 0.4, status=status),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class TrapezoidalWeir(BasicHS):
|
class TrapezoidalWeir(BasicHS):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(TrapezoidalWeir, self).__init__(
|
super(TrapezoidalWeir, self).__init__(
|
||||||
id=id, name=name,
|
id=id, name=name,
|
||||||
status=status,
|
status=status
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self._type = "S2"
|
self._type = "S2"
|
||||||
self._data = [
|
self._data = [
|
||||||
BHSValue(-1, "width", float, 1.0, status=status),
|
BHSValue("width", float, 1.0, status=status),
|
||||||
BHSValue(-1, "elevation", float, 1.0, status=status),
|
BHSValue("elevation", float, 1.0, status=status),
|
||||||
BHSValue(-1, "loading_elevation", float, 9999.999, status=status),
|
BHSValue("loading_elevation", float, 9999.999, status=status),
|
||||||
BHSValue(-1, "discharge_coefficient", float, 0.4, status=status),
|
BHSValue("discharge_coefficient", float, 0.4, status=status),
|
||||||
BHSValue(-1, "half-angle_tangent", float, 0.0, status=status),
|
BHSValue("half-angle_tangent", float, 0.0, status=status),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class TriangularWeir(BasicHS):
|
class TriangularWeir(BasicHS):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(TriangularWeir, self).__init__(
|
super(TriangularWeir, self).__init__(
|
||||||
id=id, name=name,
|
id=id, name=name,
|
||||||
status=status,
|
status=status
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self._type = "S3"
|
self._type = "S3"
|
||||||
self._data = [
|
self._data = [
|
||||||
BHSValue(-1, "elevation", float, 1.0, status=status),
|
BHSValue("elevation", float, 1.0, status=status),
|
||||||
BHSValue(-1, "loading_elevation", float, 9999.999, status=status),
|
BHSValue("loading_elevation", float, 9999.999, status=status),
|
||||||
BHSValue(-1, "discharge_coefficient", float, 0.31, status=status),
|
BHSValue("discharge_coefficient", float, 0.31, status=status),
|
||||||
BHSValue(-1, "half-angle_tangent", float, 1.0, status=status),
|
BHSValue("half-angle_tangent", float, 1.0, status=status),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class RectangularOrifice(BasicHS):
|
class RectangularOrifice(BasicHS):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(RectangularOrifice, self).__init__(
|
super(RectangularOrifice, self).__init__(
|
||||||
id=id, name=name,
|
id=id, name=name,
|
||||||
status=status,
|
status=status
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self._type = "OR"
|
self._type = "OR"
|
||||||
self._data = [
|
self._data = [
|
||||||
BHSValue(-1, "width", float, 0.0, status=status),
|
BHSValue("width", float, 0.0, status=status),
|
||||||
BHSValue(-1, "elevation", float, 0.0, status=status),
|
BHSValue("elevation", float, 0.0, status=status),
|
||||||
BHSValue(-1, "loading_elevation", float, 9999.999,
|
BHSValue("loading_elevation", float, 9999.999,
|
||||||
status=status),
|
status=status),
|
||||||
BHSValue(-1, "discharge_coefficient", float, 0.4, status=status),
|
BHSValue("discharge_coefficient", float, 0.4, status=status),
|
||||||
BHSValue(-1, "maximal_loading_elevation", float, 9999.999,
|
BHSValue("maximal_loading_elevation", float, 9999.999,
|
||||||
status=status),
|
status=status),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class CircularOrifice(BasicHS):
|
class CircularOrifice(BasicHS):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(CircularOrifice, self).__init__(
|
super(CircularOrifice, self).__init__(
|
||||||
id=id, name=name,
|
id=id, name=name,
|
||||||
status=status,
|
status=status
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self._type = "OC"
|
self._type = "OC"
|
||||||
self._data = [
|
self._data = [
|
||||||
BHSValue(-1, "diameter", float, 0.0, status=status),
|
BHSValue("diameter", float, 0.0, status=status),
|
||||||
BHSValue(-1, "elevation", float, 0.0, status=status),
|
BHSValue("elevation", float, 0.0, status=status),
|
||||||
BHSValue(-1, "siltation_height", float, 0.0, status=status),
|
BHSValue("siltation_height", float, 0.0, status=status),
|
||||||
BHSValue(-1, "discharge_coefficient", float, 0.4, status=status),
|
BHSValue("discharge_coefficient", float, 0.4, status=status),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class VaultedOrifice(BasicHS):
|
class VaultedOrifice(BasicHS):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(VaultedOrifice, self).__init__(
|
super(VaultedOrifice, self).__init__(
|
||||||
id=id, name=name,
|
id=id, name=name,
|
||||||
status=status,
|
status=status
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self._type = "OV"
|
self._type = "OV"
|
||||||
self._data = [
|
self._data = [
|
||||||
BHSValue(-1, "elevation", float, 1.0, status=status),
|
BHSValue("elevation", float, 1.0, status=status),
|
||||||
BHSValue(-1, "width", float, 1.0, status=status),
|
BHSValue("width", float, 1.0, status=status),
|
||||||
BHSValue(-1, "top_of_the_vault", float, 0.0, status=status),
|
BHSValue("top_of_the_vault", float, 0.0, status=status),
|
||||||
BHSValue(-1, "bottom_of_the_vault", float, 0.0, status=status),
|
BHSValue("bottom_of_the_vault", float, 0.0, status=status),
|
||||||
BHSValue(-1, "discharge_coefficient", float, 0.4, status=status),
|
BHSValue("discharge_coefficient", float, 0.4, status=status),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class RectangularGate(BasicHS):
|
class RectangularGate(BasicHS):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(RectangularGate, self).__init__(
|
super(RectangularGate, self).__init__(
|
||||||
id=id, name=name,
|
id=id, name=name,
|
||||||
status=status,
|
status=status
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self._type = "V1"
|
self._type = "V1"
|
||||||
self._data = [
|
self._data = [
|
||||||
BHSValue(-1, "width", float, 1.0, status=status),
|
BHSValue("width", float, 1.0, status=status),
|
||||||
BHSValue(-1, "elevation", float, 0.0, status=status),
|
BHSValue("elevation", float, 0.0, status=status),
|
||||||
BHSValue(-1, "discharge coefficient", float, 0.4, status=status),
|
BHSValue("discharge coefficient", float, 0.4, status=status),
|
||||||
BHSValue(-1, "opening", float, 1.0, status=status),
|
BHSValue("opening", float, 1.0, status=status),
|
||||||
BHSValue(-1, "maximal_opening", float, 1.0, status=status),
|
BHSValue("maximal_opening", float, 1.0, status=status),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SimplifiedRectangularGate(BasicHS):
|
class SimplifiedRectangularGate(BasicHS):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(SimplifiedRectangularGate, self).__init__(
|
super(SimplifiedRectangularGate, self).__init__(
|
||||||
id=id, name=name,
|
id=id, name=name,
|
||||||
status=status,
|
status=status
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self._type = "V2"
|
self._type = "V2"
|
||||||
self._data = [
|
self._data = [
|
||||||
BHSValue(-1, "width", float, 1.0, status=status),
|
BHSValue("width", float, 1.0, status=status),
|
||||||
BHSValue(-1, "elevation", float, 0.0, status=status),
|
BHSValue("elevation", float, 0.0, status=status),
|
||||||
BHSValue(-1, "discharge coefficient", float, 0.4, status=status),
|
BHSValue("discharge coefficient", float, 0.4, status=status),
|
||||||
BHSValue(-1, "opening", float, 1.0, status=status),
|
BHSValue("opening", float, 1.0, status=status),
|
||||||
BHSValue(-1, "maximal_opening", float, 1.0, status=status),
|
BHSValue("maximal_opening", float, 1.0, status=status),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class Borda(BasicHS):
|
class Borda(BasicHS):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(Borda, self).__init__(
|
super(Borda, self).__init__(
|
||||||
id=id, name=name,
|
id=id, name=name,
|
||||||
status=status,
|
status=status
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self._type = "BO"
|
self._type = "BO"
|
||||||
self._data = [
|
self._data = [
|
||||||
BHSValue(-1, "step_space", float, 0.1, status=status),
|
BHSValue("step_space", float, 0.1, status=status),
|
||||||
BHSValue(-1, "weir", float, 0.15, status=status),
|
BHSValue("weir", float, 0.15, status=status),
|
||||||
BHSValue(-1, "coefficient", float, 0.4, status=status),
|
BHSValue("coefficient", float, 0.4, status=status),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class CheckValve(BasicHS):
|
class CheckValve(BasicHS):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(CheckValve, self).__init__(
|
super(CheckValve, self).__init__(
|
||||||
id=id, name=name,
|
id=id, name=name,
|
||||||
status=status,
|
status=status
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self._type = "CV"
|
self._type = "CV"
|
||||||
self._data = [
|
self._data = [
|
||||||
BHSValue(-1, "width", float, 0.0, status=status),
|
BHSValue("width", float, 0.0, status=status),
|
||||||
BHSValue(-1, "elevation", float, 0.0, status=status),
|
BHSValue("elevation", float, 0.0, status=status),
|
||||||
BHSValue(-1, "loading_elevation", float, 9999.999,
|
BHSValue("loading_elevation", float, 9999.999,
|
||||||
status=status),
|
status=status),
|
||||||
BHSValue(-1, "discharge_coefficient", float, 0.4, status=status),
|
BHSValue("discharge_coefficient", float, 0.4, status=status),
|
||||||
BHSValue(-1, "maximal_loading_elevation", float, 9999.999,
|
BHSValue("maximal_loading_elevation", float, 9999.999,
|
||||||
status=status),
|
status=status),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class UserDefined(BasicHS):
|
class UserDefined(BasicHS):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(UserDefined, self).__init__(
|
super(UserDefined, self).__init__(
|
||||||
id=id, name=name,
|
id=id, name=name,
|
||||||
status=status,
|
status=status
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self._type = "UD"
|
self._type = "UD"
|
||||||
self._data = [
|
self._data = [
|
||||||
BHSValue(-1, "parameter_1", float, 0.0, status=status),
|
BHSValue("parameter_1", float, 0.0, status=status),
|
||||||
BHSValue(-1, "parameter_2", float, 0.0, status=status),
|
BHSValue("parameter_2", float, 0.0, status=status),
|
||||||
BHSValue(-1, "parameter_3", float, 0.0, status=status),
|
BHSValue("parameter_3", float, 0.0, status=status),
|
||||||
BHSValue(-1, "parameter_4", float, 0.0, status=status),
|
BHSValue("parameter_4", float, 0.0, status=status),
|
||||||
BHSValue(-1, "parameter_5", float, 0.0, status=status),
|
BHSValue("parameter_5", float, 0.0, status=status),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Value.py -- Pamhyr
|
# Value.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,92 +17,45 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Scenario import Scenario
|
|
||||||
|
|
||||||
|
|
||||||
class BHSValue(SQLSubModel):
|
class BHSValue(SQLSubModel):
|
||||||
_sub_classes = []
|
_sub_classes = []
|
||||||
_id_cnt = 0
|
_id_cnt = 0
|
||||||
|
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, name: str = "", type=float, value=0.0,
|
||||||
type=float, value=0.0,
|
status=None):
|
||||||
status=None, owner_scenario=-1):
|
super(BHSValue, self).__init__()
|
||||||
super(BHSValue, self).__init__(
|
|
||||||
id=id, status=status,
|
self._status = status
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self._type = type
|
self._type = type
|
||||||
self._value = type(value)
|
self._value = type(value)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE hydraulic_structures_basic_value{ext} (
|
CREATE TABLE hydraulic_structures_basic_value(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
type TEXT NOT NULL,
|
type TEXT NOT NULL,
|
||||||
value TEXT NOT NULL,
|
value TEXT NOT NULL,
|
||||||
bhs INTEGER,
|
bhs INTEGER,
|
||||||
{Scenario.create_db_add_scenario()},
|
FOREIGN KEY(bhs) REFERENCES hydraulic_structures_basic(id)
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
|
||||||
FOREIGN KEY(bhs) REFERENCES hydraulic_structures_basic(id),
|
|
||||||
PRIMARY KEY(pamhyr_id, scenario)
|
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
if major == minor == "0":
|
if major == minor == "0":
|
||||||
if int(release) < 6:
|
if int(release) < 6:
|
||||||
cls._db_create(execute)
|
cls._db_create(execute)
|
||||||
return True
|
|
||||||
else:
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
return True
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "hydraulic_structures_basic_value"
|
|
||||||
bhs = data['id2pid']['hydraulic_structures_basic']
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
cls._db_update_to_0_2_0_set_bhs_pid(execute, table, bhs)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
"(pamhyr_id, name, type, value, bhs, scenario) " +
|
|
||||||
"SELECT pamhyr_id, name, type, value, bhs, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_bhs_pid(cls, execute, table, bhs):
|
|
||||||
els = execute(
|
|
||||||
f"SELECT pamhyr_id, bhs FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in els:
|
|
||||||
it = iter(row)
|
|
||||||
pid = next(it)
|
|
||||||
bhs_id = next(it)
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE {table} " +
|
|
||||||
f"SET bhs = {bhs[bhs_id]} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _str_to_type(cls, type):
|
def _str_to_type(cls, type):
|
||||||
@@ -133,62 +86,44 @@ class BHSValue(SQLSubModel):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
new = []
|
||||||
status = data["status"]
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
bhs_id = data["bhs_id"]
|
bhs_id = data["bhs_id"]
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return []
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, name, type, value, scenario " +
|
"SELECT name, type, value " +
|
||||||
"FROM hydraulic_structures_basic_value " +
|
"FROM hydraulic_structures_basic_value " +
|
||||||
f"WHERE bhs = '{bhs_id}' " +
|
f"WHERE bhs = '{bhs_id}'"
|
||||||
f"AND scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
name = row[0]
|
||||||
|
type = cls._str_to_type(row[1])
|
||||||
pid = next(it)
|
value = row[2]
|
||||||
name = next(it)
|
|
||||||
type = cls._str_to_type(next(it))
|
|
||||||
value = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
val = cls(
|
val = cls(
|
||||||
id=pid, name=name,
|
name=name,
|
||||||
type=type, value=value,
|
type=type,
|
||||||
status=status, owner_scenario=owner_scenario
|
value=value,
|
||||||
|
status=data['status']
|
||||||
)
|
)
|
||||||
|
|
||||||
new.append(val)
|
new.append(val)
|
||||||
loaded.add(pid)
|
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
bhs_id = data["bhs_id"]
|
bhs_id = data["bhs_id"]
|
||||||
|
|
||||||
execute(
|
sql = (
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"hydraulic_structures_basic_value" +
|
"hydraulic_structures_basic_value(name, type, value, bhs) " +
|
||||||
"(pamhyr_id, name, type, value, bhs, scenario) " +
|
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self._pamhyr_id}, " +
|
|
||||||
f"'{self._db_format(self._name)}', " +
|
f"'{self._db_format(self._name)}', " +
|
||||||
f"'{self._db_format(self._type_to_str(self._type))}', " +
|
f"'{self._db_format(self._type_to_str(self._type))}', " +
|
||||||
f"'{self._db_format(self._value)}', " +
|
f"'{self._db_format(self._value)}', " +
|
||||||
f"{bhs_id}, " +
|
f"{bhs_id}" +
|
||||||
f"{self._status.scenario_id}"
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -207,4 +142,4 @@ class BHSValue(SQLSubModel):
|
|||||||
@value.setter
|
@value.setter
|
||||||
def value(self, value):
|
def value(self, value):
|
||||||
self._value = self._type(value)
|
self._value = self._type(value)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# HydraulicStructures.py -- Pamhyr
|
# HydraulicStructures.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -22,7 +22,6 @@ from functools import reduce
|
|||||||
from tools import trace, timer, old_pamhyr_date_to_timestamp
|
from tools import trace, timer, old_pamhyr_date_to_timestamp
|
||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Scenario import Scenario
|
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
from Model.HydraulicStructures.Basic.HydraulicStructures import BasicHS
|
from Model.HydraulicStructures.Basic.HydraulicStructures import BasicHS
|
||||||
@@ -37,289 +36,162 @@ class HydraulicStructure(SQLSubModel):
|
|||||||
_sub_classes = [
|
_sub_classes = [
|
||||||
BasicHS,
|
BasicHS,
|
||||||
]
|
]
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(HydraulicStructure, self).__init__(
|
super(HydraulicStructure, self).__init__()
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
self._status = status
|
||||||
)
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = HydraulicStructure._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self._input_section = None
|
self._input_rk = None
|
||||||
self._output_section = None
|
self._output_rk = None
|
||||||
self._input_reach = None
|
self._input_reach = None
|
||||||
self._output_reach = None
|
self._output_reach = None
|
||||||
self._enabled = True
|
self._enabled = True
|
||||||
self._data = []
|
self._data = []
|
||||||
|
|
||||||
|
HydraulicStructure._id_cnt = max(
|
||||||
|
HydraulicStructure._id_cnt + 1,
|
||||||
|
self.id + 1
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
|
||||||
CREATE TABLE hydraulic_structures{ext} (
|
execute("""
|
||||||
{cls.create_db_add_pamhyr_id()},
|
CREATE TABLE hydraulic_structures(
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
enabled BOOLEAN NOT NULL,
|
enabled BOOLEAN NOT NULL,
|
||||||
|
input_rk REAL NOT NULL,
|
||||||
|
output_rk REAL NOT NULL,
|
||||||
input_reach INTEGER,
|
input_reach INTEGER,
|
||||||
output_reach INTEGER,
|
output_reach INTEGER,
|
||||||
input_section INTEGER,
|
FOREIGN KEY(input_reach) REFERENCES river_reach(id),
|
||||||
output_section INTEGER,
|
FOREIGN KEY(output_reach) REFERENCES river_reach(id)
|
||||||
{Scenario.create_db_add_scenario()},
|
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
|
||||||
FOREIGN KEY(input_reach) REFERENCES river_reach(pamhyr_id),
|
|
||||||
FOREIGN KEY(output_reach) REFERENCES river_reach(pamhyr_id),
|
|
||||||
FOREIGN KEY(input_section)
|
|
||||||
REFERENCES geometry_profileXYZ(pamhyr_id),
|
|
||||||
FOREIGN KEY(output_section)
|
|
||||||
REFERENCES geometry_profileXYZ(pamhyr_id),
|
|
||||||
PRIMARY KEY(pamhyr_id, scenario)
|
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
if ext == "_tmp":
|
|
||||||
return True
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
rl = int(release)
|
|
||||||
|
|
||||||
if major == minor == "0":
|
if major == minor == "0":
|
||||||
|
rl = int(release)
|
||||||
|
|
||||||
if rl < 6:
|
if rl < 6:
|
||||||
cls._db_create(execute)
|
cls._db_create(execute)
|
||||||
return True
|
|
||||||
|
|
||||||
if rl < 11:
|
if rl < 11:
|
||||||
for v in ["input", "output"]:
|
|
||||||
execute(
|
|
||||||
f"""
|
|
||||||
ALTER TABLE hydraulic_structures
|
|
||||||
RENAME COLUMN {v}_kp TO {v}_rk
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
if major == "0" and minor == "1":
|
|
||||||
if rl < 1:
|
|
||||||
cls._db_update_to_0_1_1(execute, data)
|
|
||||||
|
|
||||||
if rl < 2:
|
|
||||||
execute(
|
execute(
|
||||||
"ALTER TABLE hydraulic_structures " +
|
"""
|
||||||
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
|
ALTER TABLE hydraulic_structures
|
||||||
|
RENAME COLUMN input_kp TO input_rk
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
execute(
|
||||||
|
"""
|
||||||
|
ALTER TABLE hydraulic_structures
|
||||||
|
RENAME COLUMN output_kp TO output_rk
|
||||||
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
return True
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "hydraulic_structures"
|
|
||||||
reachs = data['id2pid']['river_reach']
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
cls._db_update_to_0_2_0_set_reach_pid(execute, table, reachs)
|
|
||||||
cls._db_update_to_0_1_1(
|
|
||||||
execute, data,
|
|
||||||
origin_version="0.0.*"
|
|
||||||
)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
"(pamhyr_id, name, enabled, input_section, output_section, " +
|
|
||||||
"input_reach, output_reach, scenario) " +
|
|
||||||
"SELECT pamhyr_id, name, enabled, " +
|
|
||||||
"input_section, output_section, " +
|
|
||||||
"input_reach, output_reach, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_reach_pid(cls, execute, table, reachs):
|
|
||||||
els = execute(
|
|
||||||
f"SELECT pamhyr_id, input_reach, output_reach FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in els:
|
|
||||||
it = iter(row)
|
|
||||||
pid = next(it)
|
|
||||||
in_reach_id = next(it)
|
|
||||||
out_reach_id = next(it)
|
|
||||||
|
|
||||||
if in_reach_id == -1:
|
|
||||||
return
|
|
||||||
|
|
||||||
if out_reach_id == -1:
|
|
||||||
out_reach_id = in_reach_id
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE {table} " +
|
|
||||||
f"SET input_reach = {reachs[in_reach_id]}, " +
|
|
||||||
f"output_reach = {reachs[out_reach_id]} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_1_1(cls, execute, data,
|
|
||||||
origin_version="0.1.0"):
|
|
||||||
for v in ["input", "output"]:
|
|
||||||
execute(
|
|
||||||
"ALTER TABLE hydraulic_structures " +
|
|
||||||
f"ADD COLUMN {v}_section INTEGER"
|
|
||||||
)
|
|
||||||
|
|
||||||
cls._db_update_to_0_1_1_assoc_section_from_rk(
|
|
||||||
execute, "hydraulic_structures",
|
|
||||||
reach_column=f"{v}_reach",
|
|
||||||
rk_column=f"{v}_rk",
|
|
||||||
section_column=f"{v}_section",
|
|
||||||
origin_version=origin_version
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
new = []
|
||||||
status = data["status"]
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return []
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, deleted, name, enabled, " +
|
"SELECT id, name, enabled, " +
|
||||||
"input_section, output_section, " +
|
"input_rk, output_rk, " +
|
||||||
"input_reach, output_reach, scenario " +
|
"input_reach, output_reach " +
|
||||||
"FROM hydraulic_structures " +
|
"FROM hydraulic_structures "
|
||||||
f"WHERE scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for row in table:
|
for row in table:
|
||||||
new.append(cls._db_load_new(execute, data, row))
|
it = iter(row)
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
hs_id = next(it)
|
||||||
new += cls._db_load(execute, data)
|
name = next(it)
|
||||||
data["scenario"] = scenario
|
enabled = (next(it) == 1)
|
||||||
|
input_rk = next(it)
|
||||||
|
output_rk = next(it)
|
||||||
|
input_reach_id = next(it)
|
||||||
|
output_reach_id = next(it)
|
||||||
|
|
||||||
|
hs = cls(
|
||||||
|
id=hs_id,
|
||||||
|
name=name,
|
||||||
|
status=data['status']
|
||||||
|
)
|
||||||
|
|
||||||
|
hs.enabled = enabled
|
||||||
|
hs.input_rk = input_rk if input_rk != -1 else None
|
||||||
|
hs.output_rk = output_rk if output_rk != -1 else None
|
||||||
|
|
||||||
|
hs.input_reach, hs.output_reach = reduce(
|
||||||
|
lambda acc, n: (
|
||||||
|
n if n.id == input_reach_id else acc[0],
|
||||||
|
n if n.id == output_reach_id else acc[1]
|
||||||
|
),
|
||||||
|
data["edges"],
|
||||||
|
[None, None]
|
||||||
|
)
|
||||||
|
|
||||||
|
data['hs_id'] = hs_id
|
||||||
|
hs._data = BasicHS._db_load(execute, data)
|
||||||
|
|
||||||
|
new.append(hs)
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_load_new(cls, execute, data, row):
|
|
||||||
it = iter(row)
|
|
||||||
status = data["status"]
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
hs_id = next(it)
|
|
||||||
deleted = (next(it) == 1)
|
|
||||||
name = next(it)
|
|
||||||
enabled = (next(it) == 1)
|
|
||||||
input_section_id = next(it)
|
|
||||||
input_section_id = (
|
|
||||||
-1 if input_section_id is None else input_section_id
|
|
||||||
)
|
|
||||||
output_section_id = next(it)
|
|
||||||
output_section_id = (
|
|
||||||
-1 if output_section_id is None else output_section_id
|
|
||||||
)
|
|
||||||
input_reach_id = next(it)
|
|
||||||
output_reach_id = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
hs = cls(
|
|
||||||
id=hs_id, name=name, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
if deleted:
|
|
||||||
hs.set_as_deleted()
|
|
||||||
|
|
||||||
hs.enabled = enabled
|
|
||||||
hs.input_reach, hs.output_reach = reduce(
|
|
||||||
lambda acc, n: (
|
|
||||||
n if n.pamhyr_id == input_reach_id else acc[0],
|
|
||||||
n if n.pamhyr_id == output_reach_id else acc[1]
|
|
||||||
),
|
|
||||||
data["edges"],
|
|
||||||
[None, None]
|
|
||||||
)
|
|
||||||
sections = []
|
|
||||||
if hs.input_reach is not None:
|
|
||||||
sections += hs.input_reach.reach.profiles
|
|
||||||
if hs.output_reach is not None:
|
|
||||||
sections += hs.output_reach.reach.profiles
|
|
||||||
|
|
||||||
hs.input_section, hs.output_section = reduce(
|
|
||||||
lambda acc, s: (
|
|
||||||
s if s.pamhyr_id == input_section_id else acc[0],
|
|
||||||
s if s.pamhyr_id == output_section_id else acc[1]
|
|
||||||
),
|
|
||||||
sections,
|
|
||||||
[None, None]
|
|
||||||
)
|
|
||||||
|
|
||||||
loaded.add(hs_id)
|
|
||||||
|
|
||||||
data['hs_id'] = hs_id
|
|
||||||
hs._data = BasicHS._db_load(execute, data)
|
|
||||||
|
|
||||||
return hs
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute(
|
execute(f"DELETE FROM hydraulic_structures WHERE id = {self.id}")
|
||||||
"DELETE FROM hydraulic_structures " +
|
|
||||||
f"WHERE pamhyr_id = {self.pamhyr_id} " +
|
|
||||||
f"AND scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
input_reach_id = -1
|
input_reach_id = -1
|
||||||
if self._input_reach is not None:
|
if self._input_reach is not None:
|
||||||
input_reach_id = self._input_reach.pamhyr_id
|
input_reach_id = self._input_reach.id
|
||||||
|
|
||||||
output_reach_id = -1
|
output_reach_id = -1
|
||||||
if self._output_reach is not None:
|
if self._output_reach is not None:
|
||||||
output_reach_id = self._output_reach.pamhyr_id
|
output_reach_id = self._output_reach.id
|
||||||
|
|
||||||
input_section = 'NULL'
|
input_rk = -1
|
||||||
if self.input_section is not None:
|
if self.input_rk is not None:
|
||||||
input_section = self.input_section.pamhyr_id
|
input_rk = self.input_rk
|
||||||
|
|
||||||
output_section = 'NULL'
|
output_rk = -1
|
||||||
if self.output_section is not None:
|
if self.output_rk is not None:
|
||||||
output_section = self.output_section.pamhyr_id
|
output_rk = self.output_rk
|
||||||
|
|
||||||
execute(
|
sql = (
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"hydraulic_structures(" +
|
"hydraulic_structures(" +
|
||||||
" pamhyr_id, deleted, name, enabled, " +
|
" id, name, enabled, input_rk, output_rk, " +
|
||||||
" input_section, output_section, " +
|
" input_reach, output_reach" +
|
||||||
" input_reach, output_reach, " +
|
|
||||||
" scenario" +
|
|
||||||
") " +
|
") " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.pamhyr_id}, " +
|
f"{self.id}, '{self._db_format(self._name)}', " +
|
||||||
f"{self._db_format(self.is_deleted())}, " +
|
|
||||||
f"'{self._db_format(self._name)}', " +
|
|
||||||
f"{self._db_format(self.enabled)}, " +
|
f"{self._db_format(self.enabled)}, " +
|
||||||
f"{input_section}, {output_section}, " +
|
f"{input_rk}, {output_rk}, " +
|
||||||
f"{input_reach_id}, {output_reach_id}, " +
|
f"{input_reach_id}, {output_reach_id}" +
|
||||||
f"{self._status.scenario_id}"
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
data['hs_id'] = self.pamhyr_id
|
data['hs_id'] = self.id
|
||||||
execute(
|
execute(
|
||||||
"DELETE FROM hydraulic_structures_basic " +
|
"DELETE FROM hydraulic_structures_basic " +
|
||||||
f"WHERE hs = {self.pamhyr_id} " +
|
f"WHERE hs = {self.id}"
|
||||||
f"AND scenario = {self._status.scenario_id}"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for basic in self._data:
|
for basic in self._data:
|
||||||
@@ -327,71 +199,41 @@ class HydraulicStructure(SQLSubModel):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _data_traversal(self,
|
|
||||||
predicate=lambda obj, data: True,
|
|
||||||
modifier=lambda obj, data: None,
|
|
||||||
data={}):
|
|
||||||
if predicate(self, data):
|
|
||||||
modifier(self, data)
|
|
||||||
|
|
||||||
for d in self._data:
|
|
||||||
d._data_traversal(predicate, modifier, data)
|
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self.lst)
|
return len(self._data)
|
||||||
|
|
||||||
@property
|
|
||||||
def lst(self):
|
|
||||||
return list(
|
|
||||||
filter(
|
|
||||||
lambda bhs: not bhs.is_deleted(),
|
|
||||||
self._data,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
if self._name == "":
|
if self._name == "":
|
||||||
return f"HS #{self.pamhyr_id}"
|
return f"HS{self.id + 1}"
|
||||||
|
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@name.setter
|
@name.setter
|
||||||
def name(self, name):
|
def name(self, name):
|
||||||
self._name = name
|
self._name = name
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def input_rk(self):
|
def input_rk(self):
|
||||||
if self._input_section is None:
|
return self._input_rk
|
||||||
return None
|
|
||||||
|
|
||||||
return self._input_section
|
@input_rk.setter
|
||||||
|
def input_rk(self, input_rk):
|
||||||
|
if input_rk is not None:
|
||||||
|
input_rk = float(input_rk)
|
||||||
|
|
||||||
|
self._input_rk = input_rk
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def output_rk(self):
|
def output_rk(self):
|
||||||
if self._output_section is None:
|
return self._output_rk
|
||||||
return None
|
|
||||||
|
|
||||||
return self._output_section
|
@output_rk.setter
|
||||||
|
def output_rk(self, output_rk):
|
||||||
@property
|
self._output_rk = output_rk
|
||||||
def input_section(self):
|
self._status.modified()
|
||||||
return self._input_section
|
|
||||||
|
|
||||||
@input_section.setter
|
|
||||||
def input_section(self, input_section):
|
|
||||||
self._input_section = input_section
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def output_section(self):
|
|
||||||
return self._output_section
|
|
||||||
|
|
||||||
@output_section.setter
|
|
||||||
def output_section(self, output_section):
|
|
||||||
self._output_section = output_section
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def enabled(self):
|
def enabled(self):
|
||||||
@@ -400,7 +242,7 @@ class HydraulicStructure(SQLSubModel):
|
|||||||
@enabled.setter
|
@enabled.setter
|
||||||
def enabled(self, enabled):
|
def enabled(self, enabled):
|
||||||
self._enabled = enabled
|
self._enabled = enabled
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def input_reach(self):
|
def input_reach(self):
|
||||||
@@ -409,7 +251,7 @@ class HydraulicStructure(SQLSubModel):
|
|||||||
@input_reach.setter
|
@input_reach.setter
|
||||||
def input_reach(self, input_reach):
|
def input_reach(self, input_reach):
|
||||||
self._input_reach = input_reach
|
self._input_reach = input_reach
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def output_reach(self):
|
def output_reach(self):
|
||||||
@@ -418,34 +260,26 @@ class HydraulicStructure(SQLSubModel):
|
|||||||
@output_reach.setter
|
@output_reach.setter
|
||||||
def output_reach(self, output_reach):
|
def output_reach(self, output_reach):
|
||||||
self._output_reach = output_reach
|
self._output_reach = output_reach
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def basic_structures(self):
|
def basic_structures(self):
|
||||||
return self.lst.copy()
|
return self._data.copy()
|
||||||
|
|
||||||
def basic_structure(self, index: int):
|
def basic_structure(self, index: int):
|
||||||
if len(self._data) == 0:
|
return self._data[index]
|
||||||
return None
|
|
||||||
|
|
||||||
return self.lst[index]
|
|
||||||
|
|
||||||
def add(self, index: int):
|
def add(self, index: int):
|
||||||
value = NotDefined(status=self._status)
|
value = NotDefined(status=self._status)
|
||||||
self._data.insert(index, value)
|
self._data.insert(index, value)
|
||||||
|
self._status.modified()
|
||||||
self.modified()
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def insert(self, index: int, value: BasicHS):
|
def insert(self, index: int, value: BasicHS):
|
||||||
if value in self._data:
|
self._data.insert(index, value)
|
||||||
value.set_as_not_deleted()
|
self._status.modified()
|
||||||
else:
|
|
||||||
self._data.insert(index, value)
|
|
||||||
|
|
||||||
self.modified()
|
def delete_i(self, indexes):
|
||||||
|
|
||||||
def hard_delete_i(self, indexes):
|
|
||||||
self._data = list(
|
self._data = list(
|
||||||
map(
|
map(
|
||||||
lambda e: e[1],
|
lambda e: e[1],
|
||||||
@@ -455,30 +289,20 @@ class HydraulicStructure(SQLSubModel):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def delete_i(self, indexes):
|
|
||||||
list(
|
|
||||||
map(
|
|
||||||
lambda e: e[1].set_as_deleted(),
|
|
||||||
filter(
|
|
||||||
lambda e: e[0] in indexes,
|
|
||||||
enumerate(self.lst)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def delete(self, els):
|
def delete(self, els):
|
||||||
for el in els:
|
self._data = list(
|
||||||
el.set_as_deleted()
|
filter(
|
||||||
|
lambda e: e not in els,
|
||||||
self.modified()
|
self._data
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
def sort(self, _reverse=False, key=None):
|
def sort(self, _reverse=False, key=None):
|
||||||
if key is None:
|
if key is None:
|
||||||
self._data.sort(reverse=_reverse)
|
self._data.sort(reverse=_reverse)
|
||||||
else:
|
else:
|
||||||
self._data.sort(reverse=_reverse, key=key)
|
self._data.sort(reverse=_reverse, key=key)
|
||||||
|
self._status.modified()
|
||||||
self.modified()
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# HydraulicStructuresList.py -- Pamhyr
|
# HydraulicStructuresList.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
from copy import copy
|
from copy import copy
|
||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
|
|
||||||
from Model.Tools.PamhyrListExt import PamhyrModelList
|
from Model.Tools.PamhyrList import PamhyrModelList
|
||||||
from Model.HydraulicStructures.HydraulicStructures import HydraulicStructure
|
from Model.HydraulicStructures.HydraulicStructures import HydraulicStructure
|
||||||
|
|
||||||
|
|
||||||
@@ -42,10 +42,10 @@ class HydraulicStructureList(PamhyrModelList):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute(
|
execute("DELETE FROM hydraulic_structures")
|
||||||
"DELETE FROM hydraulic_structures " +
|
|
||||||
f"WHERE scenario = {self._status.scenario_id}"
|
if data is None:
|
||||||
)
|
data = {}
|
||||||
|
|
||||||
for hs in self._lst:
|
for hs in self._lst:
|
||||||
hs._db_save(execute, data=data)
|
hs._db_save(execute, data=data)
|
||||||
@@ -53,12 +53,9 @@ class HydraulicStructureList(PamhyrModelList):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def new(self, lst, index):
|
def new(self, lst, index):
|
||||||
n = HydraulicStructure(
|
n = HydraulicStructure(status=self._status)
|
||||||
status=self._status,
|
|
||||||
owner_scenario=self._status.scenario_id
|
|
||||||
)
|
|
||||||
self._lst.insert(index, n)
|
self._lst.insert(index, n)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
return n
|
return n
|
||||||
|
|
||||||
def __copy__(self):
|
def __copy__(self):
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# InitialConditions.py -- Pamhyr
|
# InitialConditions.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -24,34 +24,32 @@ from functools import reduce
|
|||||||
from numpy import interp
|
from numpy import interp
|
||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Scenario import Scenario
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
class Data(SQLSubModel):
|
class Data(SQLSubModel):
|
||||||
def __init__(self, id: int = -1,
|
def __init__(self, name: str = "",
|
||||||
name: str = "", comment: str = "",
|
comment: str = "", reach=None,
|
||||||
reach=None, section=None,
|
rk: float = 0.0, discharge: float = 0.0,
|
||||||
discharge: float = 0.0,
|
|
||||||
height: float = 0.0,
|
height: float = 0.0,
|
||||||
status=None,
|
status=None):
|
||||||
owner_scenario=-1):
|
super(Data, self).__init__()
|
||||||
super(Data, self).__init__(
|
|
||||||
id=id, status=status,
|
self._status = status
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
self._reach = reach
|
self._reach = reach
|
||||||
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self._comment = comment
|
self._comment = comment
|
||||||
|
|
||||||
self._section = section
|
self._rk = rk
|
||||||
self._discharge = discharge
|
self._discharge = discharge
|
||||||
self._speed = 0.0
|
self._speed = 0.0
|
||||||
self._elevation = 0.0
|
self._elevation = 0.0
|
||||||
self._height = height
|
self._height = height
|
||||||
|
|
||||||
if self._section is not None:
|
if self._rk != 0.0:
|
||||||
self._update_from_rk()
|
self._update_from_rk()
|
||||||
if self._height != 0.0:
|
if self._height != 0.0:
|
||||||
self._update_from_height()
|
self._update_from_height()
|
||||||
@@ -59,29 +57,25 @@ class Data(SQLSubModel):
|
|||||||
self._update_from_discharge()
|
self._update_from_discharge()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE initial_conditions{ext} (
|
CREATE TABLE initial_conditions(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
ind INTEGER NOT NULL,
|
ind INTEGER NOT NULL,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
comment TEXT NOT NULL,
|
comment TEXT NOT NULL,
|
||||||
reach INTEGER,
|
reach INTEGER,
|
||||||
section INTEGER,
|
rk REAL NOT NULL,
|
||||||
discharge REAL NOT NULL,
|
discharge REAL NOT NULL,
|
||||||
height REAL NOT NULL,
|
height REAL NOT NULL,
|
||||||
{Scenario.create_db_add_scenario()},
|
FOREIGN KEY(reach) REFERENCES river_reach(id)
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
|
||||||
FOREIGN KEY(reach) REFERENCES river_reach(pamhyr_id),
|
|
||||||
PRIMARY KEY(pamhyr_id, scenario)
|
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
if major == minor == "0":
|
if major == minor == "0":
|
||||||
if int(release) < 11:
|
if int(release) < 11:
|
||||||
@@ -89,138 +83,41 @@ class Data(SQLSubModel):
|
|||||||
"ALTER TABLE initial_conditions RENAME COLUMN kp TO rk"
|
"ALTER TABLE initial_conditions RENAME COLUMN kp TO rk"
|
||||||
)
|
)
|
||||||
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
return cls._update_submodel(execute, version)
|
||||||
|
|
||||||
if major == "0" and minor == "1":
|
|
||||||
if int(release) < 1:
|
|
||||||
cls._db_update_to_0_1_1(execute, data)
|
|
||||||
|
|
||||||
if int(release) < 2:
|
|
||||||
execute(
|
|
||||||
"ALTER TABLE initial_conditions " +
|
|
||||||
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
|
|
||||||
)
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "initial_conditions"
|
|
||||||
reachs = data['id2pid']['river_reach']
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
cls._db_update_to_0_2_0_set_reach_pid(execute, table, reachs)
|
|
||||||
cls._db_update_to_0_1_1(
|
|
||||||
execute, data,
|
|
||||||
origin_version="0.0.*"
|
|
||||||
)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
"(pamhyr_id, ind, name, comment, reach, section, " +
|
|
||||||
"discharge, height, scenario) " +
|
|
||||||
"SELECT pamhyr_id, ind, name, comment, reach, section, " +
|
|
||||||
"discharge, height, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_1_1(cls, execute, data,
|
|
||||||
origin_version="0.1.0"):
|
|
||||||
execute(
|
|
||||||
"ALTER TABLE initial_conditions " +
|
|
||||||
f"ADD COLUMN section INTEGER"
|
|
||||||
)
|
|
||||||
|
|
||||||
cls._db_update_to_0_1_1_assoc_section_from_rk(
|
|
||||||
execute, "initial_conditions",
|
|
||||||
origin_version=origin_version
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
id = data["reach"].id
|
||||||
reach = data["reach"]
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
id = reach.pamhyr_id
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, deleted, ind, name, comment, " +
|
"SELECT ind, name, comment, rk, discharge, height " +
|
||||||
"section, discharge, height, scenario " +
|
|
||||||
"FROM initial_conditions " +
|
"FROM initial_conditions " +
|
||||||
f"WHERE reach = {id} " +
|
f"WHERE reach = {id}"
|
||||||
f"AND scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) " +
|
|
||||||
"ORDER BY ind ASC"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
ok = True
|
new = []
|
||||||
|
|
||||||
|
for _ in table:
|
||||||
|
new.append(None)
|
||||||
|
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
ind = row[0]
|
||||||
|
name = row[1]
|
||||||
pid = next(it)
|
comment = row[2]
|
||||||
deleted = (next(it) == 1)
|
rk = row[3]
|
||||||
ind = next(it)
|
discharge = row[4]
|
||||||
name = next(it)
|
height = row[5]
|
||||||
comment = next(it)
|
|
||||||
section_id = next(it)
|
|
||||||
discharge = next(it)
|
|
||||||
height = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
section = reduce(
|
|
||||||
lambda acc, s: (
|
|
||||||
s if s.pamhyr_id == section_id else acc
|
|
||||||
),
|
|
||||||
reach.reach.profiles,
|
|
||||||
None
|
|
||||||
)
|
|
||||||
|
|
||||||
if section is None:
|
|
||||||
logger.debug(
|
|
||||||
f"Section ID {section_id} do not exist in reach " +
|
|
||||||
f"{reach.name} ({reach.pamhyr_id})"
|
|
||||||
)
|
|
||||||
ok = False
|
|
||||||
continue
|
|
||||||
|
|
||||||
d = cls(
|
d = cls(
|
||||||
id=pid,
|
|
||||||
reach=data["reach"],
|
reach=data["reach"],
|
||||||
status=data["status"],
|
status=data["status"],
|
||||||
name=name,
|
name=name,
|
||||||
comment=comment,
|
comment=comment,
|
||||||
section=section,
|
rk=rk,
|
||||||
discharge=discharge,
|
discharge=discharge,
|
||||||
height=height,
|
height=height,
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
if deleted:
|
|
||||||
d.set_as_deleted()
|
|
||||||
|
|
||||||
loaded.add(pid)
|
|
||||||
new.append(d)
|
|
||||||
|
|
||||||
if not ok:
|
|
||||||
logger.info(
|
|
||||||
"Please regenerate/defined the inital conditions " +
|
|
||||||
f"for reach {reach.name} ({reach.pamhyr_id})"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Recution only if no data found
|
new[ind] = d
|
||||||
if len(new) == 0:
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
@@ -229,14 +126,13 @@ class Data(SQLSubModel):
|
|||||||
|
|
||||||
execute(
|
execute(
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"initial_conditions(pamhyr_id, deleted, ind, name, comment, " +
|
"initial_conditions(ind, name, comment, rk, " +
|
||||||
"section, discharge, height, reach, scenario) " +
|
"discharge, height, reach) " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.pamhyr_id}, {self._db_format(self.is_deleted())}, " +
|
|
||||||
f"{ind}, '{self._db_format(self.name)}', " +
|
f"{ind}, '{self._db_format(self.name)}', " +
|
||||||
f"'{self._db_format(self._comment)}', " +
|
f"'{self._db_format(self._comment)}', " +
|
||||||
f"{self._section.id}, {self._discharge}, {self._height}, " +
|
f"{self._rk}, {self._discharge}, {self._height}, " +
|
||||||
f"{self._reach.id}, {self._status.scenario_id}" +
|
f"{self._reach.id}" +
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -246,7 +142,7 @@ class Data(SQLSubModel):
|
|||||||
new = Data(
|
new = Data(
|
||||||
name=self.name,
|
name=self.name,
|
||||||
comment=self._comment,
|
comment=self._comment,
|
||||||
section=self._section,
|
rk=self._rk,
|
||||||
discharge=self._discharge,
|
discharge=self._discharge,
|
||||||
height=self._height,
|
height=self._height,
|
||||||
reach=self._reach,
|
reach=self._reach,
|
||||||
@@ -265,8 +161,8 @@ class Data(SQLSubModel):
|
|||||||
val = self._name
|
val = self._name
|
||||||
elif key == "comment":
|
elif key == "comment":
|
||||||
val = self._comment
|
val = self._comment
|
||||||
elif key == "rk" or key == "section":
|
elif key == "rk":
|
||||||
val = self._section
|
val = self._rk
|
||||||
elif key == "speed":
|
elif key == "speed":
|
||||||
val = self._speed
|
val = self._speed
|
||||||
elif key == "discharge":
|
elif key == "discharge":
|
||||||
@@ -279,9 +175,9 @@ class Data(SQLSubModel):
|
|||||||
return val
|
return val
|
||||||
|
|
||||||
def _update_get_min(self):
|
def _update_get_min(self):
|
||||||
profile = self._section
|
profile = self._reach.reach.get_profiles_from_rk(self._rk)
|
||||||
if profile is not None:
|
if len(profile) > 0:
|
||||||
min = profile.z_min()
|
min = profile[0].z_min()
|
||||||
else:
|
else:
|
||||||
min = 0.0
|
min = 0.0
|
||||||
|
|
||||||
@@ -308,8 +204,8 @@ class Data(SQLSubModel):
|
|||||||
self._name = str(value)
|
self._name = str(value)
|
||||||
elif key == "comment":
|
elif key == "comment":
|
||||||
self._comment = str(value)
|
self._comment = str(value)
|
||||||
elif key == "rk" or key == "section":
|
elif key == "rk":
|
||||||
self._section = value
|
self._rk = float(value)
|
||||||
self._update_from_rk()
|
self._update_from_rk()
|
||||||
elif key == "speed":
|
elif key == "speed":
|
||||||
# Not supposed to be modified
|
# Not supposed to be modified
|
||||||
@@ -324,7 +220,7 @@ class Data(SQLSubModel):
|
|||||||
self._height = float(value)
|
self._height = float(value)
|
||||||
self._update_from_height()
|
self._update_from_height()
|
||||||
|
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
|
|
||||||
class InitialConditions(SQLSubModel):
|
class InitialConditions(SQLSubModel):
|
||||||
@@ -345,8 +241,8 @@ class InitialConditions(SQLSubModel):
|
|||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
return cls._update_submodel(execute, version, data)
|
return cls._update_submodel(execute, version)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
@@ -374,18 +270,8 @@ class InitialConditions(SQLSubModel):
|
|||||||
|
|
||||||
return ok
|
return ok
|
||||||
|
|
||||||
def _data_traversal(self,
|
|
||||||
predicate=lambda obj, data: True,
|
|
||||||
modifier=lambda obj, data: None,
|
|
||||||
data={}):
|
|
||||||
if predicate(self, data):
|
|
||||||
modifier(self, data)
|
|
||||||
|
|
||||||
for d in self._data:
|
|
||||||
d._data_traversal(predicate, modifier, data)
|
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self.data)
|
return len(self._data)
|
||||||
|
|
||||||
def lst(self):
|
def lst(self):
|
||||||
return self._data
|
return self._data
|
||||||
@@ -397,69 +283,49 @@ class InitialConditions(SQLSubModel):
|
|||||||
@reach.setter
|
@reach.setter
|
||||||
def reach(self, new):
|
def reach(self, new):
|
||||||
self._reach = reach
|
self._reach = reach
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def data(self):
|
def data(self):
|
||||||
return list(
|
return self._data.copy()
|
||||||
filter(
|
|
||||||
lambda el: not el.is_deleted(),
|
|
||||||
self._data
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@data.setter
|
@data.setter
|
||||||
def data(self, data):
|
def data(self, data):
|
||||||
self._data = data
|
self._data = data
|
||||||
|
|
||||||
def get(self, index):
|
def get(self, index):
|
||||||
return self.data[index]
|
return self._data[index]
|
||||||
|
|
||||||
def set(self, index, data):
|
def set(self, index, data):
|
||||||
self._data.insert(index, data)
|
self._data.insert(index, data)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def new(self, index):
|
def new(self, index):
|
||||||
n = Data(reach=self._reach, status=self._status)
|
n = Data(reach=self._reach, status=self._status)
|
||||||
self._data.insert(index, n)
|
self._data.insert(index, n)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def new_from_data(self, rk, discharge, elevation):
|
def new_from_data(self, rk, discharge, elevation):
|
||||||
n = Data(reach=self._reach, status=self._status)
|
n = Data(reach=self._reach, status=self._status)
|
||||||
|
|
||||||
section = reduce(
|
n['rk'] = rk
|
||||||
lambda acc, s: (
|
|
||||||
s if s.rk == rk else acc
|
|
||||||
),
|
|
||||||
self._reach.reach.profiles,
|
|
||||||
None
|
|
||||||
)
|
|
||||||
|
|
||||||
n['section'] = section
|
|
||||||
n['discharge'] = discharge
|
n['discharge'] = discharge
|
||||||
n['elevation'] = elevation
|
n['elevation'] = elevation
|
||||||
|
|
||||||
return n
|
return n
|
||||||
|
|
||||||
def insert(self, index, data):
|
def insert(self, index, data):
|
||||||
if data in self._data:
|
self._data.insert(index, data)
|
||||||
data.set_as_not_deleted()
|
self._status.modified()
|
||||||
else:
|
|
||||||
self._data.insert(index, data)
|
|
||||||
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def delete(self, data):
|
def delete(self, data):
|
||||||
list(
|
self._data = list(
|
||||||
map(
|
filter(
|
||||||
lambda x: x.set_as_deleted(),
|
lambda x: x not in data,
|
||||||
filter(
|
self._data
|
||||||
lambda x: x in data,
|
|
||||||
self._data
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def delete_i(self, indexes):
|
def delete_i(self, indexes):
|
||||||
data = list(
|
data = list(
|
||||||
@@ -475,23 +341,18 @@ class InitialConditions(SQLSubModel):
|
|||||||
|
|
||||||
def sort(self, reverse=False, key=None):
|
def sort(self, reverse=False, key=None):
|
||||||
self._data.sort(reverse=reverse, key=key)
|
self._data.sort(reverse=reverse, key=key)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def _data_get(self, key):
|
def _data_get(self, key):
|
||||||
return list(
|
return list(
|
||||||
map(
|
map(
|
||||||
lambda d: d[key],
|
lambda d: d[key],
|
||||||
self.data
|
self._data
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_rk(self):
|
def get_rk(self):
|
||||||
return list(
|
return self._data_get("rk")
|
||||||
map(
|
|
||||||
lambda d: d["rk"].rk,
|
|
||||||
self.data
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_elevation(self):
|
def get_elevation(self):
|
||||||
return self._data_get("elevation")
|
return self._data_get("elevation")
|
||||||
@@ -499,13 +360,12 @@ class InitialConditions(SQLSubModel):
|
|||||||
def get_discharge(self):
|
def get_discharge(self):
|
||||||
return self._data_get("discharge")
|
return self._data_get("discharge")
|
||||||
|
|
||||||
def clear_data(self):
|
|
||||||
for data in self._data:
|
|
||||||
data.set_as_deleted()
|
|
||||||
|
|
||||||
def generate_growing_constant_depth(self, height: float,
|
def generate_growing_constant_depth(self, height: float,
|
||||||
compute_discharge: bool):
|
compute_discharge: bool):
|
||||||
|
|
||||||
profiles = self._reach.reach.profiles.copy()
|
profiles = self._reach.reach.profiles.copy()
|
||||||
|
profiles.reverse()
|
||||||
|
|
||||||
previous_elevation = -99999.99
|
previous_elevation = -99999.99
|
||||||
|
|
||||||
data_discharge = {}
|
data_discharge = {}
|
||||||
@@ -515,14 +375,12 @@ class InitialConditions(SQLSubModel):
|
|||||||
data_discharge[profile.rk] = 0.0
|
data_discharge[profile.rk] = 0.0
|
||||||
else:
|
else:
|
||||||
for data in self._data:
|
for data in self._data:
|
||||||
data_discharge[data["rk"].rk] = data["discharge"]
|
data_discharge[data["rk"]] = data["discharge"]
|
||||||
|
|
||||||
incline = self._reach.reach.get_incline_median_mean()
|
incline = self._reach.reach.get_incline_median_mean()
|
||||||
logger.debug(f"incline = {incline}")
|
logger.debug(f"incline = {incline}")
|
||||||
|
self._data = []
|
||||||
self.clear_data()
|
for profile in profiles:
|
||||||
|
|
||||||
for profile in reversed(profiles):
|
|
||||||
width = profile.wet_width(profile.z_min() + height)
|
width = profile.wet_width(profile.z_min() + height)
|
||||||
area = profile.wet_area(profile.z_min() + height)
|
area = profile.wet_area(profile.z_min() + height)
|
||||||
radius = profile.wet_radius(profile.z_min() + height)
|
radius = profile.wet_radius(profile.z_min() + height)
|
||||||
@@ -563,17 +421,21 @@ class InitialConditions(SQLSubModel):
|
|||||||
logger.debug(f" discharge = {discharge}")
|
logger.debug(f" discharge = {discharge}")
|
||||||
|
|
||||||
new = Data(reach=self._reach, status=self._status)
|
new = Data(reach=self._reach, status=self._status)
|
||||||
new["rk"] = profile
|
new["rk"] = profile.rk
|
||||||
new["discharge"] = discharge
|
new["discharge"] = discharge
|
||||||
new["elevation"] = elevation
|
new["elevation"] = elevation
|
||||||
|
|
||||||
previous_elevation = elevation
|
previous_elevation = elevation
|
||||||
self._data.append(new)
|
self._data.append(new)
|
||||||
|
|
||||||
|
self._data.reverse()
|
||||||
|
|
||||||
def generate_discharge(self, discharge: float, compute_height: bool):
|
def generate_discharge(self, discharge: float, compute_height: bool):
|
||||||
|
|
||||||
profiles = self._reach.reach.profiles.copy()
|
profiles = self._reach.reach.profiles.copy()
|
||||||
if profiles is None:
|
if profiles is None:
|
||||||
return None
|
return None
|
||||||
|
profiles.reverse()
|
||||||
|
|
||||||
previous_elevation = -99999.99
|
previous_elevation = -99999.99
|
||||||
|
|
||||||
@@ -584,26 +446,20 @@ class InitialConditions(SQLSubModel):
|
|||||||
data_height[profile.rk] = 0.0
|
data_height[profile.rk] = 0.0
|
||||||
else:
|
else:
|
||||||
for data in self._data:
|
for data in self._data:
|
||||||
data_height[data["rk"].rk] = data["height"]
|
data_height[data["rk"]] = data["height"]
|
||||||
|
|
||||||
print(data_height)
|
|
||||||
|
|
||||||
incline = self._reach.reach.get_incline_median_mean()
|
incline = self._reach.reach.get_incline_median_mean()
|
||||||
logger.debug(f"incline = {incline}")
|
logger.debug(f"incline = {incline}")
|
||||||
|
self._data = []
|
||||||
self.clear_data()
|
for profile in profiles:
|
||||||
|
|
||||||
for profile in reversed(profiles):
|
|
||||||
width = profile.width_approximation()
|
width = profile.width_approximation()
|
||||||
frictions = self._reach.frictions.frictions
|
frictions = self._reach.frictions.frictions
|
||||||
strickler = None
|
strickler = None
|
||||||
|
|
||||||
if frictions is not None:
|
if frictions is not None:
|
||||||
if len(frictions) >= 1:
|
if len(frictions) >= 1:
|
||||||
for f in frictions:
|
for f in frictions:
|
||||||
if f.contains_rk(profile.rk):
|
if f.contains_rk(profile.rk):
|
||||||
strickler = f.get_friction(profile.rk)[0]
|
strickler = f.get_friction(profile.rk)[0]
|
||||||
|
|
||||||
if strickler is None:
|
if strickler is None:
|
||||||
strickler = 25.0
|
strickler = 25.0
|
||||||
|
|
||||||
@@ -618,9 +474,8 @@ class InitialConditions(SQLSubModel):
|
|||||||
else:
|
else:
|
||||||
height = (
|
height = (
|
||||||
discharge
|
discharge
|
||||||
/ ((width * 0.8)
|
/
|
||||||
* strickler
|
((width * 0.8) * strickler * (abs(incline) ** (0.5)))
|
||||||
* (abs(incline) ** (0.5)))
|
|
||||||
) ** (0.6)
|
) ** (0.6)
|
||||||
|
|
||||||
elevation = max(
|
elevation = max(
|
||||||
@@ -634,13 +489,15 @@ class InitialConditions(SQLSubModel):
|
|||||||
logger.debug(f" height = {height}")
|
logger.debug(f" height = {height}")
|
||||||
|
|
||||||
new = Data(reach=self._reach, status=self._status)
|
new = Data(reach=self._reach, status=self._status)
|
||||||
new["section"] = profile
|
new["rk"] = profile.rk
|
||||||
new["discharge"] = discharge
|
new["discharge"] = discharge
|
||||||
new["elevation"] = elevation
|
new["elevation"] = elevation
|
||||||
|
|
||||||
previous_elevation = elevation
|
previous_elevation = elevation
|
||||||
self._data.append(new)
|
self._data.append(new)
|
||||||
|
|
||||||
|
self._data.reverse()
|
||||||
|
|
||||||
def generate_height(self,
|
def generate_height(self,
|
||||||
elevation1: float,
|
elevation1: float,
|
||||||
elevation2: float,
|
elevation2: float,
|
||||||
@@ -656,9 +513,9 @@ class InitialConditions(SQLSubModel):
|
|||||||
data_discharge[profile.rk] = 0.0
|
data_discharge[profile.rk] = 0.0
|
||||||
else:
|
else:
|
||||||
for data in self._data:
|
for data in self._data:
|
||||||
data_discharge[data["rk"].rk] = data["discharge"]
|
data_discharge[data["rk"]] = data["discharge"]
|
||||||
|
|
||||||
self.clear_data()
|
self._data = []
|
||||||
for profile in profiles:
|
for profile in profiles:
|
||||||
|
|
||||||
if not compute_discharge:
|
if not compute_discharge:
|
||||||
@@ -672,7 +529,7 @@ class InitialConditions(SQLSubModel):
|
|||||||
[upstream_rk, downstream_rk],
|
[upstream_rk, downstream_rk],
|
||||||
[elevation1, elevation2])
|
[elevation1, elevation2])
|
||||||
new = Data(reach=self._reach, status=self._status)
|
new = Data(reach=self._reach, status=self._status)
|
||||||
new["rk"] = profile
|
new["rk"] = profile.rk
|
||||||
new["discharge"] = d
|
new["discharge"] = d
|
||||||
new["elevation"] = elevation
|
new["elevation"] = elevation
|
||||||
self._data.append(new)
|
self._data.append(new)
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# InitialConditionsDict.py -- Pamhyr
|
# InitialConditionsDict.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -64,8 +64,7 @@ class InitialConditionsDict(PamhyrModelDict):
|
|||||||
|
|
||||||
execute(
|
execute(
|
||||||
"DELETE FROM initial_conditions " +
|
"DELETE FROM initial_conditions " +
|
||||||
f"WHERE reach = '{reach.id}' " +
|
f"WHERE reach = '{reach.id}'"
|
||||||
f"AND scenario = {self._status.scenario_id}"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
ok &= self._dict[reach]._db_save(execute, data)
|
ok &= self._dict[reach]._db_save(execute, data)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# InitialConditionsAdisTS.py -- Pamhyr
|
# InitialConditionsAdisTS.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -23,7 +23,6 @@ from tools import trace, timer, old_pamhyr_date_to_timestamp
|
|||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
from Model.Scenario import Scenario
|
|
||||||
|
|
||||||
from Model.InitialConditionsAdisTS.InitialConditionsAdisTSSpec \
|
from Model.InitialConditionsAdisTS.InitialConditionsAdisTSSpec \
|
||||||
import ICAdisTSSpec
|
import ICAdisTSSpec
|
||||||
@@ -35,17 +34,19 @@ class InitialConditionsAdisTS(SQLSubModel):
|
|||||||
_sub_classes = [
|
_sub_classes = [
|
||||||
ICAdisTSSpec,
|
ICAdisTSSpec,
|
||||||
]
|
]
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
def __init__(self, id: int = -1, name: str = "default",
|
def __init__(self, id: int = -1, name: str = "default",
|
||||||
pollutant: int = -1, status=None,
|
pollutant: int = -1, status=None):
|
||||||
owner_scenario=-1):
|
super(InitialConditionsAdisTS, self).__init__()
|
||||||
super(InitialConditionsAdisTS, self).__init__(
|
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._status = status
|
self._status = status
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = InitialConditionsAdisTS._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self._pollutant = pollutant
|
self._pollutant = pollutant
|
||||||
self._concentration = None
|
self._concentration = None
|
||||||
@@ -55,159 +56,81 @@ class InitialConditionsAdisTS(SQLSubModel):
|
|||||||
self._enabled = True
|
self._enabled = True
|
||||||
self._data = []
|
self._data = []
|
||||||
|
|
||||||
|
InitialConditionsAdisTS._id_cnt = max(
|
||||||
|
InitialConditionsAdisTS._id_cnt + 1,
|
||||||
|
self.id + 1
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE initial_conditions_adists{ext}(
|
CREATE TABLE initial_conditions_adists(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
pollutant INTEGER NOT NULL,
|
||||||
pollutant INTEGER NOT NULL,
|
name TEXT NOT NULL,
|
||||||
name TEXT NOT NULL,
|
concentration REAL NOT NULL,
|
||||||
concentration REAL NOT NULL,
|
eg REAL NOT NULL,
|
||||||
eg REAL NOT NULL,
|
em REAL NOT NULL,
|
||||||
em REAL NOT NULL,
|
ed REAL NOT NULL,
|
||||||
ed REAL NOT NULL,
|
enabled BOOLEAN NOT NULL,
|
||||||
enabled BOOLEAN NOT NULL,
|
FOREIGN KEY(pollutant) REFERENCES Pollutants(id)
|
||||||
{Scenario.create_db_add_scenario()},
|
)
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
""")
|
||||||
FOREIGN KEY(pollutant) REFERENCES Pollutants(pamhyr_id)
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
created = False
|
if major == 0 and minor < 1:
|
||||||
|
|
||||||
if major == "0" and int(minor) <= 1:
|
|
||||||
if int(release) < 6:
|
if int(release) < 6:
|
||||||
cls._db_create(execute)
|
cls._db_create(execute)
|
||||||
created = True
|
|
||||||
|
|
||||||
if major == "0" and int(minor) < 2:
|
|
||||||
if not created:
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "initial_conditions_adists"
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
"(pamhyr_id, pollutant, name, concentration, " +
|
|
||||||
"eg, em, ed, enabled, scenario) " +
|
|
||||||
"SELECT pamhyr_id, pollutant, name, concentration, " +
|
|
||||||
"eg, em, ed, enabled, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
cls._db_update_to_0_2_0_set_pollutants_pid(execute, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_pollutants_pid(cls, execute, data):
|
|
||||||
pid_pol = data["id2pid"]["Pollutants"]
|
|
||||||
els = execute(
|
|
||||||
f"SELECT pamhyr_id, pollutant " +
|
|
||||||
"FROM initial_conditions_adists"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in els:
|
|
||||||
it = iter(row)
|
|
||||||
pid = next(it)
|
|
||||||
pol_id = next(it)
|
|
||||||
|
|
||||||
if pol_id == -1:
|
|
||||||
continue
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE initial_conditions_adists " +
|
|
||||||
f"SET pollutant = {pid_pol[pol_id]} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
new = []
|
||||||
status = data['status']
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, deleted, " +
|
"SELECT id, pollutant, name, concentration, eg, em, ed, " +
|
||||||
"pollutant, name, concentration, eg, em, ed, " +
|
"enabled " +
|
||||||
"enabled, scenario " +
|
"FROM initial_conditions_adists"
|
||||||
"FROM initial_conditions_adists " +
|
|
||||||
f"WHERE scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if table is not None:
|
if table is not None:
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
IC_id = row[0]
|
||||||
|
pollutant = row[1]
|
||||||
|
name = row[2]
|
||||||
|
concentration = row[3]
|
||||||
|
eg = row[4]
|
||||||
|
em = row[5]
|
||||||
|
ed = row[6]
|
||||||
|
enabled = (row[7] == 1)
|
||||||
|
|
||||||
pid = next(it)
|
IC = cls(
|
||||||
deleted = (next(it) == 1)
|
id=IC_id,
|
||||||
pollutant = next(it)
|
|
||||||
name = next(it)
|
|
||||||
concentration = next(it)
|
|
||||||
eg = next(it)
|
|
||||||
em = next(it)
|
|
||||||
ed = next(it)
|
|
||||||
enabled = (next(it) == 1)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
ic = cls(
|
|
||||||
id=pid,
|
|
||||||
name=name,
|
name=name,
|
||||||
status=status,
|
status=data['status']
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
if deleted:
|
|
||||||
ic.set_as_deleted()
|
|
||||||
|
|
||||||
ic.pollutant = pollutant
|
IC.pollutant = pollutant
|
||||||
ic.concentration = concentration
|
IC.concentration = concentration
|
||||||
ic.eg = eg
|
IC.eg = eg
|
||||||
ic.em = em
|
IC.em = em
|
||||||
ic.ed = ed
|
IC.ed = ed
|
||||||
ic.enabled = enabled
|
IC.enabled = enabled
|
||||||
|
|
||||||
data['ic_default_id'] = pid
|
data['ic_default_id'] = IC_id
|
||||||
ic._data = ICAdisTSSpec._db_load(execute, data)
|
IC._data = ICAdisTSSpec._db_load(execute, data)
|
||||||
|
|
||||||
loaded.add(pid)
|
new.append(IC)
|
||||||
new.append(ic)
|
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
if not self.must_be_saved():
|
execute(f"DELETE FROM initial_conditions_adists WHERE id = {self.id}")
|
||||||
return True
|
|
||||||
|
|
||||||
execute(
|
|
||||||
"DELETE FROM initial_conditions_adists " +
|
|
||||||
f"WHERE pamhyr_id = {self.id} " +
|
|
||||||
f"AND scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
pollutant = -1
|
pollutant = -1
|
||||||
if self.pollutant is not None:
|
if self.pollutant is not None:
|
||||||
@@ -232,14 +155,12 @@ class InitialConditionsAdisTS(SQLSubModel):
|
|||||||
sql = (
|
sql = (
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"initial_conditions_adists(" +
|
"initial_conditions_adists(" +
|
||||||
"pamhyr_id, deleted, pollutant, name, concentration, " +
|
"id, pollutant, name, concentration, " +
|
||||||
"eg, em, ed, enabled, scenario" +
|
"eg, em, ed, enabled" +
|
||||||
") " +
|
") " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, {self.is_deleted()}, " +
|
f"{self.id}, {pollutant}, '{self._db_format(self._name)}', " +
|
||||||
f"{pollutant}, '{self._db_format(self._name)}', " +
|
f"{concentration}, {eg}, {em}, {ed}, {self._enabled}" +
|
||||||
f"{concentration}, {eg}, {em}, {ed}, {self._enabled}, " +
|
|
||||||
f"{self._status.scenario_id}" +
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -247,9 +168,8 @@ class InitialConditionsAdisTS(SQLSubModel):
|
|||||||
|
|
||||||
data['ic_default_id'] = self.id
|
data['ic_default_id'] = self.id
|
||||||
execute(
|
execute(
|
||||||
"DELETE FROM initial_conditions_adists_spec " +
|
"DELETE FROM initial_conditions_spec " +
|
||||||
f"WHERE ic_default = {self.id} " +
|
f"WHERE ic_default = {self.id}"
|
||||||
f"AND scenario = {self._status.scenario_id}"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for ic_spec in self._data:
|
for ic_spec in self._data:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# InitialConditionsAdisTSList.py -- Pamhyr
|
# InitialConditionsAdisTSList.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
from copy import copy
|
from copy import copy
|
||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
|
|
||||||
from Model.Tools.PamhyrListExt import PamhyrModelList
|
from Model.Tools.PamhyrList import PamhyrModelList
|
||||||
from Model.InitialConditionsAdisTS.InitialConditionsAdisTS \
|
from Model.InitialConditionsAdisTS.InitialConditionsAdisTS \
|
||||||
import InitialConditionsAdisTS
|
import InitialConditionsAdisTS
|
||||||
|
|
||||||
@@ -43,10 +43,7 @@ class InitialConditionsAdisTSList(PamhyrModelList):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute(
|
execute("DELETE FROM initial_conditions_adists")
|
||||||
"DELETE FROM initial_conditions_adists " +
|
|
||||||
f"WHERE scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if data is None:
|
if data is None:
|
||||||
data = {}
|
data = {}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# InitialConditionsAdisTSSpec.py -- Pamhyr
|
# InitialConditionsAdisTSSpec.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -22,13 +22,14 @@ from tools import trace, timer
|
|||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
from Model.Scenario import Scenario
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
class ICAdisTSSpec(SQLSubModel):
|
class ICAdisTSSpec(SQLSubModel):
|
||||||
_sub_classes = []
|
_sub_classes = [
|
||||||
|
]
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
status=None):
|
status=None):
|
||||||
@@ -36,6 +37,11 @@ class ICAdisTSSpec(SQLSubModel):
|
|||||||
|
|
||||||
self._status = status
|
self._status = status
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = ICAdisTSSpec._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
self._name_section = name
|
self._name_section = name
|
||||||
self._reach = None
|
self._reach = None
|
||||||
self._start_rk = None
|
self._start_rk = None
|
||||||
@@ -47,142 +53,73 @@ class ICAdisTSSpec(SQLSubModel):
|
|||||||
self._rate = None
|
self._rate = None
|
||||||
self._enabled = True
|
self._enabled = True
|
||||||
|
|
||||||
|
ICAdisTSSpec._id_cnt = max(ICAdisTSSpec._id_cnt + 1, self.id + 1)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE initial_conditions_adists_spec{ext}(
|
CREATE TABLE initial_conditions_spec(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
ic_default INTEGER NOT NULL,
|
||||||
ic_default INTEGER NOT NULL,
|
name TEXT NOT NULL,
|
||||||
name TEXT NOT NULL,
|
reach INTEGER NOT NULL,
|
||||||
reach INTEGER NOT NULL,
|
start_rk REAL NOT NULL,
|
||||||
start_rk REAL NOT NULL,
|
end_rk REAL NOT NULL,
|
||||||
end_rk REAL NOT NULL,
|
concentration REAL NOT NULL,
|
||||||
concentration REAL NOT NULL,
|
eg REAL NOT NULL,
|
||||||
eg REAL NOT NULL,
|
em REAL NOT NULL,
|
||||||
em REAL NOT NULL,
|
ed REAL NOT NULL,
|
||||||
ed REAL NOT NULL,
|
rate REAL NOT NULL,
|
||||||
rate REAL NOT NULL,
|
enabled BOOLEAN NOT NULL,
|
||||||
enabled BOOLEAN NOT NULL,
|
FOREIGN KEY(ic_default) REFERENCES initial_conditions_adists(id),
|
||||||
{Scenario.create_db_add_scenario()},
|
FOREIGN KEY(reach) REFERENCES river_reach(id)
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
)
|
||||||
FOREIGN KEY(ic_default)
|
""")
|
||||||
REFERENCES initial_conditions_adists(pamhyr_id),
|
|
||||||
FOREIGN KEY(reach) REFERENCES river_reach(pamhyr_id)
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
created = False
|
|
||||||
|
|
||||||
if major == 0 and minor < 1:
|
if major == 0 and minor < 1:
|
||||||
if int(release) < 6:
|
if int(release) < 6:
|
||||||
cls._db_create(execute)
|
cls._db_create(execute)
|
||||||
created = True
|
|
||||||
|
|
||||||
if major == "0" and int(minor) < 2:
|
|
||||||
if not created:
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "initial_conditions_spec"
|
|
||||||
table_new = "initial_conditions_adists_spec"
|
|
||||||
reachs = data['id2pid']['river_reach']
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table_new}_tmp " +
|
|
||||||
"(pamhyr_id, ic_default, name, reach, start_rk, end_rk, " +
|
|
||||||
"concentration, eg, em, ed, rate, enabled, scenario) " +
|
|
||||||
"SELECT pamhyr_id, ic_default, name, reach, start_rk, end_rk, " +
|
|
||||||
"concentration, eg, em, ed, rate, enabled, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table_new}_tmp RENAME TO {table_new}")
|
|
||||||
|
|
||||||
cls._db_update_to_0_2_0_set_reach_pid(execute, table_new, reachs)
|
|
||||||
cls._db_update_to_0_2_0_set_ica_pid(execute, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_ica_pid(cls, execute, data):
|
|
||||||
pid_ica = data["id2pid"]["initial_conditions_adist"]
|
|
||||||
els = execute(
|
|
||||||
"SELECT pamhyr_id, ic_default " +
|
|
||||||
"FROM initial_conditions_adists_spec"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in els:
|
|
||||||
it = iter(row)
|
|
||||||
pid = next(it)
|
|
||||||
ica_id = next(it)
|
|
||||||
|
|
||||||
if ica_id == -1:
|
|
||||||
continue
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE initial_conditions_adists_spec " +
|
|
||||||
f"SET ic_default = {pid_ica[ica_id]} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
new = []
|
||||||
status = data['status']
|
|
||||||
reachs = data["edges"]
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, deleted, name, reach, start_rk, end_rk, " +
|
"SELECT id, ic_default, name, reach, start_rk, end_rk, " +
|
||||||
"concentration, eg, em, ed, rate, enabled, scenario " +
|
"concentration, eg, em, ed, rate, enabled " +
|
||||||
"FROM initial_conditions_adists_spec " +
|
"FROM initial_conditions_spec " +
|
||||||
f"WHERE ic_default = {data['ic_default_id']} " +
|
f"WHERE ic_default = {data['ic_default_id']} "
|
||||||
f"AND scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) "
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if table is not None:
|
if table is not None:
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
id = row[0]
|
||||||
|
name = row[2]
|
||||||
|
reach = row[3]
|
||||||
|
start_rk = row[4]
|
||||||
|
end_rk = row[5]
|
||||||
|
concentration = row[6]
|
||||||
|
eg = row[7]
|
||||||
|
em = row[8]
|
||||||
|
ed = row[9]
|
||||||
|
rate = row[10]
|
||||||
|
enabled = (row[11] == 1)
|
||||||
|
|
||||||
id = next(it)
|
# new_spec = [name, reach, start_rk, end_rk,
|
||||||
deleted = (next(it) == 1)
|
# concentration, eg, em, ed, rate, enabled]
|
||||||
name = next(it)
|
|
||||||
reach = next(it)
|
|
||||||
start_rk = next(it)
|
|
||||||
end_rk = next(it)
|
|
||||||
concentration = next(it)
|
|
||||||
eg = next(it)
|
|
||||||
em = next(it)
|
|
||||||
ed = next(it)
|
|
||||||
rate = next(it)
|
|
||||||
enabled = (next(it) == 1)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
new_spec = cls(
|
new_spec = cls(
|
||||||
id=id, name=name,
|
id=id,
|
||||||
status=status,
|
name=name,
|
||||||
owner_scenario=owner_scenario
|
status=data['status']
|
||||||
)
|
)
|
||||||
if deleted:
|
|
||||||
new_spec.set_as_deleted()
|
|
||||||
|
|
||||||
new_spec.reach = reach
|
new_spec.reach = reach
|
||||||
new_spec.start_rk = start_rk
|
new_spec.start_rk = start_rk
|
||||||
@@ -194,37 +131,30 @@ class ICAdisTSSpec(SQLSubModel):
|
|||||||
new_spec.rate = rate
|
new_spec.rate = rate
|
||||||
new_spec.enabled = enabled
|
new_spec.enabled = enabled
|
||||||
|
|
||||||
loaded.add(pid)
|
|
||||||
new.append(new_spec)
|
new.append(new_spec)
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
if not self.must_be_saved():
|
|
||||||
return True
|
|
||||||
|
|
||||||
ic_default = data['ic_default_id']
|
ic_default = data['ic_default_id']
|
||||||
|
|
||||||
sql = (
|
sql = (
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"initial_conditions_adists_spec(id, deleted, " +
|
"initial_conditions_spec(id, ic_default, name, reach, " +
|
||||||
"ic_default, name, reach, " +
|
"start_rk, end_rk, concentration, eg, em, ed, rate, enabled) " +
|
||||||
"start_rk, end_rk, concentration, eg, em, ed, rate, " +
|
|
||||||
"enabled, scenario) " +
|
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, {self._db_format(self.is_deleted())}, " +
|
f"{self.id}, " +
|
||||||
f"{ic_default}, " +
|
f"{ic_default}, " +
|
||||||
f"'{self._db_format(self._name_section)}', " +
|
f"'{self._db_format(self._name_section)}', " +
|
||||||
f"{self._reach}, " +
|
f"{self._reach}, " +
|
||||||
f"{self._start_rk}, {self._end_rk}, " +
|
f"{self._start_rk}, " +
|
||||||
|
f"{self._end_rk}, " +
|
||||||
f"{self._concentration}, " +
|
f"{self._concentration}, " +
|
||||||
f"{self._eg}, {self._em}, {self._ed}, " +
|
f"{self._eg}, " +
|
||||||
f"{self._rate}, {self._enabled}, " +
|
f"{self._em}, " +
|
||||||
f"{self._status.scenario_id}" +
|
f"{self._ed}, " +
|
||||||
|
f"{self._rate}, " +
|
||||||
|
f"{self._enabled}" +
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
execute(sql)
|
execute(sql)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# LateralContribution.py -- Pamhyr
|
# LateralContribution.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -27,226 +27,66 @@ from tools import (
|
|||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
from Model.Scenario import Scenario
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
class Data(SQLSubModel):
|
|
||||||
_sub_classes = []
|
|
||||||
|
|
||||||
def __init__(self,
|
|
||||||
data0, data1,
|
|
||||||
id: int = -1,
|
|
||||||
types=[float, float],
|
|
||||||
status=None,
|
|
||||||
owner_scenario=-1):
|
|
||||||
super(Data, self).__init__(
|
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._types = types
|
|
||||||
self._data = [data0, data1]
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_create(cls, execute, ext=""):
|
|
||||||
execute(f"""
|
|
||||||
CREATE TABLE lateral_contribution_data{ext} (
|
|
||||||
{cls.create_db_add_pamhyr_id()},
|
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
ind INTEGER NOT NULL,
|
|
||||||
data0 TEXT NOT NULL,
|
|
||||||
data1 TEXT NOT NULL,
|
|
||||||
lc INTEGER,
|
|
||||||
{Scenario.create_db_add_scenario()},
|
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
|
||||||
FOREIGN KEY(lc) REFERENCES lateral_contribution(pamhyr_id),
|
|
||||||
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 == minor == "0":
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
if major == "0" and minor == "1":
|
|
||||||
if int(release) < 2:
|
|
||||||
execute(
|
|
||||||
"ALTER TABLE boundary_condition_data " +
|
|
||||||
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
|
|
||||||
)
|
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data=None):
|
|
||||||
table = "lateral_contribution_data"
|
|
||||||
lcs = data['id2pid']['lateral_contribution']
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table} " +
|
|
||||||
"(pamhyr_id, ind, data0, data1, lc, scenario) " +
|
|
||||||
"SELECT pamhyr_id, ind, data0, data1, lc, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
cls._db_update_to_0_2_0_set_lc_pid(execute, table, lcs)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_lc_pid(cls, execute, table, lcs):
|
|
||||||
els = execute(
|
|
||||||
f"SELECT pamhyr_id, lc FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in els:
|
|
||||||
it = iter(row)
|
|
||||||
pid = next(it)
|
|
||||||
lc_id = next(it)
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE {table} " +
|
|
||||||
f"SET lc = {lcs[lc_id]} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_load(cls, execute, data=None):
|
|
||||||
new = []
|
|
||||||
lc = data["lc"]
|
|
||||||
status = data['status']
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
values = execute(
|
|
||||||
"SELECT pamhyr_id, deleted, " +
|
|
||||||
"data0, data1, scenario " +
|
|
||||||
"FROM lateral_contribution_data " +
|
|
||||||
f"WHERE lc = {lc._pamhyr_id} " +
|
|
||||||
f"AND 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)
|
|
||||||
delete = next(it)
|
|
||||||
data0 = bc._types[0](next(it))
|
|
||||||
data1 = bc._types[1](next(it))
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
nd = cls(
|
|
||||||
data0, data1,
|
|
||||||
id=pid,
|
|
||||||
types=lc._types,
|
|
||||||
status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
if deleted:
|
|
||||||
nd.set_as_deleted()
|
|
||||||
|
|
||||||
loaded.add(pid)
|
|
||||||
new.append(nd)
|
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
|
||||||
pid = self._pamhyr_id
|
|
||||||
ind = data["ind"]
|
|
||||||
data0 = self._db_format(str(self[0]))
|
|
||||||
data1 = self._db_format(str(self[1]))
|
|
||||||
lc = data["lc"]
|
|
||||||
|
|
||||||
execute(
|
|
||||||
"INSERT INTO " +
|
|
||||||
"lateral_contribution_data (pamhyr_id, deleted, ind, " +
|
|
||||||
"data0, data1, lc, scenario) " +
|
|
||||||
f"VALUES ({pid}, {self._db_format(self.is_deleted())}, " +
|
|
||||||
f"{ind}, '{self._db_format(data0)}', {self._db_format(data1)}, " +
|
|
||||||
f"{lc._pamhyr_id}, {self._status.scenario_id}" +
|
|
||||||
")"
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
|
||||||
return self._types[key](self._data[key])
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
self._data[key] = self._types[key](value)
|
|
||||||
|
|
||||||
|
|
||||||
class LateralContribution(SQLSubModel):
|
class LateralContribution(SQLSubModel):
|
||||||
_sub_classes = [Data]
|
_sub_classes = []
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
def __init__(self, id: int = -1,
|
def __init__(self, id: int = -1, name: str = "", status=None):
|
||||||
name: str = "",
|
super(LateralContribution, self).__init__()
|
||||||
status=None, owner_scenario=-1):
|
|
||||||
super(LateralContribution, self).__init__(
|
self._status = status
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
if id == -1:
|
||||||
)
|
self.id = LateralContribution._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self._type = ""
|
self._type = ""
|
||||||
self._reach = None
|
self._edge = None
|
||||||
self._begin_section = None
|
self._begin_rk = 0.0
|
||||||
self._end_section = None
|
self._end_rk = 0.0
|
||||||
self._data = []
|
self._data = []
|
||||||
self._header = []
|
self._header = []
|
||||||
self._types = [float, float]
|
self._types = [float, float]
|
||||||
|
|
||||||
|
LateralContribution._id_cnt = max(
|
||||||
|
LateralContribution._id_cnt + 1, self.id + 1)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE lateral_contribution{ext}(
|
CREATE TABLE lateral_contribution(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
type TEXT NOT NULL,
|
type TEXT NOT NULL,
|
||||||
tab TEXT NOT NULL,
|
tab TEXT NOT NULL,
|
||||||
reach INTEGER,
|
edge INTEGER,
|
||||||
begin_section INTEGER,
|
begin_rk REAL NOT NULL,
|
||||||
end_section INTEGER,
|
end_rk REAL NOT NULL,
|
||||||
{Scenario.create_db_add_scenario()},
|
FOREIGN KEY(edge) REFERENCES river_reach(id)
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
|
||||||
FOREIGN KEY(reach) REFERENCES river_reach(pamhyr_id),
|
|
||||||
FOREIGN KEY(begin_section)
|
|
||||||
REFERENCES geometry_profileXYZ(pamhyr_id),
|
|
||||||
FOREIGN KEY(end_section)
|
|
||||||
REFERENCES geometry_profileXYZ(pamhyr_id),
|
|
||||||
PRIMARY KEY(pamhyr_id, scenario)
|
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
if ext == "_tmp":
|
execute("""
|
||||||
return True
|
CREATE TABLE lateral_contribution_data(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
ind INTEGER NOT NULL,
|
||||||
|
data0 TEXT NOT NULL,
|
||||||
|
data1 TEXT NOT NULL,
|
||||||
|
lc INTEGER,
|
||||||
|
FOREIGN KEY(lc) REFERENCES lateral_contribution(id)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
if major == minor == "0":
|
if major == minor == "0":
|
||||||
if int(release) < 11:
|
if int(release) < 11:
|
||||||
@@ -263,45 +103,7 @@ class LateralContribution(SQLSubModel):
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
execute(
|
return True
|
||||||
"""
|
|
||||||
ALTER TABLE lateral_contribution
|
|
||||||
RENAME COLUMN edge TO reach
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
if major == "0" and minor == "1":
|
|
||||||
if int(release) < 2:
|
|
||||||
execute(
|
|
||||||
"ALTER TABLE lateral_contribution " +
|
|
||||||
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
|
|
||||||
)
|
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "lateral_contribution"
|
|
||||||
reachs = data['id2pid']['river_reach']
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table} " +
|
|
||||||
"(pamhyr_id, name, type, tab, reach, scenario) " +
|
|
||||||
"SELECT pamhyr_id, name, type, tab, reach, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
cls._db_update_to_0_2_0_set_reach_pid(execute, table, reachs)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_ctor_from_type(cls, t):
|
def _get_ctor_from_type(cls, t):
|
||||||
@@ -322,142 +124,84 @@ class LateralContribution(SQLSubModel):
|
|||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
new = []
|
||||||
tab = data["tab"]
|
tab = data["tab"]
|
||||||
status = data['status']
|
|
||||||
edges = data["edges"]
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, deleted, name, type, " +
|
"SELECT id, name, type, edge, begin_rk, end_rk " +
|
||||||
"reach, begin_section, end_section, scenario " +
|
f"FROM lateral_contribution WHERE tab = '{tab}'"
|
||||||
"FROM lateral_contribution " +
|
|
||||||
f"WHERE tab = '{tab}'" +
|
|
||||||
f"AND scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) "
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
t = row[2]
|
||||||
|
|
||||||
pid = next(it)
|
|
||||||
deleted = next(it)
|
|
||||||
name = next(it)
|
|
||||||
t = next(it)
|
|
||||||
reach = next(it)
|
|
||||||
b_section = next(it)
|
|
||||||
e_section = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
ctor = cls._get_ctor_from_type(t)
|
ctor = cls._get_ctor_from_type(t)
|
||||||
lc = ctor(
|
lc = ctor(
|
||||||
id=pid, name=name,
|
id=row[0],
|
||||||
status=status,
|
name=row[1],
|
||||||
owner_scenario=owner_scenario
|
status=data['status']
|
||||||
)
|
)
|
||||||
lc.reach = None
|
lc.edge = None
|
||||||
lc._begin_section = None
|
|
||||||
lc._end_section = None
|
|
||||||
|
|
||||||
if row[3] != -1:
|
if row[3] != -1:
|
||||||
lc.reach = next(
|
lc.edge = next(filter(lambda e: e.id == row[3], data["edges"]))
|
||||||
filter(
|
lc._begin_rk = row[4]
|
||||||
lambda e: e.id == reach,
|
lc._end_rk = row[5]
|
||||||
edges
|
|
||||||
)
|
|
||||||
)
|
|
||||||
lc._begin_section = next(
|
|
||||||
filter(
|
|
||||||
lambda p: p.id == b_section,
|
|
||||||
lc.reach.reach.profiles
|
|
||||||
)
|
|
||||||
)
|
|
||||||
lc._end_section = next(
|
|
||||||
filter(
|
|
||||||
lambda p: p.id == e_section,
|
|
||||||
lc.reach.reach.profiles
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
data["lc"] = lc
|
values = execute(
|
||||||
lc._data = Data._db_load(execute, data=data)
|
"SELECT ind, data0, data1 FROM lateral_contribution_data " +
|
||||||
|
f"WHERE lc = '{lc.id}'"
|
||||||
|
)
|
||||||
|
# Create dummy data list
|
||||||
|
for _ in values:
|
||||||
|
lc.add(0)
|
||||||
|
# Write data
|
||||||
|
for v in values:
|
||||||
|
ind = v[0]
|
||||||
|
data0 = lc._types[0](v[1])
|
||||||
|
data1 = lc._types[1](v[2])
|
||||||
|
# Replace data at pos ind
|
||||||
|
lc._data[ind] = (data0, data1)
|
||||||
|
|
||||||
loaded.add(pid)
|
|
||||||
new.append(lc)
|
new.append(lc)
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
if not self.must_be_saved():
|
|
||||||
return True
|
|
||||||
|
|
||||||
tab = data["tab"]
|
tab = data["tab"]
|
||||||
data["lc"] = self
|
|
||||||
|
|
||||||
execute(
|
execute(f"DELETE FROM lateral_contribution WHERE id = {self.id}")
|
||||||
"DELETE FROM lateral_contribution_data " +
|
execute(f"DELETE FROM lateral_contribution_data WHERE lc = {self.id}")
|
||||||
f"WHERE lc = {self._pamhyr_id} " +
|
|
||||||
f"AND scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
reach = -1
|
edge = -1
|
||||||
begin_section = -1
|
if self._edge is not None:
|
||||||
end_section = -1
|
edge = self._edge.id
|
||||||
|
|
||||||
if self._reach is not None:
|
sql = (
|
||||||
reach = self._reach._pamhyr_id
|
|
||||||
begin_section = self._begin_section._pamhyr_id
|
|
||||||
end_section = self._end_section._pamhyr_id
|
|
||||||
|
|
||||||
execute(
|
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"lateral_contribution(" +
|
"lateral_contribution(id, name, type, tab, " +
|
||||||
"pamhyr_id, deleted, name, type, tab, " +
|
"edge, begin_rk, end_rk) " +
|
||||||
"reach, begin_section, end_section, scenario) " +
|
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, {self._db_format(self.is_deleted())}, " +
|
f"{self.id}, '{self._db_format(self._name)}', " +
|
||||||
f"'{self._db_format(self._name)}', " +
|
f"'{self._db_format(self._type)}', '{tab}', {edge}, " +
|
||||||
f"'{self._db_format(self._type)}', '{tab}', {reach}, " +
|
f"{self._begin_rk}, {self._end_rk}" +
|
||||||
f"{begin_section}, {end_section}, " +
|
|
||||||
f"{self._status.scenario_id}" +
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
ind = 0
|
ind = 0
|
||||||
for d in self._data:
|
for d in self._data:
|
||||||
data["ind"] = ind
|
data0 = self._db_format(str(d[0]))
|
||||||
|
data1 = self._db_format(str(d[1]))
|
||||||
d._db_save(execute, data)
|
|
||||||
|
|
||||||
|
sql = (
|
||||||
|
"INSERT INTO " +
|
||||||
|
"lateral_contribution_data(ind, data0, data1, lc) " +
|
||||||
|
f"VALUES ({ind}, '{data0}', {data1}, {self.id})"
|
||||||
|
)
|
||||||
|
execute(sql)
|
||||||
ind += 1
|
ind += 1
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _data_traversal(self,
|
|
||||||
predicate=lambda obj, data: True,
|
|
||||||
modifier=lambda obj, data: None,
|
|
||||||
data={}):
|
|
||||||
if predicate(self, data):
|
|
||||||
modifier(self, data)
|
|
||||||
|
|
||||||
for d in self._data:
|
|
||||||
d._data_traversal(predicate, modifier, data)
|
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(
|
return len(self._data)
|
||||||
list(
|
|
||||||
filter(
|
|
||||||
lambda el: not el.is_deleted(),
|
|
||||||
self._data
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def compatibility(cls):
|
def compatibility(cls):
|
||||||
@@ -482,66 +226,67 @@ class LateralContribution(SQLSubModel):
|
|||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
if self._name == "":
|
if self._name == "":
|
||||||
return f"LC #{self.id}"
|
return f"L{self.id + 1}"
|
||||||
|
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@name.setter
|
@name.setter
|
||||||
def name(self, name):
|
def name(self, name):
|
||||||
self._name = name
|
self._name = name
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lctype(self):
|
def lctype(self):
|
||||||
return self._type
|
return self._type
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def reach(self):
|
def edge(self):
|
||||||
return self._reach
|
return self._edge
|
||||||
|
|
||||||
@reach.setter
|
@edge.setter
|
||||||
def reach(self, reach):
|
def edge(self, edge):
|
||||||
self._reach = reach
|
self._edge = edge
|
||||||
if reach is not None:
|
if edge is not None:
|
||||||
self._begin_section = self._reach.reach.profiles[0]
|
self._begin_rk = self._edge.reach.get_rk_min()
|
||||||
self._end_section = self._reach.reach.profiles[-1]
|
self._end_rk = self._edge.reach.get_rk_max()
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
self.modified()
|
def has_edge(self):
|
||||||
|
return self._edge is not None
|
||||||
def has_reach(self):
|
|
||||||
return self._reach is not None
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def begin_rk(self):
|
def begin_rk(self):
|
||||||
if self._begin_section is None:
|
return self._begin_rk
|
||||||
return 0
|
|
||||||
|
|
||||||
return self._begin_section.rk
|
@begin_rk.setter
|
||||||
|
def begin_rk(self, begin_rk):
|
||||||
|
if self._edge is None:
|
||||||
|
self._begin_rk = begin_rk
|
||||||
|
else:
|
||||||
|
_min = self._edge.reach.get_rk_min()
|
||||||
|
_max = self._edge.reach.get_rk_max()
|
||||||
|
|
||||||
@property
|
if _min <= begin_rk <= _max:
|
||||||
def begin_section(self):
|
self._begin_rk = begin_rk
|
||||||
return self._begin_section
|
|
||||||
|
|
||||||
@begin_section.setter
|
self._status.modified()
|
||||||
def begin_section(self, section):
|
|
||||||
self._begin_section = section
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def end_rk(self):
|
def end_rk(self):
|
||||||
if self._end_section is None:
|
return self._end_rk
|
||||||
return 0
|
|
||||||
|
|
||||||
return self._end_section.rk
|
@end_rk.setter
|
||||||
|
def end_rk(self, end_rk):
|
||||||
|
if self._edge is None:
|
||||||
|
self._end_rk = end_rk
|
||||||
|
else:
|
||||||
|
_min = self._edge.reach.get_rk_min()
|
||||||
|
_max = self._edge.reach.get_rk_max()
|
||||||
|
|
||||||
@property
|
if _min <= end_rk <= _max:
|
||||||
def end_section(self):
|
self._end_rk = end_rk
|
||||||
return self._end_section
|
|
||||||
|
|
||||||
@end_section.setter
|
self._status.modified()
|
||||||
def end_section(self, section):
|
|
||||||
self._end_section = section
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def header(self):
|
def header(self):
|
||||||
@@ -549,12 +294,7 @@ class LateralContribution(SQLSubModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def data(self):
|
def data(self):
|
||||||
return list(
|
return self._data.copy()
|
||||||
filter(
|
|
||||||
lambda el: not el.is_deleted(),
|
|
||||||
self._data
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_type_column(self, column):
|
def get_type_column(self, column):
|
||||||
if 0 <= column < 2:
|
if 0 <= column < 2:
|
||||||
@@ -588,17 +328,17 @@ class LateralContribution(SQLSubModel):
|
|||||||
new_0 = self._types[0](data[0].replace(",", "."))
|
new_0 = self._types[0](data[0].replace(",", "."))
|
||||||
new_1 = self._types[1](data[1].replace(",", "."))
|
new_1 = self._types[1](data[1].replace(",", "."))
|
||||||
|
|
||||||
return Data(new_0, new_1, status=self._status)
|
return (new_0, new_1)
|
||||||
|
|
||||||
def add(self, index: int):
|
def add(self, index: int):
|
||||||
value = Data(self._default_0, self._default_1, status=self._status)
|
value = (self._default_0, self._default_1)
|
||||||
self._data.insert(index, value)
|
self._data.insert(index, value)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def insert(self, index: int, value):
|
def insert(self, index: int, value):
|
||||||
self._data.insert(index, value)
|
self._data.insert(index, value)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def delete_i(self, indexes):
|
def delete_i(self, indexes):
|
||||||
self._data = list(
|
self._data = list(
|
||||||
@@ -610,7 +350,7 @@ class LateralContribution(SQLSubModel):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def delete(self, els):
|
def delete(self, els):
|
||||||
self._data = list(
|
self._data = list(
|
||||||
@@ -619,14 +359,14 @@ class LateralContribution(SQLSubModel):
|
|||||||
self.data
|
self.data
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def sort(self, _reverse=False, key=None):
|
def sort(self, _reverse=False, key=None):
|
||||||
if key is None:
|
if key is None:
|
||||||
self._data.sort(reverse=_reverse)
|
self._data.sort(reverse=_reverse)
|
||||||
else:
|
else:
|
||||||
self._data.sort(reverse=_reverse, key=key)
|
self._data.sort(reverse=_reverse, key=key)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def get_i(self, index):
|
def get_i(self, index):
|
||||||
return self.data[index]
|
return self.data[index]
|
||||||
@@ -638,10 +378,10 @@ class LateralContribution(SQLSubModel):
|
|||||||
return lst
|
return lst
|
||||||
|
|
||||||
def _set_i_c_v(self, index, column, value):
|
def _set_i_c_v(self, index, column, value):
|
||||||
v = self._data[index]
|
v = list(self._data[index])
|
||||||
v[column] = self._types[column](value)
|
v[column] = self._types[column](value)
|
||||||
self._data[index] = v
|
self._data[index] = tuple(v)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def set_i_0(self, index: int, value):
|
def set_i_0(self, index: int, value):
|
||||||
self._set_i_c_v(index, 0, value)
|
self._set_i_c_v(index, 0, value)
|
||||||
@@ -652,9 +392,9 @@ class LateralContribution(SQLSubModel):
|
|||||||
@timer
|
@timer
|
||||||
def convert(self, cls):
|
def convert(self, cls):
|
||||||
new = cls(name=self.name, status=self._status)
|
new = cls(name=self.name, status=self._status)
|
||||||
new.reach = self.reach
|
new.edge = self.edge
|
||||||
new.begin_section = self.begin_section
|
new.begin_rk = self.begin_rk
|
||||||
new.end_section = self.end_section
|
new.end_rk = self.end_rk
|
||||||
|
|
||||||
for i, _ in enumerate(self.data):
|
for i, _ in enumerate(self.data):
|
||||||
new.add(i)
|
new.add(i)
|
||||||
@@ -668,7 +408,7 @@ class LateralContribution(SQLSubModel):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.info(e)
|
logger.info(e)
|
||||||
|
|
||||||
self.modified()
|
self._status.modified()
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def move_up(self, index):
|
def move_up(self, index):
|
||||||
@@ -676,11 +416,11 @@ class LateralContribution(SQLSubModel):
|
|||||||
next = index - 1
|
next = index - 1
|
||||||
d = self._data
|
d = self._data
|
||||||
d[index], d[next] = d[next], d[index]
|
d[index], d[next] = d[next], d[index]
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def move_down(self, index):
|
def move_down(self, index):
|
||||||
if index >= 0:
|
if index >= 0:
|
||||||
prev = index + 1
|
prev = index + 1
|
||||||
d = self._data
|
d = self._data
|
||||||
d[index], d[prev] = d[prev], d[index]
|
d[index], d[prev] = d[prev], d[index]
|
||||||
self.modified()
|
self._status.modified()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# LateralContributionList.py -- Pamhyr
|
# LateralContributionList.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
from copy import copy
|
from copy import copy
|
||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
|
|
||||||
from Model.Tools.PamhyrListExt import PamhyrModelListWithTab
|
from Model.Tools.PamhyrList import PamhyrModelListWithTab
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
from Model.LateralContribution.LateralContribution import LateralContribution
|
from Model.LateralContribution.LateralContribution import LateralContribution
|
||||||
@@ -50,10 +50,7 @@ class LateralContributionList(PamhyrModelListWithTab):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute(
|
execute("DELETE FROM lateral_contribution")
|
||||||
"DELETE FROM lateral_contribution " +
|
|
||||||
f"WHERE scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if data is None:
|
if data is None:
|
||||||
data = {}
|
data = {}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# LateralContributionTypes.py -- Pamhyr
|
# LateralContributionTypes.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -22,12 +22,8 @@ from Model.LateralContribution.LateralContribution import LateralContribution
|
|||||||
|
|
||||||
|
|
||||||
class NotDefined(LateralContribution):
|
class NotDefined(LateralContribution):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "", status=None):
|
||||||
status=None, owner_scenario=-1):
|
super(NotDefined, self).__init__(id=id, name=name, status=status)
|
||||||
super(NotDefined, self).__init__(
|
|
||||||
id=id, name=name, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._type = "ND"
|
self._type = "ND"
|
||||||
self._header = ["x", "y"]
|
self._header = ["x", "y"]
|
||||||
@@ -38,12 +34,8 @@ class NotDefined(LateralContribution):
|
|||||||
|
|
||||||
|
|
||||||
class LateralContrib(LateralContribution):
|
class LateralContrib(LateralContribution):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "", status=None):
|
||||||
status=None, owner_scenario=-1):
|
super(LateralContrib, self).__init__(id=id, name=name, status=status)
|
||||||
super(LateralContrib, self).__init__(
|
|
||||||
id=id, name=name, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._type = "LC"
|
self._type = "LC"
|
||||||
self._header = ["time", "discharge"]
|
self._header = ["time", "discharge"]
|
||||||
@@ -55,12 +47,8 @@ class LateralContrib(LateralContribution):
|
|||||||
|
|
||||||
|
|
||||||
class Rain(LateralContribution):
|
class Rain(LateralContribution):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "", status=None):
|
||||||
status=None, owner_scenario=-1):
|
super(Rain, self).__init__(id=id, name=name, status=status)
|
||||||
super(Rain, self).__init__(
|
|
||||||
id=id, name=name, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._type = "RA"
|
self._type = "RA"
|
||||||
self._header = ["time", "discharge"]
|
self._header = ["time", "discharge"]
|
||||||
@@ -72,12 +60,8 @@ class Rain(LateralContribution):
|
|||||||
|
|
||||||
|
|
||||||
class Evaporation(LateralContribution):
|
class Evaporation(LateralContribution):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "", status=None):
|
||||||
status=None, owner_scenario=-1):
|
super(Evaporation, self).__init__(id=id, name=name, status=status)
|
||||||
super(Evaporation, self).__init__(
|
|
||||||
id=id, name=name, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._type = "EV"
|
self._type = "EV"
|
||||||
self._header = ["time", "discharge"]
|
self._header = ["time", "discharge"]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# LateralContributionAdisTS.py -- Pamhyr
|
# LateralContributionAdisTS.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -27,390 +27,137 @@ from tools import (
|
|||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
from Model.Scenario import Scenario
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
class Data(SQLSubModel):
|
|
||||||
_sub_classes = []
|
|
||||||
|
|
||||||
def __init__(self,
|
|
||||||
data0, data1,
|
|
||||||
id: int = -1,
|
|
||||||
types=[float, float],
|
|
||||||
status=None,
|
|
||||||
owner_scenario=-1):
|
|
||||||
super(Data, self).__init__(
|
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._types = types
|
|
||||||
self._data = [data0, data1]
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_create(cls, execute, ext=""):
|
|
||||||
execute(f"""
|
|
||||||
CREATE TABLE lateral_contribution_data_adists{ext}(
|
|
||||||
{cls.create_db_add_pamhyr_id()},
|
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
data0 TEXT NOT NULL,
|
|
||||||
data1 TEXT NOT NULL,
|
|
||||||
lca INTEGER,
|
|
||||||
{Scenario.create_db_add_scenario()},
|
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
|
||||||
FOREIGN KEY(lca) REFERENCES lateral_contribution_adists(pamhyr_id)
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update(cls, execute, version, data=None):
|
|
||||||
major, minor, release = version.strip().split(".")
|
|
||||||
created = False
|
|
||||||
|
|
||||||
if major == "0" and int(minor) <= 1:
|
|
||||||
if int(release) < 7:
|
|
||||||
cls._db_create(execute)
|
|
||||||
created = True
|
|
||||||
|
|
||||||
if major == "0" and int(minor) < 2:
|
|
||||||
if not created:
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "lateral_contribution_data_adists"
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
"(pamhyr_id, data0, data1, lca, scenario) " +
|
|
||||||
"SELECT pamhyr_id, data0, data1, lc, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
cls._db_update_to_0_2_0_set_lca_pid(execute, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_lca_pid(cls, execute, data):
|
|
||||||
pid_lca = data["id2pid"]["lateral_contribution_adists"]
|
|
||||||
els = execute(
|
|
||||||
"SELECT pamhyr_id, lca " +
|
|
||||||
"FROM lateral_contribution_data_adists"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in els:
|
|
||||||
it = iter(row)
|
|
||||||
pid = next(it)
|
|
||||||
lca_id = next(it)
|
|
||||||
|
|
||||||
if lca_id == -1:
|
|
||||||
continue
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE lateral_contribution_data_adists " +
|
|
||||||
f"SET lca = {pid_lca[lca_id]} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_load(cls, execute, data=None):
|
|
||||||
new = []
|
|
||||||
status = data['status']
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
|
||||||
"SELECT pamhyr_id, deleted, data0, data1 " +
|
|
||||||
"FROM lateral_contribution_data_adists " +
|
|
||||||
f"WHERE scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) "
|
|
||||||
f"AND lca = '{lca.id}'"
|
|
||||||
)
|
|
||||||
|
|
||||||
if table is not None:
|
|
||||||
for row in table:
|
|
||||||
it = iter(row)
|
|
||||||
|
|
||||||
pid = next(it)
|
|
||||||
deleted = (next(it) == 1)
|
|
||||||
data0 = next(it)
|
|
||||||
data1 = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
data = cls(
|
|
||||||
data0, data1,
|
|
||||||
id=pid, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
if deleted:
|
|
||||||
data.set_as_deleted()
|
|
||||||
|
|
||||||
loaded.add(pid)
|
|
||||||
new.append(data)
|
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
|
||||||
if not self.must_be_saved():
|
|
||||||
return True
|
|
||||||
|
|
||||||
lca = data["lca"]
|
|
||||||
|
|
||||||
execute(
|
|
||||||
"INSERT INTO " +
|
|
||||||
"lateral_contribution_adists(pamhyr_id, deleted," +
|
|
||||||
"data0, data1, lca, scenario) " +
|
|
||||||
"VALUES (" +
|
|
||||||
f"{self.id}, {self._db_format(self.is_deleted())}, " +
|
|
||||||
f"{self._data[0]}, {self._data[1]}, " +
|
|
||||||
f"{lca.id}, {self._status.scenario_id}" +
|
|
||||||
")"
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
|
||||||
return self._types[key](self._data[key])
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
self._data[key] = self._types[key](value)
|
|
||||||
|
|
||||||
|
|
||||||
class LateralContributionAdisTS(SQLSubModel):
|
class LateralContributionAdisTS(SQLSubModel):
|
||||||
_sub_classes = [Data]
|
_sub_classes = []
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
def __init__(self, id: int = -1, pollutant: int = -1,
|
def __init__(self, id: int = -1, pollutant: int = -1,
|
||||||
name: str = "", status=None,
|
name: str = "", status=None):
|
||||||
owner_scenario=-1):
|
super(LateralContributionAdisTS, self).__init__()
|
||||||
super(LateralContributionAdisTS, self).__init__(
|
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._status = status
|
self._status = status
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = LateralContributionAdisTS._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
self._pollutant = pollutant
|
self._pollutant = pollutant
|
||||||
self._reach = None
|
self._edge = None
|
||||||
self._begin_rk = 0.0
|
self._begin_rk = 0.0
|
||||||
self._end_rk = 0.0
|
self._end_rk = 0.0
|
||||||
self._data = []
|
self._data = []
|
||||||
self._header = ["time", "rate"]
|
self._header = ["time", "rate"]
|
||||||
self._types = [self.time_convert, float]
|
self._types = [self.time_convert, float]
|
||||||
|
|
||||||
|
LateralContributionAdisTS._id_cnt = max(
|
||||||
|
LateralContributionAdisTS._id_cnt + 1, self.id)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE lateral_contribution_adists{ext}(
|
CREATE TABLE lateral_contribution_adists(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
pollutant INTEGER NOT NULL,
|
pollutant INTEGER NOT NULL,
|
||||||
reach INTEGER NOT NULL,
|
edge INTEGER NOT NULL,
|
||||||
begin_rk REAL NOT NULL,
|
begin_rk REAL NOT NULL,
|
||||||
end_rk REAL NOT NULL,
|
end_rk REAL NOT NULL,
|
||||||
{Scenario.create_db_add_scenario()},
|
FOREIGN KEY(pollutant) REFERENCES Pollutants(id),
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
FOREIGN KEY(edge) REFERENCES river_reach(id)
|
||||||
FOREIGN KEY(pollutant) REFERENCES Pollutants(pamhyr_id),
|
|
||||||
FOREIGN KEY(reach) REFERENCES river_reach(pamhyr_id)
|
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
if ext != "":
|
execute("""
|
||||||
return True
|
CREATE TABLE lateral_contribution_data_adists(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
data0 TEXT NOT NULL,
|
||||||
|
data1 TEXT NOT NULL,
|
||||||
|
lc INTEGER,
|
||||||
|
FOREIGN KEY(lc) REFERENCES lateral_contribution(id)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
|
||||||
created = False
|
|
||||||
|
|
||||||
if major == "0" and int(minor) <= 1:
|
|
||||||
if int(release) < 7:
|
|
||||||
cls._db_create(execute)
|
|
||||||
created = True
|
|
||||||
|
|
||||||
if major == "0" and int(minor) < 2:
|
|
||||||
if not created:
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
if not created:
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "lateral_contribution_adists"
|
|
||||||
reachs = data['id2pid']['river_reach']
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
"(pamhyr_id, pollutant, reach, begin_rk, end_rk, scenario) " +
|
|
||||||
"SELECT pamhyr_id, pollutant, edge, begin_rk, end_rk, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
cls._db_update_to_0_2_0_set_reach_pid(execute, table, reachs)
|
|
||||||
cls._db_update_to_0_2_0_set_pollutants_pid(execute, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_pollutants_pid(cls, execute, data):
|
|
||||||
pid_pol = data["id2pid"]["Pollutants"]
|
|
||||||
els = execute(
|
|
||||||
f"SELECT pamhyr_id, pollutant " +
|
|
||||||
"FROM lateral_contribution_adists"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in els:
|
|
||||||
it = iter(row)
|
|
||||||
pid = next(it)
|
|
||||||
pol_id = next(it)
|
|
||||||
|
|
||||||
if pol_id == -1:
|
|
||||||
continue
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE lateral_contribution_adists " +
|
|
||||||
f"SET pollutant = {pid_pol[pol_id]} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
new = []
|
||||||
status = data['status']
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, deleted, pollutant, reach, " +
|
"SELECT id, pollutant, edge, begin_rk, end_rk " +
|
||||||
"begin_rk, end_rk, scenario " +
|
"FROM lateral_contribution_adists"
|
||||||
"FROM lateral_contribution_adists " +
|
|
||||||
f"WHERE scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) "
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if table is not None:
|
if table is not None:
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
lc = cls(
|
||||||
|
id=row[0],
|
||||||
pid = next(it)
|
pollutant=row[1],
|
||||||
deleted = (next(it) == 1)
|
status=data['status']
|
||||||
pollutant = next(it)
|
|
||||||
reach = next(it)
|
|
||||||
brk = next(it)
|
|
||||||
erk = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
lca = cls(
|
|
||||||
id=pid,
|
|
||||||
pollutant=pollutant,
|
|
||||||
status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
if deleted:
|
|
||||||
lca.set_as_deleted()
|
|
||||||
|
|
||||||
lca.reach = reach
|
lc.edge = row[2]
|
||||||
lca.begin_rk = brk
|
lc.begin_rk = row[3]
|
||||||
lca.end_rk = erk
|
lc.end_rk = row[4]
|
||||||
|
|
||||||
data["lca"] = lca
|
values = execute(
|
||||||
lca._data = Data._db_load(execute, data=data)
|
"SELECT data0," +
|
||||||
|
" data1 FROM lateral_contribution_data_adists " +
|
||||||
|
f"WHERE lc = '{lc.id}'"
|
||||||
|
)
|
||||||
|
|
||||||
loaded.add(pid)
|
# Write data
|
||||||
new.append(lca)
|
for v in values:
|
||||||
|
data0 = lc._types[0](v[0])
|
||||||
|
data1 = lc._types[1](v[1])
|
||||||
|
# Replace data at pos ind
|
||||||
|
lc._data.append((data0, data1))
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
new.append(lc)
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
if not self.must_be_saved():
|
|
||||||
return True
|
|
||||||
|
|
||||||
execute(
|
execute(f"DELETE FROM lateral_contribution_adists" +
|
||||||
f"DELETE FROM lateral_contribution_adists " +
|
f" WHERE id = {self.id}")
|
||||||
f"WHERE pamhyr_id = {self.id} " +
|
execute(f"DELETE FROM lateral_contribution_data_adists" +
|
||||||
f"AND scenario = {self._status.scenario_id}"
|
f" WHERE lc = {self.id}")
|
||||||
)
|
|
||||||
execute(
|
|
||||||
f"DELETE FROM lateral_contribution_data_adists " +
|
|
||||||
f"WHERE lca = {self.id} " +
|
|
||||||
f"AND scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(
|
sql = (
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"lateral_contribution_adists(id, deleted," +
|
"lateral_contribution_adists(id, " +
|
||||||
"pollutant, reach, begin_rk, end_rk, scenario) " +
|
"pollutant, edge, begin_rk, end_rk) " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, {self._db_format(self.is_deleted())}, " +
|
f"{self.id}, {self._pollutant}, {self.edge}, " +
|
||||||
f"{self._pollutant}, {self.reach}, " +
|
|
||||||
f"{self._begin_rk}, {self._end_rk}" +
|
f"{self._begin_rk}, {self._end_rk}" +
|
||||||
f"{self._status.scenario_id}" +
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
ind = 0
|
|
||||||
for d in self._data:
|
for d in self._data:
|
||||||
data["ind"] = ind
|
data0 = self._db_format(str(d[0]))
|
||||||
|
data1 = self._db_format(str(d[1]))
|
||||||
|
|
||||||
d._db_save(execute, data)
|
sql = (
|
||||||
|
"INSERT INTO " +
|
||||||
ind += 1
|
"lateral_contribution_data_adists(data0, data1, lc) " +
|
||||||
|
f"VALUES ('{data0}', {data1}, {self.id})"
|
||||||
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _data_traversal(self,
|
|
||||||
predicate=lambda obj, data: True,
|
|
||||||
modifier=lambda obj, data: None,
|
|
||||||
data={}):
|
|
||||||
if predicate(self, data):
|
|
||||||
modifier(self, data)
|
|
||||||
|
|
||||||
for d in self._data:
|
|
||||||
d._data_traversal(predicate, modifier, data)
|
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(
|
return len(self._data)
|
||||||
list(
|
|
||||||
filter(
|
|
||||||
lambda el: not el.is_deleted(),
|
|
||||||
self._data
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def time_convert(cls, data):
|
def time_convert(cls, data):
|
||||||
@@ -429,13 +176,13 @@ class LateralContributionAdisTS(SQLSubModel):
|
|||||||
return int(data)
|
return int(data)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def reach(self):
|
def edge(self):
|
||||||
return self._reach
|
return self._edge
|
||||||
|
|
||||||
@reach.setter
|
@edge.setter
|
||||||
def reach(self, reach):
|
def edge(self, edge):
|
||||||
self._reach = reach
|
self._edge = edge
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def header(self):
|
def header(self):
|
||||||
@@ -444,7 +191,7 @@ class LateralContributionAdisTS(SQLSubModel):
|
|||||||
@header.setter
|
@header.setter
|
||||||
def header(self, header):
|
def header(self, header):
|
||||||
self._header = header
|
self._header = header
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pollutant(self):
|
def pollutant(self):
|
||||||
@@ -461,7 +208,7 @@ class LateralContributionAdisTS(SQLSubModel):
|
|||||||
@begin_rk.setter
|
@begin_rk.setter
|
||||||
def begin_rk(self, begin_rk):
|
def begin_rk(self, begin_rk):
|
||||||
self._begin_rk = begin_rk
|
self._begin_rk = begin_rk
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def end_rk(self):
|
def end_rk(self):
|
||||||
@@ -470,4 +217,4 @@ class LateralContributionAdisTS(SQLSubModel):
|
|||||||
@end_rk.setter
|
@end_rk.setter
|
||||||
def end_rk(self, end_rk):
|
def end_rk(self, end_rk):
|
||||||
self._end_rk = end_rk
|
self._end_rk = end_rk
|
||||||
self.modified()
|
self._status.modified()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# LateralContributionsAdisTSList.py -- Pamhyr
|
# LateralContributionsAdisTSList.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
from copy import copy
|
from copy import copy
|
||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
|
|
||||||
from Model.Tools.PamhyrListExt import PamhyrModelList
|
from Model.Tools.PamhyrList import PamhyrModelList
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
from Model.LateralContributionsAdisTS.LateralContributionAdisTS \
|
from Model.LateralContributionsAdisTS.LateralContributionAdisTS \
|
||||||
@@ -45,14 +45,8 @@ class LateralContributionsAdisTSList(PamhyrModelList):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute(
|
execute("DELETE FROM lateral_contribution_adists")
|
||||||
"DELETE FROM lateral_contribution_adists " +
|
execute("DELETE FROM lateral_condition_data_adists")
|
||||||
f"WHERE scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
execute(
|
|
||||||
"DELETE FROM lateral_contribution_data_adists " +
|
|
||||||
f"WHERE scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if data is None:
|
if data is None:
|
||||||
data = {}
|
data = {}
|
||||||
|
|||||||
+23
-16
@@ -1,5 +1,5 @@
|
|||||||
# Edge.py -- Pamhyr
|
# Edge.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,18 +16,29 @@
|
|||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
|
||||||
from Model.Network.Node import Node
|
from Model.Network.Node import Node
|
||||||
|
|
||||||
|
|
||||||
class Edge(SQLSubModel):
|
class Edge(object):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
_id_cnt = 0
|
||||||
|
|
||||||
|
def __init__(self, id: int, name: str,
|
||||||
node1: Node = None,
|
node1: Node = None,
|
||||||
node2: Node = None,
|
node2: Node = None,
|
||||||
**kwargs):
|
status=None):
|
||||||
super(Edge, self).__init__(id=id, **kwargs)
|
super(Edge, self).__init__()
|
||||||
|
|
||||||
|
self._status = status
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = type(self)._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
type(self)._id_cnt = max(self.id + 1, type(self)._id_cnt + 1)
|
||||||
|
|
||||||
self._name = name
|
self._name = name
|
||||||
|
|
||||||
self.node1 = node1
|
self.node1 = node1
|
||||||
self.node2 = node2
|
self.node2 = node2
|
||||||
|
|
||||||
@@ -39,7 +50,7 @@ class Edge(SQLSubModel):
|
|||||||
if name == "name":
|
if name == "name":
|
||||||
ret = self.name
|
ret = self.name
|
||||||
elif name == "id":
|
elif name == "id":
|
||||||
ret = self.pamhyr_id
|
ret = self.id
|
||||||
elif name == "node1":
|
elif name == "node1":
|
||||||
ret = self.node1
|
ret = self.node1
|
||||||
elif name == "node2":
|
elif name == "node2":
|
||||||
@@ -53,7 +64,7 @@ class Edge(SQLSubModel):
|
|||||||
if name == "name":
|
if name == "name":
|
||||||
self._name = value
|
self._name = value
|
||||||
elif name == "id":
|
elif name == "id":
|
||||||
self.pamhyr_id = value
|
self.id = value
|
||||||
elif name == "node1":
|
elif name == "node1":
|
||||||
self.node1 = value
|
self.node1 = value
|
||||||
elif name == "node2":
|
elif name == "node2":
|
||||||
@@ -61,11 +72,7 @@ class Edge(SQLSubModel):
|
|||||||
elif name == "enable":
|
elif name == "enable":
|
||||||
self._enable = value
|
self._enable = value
|
||||||
|
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
|
||||||
def id(self):
|
|
||||||
return self.pamhyr_id
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
@@ -79,14 +86,14 @@ class Edge(SQLSubModel):
|
|||||||
|
|
||||||
def enable(self, enable=True):
|
def enable(self, enable=True):
|
||||||
self._enable = enable
|
self._enable = enable
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def disable(self):
|
def disable(self):
|
||||||
self._enable = False
|
self._enable = False
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def reverse(self):
|
def reverse(self):
|
||||||
tmp = self.node1
|
tmp = self.node1
|
||||||
self.node1 = self.node2
|
self.node1 = self.node2
|
||||||
self.node2 = tmp
|
self.node2 = tmp
|
||||||
self.modified()
|
self._status.modified()
|
||||||
+34
-40
@@ -1,5 +1,5 @@
|
|||||||
# Graph.py -- Pamhyr
|
# Graph.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,15 +18,15 @@
|
|||||||
|
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
|
||||||
|
|
||||||
from Model.Network.Node import Node
|
from Model.Network.Node import Node
|
||||||
from Model.Network.Edge import Edge
|
from Model.Network.Edge import Edge
|
||||||
|
|
||||||
|
|
||||||
class Graph(SQLSubModel):
|
class Graph(object):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, status=None):
|
||||||
super(Graph, self).__init__(**kwargs)
|
super(Graph, self).__init__()
|
||||||
|
|
||||||
|
self._status = status
|
||||||
|
|
||||||
self._node_ctor = Node
|
self._node_ctor = Node
|
||||||
self._edge_ctor = Edge
|
self._edge_ctor = Edge
|
||||||
@@ -38,13 +38,13 @@ class Graph(SQLSubModel):
|
|||||||
return f"Graph {{nodes: {self._nodes}, edges: {self._edges}}}"
|
return f"Graph {{nodes: {self._nodes}, edges: {self._edges}}}"
|
||||||
|
|
||||||
def nodes(self):
|
def nodes(self):
|
||||||
return list(filter(lambda n: not n.is_deleted(), self._nodes))
|
return self._nodes
|
||||||
|
|
||||||
def nodes_names(self):
|
def nodes_names(self):
|
||||||
return list(map(lambda n: n.name, self.nodes()))
|
return list(map(lambda n: n.name, self._nodes))
|
||||||
|
|
||||||
def edges(self):
|
def edges(self):
|
||||||
return list(filter(lambda e: not e.is_deleted(), self._edges))
|
return self._edges
|
||||||
|
|
||||||
def enable_edges(self):
|
def enable_edges(self):
|
||||||
return list(
|
return list(
|
||||||
@@ -54,44 +54,44 @@ class Graph(SQLSubModel):
|
|||||||
def _enable_edges(self):
|
def _enable_edges(self):
|
||||||
"""Return a generator"""
|
"""Return a generator"""
|
||||||
return filter(
|
return filter(
|
||||||
lambda e: e.is_enable() and not e.is_deleted(),
|
lambda e: e.is_enable(),
|
||||||
self.edges()
|
self._edges
|
||||||
)
|
)
|
||||||
|
|
||||||
def edges_names(self):
|
def edges_names(self):
|
||||||
return list(map(lambda e: e.name, self.edges()))
|
return list(map(lambda e: e.name, self._edges))
|
||||||
|
|
||||||
def nodes_counts(self):
|
def nodes_counts(self):
|
||||||
return len(self.nodes())
|
return len(self._nodes)
|
||||||
|
|
||||||
def edges_counts(self):
|
def edges_counts(self):
|
||||||
return len(self.edges())
|
return len(self._edges)
|
||||||
|
|
||||||
def enable_nodes_counts(self):
|
def enable_nodes_counts(self):
|
||||||
return reduce(
|
return reduce(
|
||||||
lambda acc, n: acc + 1 if self.is_enable_node(n) else acc,
|
lambda acc, n: acc + 1 if self.is_enable_node(n) else acc,
|
||||||
self.nodes(),
|
self._nodes,
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
|
|
||||||
def enable_edges_counts(self):
|
def enable_edges_counts(self):
|
||||||
return reduce(
|
return reduce(
|
||||||
lambda acc, e: acc + 1 if e.is_enable() else acc,
|
lambda acc, e: acc + 1 if e.is_enable() else acc,
|
||||||
self.edges(),
|
self._edges,
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
|
|
||||||
def is_node_exists(self, node_name):
|
def is_node_exists(self, node_name):
|
||||||
return reduce(
|
return reduce(
|
||||||
lambda acc, n: (acc or (n.name == node_name)),
|
lambda acc, n: (acc or (n.name == node_name)),
|
||||||
self.nodes(),
|
self._nodes,
|
||||||
False
|
False
|
||||||
)
|
)
|
||||||
|
|
||||||
def is_edge_exists(self, edge_name):
|
def is_edge_exists(self, edge_name):
|
||||||
return reduce(
|
return reduce(
|
||||||
lambda acc, e: (acc or (e.name == edge_name)),
|
lambda acc, e: (acc or (e.name == edge_name)),
|
||||||
self.edges(),
|
self._edges,
|
||||||
False
|
False
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ class Graph(SQLSubModel):
|
|||||||
node = list(
|
node = list(
|
||||||
filter(
|
filter(
|
||||||
lambda n: n.name == node_name,
|
lambda n: n.name == node_name,
|
||||||
self.nodes()
|
self._nodes
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ class Graph(SQLSubModel):
|
|||||||
edge = list(
|
edge = list(
|
||||||
filter(
|
filter(
|
||||||
lambda e: e.name == edge_name,
|
lambda e: e.name == edge_name,
|
||||||
self.edges()
|
self._edges
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -121,23 +121,18 @@ class Graph(SQLSubModel):
|
|||||||
|
|
||||||
return edge[0]
|
return edge[0]
|
||||||
|
|
||||||
def reach(self, reach_name: str):
|
|
||||||
return self.edge(reach_name)
|
|
||||||
|
|
||||||
def _create_node(self, x: float, y: float):
|
def _create_node(self, x: float, y: float):
|
||||||
node = self._node_ctor(
|
node = self._node_ctor(
|
||||||
id=-1, name="", x=x, y=y,
|
-1,
|
||||||
|
"",
|
||||||
|
x=x, y=y,
|
||||||
status=self._status
|
status=self._status
|
||||||
)
|
)
|
||||||
return node
|
return node
|
||||||
|
|
||||||
def _add_node(self, node):
|
def _add_node(self, node):
|
||||||
if node in self._nodes:
|
self._nodes.append(node)
|
||||||
node.set_as_not_deleted()
|
self._status.modified()
|
||||||
else:
|
|
||||||
self._nodes.append(node)
|
|
||||||
|
|
||||||
self.modified()
|
|
||||||
return node
|
return node
|
||||||
|
|
||||||
def add_node(self, x: float = 0.0, y: float = 0.0):
|
def add_node(self, x: float = 0.0, y: float = 0.0):
|
||||||
@@ -148,9 +143,9 @@ class Graph(SQLSubModel):
|
|||||||
return self._add_node(node)
|
return self._add_node(node)
|
||||||
|
|
||||||
def remove_node(self, node):
|
def remove_node(self, node):
|
||||||
node.set_as_deleted()
|
self._nodes.remove(node)
|
||||||
self._remove_associated_edge(node)
|
self._remove_associated_edge(node)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def _remove_associated_edge(self, node: str):
|
def _remove_associated_edge(self, node: str):
|
||||||
edges = list(
|
edges = list(
|
||||||
@@ -169,7 +164,8 @@ class Graph(SQLSubModel):
|
|||||||
|
|
||||||
def _create_edge(self, n1: Node, n2: Node):
|
def _create_edge(self, n1: Node, n2: Node):
|
||||||
edge = self._edge_ctor(
|
edge = self._edge_ctor(
|
||||||
node1=n1, node2=n2,
|
-1,
|
||||||
|
"", n1, n2,
|
||||||
status=self._status
|
status=self._status
|
||||||
)
|
)
|
||||||
return edge
|
return edge
|
||||||
@@ -180,12 +176,10 @@ class Graph(SQLSubModel):
|
|||||||
# e.node2 == edge.node2),
|
# e.node2 == edge.node2),
|
||||||
# self._edges)):
|
# self._edges)):
|
||||||
# return None
|
# return None
|
||||||
if edge in self._edges:
|
|
||||||
edge.set_as_not_deleted()
|
|
||||||
else:
|
|
||||||
self._edges.append(edge)
|
|
||||||
|
|
||||||
self.modified()
|
self._edges.append(edge)
|
||||||
|
|
||||||
|
self._status.modified()
|
||||||
return edge
|
return edge
|
||||||
|
|
||||||
def add_edge(self, n1: Node, n2: Node):
|
def add_edge(self, n1: Node, n2: Node):
|
||||||
@@ -199,8 +193,8 @@ class Graph(SQLSubModel):
|
|||||||
return self._create_edge(n1, n2)
|
return self._create_edge(n1, n2)
|
||||||
|
|
||||||
def remove_edge(self, edge):
|
def remove_edge(self, edge):
|
||||||
edge.set_as_deleted()
|
self._edges.remove(edge)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def is_upstream_node(self, node):
|
def is_upstream_node(self, node):
|
||||||
return reduce(
|
return reduce(
|
||||||
|
|||||||
+20
-10
@@ -1,5 +1,5 @@
|
|||||||
# Node.py -- Pamhyr
|
# Node.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,15 +16,25 @@
|
|||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
|
||||||
from Model.Network.Point import Point
|
from Model.Network.Point import Point
|
||||||
|
|
||||||
|
|
||||||
class Node(SQLSubModel):
|
class Node(object):
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
_id_cnt = 0
|
||||||
|
|
||||||
|
def __init__(self, id: int, name: str,
|
||||||
x: float = 0.0, y: float = 0.0,
|
x: float = 0.0, y: float = 0.0,
|
||||||
**kwargs):
|
status=None):
|
||||||
super(Node, self).__init__(id=id, **kwargs)
|
super(Node, self).__init__()
|
||||||
|
|
||||||
|
self._status = status
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = type(self)._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
type(self)._id_cnt = max(self.id + 1, type(self)._id_cnt + 1)
|
||||||
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self.pos = Point(x, y)
|
self.pos = Point(x, y)
|
||||||
@@ -35,7 +45,7 @@ class Node(SQLSubModel):
|
|||||||
if key == "name":
|
if key == "name":
|
||||||
ret = self._name
|
ret = self._name
|
||||||
elif key == "id":
|
elif key == "id":
|
||||||
ret = self.pamhyr_id
|
ret = self.id
|
||||||
elif key == "pos":
|
elif key == "pos":
|
||||||
ret = f"({self.pos.x},{self.pos.y})"
|
ret = f"({self.pos.x},{self.pos.y})"
|
||||||
|
|
||||||
@@ -45,9 +55,9 @@ class Node(SQLSubModel):
|
|||||||
if key == "name":
|
if key == "name":
|
||||||
self._name = value
|
self._name = value
|
||||||
elif key == "id":
|
elif key == "id":
|
||||||
self.pamhyr_id = value
|
self.id = value
|
||||||
|
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
@@ -67,4 +77,4 @@ class Node(SQLSubModel):
|
|||||||
def setPos(self, x, y):
|
def setPos(self, x, y):
|
||||||
self.pos.x = x
|
self.pos.x = x
|
||||||
self.pos.y = y
|
self.pos.y = y
|
||||||
self.modified()
|
self._status.modified()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# Point.py -- Pamhyr
|
# Point.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# __init__.py -- Pamhyr
|
# __init__.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# test_Network.py -- Pamhyr
|
# test_Network.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -20,7 +20,7 @@ import os
|
|||||||
import unittest
|
import unittest
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from Model.Status import StudyStatus
|
from Model.Saved import SavedStatus
|
||||||
|
|
||||||
from Model.Network.Graph import Graph
|
from Model.Network.Graph import Graph
|
||||||
from Model.Network.Edge import Edge
|
from Model.Network.Edge import Edge
|
||||||
@@ -28,7 +28,7 @@ from Model.Network.Node import Node
|
|||||||
|
|
||||||
|
|
||||||
def new_graph():
|
def new_graph():
|
||||||
status = StudyStatus()
|
status = SavedStatus()
|
||||||
g = Graph(status=status)
|
g = Graph(status=status)
|
||||||
|
|
||||||
return g
|
return g
|
||||||
@@ -60,9 +60,6 @@ class GraphTestCase(unittest.TestCase):
|
|||||||
|
|
||||||
e0 = g.add_edge(n0, n1)
|
e0 = g.add_edge(n0, n1)
|
||||||
|
|
||||||
self.assertEqual(g.nodes_counts(), 2)
|
|
||||||
self.assertEqual(g.edges_counts(), 1)
|
|
||||||
|
|
||||||
g.remove_edge(e0)
|
g.remove_edge(e0)
|
||||||
|
|
||||||
self.assertEqual(g.nodes_counts(), 2)
|
self.assertEqual(g.nodes_counts(), 2)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# OutputRKAdists.py -- Pamhyr
|
# OutputRKAdists.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -27,27 +27,33 @@ from tools import (
|
|||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
from Model.Scenario import Scenario
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
class OutputRKAdists(SQLSubModel):
|
class OutputRKAdists(SQLSubModel):
|
||||||
_sub_classes = []
|
_sub_classes = []
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
def __init__(self, id: int = -1, reach=None,
|
def __init__(self, id: int = -1, reach=None,
|
||||||
rk=None, title: str = "", status=None,
|
rk=None, title: str = "", status=None):
|
||||||
owner_scenario=-1):
|
super(OutputRKAdists, self).__init__()
|
||||||
super(OutputRKAdists, self).__init__(
|
|
||||||
id=id, status=status,
|
self._status = status
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
if id == -1:
|
||||||
|
self.id = OutputRKAdists._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
self._reach = reach
|
self._reach = reach
|
||||||
self._rk = rk
|
self._rk = rk
|
||||||
self._title = str(title)
|
self._title = str(title)
|
||||||
self._enabled = True
|
self._enabled = True
|
||||||
|
|
||||||
|
OutputRKAdists._id_cnt = max(
|
||||||
|
OutputRKAdists._id_cnt + 1, self.id + 1)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def reach(self):
|
def reach(self):
|
||||||
return self._reach
|
return self._reach
|
||||||
@@ -76,130 +82,71 @@ class OutputRKAdists(SQLSubModel):
|
|||||||
self._status.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
|
||||||
CREATE TABLE output_rk_adists{ext}(
|
sql = (
|
||||||
{cls.create_db_add_pamhyr_id()},
|
"CREATE TABLE OutputRKAdists(" +
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
"id INTEGER NOT NULL PRIMARY KEY, " +
|
||||||
reach INTEGER NOT NULL,
|
"reach INTEGER NOT NULL, " +
|
||||||
rk REAL NOT NULL,
|
"rk REAL NOT NULL, " +
|
||||||
title TEXT NOT NULL,
|
"title TEXT NOT NULL, " +
|
||||||
{Scenario.create_db_add_scenario()},
|
"FOREIGN KEY(reach) REFERENCES river_reach(id)" +
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
")"
|
||||||
FOREIGN KEY(reach) REFERENCES river_reach(id)
|
)
|
||||||
)
|
|
||||||
""")
|
execute(sql)
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
|
||||||
created = False
|
|
||||||
|
|
||||||
if major == "0" and int(minor) <= 1:
|
|
||||||
if int(release) < 7:
|
|
||||||
cls._db_create(execute)
|
|
||||||
created = True
|
|
||||||
|
|
||||||
if major == "0" and int(minor) < 2:
|
|
||||||
if not created:
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "OutputRKAdists"
|
|
||||||
table_new = "output_rk_adists"
|
|
||||||
reachs = data['id2pid']['river_reach']
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table_new}_tmp " +
|
|
||||||
"(pamhyr_id, reach, rk, title, scenario) " +
|
|
||||||
"SELECT pamhyr_id, reach, rk, title, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table_new}_tmp RENAME TO {table_new}")
|
|
||||||
|
|
||||||
cls._db_update_to_0_2_0_set_reach_pid(execute, table_new, reachs)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
new = []
|
||||||
status = data['status']
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
# reach = data["reach"]
|
# reach = data["reach"]
|
||||||
# profile = data["profile"]
|
# profile = data["profile"]
|
||||||
|
status = data["status"]
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, deleted, reach, rk, title, scenario " +
|
"SELECT id, reach, rk, title " +
|
||||||
f"FROM output_rk_adists"
|
f"FROM OutputRKAdists"
|
||||||
)
|
)
|
||||||
|
|
||||||
if table is not None:
|
if table is not None:
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
id = row[0]
|
||||||
|
id_reach = row[1]
|
||||||
pid = next(it)
|
id_rk = row[2]
|
||||||
deleted = (next(it) == 1)
|
title = row[3]
|
||||||
id_reach = next(it)
|
|
||||||
id_rk = next(it)
|
|
||||||
title = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
new_output = cls(
|
new_output = cls(
|
||||||
id=pid, reach=id_reach,
|
id=id, reach=id_reach,
|
||||||
rk=id_rk, title=title,
|
rk=id_rk, title=title,
|
||||||
status=status,
|
status=status
|
||||||
owner_scenario=owner_scenario,
|
|
||||||
)
|
)
|
||||||
if deleted:
|
|
||||||
new_output.set_as_deleted()
|
|
||||||
|
|
||||||
loaded.add(pid)
|
|
||||||
new.append(new_output)
|
new.append(new_output)
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
if not self.must_be_saved():
|
|
||||||
return True
|
|
||||||
|
|
||||||
execute(
|
execute(f"DELETE FROM OutputRKAdists WHERE id = {self.id}")
|
||||||
"DELETE FROM output_rk_adists " +
|
|
||||||
f"WHERE pamhyr_id = {self.id} " +
|
|
||||||
f"AND scenario = {self._status.scenario_id} "
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(
|
sql = (
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"output_rk_adists(pamhyr_id, deleted, " +
|
"OutputRKAdists(id, reach, rk, title) " +
|
||||||
"reach, rk, title, scenario) " +
|
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, {self._db_format(self.is_deleted())}" +
|
f"{self.id}, {self._reach}, {self._rk}, " +
|
||||||
f"{self._reach}, {self._rk}, " +
|
f"'{self._db_format(self._title)}'" +
|
||||||
f"'{self._db_format(self._title)}', " +
|
|
||||||
f"{self._status.scenario_id}" +
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# OutputRKListAdists.py -- Pamhyr
|
# OutputRKListAdists.py -- Pamhyr
|
||||||
# Copyright (C) 2024-2025 INRAE
|
# Copyright (C) 2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
|
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
from Model.Tools.PamhyrListExt import PamhyrModelList
|
from Model.Tools.PamhyrList import PamhyrModelList
|
||||||
from Model.OutputRKAdists.OutputRKAdists import OutputRKAdists
|
from Model.OutputRKAdists.OutputRKAdists import OutputRKAdists
|
||||||
|
|
||||||
|
|
||||||
@@ -38,10 +38,7 @@ class OutputRKAdistsList(PamhyrModelList):
|
|||||||
ok = True
|
ok = True
|
||||||
|
|
||||||
# Delete previous data
|
# Delete previous data
|
||||||
execute(
|
execute("DELETE FROM OutputRKAdists")
|
||||||
"DELETE FROM output_rk_adists " +
|
|
||||||
f"WHERE scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
for sl in self._lst:
|
for sl in self._lst:
|
||||||
ok &= sl._db_save(execute, data)
|
ok &= sl._db_save(execute, data)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Pollutants.py -- Pamhyr
|
# Pollutants.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -27,265 +27,24 @@ from tools import (
|
|||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
from Model.Scenario import Scenario
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
class PollutantCharacteristics(SQLSubModel):
|
|
||||||
_sub_classes = []
|
|
||||||
|
|
||||||
def __init__(self, id: int = -1, type=-1,
|
|
||||||
diametre=-1, rho=-1,
|
|
||||||
porosity=-1,
|
|
||||||
cdc_riv=-1, cdc_cas=-1,
|
|
||||||
apd=-1, ac=-1, bc=-1,
|
|
||||||
pollutant=None,
|
|
||||||
status=None,
|
|
||||||
owner_scenario=-1):
|
|
||||||
super(PollutantCharacteristics, self).__init__(
|
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._status = status
|
|
||||||
|
|
||||||
self._type = type
|
|
||||||
self._diametre = diametre
|
|
||||||
self._rho = rho
|
|
||||||
self._porosity = porosity
|
|
||||||
self._cdc_riv = cdc_riv
|
|
||||||
self._cdc_cas = cdc_cas
|
|
||||||
self._apd = apd
|
|
||||||
self._ac = ac
|
|
||||||
self._bc = bc
|
|
||||||
self._pollutant = pollutant
|
|
||||||
|
|
||||||
# FIXME: This four next method is dirty work, just an hack to keep
|
|
||||||
# the View API working... Must be refactoring
|
|
||||||
def to_list(self):
|
|
||||||
return [
|
|
||||||
self._type,
|
|
||||||
self._diametre,
|
|
||||||
self._rho,
|
|
||||||
self._porosity,
|
|
||||||
self._cdc_riv,
|
|
||||||
self._cdc_cas,
|
|
||||||
self._apd,
|
|
||||||
self._ac,
|
|
||||||
self._bc,
|
|
||||||
]
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
return len(self.to_list())
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
|
||||||
return self.to_list()[key]
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
if key == 0:
|
|
||||||
self._type = value
|
|
||||||
elif key == 1:
|
|
||||||
self._diametre = value
|
|
||||||
elif key == 2:
|
|
||||||
self._rho = value
|
|
||||||
elif key == 3:
|
|
||||||
self._porosity = value
|
|
||||||
elif key == 4:
|
|
||||||
self._cdc_riv = value
|
|
||||||
elif key == 5:
|
|
||||||
self._cdc_cas = value
|
|
||||||
elif key == 6:
|
|
||||||
self._apd = value
|
|
||||||
elif key == 7:
|
|
||||||
self._ac = value
|
|
||||||
elif key == 8:
|
|
||||||
self._bc = value
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_create(cls, execute, ext=""):
|
|
||||||
execute(f"""
|
|
||||||
CREATE TABLE pollutants_characteristics{ext}(
|
|
||||||
{cls.create_db_add_pamhyr_id()},
|
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
type INTEGER NOT NULL,
|
|
||||||
diametre REAL NOT NULL,
|
|
||||||
rho REAL NOT NULL,
|
|
||||||
porosity REAL NOT NULL,
|
|
||||||
cdc_riv REAL NOT NULL,
|
|
||||||
cdc_cas REAL NOT NULL,
|
|
||||||
apd REAL NOT NULL,
|
|
||||||
ac REAL NOT NULL,
|
|
||||||
bc REAL NOT NULL,
|
|
||||||
pollutant INTEGER NOT NULL,
|
|
||||||
{Scenario.create_db_add_scenario()},
|
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
|
||||||
FOREIGN KEY(pollutant) REFERENCES pollutants(pamhyr_id)
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update(cls, execute, version, data=None):
|
|
||||||
major, minor, release = version.strip().split(".")
|
|
||||||
created = False
|
|
||||||
|
|
||||||
if major == "0" and int(minor) <= 1:
|
|
||||||
if int(release) < 7:
|
|
||||||
cls._db_create(execute)
|
|
||||||
created = True
|
|
||||||
|
|
||||||
if major == "0" and int(minor) < 2:
|
|
||||||
if not created:
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "Pollutants_characteristics"
|
|
||||||
table_new = "pollutants_characteristics"
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table_new}_tmp " +
|
|
||||||
"(pamhyr_id, type, diametre, rho, porosity, " +
|
|
||||||
"cdc_riv, cdc_cas, apd, ac, bc, pollutant, scenario) " +
|
|
||||||
"SELECT pamhyr_id, type, diametre, rho, porosity, " +
|
|
||||||
"cdc_riv, cdc_cas, apd, ac, bc, pollutant, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table_new}_tmp RENAME TO {table_new}")
|
|
||||||
|
|
||||||
cls._db_update_to_0_2_0_set_pollutants_pid(execute, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_pollutants_pid(cls, execute, data):
|
|
||||||
pid_pol = data["id2pid"]["Pollutants"]
|
|
||||||
els = execute(
|
|
||||||
f"SELECT pamhyr_id, pollutant " +
|
|
||||||
"FROM pollutants_characteristics"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in els:
|
|
||||||
it = iter(row)
|
|
||||||
pid = next(it)
|
|
||||||
pol_id = next(it)
|
|
||||||
|
|
||||||
if pol_id == -1:
|
|
||||||
continue
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE pollutants_characteristics " +
|
|
||||||
f"SET pollutant = {pid_pol[pol_id]} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_load(cls, execute, data=None):
|
|
||||||
new = []
|
|
||||||
|
|
||||||
status = data["status"]
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
pollutant = data['pollutant']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
|
||||||
"SELECT pamhyr_id, deleted, type, diametre, rho, porosity, " +
|
|
||||||
"cdc_riv, cdc_cas, apd, ac, bc, scenario " +
|
|
||||||
"FROM pollutants_characteristics " +
|
|
||||||
f"WHERE pollutant = {pollutant.id} " +
|
|
||||||
f"AND scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})"
|
|
||||||
)
|
|
||||||
|
|
||||||
if table is not None:
|
|
||||||
for row in table:
|
|
||||||
it = iter(row)
|
|
||||||
|
|
||||||
pid = next(it)
|
|
||||||
deleted = (next(it) == 1)
|
|
||||||
type = next(it)
|
|
||||||
diametre = next(it)
|
|
||||||
rho = next(it)
|
|
||||||
porosity = next(it)
|
|
||||||
cdc_riv = next(it)
|
|
||||||
cdc_cas = next(it)
|
|
||||||
apd = next(it)
|
|
||||||
ac = next(it)
|
|
||||||
bc = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
pc = cls(
|
|
||||||
id=pid, type=type,
|
|
||||||
diametre=diametre,
|
|
||||||
rho=rho,
|
|
||||||
porosity=porosity,
|
|
||||||
cdc_riv=cdc_riv, cdc_cas=cdc_cas,
|
|
||||||
apd=apd, ac=ac, bc=bc,
|
|
||||||
pollutant=pollutant,
|
|
||||||
status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
if deleted:
|
|
||||||
pc.set_as_deleted()
|
|
||||||
|
|
||||||
loaded.add(pid)
|
|
||||||
new.append(pc)
|
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
|
||||||
if not self.must_be_saved():
|
|
||||||
return True
|
|
||||||
|
|
||||||
execute(
|
|
||||||
"INSERT INTO " +
|
|
||||||
"pollutants_characteristics(pamhyr_id, deleted, " +
|
|
||||||
"type, diametre, rho, porosity, " +
|
|
||||||
"cdc_riv, cdc_cas, apd, ac, bc, " +
|
|
||||||
"pollutant, scenario) " +
|
|
||||||
"VALUES (" +
|
|
||||||
f"{self.id}, {self._db_format(self.is_deleted())}, " +
|
|
||||||
f"{self._type}, " +
|
|
||||||
f"{self._diametre}, {self._rho}, " +
|
|
||||||
f"{self._porosity}, " +
|
|
||||||
f"{self._cdc_riv}, {self._cdc_cas}, " +
|
|
||||||
f"{self._apd}, {self._ac}, {self._bc}, " +
|
|
||||||
f"{self._pollutant.id}, " +
|
|
||||||
f"{self._status.scenario_id}" +
|
|
||||||
")"
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class Pollutants(SQLSubModel):
|
class Pollutants(SQLSubModel):
|
||||||
_sub_classes = [PollutantCharacteristics]
|
_sub_classes = []
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1, name: str = "", status=None):
|
||||||
status=None, owner_scenario=-1):
|
super(Pollutants, self).__init__()
|
||||||
super(Pollutants, self).__init__(
|
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._status = status
|
self._status = status
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = Pollutants._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
if name is None or name == "":
|
if name is None or name == "":
|
||||||
self.name = f"pol{self.id}"
|
self.name = f"pol{self.id}"
|
||||||
else:
|
else:
|
||||||
@@ -294,6 +53,9 @@ class Pollutants(SQLSubModel):
|
|||||||
|
|
||||||
self._data = []
|
self._data = []
|
||||||
|
|
||||||
|
Pollutants._id_cnt = max(
|
||||||
|
Pollutants._id_cnt + 1, self.id + 1)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
return self._name
|
return self._name
|
||||||
@@ -305,167 +67,106 @@ class Pollutants(SQLSubModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def data(self):
|
def data(self):
|
||||||
return self._data
|
return self._data.copy()
|
||||||
|
|
||||||
@data.setter
|
|
||||||
def data(self, data):
|
|
||||||
self._data = [PollutantCharacteristics(type=int(data[0]),
|
|
||||||
diametre=float(data[1]),
|
|
||||||
rho=float(data[2]),
|
|
||||||
porosity=float(data[3]),
|
|
||||||
cdc_riv=float(data[4]),
|
|
||||||
cdc_cas=float(data[5]),
|
|
||||||
apd=float(data[6]),
|
|
||||||
ac=float(data[7]),
|
|
||||||
bc=float(data[8]),
|
|
||||||
pollutant=self,
|
|
||||||
status=self._status
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE pollutants{ext}(
|
CREATE TABLE Pollutants(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
name TEXT NOT NULL UNIQUE
|
||||||
name TEXT NOT NULL,
|
|
||||||
{Scenario.create_db_add_scenario()},
|
|
||||||
{Scenario.create_db_add_scenario_fk()}
|
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
if ext != "":
|
execute("""
|
||||||
return True
|
CREATE TABLE Pollutants_characteristics(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
type INTEGER NOT NULL,
|
||||||
|
diametre REAL NOT NULL,
|
||||||
|
rho REAL NOT NULL,
|
||||||
|
porosity REAL NOT NULL,
|
||||||
|
cdc_riv REAL NOT NULL,
|
||||||
|
cdc_cas REAL NOT NULL,
|
||||||
|
apd REAL NOT NULL,
|
||||||
|
ac REAL NOT NULL,
|
||||||
|
bc REAL NOT NULL,
|
||||||
|
pollutant INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY(pollutant) REFERENCES Pollutants(id)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
|
||||||
created = False
|
|
||||||
|
|
||||||
if major == "0" and int(minor) <= 1:
|
|
||||||
if int(release) < 7:
|
|
||||||
cls._db_create(execute)
|
|
||||||
created = True
|
|
||||||
|
|
||||||
if major == "0" and int(minor) < 2:
|
|
||||||
if not created:
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
if not created:
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "Pollutants"
|
|
||||||
table_new = "pollutants"
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table_new}_tmp " +
|
|
||||||
"(pamhyr_id, name, scenario) " +
|
|
||||||
"SELECT pamhyr_id, name, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table_new}_tmp RENAME TO {table_new}")
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
new = []
|
||||||
|
|
||||||
status = data["status"]
|
status = data["status"]
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, deleted, name FROM pollutants " +
|
"SELECT id, name " +
|
||||||
f"WHERE scenario = {scenario.id} " +
|
f"FROM Pollutants"
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if table is not None:
|
if table is not None:
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
id = row[0]
|
||||||
|
name = row[1]
|
||||||
pid = next(it)
|
|
||||||
deleted = (next(it) == 1)
|
|
||||||
name = next(it)
|
|
||||||
|
|
||||||
new_pollutant = cls(
|
new_pollutant = cls(
|
||||||
id=pid, name=name,
|
id=id, name=name,
|
||||||
status=status
|
status=status
|
||||||
)
|
)
|
||||||
if deleted:
|
|
||||||
new_pollutant.set_as_deleted()
|
|
||||||
|
|
||||||
data["pollutant"] = new_pollutant
|
new_data = []
|
||||||
new_pollutant._data = PollutantCharacteristics._db_load(
|
table = execute(
|
||||||
execute, data=data
|
"SELECT * " +
|
||||||
|
"FROM Pollutants_characteristics " +
|
||||||
|
f"WHERE pollutant = {id}"
|
||||||
)
|
)
|
||||||
|
|
||||||
loaded.add(pid)
|
if table is not None:
|
||||||
new.append(new_pollutant)
|
for t in table:
|
||||||
|
new_data = t[1:-1]
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
new_pollutant._data.append(new_data)
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
new.append(new_pollutant)
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
if not self.must_be_saved():
|
|
||||||
return True
|
|
||||||
|
|
||||||
ok = True
|
execute(f"DELETE FROM Pollutants WHERE id = {self.id}")
|
||||||
|
execute(f"DELETE FROM Pollutants_characteristics" +
|
||||||
|
f" WHERE pollutant = {self.id}")
|
||||||
|
|
||||||
execute(
|
sql = (
|
||||||
"DELETE FROM pollutants " +
|
|
||||||
f"WHERE pamhyr_id = {self.id} " +
|
|
||||||
f"AND scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
execute(
|
|
||||||
"DELETE FROM pollutants_characteristics " +
|
|
||||||
f"WHERE pollutant = {self.id} " +
|
|
||||||
f"AND scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(
|
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"pollutants(pamhyr_id, deleted, name, scenario) " +
|
"Pollutants(id, name) " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, {self._db_format(self.is_deleted())}, " +
|
f"{self.id}, " +
|
||||||
f"'{self._db_format(self._name)}', " +
|
f"'{self._db_format(self._name)}'" +
|
||||||
f"{self._status.scenario_id}" +
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
|
||||||
for d in self._data:
|
execute(sql)
|
||||||
ok &= d._db_save(execute, data)
|
|
||||||
|
|
||||||
return ok
|
|
||||||
|
|
||||||
def _data_traversal(self,
|
|
||||||
predicate=lambda obj, data: True,
|
|
||||||
modifier=lambda obj, data: None,
|
|
||||||
data={}):
|
|
||||||
if predicate(self, data):
|
|
||||||
modifier(self, data)
|
|
||||||
|
|
||||||
for d in self._data:
|
for d in self._data:
|
||||||
d._data_traversal(predicate, modifier, data)
|
sql = (
|
||||||
|
"INSERT INTO " +
|
||||||
|
"Pollutants_characteristics(type, diametre, rho, porosity, " +
|
||||||
|
"cdc_riv, cdc_cas, apd, ac, bc, pollutant) " +
|
||||||
|
f"VALUES ({d[0]}, {d[1]}, {d[2]},{d[3]}, {d[4]}, "
|
||||||
|
f"{d[5]}, {d[6]}, {d[7]}, {d[8]}, {self.id})"
|
||||||
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def enabled(self):
|
def enabled(self):
|
||||||
@@ -480,6 +181,7 @@ class Pollutants(SQLSubModel):
|
|||||||
return len(self._data) != 0
|
return len(self._data) != 0
|
||||||
|
|
||||||
def new_from_data(self, data):
|
def new_from_data(self, data):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
new = [int(data[0])]
|
new = [int(data[0])]
|
||||||
new += [float(d) for d in data[1:]]
|
new += [float(d) for d in data[1:]]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# PollutantsList.py -- Pamhyr
|
# PollutantsList.py -- Pamhyr
|
||||||
# Copyright (C) 2024-2025 INRAE
|
# Copyright (C) 2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -38,10 +38,7 @@ class PollutantsList(PamhyrModelList):
|
|||||||
ok = True
|
ok = True
|
||||||
|
|
||||||
# Delete previous data
|
# Delete previous data
|
||||||
execute(
|
execute("DELETE FROM Pollutants")
|
||||||
"DELETE FROM Pollutants " +
|
|
||||||
f"WHERE scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
for sl in self._lst:
|
for sl in self._lst:
|
||||||
ok &= sl._db_save(execute, data)
|
ok &= sl._db_save(execute, data)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# REPLine.py -- Pamhyr
|
# REPLine.py -- Pamhyr
|
||||||
# Copyright (C) 2024-2025 INRAE
|
# Copyright (C) 2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -22,25 +22,31 @@ from tools import trace, timer
|
|||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
from Model.Scenario import Scenario
|
|
||||||
|
|
||||||
|
|
||||||
class REPLine(SQLSubModel):
|
class REPLine(SQLSubModel):
|
||||||
_sub_classes = []
|
_sub_classes = []
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
def __init__(self, id: int = -1, enabled=True,
|
def __init__(self, id: int = -1, enabled=True,
|
||||||
name="", line="", solvers=set(),
|
name="", line="", solvers=set(),
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(REPLine, self).__init__(
|
super(REPLine, self).__init__()
|
||||||
id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
if id == -1:
|
||||||
)
|
self.id = REPLine._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
self._status = status
|
||||||
|
|
||||||
self._enabled = enabled
|
self._enabled = enabled
|
||||||
self._name = f"Line #{self._pamhyr_id}" if name == "" else name
|
self._name = f"Line{self.id}" if name == "" else name
|
||||||
self._line = line
|
self._line = line
|
||||||
self._solvers = solvers
|
self._solvers = solvers
|
||||||
|
|
||||||
|
REPLine._id_cnt = max(self.id + 1, REPLine._id_cnt + 1)
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
value = None
|
value = None
|
||||||
|
|
||||||
@@ -107,75 +113,35 @@ class REPLine(SQLSubModel):
|
|||||||
self._status.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE rep_lines{ext} (
|
CREATE TABLE rep_lines(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
enabled BOOLEAN NOT NULL,
|
enabled BOOLEAN NOT NULL,
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
line TEXT NOT NULL,
|
line TEXT NOT NULL,
|
||||||
solvers TEXT NOT NULL,
|
solvers TEXT NOT NULL
|
||||||
{Scenario.create_db_add_scenario()},
|
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
|
||||||
PRIMARY KEY(pamhyr_id, scenario)
|
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
if major == minor == "0":
|
if major == minor == "0":
|
||||||
if int(release) < 9:
|
if int(release) < 9:
|
||||||
cls._db_create(execute)
|
cls._db_create(execute)
|
||||||
return True
|
|
||||||
else:
|
|
||||||
cls._db_update_to_0_0_14(execute, data)
|
|
||||||
|
|
||||||
if major == "0" and int(minor) <= 1:
|
|
||||||
if int(release) < 2:
|
|
||||||
execute(
|
|
||||||
"ALTER TABLE rep_lines " +
|
|
||||||
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_0_14(cls, execute, data):
|
|
||||||
table = "rep_lines"
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
"(pamhyr_id, enabled, name, line, solvers, scenario) " +
|
|
||||||
"SELECT pamhyr_id, enabled, name, line, solvers, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
new = []
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, enabled, deleted, name, line, " +
|
"SELECT id, enabled, name, line, solvers " +
|
||||||
"solvers, scenario " +
|
"FROM rep_lines"
|
||||||
"FROM rep_lines " +
|
|
||||||
f"WHERE scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for row in table:
|
for row in table:
|
||||||
@@ -183,47 +149,32 @@ class REPLine(SQLSubModel):
|
|||||||
|
|
||||||
id = next(it)
|
id = next(it)
|
||||||
enabled = (next(it) == 1)
|
enabled = (next(it) == 1)
|
||||||
deleted = (next(it) == 1)
|
|
||||||
name = next(it)
|
name = next(it)
|
||||||
line = next(it)
|
line = next(it)
|
||||||
solvers = set(next(it).split(";;"))
|
solvers = set(next(it).split(";;"))
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
f = cls(
|
f = cls(
|
||||||
id=id, enabled=enabled,
|
id=id, enabled=enabled, name=name, line=line,
|
||||||
name=name, line=line, solvers=solvers,
|
solvers=solvers, status=data['status']
|
||||||
status=data['status'], owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
if deleted:
|
|
||||||
f.set_as_deleted()
|
|
||||||
|
|
||||||
loaded.add(id)
|
|
||||||
new.append(f)
|
new.append(f)
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
if not self.must_be_saved():
|
|
||||||
return True
|
|
||||||
|
|
||||||
solvers = ";;".join(self._solvers)
|
solvers = ";;".join(self._solvers)
|
||||||
|
|
||||||
execute(
|
sql = (
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"rep_lines(pamhyr_id, enabled, deleted, " +
|
"rep_lines(id, enabled, name, line, solvers) " +
|
||||||
"name, line, solvers, scenario) " +
|
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self._pamhyr_id}, {self._enabled}, " +
|
f"{self.id}, {self._enabled}, " +
|
||||||
f"{self._db_format(self.is_deleted())}, " +
|
|
||||||
f"'{self._db_format(self._name)}', " +
|
f"'{self._db_format(self._name)}', " +
|
||||||
f"'{self._db_format(self._line)}', " +
|
f"'{self._db_format(self._line)}', " +
|
||||||
f"'{self._db_format(solvers)}', " +
|
f"'{self._db_format(solvers)}'" +
|
||||||
f"{self._status.scenario_id}" +
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# REPLineList.py -- Pamhyr
|
# REPLineList.py -- Pamhyr
|
||||||
# Copyright (C) 2024-2025 INRAE
|
# Copyright (C) 2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
|
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
from Model.Tools.PamhyrListExt import PamhyrModelList
|
from Model.Tools.PamhyrList import PamhyrModelList
|
||||||
from Model.REPLine.REPLine import REPLine
|
from Model.REPLine.REPLine import REPLine
|
||||||
|
|
||||||
|
|
||||||
@@ -38,10 +38,7 @@ class REPLineList(PamhyrModelList):
|
|||||||
ok = True
|
ok = True
|
||||||
|
|
||||||
# Delete previous data
|
# Delete previous data
|
||||||
execute(
|
execute("DELETE FROM rep_lines")
|
||||||
"DELETE FROM rep_lines " +
|
|
||||||
f"WHERE scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
for af in self._lst:
|
for af in self._lst:
|
||||||
ok &= af._db_save(execute, data)
|
ok &= af._db_save(execute, data)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Reservoir.py -- Pamhyr
|
# Reservoir.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,386 +19,148 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Scenario import Scenario
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
class Data(SQLSubModel):
|
|
||||||
_sub_classes = []
|
|
||||||
|
|
||||||
def __init__(self, id: int = -1,
|
|
||||||
elevation: float = 0.,
|
|
||||||
surface: float = 0.,
|
|
||||||
status=None, owner_scenario=-1):
|
|
||||||
super(Data, self).__init__(
|
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._data = [elevation, surface]
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_create(cls, execute, ext=""):
|
|
||||||
execute(f"""
|
|
||||||
CREATE TABLE reservoir_data{ext}(
|
|
||||||
{cls.create_db_add_pamhyr_id()},
|
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
ind INTEGER NOT NULL,
|
|
||||||
elevation REAL NOT NULL,
|
|
||||||
surface REAL NOT NULL,
|
|
||||||
reservoir INTEGER,
|
|
||||||
{Scenario.create_db_add_scenario()},
|
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
|
||||||
FOREIGN KEY(reservoir) REFERENCES reservoir(pamhyr_id),
|
|
||||||
PRIMARY KEY(pamhyr_id, scenario)
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
|
|
||||||
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 == minor == "0":
|
|
||||||
if int(release) > 5:
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
if major == "0" and int(minor) <= 1:
|
|
||||||
if int(release) < 2:
|
|
||||||
execute(
|
|
||||||
"ALTER TABLE reservoir_data " +
|
|
||||||
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
|
|
||||||
)
|
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "reservoir_data"
|
|
||||||
id2pid = data['id2pid']
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(
|
|
||||||
execute, table,
|
|
||||||
data=data
|
|
||||||
)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
"(pamhyr_id, ind, elevation, surface, reservoir, scenario) " +
|
|
||||||
"SELECT pamhyr_id, ind, elevation, surface, reservoir, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
if 'reservoir' in id2pid:
|
|
||||||
reservoirs = id2pid['reservoir']
|
|
||||||
cls._db_update_to_0_2_0_set_reservoir_pid(
|
|
||||||
execute, table, reservoirs
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_reservoir_pid(cls, execute, table, reservoirs):
|
|
||||||
bcs = execute(
|
|
||||||
f"SELECT pamhyr_id, reservoir FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in bcs:
|
|
||||||
it = iter(row)
|
|
||||||
pid = next(it)
|
|
||||||
reservoir_id = next(it)
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE {table} " +
|
|
||||||
f"SET reservoir = {reservoirs[reservoir_id]} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_load(cls, execute, data=None):
|
|
||||||
new = []
|
|
||||||
scenario = data["scenario"]
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
reseroire = data["reservoir"]
|
|
||||||
|
|
||||||
values = execute(
|
|
||||||
"SELECT pamhyr_id, deleted, ind, " +
|
|
||||||
"elevation, surface, scenario " +
|
|
||||||
"FROM reservoir_data " +
|
|
||||||
f"WHERE reservoir = {reseroire.pamhyr_id} " +
|
|
||||||
f"AND 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)
|
|
||||||
deleted = (next(it) == 1)
|
|
||||||
ind = next(it)
|
|
||||||
elevation = next(it)
|
|
||||||
surface = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
nd = cls(
|
|
||||||
id=pid, status=data['status'],
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
if deleted:
|
|
||||||
nd.set_as_deleted()
|
|
||||||
|
|
||||||
nd._data = [elevation, surface]
|
|
||||||
|
|
||||||
loaded.add(pid)
|
|
||||||
new.append(nd)
|
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
|
||||||
if not self.must_be_saved():
|
|
||||||
return True
|
|
||||||
|
|
||||||
pid = self._pamhyr_id
|
|
||||||
ind = data["ind"]
|
|
||||||
elevation = self._db_format(str(self[0]))
|
|
||||||
surface = self._db_format(str(self[1]))
|
|
||||||
reservoir = data["reservoir"]
|
|
||||||
|
|
||||||
execute(
|
|
||||||
"INSERT INTO reservoir_data " +
|
|
||||||
"(pamhyr_id, deleted, ind, " +
|
|
||||||
"elevation, surface, reservoir, scenario) " +
|
|
||||||
"VALUES (" +
|
|
||||||
f"{pid}, " +
|
|
||||||
f"{self._db_format(self.is_deleted())}, " +
|
|
||||||
f"{ind}, '{elevation}', {surface}, " +
|
|
||||||
f"{reservoir._pamhyr_id}, " +
|
|
||||||
f"{self._status.scenario_id}" +
|
|
||||||
")"
|
|
||||||
)
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
|
||||||
return self._data[key]
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
self._data[key] = float(value)
|
|
||||||
|
|
||||||
|
|
||||||
class Reservoir(SQLSubModel):
|
class Reservoir(SQLSubModel):
|
||||||
_sub_classes = [Data]
|
_sub_classes = []
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
def __init__(self, id: int = -1,
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
name: str = "",
|
status=None):
|
||||||
status=None, owner_scenario=-1):
|
super(Reservoir, self).__init__()
|
||||||
super(Reservoir, self).__init__(
|
|
||||||
id=id, status=status,
|
self._status = status
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
if id == -1:
|
||||||
|
self.id = Reservoir._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self._node = None
|
self._node = None
|
||||||
self._data = []
|
self._data = []
|
||||||
|
|
||||||
|
Reservoir._id_cnt = max(Reservoir._id_cnt + 1, self.id + 1)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE reservoir{ext} (
|
CREATE TABLE reservoir(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
node INTEGER,
|
node INTEGER,
|
||||||
{Scenario.create_db_add_scenario()},
|
FOREIGN KEY(node) REFERENCES river_node(id)
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
|
||||||
FOREIGN KEY(node) REFERENCES river_node(pamhyr_id),
|
|
||||||
PRIMARY KEY(pamhyr_id, scenario)
|
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
if ext == "_tmp":
|
execute("""
|
||||||
return True
|
CREATE TABLE reservoir_data(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
ind INTEGER NOT NULL,
|
||||||
|
elevation REAL NOT NULL,
|
||||||
|
surface REAL NOT NULL,
|
||||||
|
reservoir INTEGER,
|
||||||
|
FOREIGN KEY(reservoir) REFERENCES reservoir(id)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
if major == minor == "0":
|
if major == minor == "0":
|
||||||
if int(release) < 5:
|
if int(release) < 5:
|
||||||
cls._db_create(execute)
|
cls._db_create(execute)
|
||||||
return True
|
|
||||||
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
return True
|
||||||
|
|
||||||
if major == "0" and minor == "1":
|
|
||||||
if int(release) < 2:
|
|
||||||
execute(
|
|
||||||
"ALTER TABLE additional_files " +
|
|
||||||
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
|
|
||||||
)
|
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "reservoir"
|
|
||||||
nodes = data['id2pid']['river_node']
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data=data)
|
|
||||||
cls._db_update_to_0_2_0_set_node_pid(execute, table, nodes)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
print(execute(f"SELECT * FROM {table}"))
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
"(pamhyr_id, name, node, scenario) " +
|
|
||||||
"SELECT pamhyr_id, name, node, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
new = []
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, deleted, name, node, scenario " +
|
"SELECT id, name, node " +
|
||||||
"FROM reservoir " +
|
"FROM reservoir "
|
||||||
f"WHERE scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
id = row[0]
|
||||||
|
name = row[1]
|
||||||
id = next(it)
|
node_id = row[2]
|
||||||
deleted = (next(it) == 1)
|
new_reservoir = cls(id, name, status=data["status"])
|
||||||
name = next(it)
|
|
||||||
node_id = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
new_reservoir = cls(
|
|
||||||
id, name,
|
|
||||||
status=data["status"],
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
if deleted:
|
|
||||||
new_reservoir.set_as_deleted()
|
|
||||||
|
|
||||||
new_reservoir._node = None
|
new_reservoir._node = None
|
||||||
if node_id != -1 and node_id is not None:
|
if node_id != -1:
|
||||||
new_reservoir._node = next(
|
new_reservoir._node = next(
|
||||||
filter(
|
filter(
|
||||||
lambda n: n.id == node_id, data["nodes"]
|
lambda n: n.id == node_id, data["nodes"]
|
||||||
)
|
), None
|
||||||
)
|
)
|
||||||
|
|
||||||
data["reservoir"] = new_reservoir
|
new_data = []
|
||||||
new_reservoir._data = Data._db_load(
|
table = execute(
|
||||||
execute, data=data
|
"SELECT elevation, surface " +
|
||||||
|
"FROM reservoir_data " +
|
||||||
|
f"WHERE reservoir = {id} " +
|
||||||
|
"ORDER BY ind ASC"
|
||||||
)
|
)
|
||||||
|
for t in table:
|
||||||
|
new_data.append((t[0], t[1]))
|
||||||
|
|
||||||
|
new_reservoir._data = new_data
|
||||||
|
|
||||||
loaded.add(id)
|
|
||||||
new.append(new_reservoir)
|
new.append(new_reservoir)
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
if not self.must_be_saved():
|
|
||||||
return True
|
|
||||||
|
|
||||||
execute(
|
execute(f"DELETE FROM reservoir WHERE id = {self.id}")
|
||||||
"DELETE FROM reservoir " +
|
execute(f"DELETE FROM reservoir_data WHERE reservoir = {self.id}")
|
||||||
f"WHERE pamhyr_id = {self.pamhyr_id} " +
|
|
||||||
f"AND scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
execute(
|
|
||||||
"DELETE FROM reservoir_data " +
|
|
||||||
f"WHERE reservoir = {self.pamhyr_id} " +
|
|
||||||
f"AND scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
node_id = 'NULL'
|
node_id = -1
|
||||||
if self._node is not None:
|
if self._node is not None:
|
||||||
node_id = self._node.pamhyr_id
|
node_id = self._node.id
|
||||||
|
|
||||||
sql = (
|
sql = (
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"reservoir(pamhyr_id, deleted, name, node, scenario) " +
|
"reservoir(id, name, node) " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.pamhyr_id}, " +
|
f"{self.id}, '{self._db_format(self._name)}', " +
|
||||||
f"{self._db_format(self.is_deleted())}, " +
|
f"{node_id}" +
|
||||||
f"'{self._db_format(self._name)}', " +
|
|
||||||
f"{node_id}, " +
|
|
||||||
f"{self._status.scenario_id}" +
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
execute(sql)
|
execute(sql)
|
||||||
|
|
||||||
data["reservoir"] = self
|
|
||||||
|
|
||||||
ind = 0
|
ind = 0
|
||||||
for d in self._data:
|
for d in self._data:
|
||||||
data["ind"] = ind
|
sql = (
|
||||||
|
"INSERT INTO " +
|
||||||
d._db_save(execute, data)
|
"reservoir_data(ind, elevation, surface, reservoir) " +
|
||||||
|
f"VALUES ({ind}, '{d[0]}', {d[1]}, {self.id})"
|
||||||
|
)
|
||||||
|
execute(sql)
|
||||||
ind += 1
|
ind += 1
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _data_traversal(self,
|
|
||||||
predicate=lambda obj, data: True,
|
|
||||||
modifier=lambda obj, data: None,
|
|
||||||
data={}):
|
|
||||||
if predicate(self, data):
|
|
||||||
modifier(self, data)
|
|
||||||
|
|
||||||
for d in self._data:
|
|
||||||
d._data_traversal(predicate, modifier, data)
|
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self._data)
|
return len(self._data)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
if self._name == "":
|
if self._name == "":
|
||||||
return f"Reservoir #{self.pamhyr_id}"
|
return f"R{self.id + 1}"
|
||||||
|
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@name.setter
|
@name.setter
|
||||||
def name(self, name):
|
def name(self, name):
|
||||||
self._name = name
|
self._name = name
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def node(self):
|
def node(self):
|
||||||
@@ -407,7 +169,7 @@ class Reservoir(SQLSubModel):
|
|||||||
@node.setter
|
@node.setter
|
||||||
def node(self, node):
|
def node(self, node):
|
||||||
self._node = node
|
self._node = node
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def has_node(self):
|
def has_node(self):
|
||||||
return self._node is not None
|
return self._node is not None
|
||||||
@@ -440,63 +202,42 @@ class Reservoir(SQLSubModel):
|
|||||||
return (new_0, new_1)
|
return (new_0, new_1)
|
||||||
|
|
||||||
def add(self, index: int):
|
def add(self, index: int):
|
||||||
data = Data(
|
value = (self._default_elevation, self._default_surface)
|
||||||
elevation=self._default_elevation,
|
self._data.insert(index, value)
|
||||||
surface=self._default_surface,
|
self._status.modified()
|
||||||
status=self._status,
|
return value
|
||||||
)
|
|
||||||
|
|
||||||
self._data.insert(index, data)
|
|
||||||
self.modified()
|
|
||||||
return data
|
|
||||||
|
|
||||||
def insert(self, index: int, value):
|
def insert(self, index: int, value):
|
||||||
if type(value) is tuple or type(value) is list:
|
|
||||||
value = Data(
|
|
||||||
elevation=value[0],
|
|
||||||
surface=value[1]
|
|
||||||
)
|
|
||||||
|
|
||||||
self._data.insert(index, value)
|
self._data.insert(index, value)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def hard_delete_i(self, indexes):
|
def delete_i(self, indexes):
|
||||||
self._data = list(
|
self._data = list(
|
||||||
map(
|
map(
|
||||||
lambda e: e[1],
|
lambda e: e[1],
|
||||||
filter(
|
filter(
|
||||||
lambda e: e[0] not in indexes,
|
lambda e: e[0] not in indexes,
|
||||||
enumerate(self._data)
|
enumerate(self.data)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def delete_i(self, indexes):
|
|
||||||
list(
|
|
||||||
map(
|
|
||||||
lambda e: e[1].set_as_deleted(),
|
|
||||||
filter(
|
|
||||||
lambda e: e[0] in indexes,
|
|
||||||
enumerate(self.lst)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def delete(self, els):
|
def delete(self, els):
|
||||||
for el in els:
|
self._data = list(
|
||||||
el.set_as_deleted()
|
filter(
|
||||||
|
lambda e: e not in els,
|
||||||
self.modified()
|
self.data
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
def sort(self, _reverse=False, key=None):
|
def sort(self, _reverse=False, key=None):
|
||||||
if key is None:
|
if key is None:
|
||||||
self._data.sort(reverse=_reverse, key=lambda d: d[0])
|
self._data.sort(reverse=_reverse)
|
||||||
else:
|
else:
|
||||||
self._data.sort(reverse=_reverse, key=key)
|
self._data.sort(reverse=_reverse, key=key)
|
||||||
|
self._status.modified()
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def get_i(self, index):
|
def get_i(self, index):
|
||||||
return self.data[index]
|
return self.data[index]
|
||||||
@@ -505,12 +246,13 @@ class Reservoir(SQLSubModel):
|
|||||||
lst = []
|
lst = []
|
||||||
for r in _range:
|
for r in _range:
|
||||||
lst.append(r)
|
lst.append(r)
|
||||||
|
|
||||||
return lst
|
return lst
|
||||||
|
|
||||||
def _set_i_c_v(self, index, column, value):
|
def _set_i_c_v(self, index, column, value):
|
||||||
self._data[index][column] = value
|
v = list(self._data[index])
|
||||||
self.modified()
|
v[column] = value
|
||||||
|
self._data[index] = tuple(v)
|
||||||
|
self._status.modified()
|
||||||
|
|
||||||
def set_i_elevation(self, index: int, value):
|
def set_i_elevation(self, index: int, value):
|
||||||
self._set_i_c_v(index, 0, value)
|
self._set_i_c_v(index, 0, value)
|
||||||
@@ -523,11 +265,11 @@ class Reservoir(SQLSubModel):
|
|||||||
next = index - 1
|
next = index - 1
|
||||||
d = self._data
|
d = self._data
|
||||||
d[index], d[next] = d[next], d[index]
|
d[index], d[next] = d[next], d[index]
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def move_down(self, index):
|
def move_down(self, index):
|
||||||
if index >= 0:
|
if index >= 0:
|
||||||
prev = index + 1
|
prev = index + 1
|
||||||
d = self._data
|
d = self._data
|
||||||
d[index], d[prev] = d[prev], d[index]
|
d[index], d[prev] = d[prev], d[index]
|
||||||
self.modified()
|
self._status.modified()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# ReservoirList.py -- Pamhyr
|
# ReservoirList.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
from copy import copy
|
from copy import copy
|
||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
|
|
||||||
from Model.Tools.PamhyrListExt import PamhyrModelList
|
from Model.Tools.PamhyrList import PamhyrModelList
|
||||||
from Model.Reservoir.Reservoir import Reservoir
|
from Model.Reservoir.Reservoir import Reservoir
|
||||||
|
|
||||||
|
|
||||||
@@ -39,14 +39,8 @@ class ReservoirList(PamhyrModelList):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute(
|
execute("DELETE FROM reservoir")
|
||||||
"DELETE FROM reservoir " +
|
execute("DELETE FROM reservoir_data")
|
||||||
f"WHERE scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
execute(
|
|
||||||
"DELETE FROM reservoir_data " +
|
|
||||||
f"WHERE scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if data is None:
|
if data is None:
|
||||||
data = {}
|
data = {}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Results.py -- Pamhyr
|
# Results.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -14,174 +14,32 @@
|
|||||||
# 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
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from Model.Scenario import Scenario
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
|
||||||
from Model.Results.River.River import River
|
from Model.Results.River.River import River
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
class AdditionalData(SQLSubModel):
|
class Results(object):
|
||||||
_sub_classes = []
|
def __init__(self, study=None, solver=None,
|
||||||
|
|
||||||
def __init__(self, id=-1, study=None, data=None):
|
|
||||||
super(AdditionalData, self).__init__(
|
|
||||||
id=id, status=study.status,
|
|
||||||
owner_scenario=study.status.scenario.id
|
|
||||||
)
|
|
||||||
|
|
||||||
self._study = study
|
|
||||||
self._data = data
|
|
||||||
|
|
||||||
@property
|
|
||||||
def data(self):
|
|
||||||
return self._data
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_create(cls, execute, ext=""):
|
|
||||||
execute(f"""
|
|
||||||
CREATE TABLE results_add_data{ext} (
|
|
||||||
{cls.create_db_add_pamhyr_id()},
|
|
||||||
result INTEGER NOT NULL,
|
|
||||||
type_x TEXT NOT NULL,
|
|
||||||
type_y TEXT NOT NULL,
|
|
||||||
legend TEXT NOT NULL,
|
|
||||||
unit TEXT NOT NULL,
|
|
||||||
data_len INTEGER NOT NULL,
|
|
||||||
x BLOB NOT NULL,
|
|
||||||
y BLOB NOT NULL,
|
|
||||||
{Scenario.create_db_add_scenario()},
|
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
|
||||||
FOREIGN KEY(result) REFERENCES results(pamhyr_id),
|
|
||||||
PRIMARY KEY(pamhyr_id, result, scenario)
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
|
|
||||||
if ext != "":
|
|
||||||
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 and int(release) <= 1:
|
|
||||||
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"]
|
|
||||||
|
|
||||||
table = execute(
|
|
||||||
"SELECT pamhyr_id, type_x, type_y, " +
|
|
||||||
"legend, unit, data_len, x, y, " +
|
|
||||||
"scenario " +
|
|
||||||
"FROM results_add_data " +
|
|
||||||
f"WHERE scenario = {scenario.id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if table is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
for v in table:
|
|
||||||
it = iter(v)
|
|
||||||
|
|
||||||
pid = next(it)
|
|
||||||
type_x = next(it)
|
|
||||||
type_y = next(it)
|
|
||||||
legend = next(it)
|
|
||||||
unit = next(it)
|
|
||||||
data_len = next(it)
|
|
||||||
bx = next(it)
|
|
||||||
by = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
data_format = ">" + ''.join(itertools.repeat("d", data_len))
|
|
||||||
x = struct.unpack(data_format, bx)
|
|
||||||
y = struct.unpack(data_format, by)
|
|
||||||
|
|
||||||
data = {
|
|
||||||
'type_x': type_x,
|
|
||||||
'type_y': type_y,
|
|
||||||
'legend': legend,
|
|
||||||
'unit': unit,
|
|
||||||
'x': x, 'y': y
|
|
||||||
}
|
|
||||||
|
|
||||||
new_data = cls(study=study)
|
|
||||||
new_data._data = data
|
|
||||||
new.append(new_data)
|
|
||||||
|
|
||||||
return new
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
|
||||||
if self._status.scenario.id != self._owner_scenario:
|
|
||||||
return
|
|
||||||
|
|
||||||
pid = self._pamhyr_id
|
|
||||||
data_len = len(self._data["x"])
|
|
||||||
|
|
||||||
data_format = ">" + ''.join(itertools.repeat("d", data_len))
|
|
||||||
bx = struct.pack(data_format, *self._data["x"])
|
|
||||||
by = struct.pack(data_format, *self._data["y"])
|
|
||||||
|
|
||||||
execute(
|
|
||||||
"INSERT INTO " +
|
|
||||||
"results_add_data (pamhyr_id, result, " +
|
|
||||||
"type_x, type_y, " +
|
|
||||||
"legend, unit, data_len, x, y, " +
|
|
||||||
"scenario) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
||||||
self._pamhyr_id, data["result"],
|
|
||||||
self._data["type_x"], self._data["type_y"],
|
|
||||||
self._data["legend"], self._data["unit"],
|
|
||||||
data_len, bx, by, self._owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class Results(SQLSubModel):
|
|
||||||
_sub_classes = [River, AdditionalData]
|
|
||||||
|
|
||||||
def __init__(self, id=-1, study=None, solver=None,
|
|
||||||
repertory="", name="0"):
|
repertory="", name="0"):
|
||||||
super(Results, self).__init__(
|
|
||||||
id=id, status=study.status,
|
|
||||||
owner_scenario=study.status.scenario.id
|
|
||||||
)
|
|
||||||
|
|
||||||
self._name = name
|
|
||||||
self._study = study
|
self._study = study
|
||||||
self._river = River(self._study)
|
|
||||||
|
|
||||||
self._solver = solver
|
self._solver = solver
|
||||||
self._repertory = repertory
|
self._repertory = repertory
|
||||||
|
self._name = name
|
||||||
|
|
||||||
|
self._river = River(self._study)
|
||||||
|
|
||||||
self._meta_data = {
|
self._meta_data = {
|
||||||
# Keep results creation date
|
# Keep results creation date
|
||||||
"creation_date": datetime.now(),
|
"creation_date": datetime.now(),
|
||||||
"study_revision": study.status.version,
|
|
||||||
"additional_data": [],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if solver is not None:
|
|
||||||
self.set("solver_type", solver._type)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def date(self):
|
def date(self):
|
||||||
date = self._meta_data["creation_date"]
|
date = self._meta_data["creation_date"]
|
||||||
@@ -202,13 +60,6 @@ class Results(SQLSubModel):
|
|||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
return ("timestamps" in self._meta_data)
|
return ("timestamps" in self._meta_data)
|
||||||
|
|
||||||
@property
|
|
||||||
def solver_name(self):
|
|
||||||
if self._solver is None:
|
|
||||||
return self._meta_data["solver_name"]
|
|
||||||
|
|
||||||
return self._solver.name
|
|
||||||
|
|
||||||
def get(self, key):
|
def get(self, key):
|
||||||
return self._meta_data[key]
|
return self._meta_data[key]
|
||||||
|
|
||||||
@@ -218,164 +69,3 @@ 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_name TEXT NOT NULL,
|
|
||||||
solver_type 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)
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
|
|
||||||
if ext != "":
|
|
||||||
return True
|
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update(cls, execute, version, data=None):
|
|
||||||
major, minor, release = version.strip().split(".")
|
|
||||||
create = False
|
|
||||||
|
|
||||||
if major == "0" and int(minor) < 2:
|
|
||||||
cls._db_create(execute)
|
|
||||||
create = True
|
|
||||||
|
|
||||||
if major == "0" and int(minor) == 2:
|
|
||||||
if int(release) < 1 and not create:
|
|
||||||
cls._db_create(execute)
|
|
||||||
create = True
|
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_load(cls, execute, data=None):
|
|
||||||
new = None
|
|
||||||
|
|
||||||
study = data['study']
|
|
||||||
status = data['status']
|
|
||||||
scenario = data["scenario"]
|
|
||||||
|
|
||||||
table = execute(
|
|
||||||
"SELECT pamhyr_id, solver_name, solver_type, " +
|
|
||||||
"study_revision, creation_data, nb_timestamps, timestamps, " +
|
|
||||||
"scenario " +
|
|
||||||
"FROM results " +
|
|
||||||
f"WHERE scenario = {scenario.id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if table is None:
|
|
||||||
yield
|
|
||||||
|
|
||||||
if len(table) > 1:
|
|
||||||
logger.warning("Multiple results for this scenario")
|
|
||||||
|
|
||||||
for v in table:
|
|
||||||
it = iter(v)
|
|
||||||
|
|
||||||
pid = next(it)
|
|
||||||
solver_name = next(it)
|
|
||||||
solver_type = next(it)
|
|
||||||
revision = next(it)
|
|
||||||
creation_date = next(it)
|
|
||||||
nb_timestamps = next(it)
|
|
||||||
timestamps_bytes = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
new_results = cls(study=study)
|
|
||||||
|
|
||||||
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", nb_timestamps))
|
|
||||||
ts = struct.unpack(sf, timestamps_bytes)
|
|
||||||
new_results.set("timestamps", ts)
|
|
||||||
|
|
||||||
data["timestamps"] = sorted(ts)
|
|
||||||
new_results._river = River._db_load(execute, data)
|
|
||||||
|
|
||||||
new_results.set(
|
|
||||||
"additional_data",
|
|
||||||
AdditionalData._db_load(execute, data)
|
|
||||||
)
|
|
||||||
|
|
||||||
yield (solver_type, new_results)
|
|
||||||
|
|
||||||
def _db_save_clear(self, execute, solver_type, data=None):
|
|
||||||
old_pid = execute(
|
|
||||||
"SELECT pamhyr_id FROM results " +
|
|
||||||
f"WHERE scenario = {self._owner_scenario} " +
|
|
||||||
f"AND solver_type = '{solver_type}'"
|
|
||||||
)
|
|
||||||
if len(old_pid) != 0:
|
|
||||||
for pid in old_pid:
|
|
||||||
pid = pid[0]
|
|
||||||
|
|
||||||
execute(
|
|
||||||
"DELETE FROM results " +
|
|
||||||
f"WHERE scenario = {self._owner_scenario} " +
|
|
||||||
f"AND solver_type = '{solver_type}'"
|
|
||||||
)
|
|
||||||
execute(
|
|
||||||
"DELETE FROM results_data " +
|
|
||||||
f"WHERE scenario = {self._owner_scenario} " +
|
|
||||||
f"AND result = {pid}"
|
|
||||||
)
|
|
||||||
execute(
|
|
||||||
"DELETE FROM results_add_data " +
|
|
||||||
f"WHERE scenario = {self._owner_scenario} " +
|
|
||||||
f"AND result = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
|
||||||
if self._status.scenario.id != self._owner_scenario:
|
|
||||||
return
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
self._db_save_clear(execute, solver_type, data=data)
|
|
||||||
|
|
||||||
ts = sorted(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._status.version, self.get("creation_date"),
|
|
||||||
len(ts), struct.pack(sf, *ts), self._owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
data["result"] = self._pamhyr_id
|
|
||||||
self._river._db_save(execute, data)
|
|
||||||
|
|
||||||
for add_data in self.get("additional_data"):
|
|
||||||
add_data._db_save(execute, data)
|
|
||||||
|
|
||||||
return True
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# Results.py -- Pamhyr
|
# Results.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -50,9 +50,6 @@ class Results(object):
|
|||||||
|
|
||||||
self._phys_var_list = ["C", "G", "M", "D", "L", "N", "R"]
|
self._phys_var_list = ["C", "G", "M", "D", "L", "N", "R"]
|
||||||
|
|
||||||
if solver is not None:
|
|
||||||
self.set("solver_type", solver._type)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def date(self):
|
def date(self):
|
||||||
date = self._meta_data["creation_date"]
|
date = self._meta_data["creation_date"]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# River.py -- Pamhyr
|
# River.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -14,27 +14,15 @@
|
|||||||
# 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 tools import flatten
|
|
||||||
from functools import reduce
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from Model.Scenario import Scenario
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
class Profile(SQLSubModel):
|
class Profile(object):
|
||||||
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>, ...}, ...}
|
||||||
@@ -64,11 +52,9 @@ class Profile(SQLSubModel):
|
|||||||
return self._data[timestamp]
|
return self._data[timestamp]
|
||||||
|
|
||||||
def get_key(self, key):
|
def get_key(self, key):
|
||||||
res = list(
|
return list(
|
||||||
map(lambda ts: self._data[ts][key],
|
map(lambda ts: self._data[ts][key], self._data)
|
||||||
sorted(self._data.keys()))
|
|
||||||
)
|
)
|
||||||
return res
|
|
||||||
|
|
||||||
def get_ts_key(self, timestamp, key):
|
def get_ts_key(self, timestamp, key):
|
||||||
if timestamp in self._data:
|
if timestamp in self._data:
|
||||||
@@ -79,196 +65,17 @@ class Profile(SQLSubModel):
|
|||||||
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)
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update(cls, execute, version, data=None):
|
|
||||||
major, minor, release = version.strip().split(".")
|
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_load(cls, execute, data=None):
|
|
||||||
new = {}
|
|
||||||
status = data['status']
|
|
||||||
|
|
||||||
study = data['study']
|
|
||||||
reach = data['reach']
|
|
||||||
profile = data['profile']
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
timestamps = data['timestamps']
|
|
||||||
|
|
||||||
values = execute(
|
|
||||||
"SELECT pamhyr_id, result, key, " +
|
|
||||||
"len_data, data, scenario " +
|
|
||||||
"FROM results_data " +
|
|
||||||
f"WHERE scenario = {scenario.id} " +
|
|
||||||
f"AND reach = {reach.pamhyr_id} " +
|
|
||||||
f"AND section = {profile.pamhyr_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
for v in values:
|
|
||||||
it = iter(v)
|
|
||||||
|
|
||||||
pid = next(it)
|
|
||||||
result = next(it)
|
|
||||||
key = next(it)
|
|
||||||
len_data = next(it)
|
|
||||||
data = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
if profile not in new:
|
|
||||||
new_data = cls(profile, study)
|
|
||||||
new[profile] = new_data
|
|
||||||
else:
|
|
||||||
new_data = new[profile]
|
|
||||||
|
|
||||||
if key in ["Z", "Q", "V"]:
|
|
||||||
sf = ">" + ''.join(itertools.repeat("f", len_data))
|
|
||||||
len_values = len(values)
|
|
||||||
values = struct.unpack(sf, data)
|
|
||||||
elif key in ["sl"]:
|
|
||||||
sf = ">" + ''.join(itertools.repeat("f", len_data))
|
|
||||||
values = struct.unpack(sf, data)
|
|
||||||
values = cls._db_load_data_sl_format(
|
|
||||||
values, len_data, timestamps
|
|
||||||
)
|
|
||||||
|
|
||||||
for timestamp, value in zip(timestamps, values):
|
|
||||||
new_data.set(timestamp, key, value)
|
|
||||||
if key == "Z":
|
|
||||||
new_data.update_water_limits(timestamp, value)
|
|
||||||
|
|
||||||
return list(new.values())
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_load_data_sl_format(cls, values, len_data, timestamps):
|
|
||||||
tuple_size = 3
|
|
||||||
|
|
||||||
# HACK: Transforme list of value to list of n-tuple
|
|
||||||
# sediment results: (h, d50, sigma)
|
|
||||||
tmp_values = [[]]
|
|
||||||
for value in values:
|
|
||||||
t = tmp_values[-1]
|
|
||||||
if len(t) < tuple_size:
|
|
||||||
t.append(value)
|
|
||||||
else:
|
|
||||||
tmp_values.append([value])
|
|
||||||
|
|
||||||
tmp_values = list(map(tuple, tmp_values))
|
|
||||||
|
|
||||||
# HACK: Transforme list of n-tuple to list of n-list
|
|
||||||
# of n-tuple, where n is the number of layer.
|
|
||||||
nb_layer = len_data / len(timestamps) / tuple_size
|
|
||||||
values = [[]]
|
|
||||||
for value in tmp_values:
|
|
||||||
t = values[-1]
|
|
||||||
if len(t) < nb_layer:
|
|
||||||
t.append(value)
|
|
||||||
else:
|
|
||||||
values.append([value])
|
|
||||||
|
|
||||||
return list(map(lambda x: [x], values))
|
|
||||||
|
|
||||||
def update_water_limits(self, timestamp, z):
|
|
||||||
limits = self.geometry.get_water_limits(z)
|
|
||||||
self.set(
|
|
||||||
timestamp, "water_limits", limits
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_keys(self):
|
|
||||||
return reduce(
|
|
||||||
lambda acc, ts: acc.union(self._data[ts].keys()),
|
|
||||||
self._data.keys(), set()
|
|
||||||
)
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
|
||||||
pid = self._pamhyr_id
|
|
||||||
result = data["result"]
|
|
||||||
|
|
||||||
keys = self.get_keys()
|
|
||||||
logger.debug(f"{keys}...")
|
|
||||||
for key in keys:
|
|
||||||
values = self.get_key(key)
|
|
||||||
|
|
||||||
if key in ["Z", "Q", "V"]:
|
|
||||||
values = list(map(float, values))
|
|
||||||
sf = ">" + ''.join(itertools.repeat("f", len(values)))
|
|
||||||
len_values = len(values)
|
|
||||||
elif key == "sl":
|
|
||||||
# HACK: Some dirty code to transforme list of list of
|
|
||||||
# tuple to list of values and ensure the values is
|
|
||||||
# float type...
|
|
||||||
values = flatten(
|
|
||||||
flatten(
|
|
||||||
map(lambda v: list(
|
|
||||||
map(lambda t: list(
|
|
||||||
map(float, t)), v[0])),
|
|
||||||
values)))
|
|
||||||
len_values = len(values)
|
|
||||||
sf = ">" + ''.join(itertools.repeat("f", len_values))
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
|
|
||||||
data_bytes = struct.pack(sf, *values)
|
|
||||||
|
|
||||||
execute(
|
|
||||||
"INSERT INTO " +
|
|
||||||
"results_data (pamhyr_id, result, " +
|
|
||||||
"reach, section, " +
|
|
||||||
"key, len_data, data, " +
|
|
||||||
"scenario) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
|
||||||
pid, result,
|
|
||||||
data["reach"].pamhyr_id,
|
|
||||||
self._profile.pamhyr_id,
|
|
||||||
key, len_values, data_bytes,
|
|
||||||
self._owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class Reach(SQLSubModel):
|
|
||||||
_sub_classes = [Profile]
|
|
||||||
|
|
||||||
def __init__(self, reach, study, with_init=True):
|
|
||||||
super(Reach, self).__init__(
|
|
||||||
id=-1, status=study.status,
|
|
||||||
owner_scenario=study.status.scenario.id
|
|
||||||
)
|
|
||||||
|
|
||||||
|
class Reach(object):
|
||||||
|
def __init__(self, reach, study):
|
||||||
self._study = study
|
self._study = study
|
||||||
self._reach = reach # Source reach in the study
|
self._reach = reach # Source reach in the study
|
||||||
self._profiles = []
|
self._profiles = list(
|
||||||
if with_init:
|
map(
|
||||||
self._profiles = list(
|
lambda p: Profile(p, self._study),
|
||||||
map(
|
reach.profiles
|
||||||
lambda p: Profile(p, self._study),
|
|
||||||
reach.profiles
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
self._profile_mask = list(
|
self._profile_mask = list(
|
||||||
map(
|
map(
|
||||||
@@ -304,44 +111,9 @@ class Reach(SQLSubModel):
|
|||||||
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)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update(cls, execute, version, data=None):
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_load(cls, execute, data=None):
|
|
||||||
reach = data["reach"]
|
|
||||||
|
|
||||||
new_reach = cls(
|
|
||||||
data["reach"], data["study"], with_init=False
|
|
||||||
)
|
|
||||||
|
|
||||||
for i, profile in enumerate(reach.profiles):
|
|
||||||
data["profile"] = profile
|
|
||||||
new_reach._profiles += Profile._db_load(execute, data)
|
|
||||||
|
|
||||||
return new_reach
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
|
||||||
logger.debug("Save reach...")
|
|
||||||
for profile in self._profiles:
|
|
||||||
data["profile"] = profile.geometry.pamhyr_id
|
|
||||||
profile._db_save(execute, data)
|
|
||||||
|
|
||||||
|
|
||||||
class River(SQLSubModel):
|
|
||||||
_sub_classes = [Reach]
|
|
||||||
|
|
||||||
|
class River(object):
|
||||||
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
|
||||||
@@ -375,30 +147,3 @@ class River(SQLSubModel):
|
|||||||
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):
|
|
||||||
study = data["study"]
|
|
||||||
new_river = cls(study)
|
|
||||||
|
|
||||||
for reach in study.river.reachs():
|
|
||||||
data["reach"] = reach.reach
|
|
||||||
new_river._reachs.append(
|
|
||||||
Reach._db_load(execute, data)
|
|
||||||
)
|
|
||||||
|
|
||||||
return new_river
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
|
||||||
logger.debug("Save river...")
|
|
||||||
for reach in self._reachs:
|
|
||||||
data["reach"] = reach.geometry
|
|
||||||
reach._db_save(execute, data)
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# River.py -- Pamhyr
|
# River.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
+78
-429
@@ -1,5 +1,5 @@
|
|||||||
# River.py -- Pamhyr river model
|
# River.py -- Pamhyr river model
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,13 +16,9 @@
|
|||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import types
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from tools import flatten, logger_exception
|
from tools import flatten, logger_exception
|
||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Scenario import Scenario
|
|
||||||
|
|
||||||
from Model.Network.Node import Node
|
from Model.Network.Node import Node
|
||||||
from Model.Network.Edge import Edge
|
from Model.Network.Edge import Edge
|
||||||
@@ -59,150 +55,64 @@ from Model.LateralContributionsAdisTS.LateralContributionsAdisTSList \
|
|||||||
import LateralContributionsAdisTSList
|
import LateralContributionsAdisTSList
|
||||||
from Model.D90AdisTS.D90AdisTSList import D90AdisTSList
|
from Model.D90AdisTS.D90AdisTSList import D90AdisTSList
|
||||||
from Model.DIFAdisTS.DIFAdisTSList import DIFAdisTSList
|
from Model.DIFAdisTS.DIFAdisTSList import DIFAdisTSList
|
||||||
from Model.GeoTIFF.GeoTIFFList import GeoTIFFList
|
|
||||||
from Model.Results.Results import Results
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
|
||||||
|
|
||||||
|
|
||||||
class RiverNode(Node):
|
class RiverNode(Node, SQLSubModel):
|
||||||
_sub_classes = []
|
_sub_classes = []
|
||||||
|
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: str, name: str,
|
||||||
x: float = 0.0, y: float = 0.0,
|
x: float, y: float,
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(RiverNode, self).__init__(
|
super(RiverNode, self).__init__(
|
||||||
id=id, name=name, x=x, y=y,
|
id, name,
|
||||||
status=status,
|
x, y,
|
||||||
owner_scenario=owner_scenario
|
status=status
|
||||||
)
|
)
|
||||||
|
|
||||||
self._locker = None
|
self._locker = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE river_node{ext}(
|
CREATE TABLE river_node(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
x REAL NOT NULL,
|
x REAL NOT NULL,
|
||||||
y REAL NOT NULL,
|
y REAL NOT NULL
|
||||||
{Scenario.create_db_add_scenario()},
|
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
|
||||||
PRIMARY KEY(pamhyr_id, scenario)
|
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
if ext == "_tmp":
|
cls._create_submodel(execute)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
return True
|
||||||
|
|
||||||
if major == "0" and int(minor) < 2:
|
|
||||||
cls._db_update_to_0_2_0(execute, data=data)
|
|
||||||
|
|
||||||
if major == "0" and minor == "1":
|
|
||||||
if int(release) < 2:
|
|
||||||
execute(
|
|
||||||
"ALTER TABLE river_node " +
|
|
||||||
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
|
|
||||||
)
|
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data=None):
|
|
||||||
table = "river_node"
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
# Copy table
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
f"(pamhyr_id, name, x, y, scenario) " +
|
|
||||||
"SELECT pamhyr_id, name, x, y, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
nodes = []
|
nodes = []
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return nodes
|
|
||||||
|
|
||||||
table = execute(
|
|
||||||
"SELECT pamhyr_id, deleted, name, x, y, scenario " +
|
|
||||||
"FROM river_node " +
|
|
||||||
f"WHERE scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) "
|
|
||||||
)
|
|
||||||
|
|
||||||
|
table = execute("SELECT id, name, x, y FROM river_node")
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
# Update id counter
|
||||||
|
cls._id_cnt = max(cls._id_cnt, row[0])
|
||||||
pid = next(it)
|
# Create new node
|
||||||
deleted = (next(it) == 1)
|
nodes.append(cls(*row, status=data["status"]))
|
||||||
name = next(it)
|
|
||||||
x = next(it)
|
|
||||||
y = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
node = cls(
|
|
||||||
id=pid,
|
|
||||||
name=name, x=x, y=y,
|
|
||||||
status=data["status"],
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
if deleted:
|
|
||||||
node.set_as_deleted()
|
|
||||||
|
|
||||||
loaded.add(pid)
|
|
||||||
nodes.append(node)
|
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
nodes += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute(
|
sql = (
|
||||||
"INSERT OR REPLACE INTO river_node(" +
|
"INSERT OR REPLACE INTO river_node(id, name, x, y) VALUES (" +
|
||||||
"pamhyr_id, deleted, name, x, y, scenario" +
|
f"{self.id}, '{self._db_format(self.name)}', " +
|
||||||
") " +
|
f"{self.x}, {self.y}" +
|
||||||
"VALUES (" +
|
|
||||||
f"{self._pamhyr_id}, {self._db_format(self.is_deleted())}, " +
|
|
||||||
f"'{self._db_format(self.name)}', " +
|
|
||||||
f"{self.x}, {self.y}, {self._status.scenario_id}" +
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _data_traversal(self,
|
|
||||||
predicate=lambda obj, data: True,
|
|
||||||
modifier=lambda obj, data: None,
|
|
||||||
data={}):
|
|
||||||
if predicate(self, data):
|
|
||||||
modifier(self, data)
|
|
||||||
|
|
||||||
def is_deleted(self):
|
|
||||||
return self._deleted
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def locker(self):
|
def locker(self):
|
||||||
return self._locker
|
return self._locker
|
||||||
@@ -212,204 +122,100 @@ class RiverNode(Node):
|
|||||||
self._locker = locker
|
self._locker = locker
|
||||||
|
|
||||||
|
|
||||||
class RiverReach(Edge):
|
class RiverReach(Edge, SQLSubModel):
|
||||||
_sub_classes = [
|
_sub_classes = [
|
||||||
Reach,
|
Reach,
|
||||||
FrictionList,
|
FrictionList,
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, id: str = -1, name: str = "",
|
def __init__(self, id: str, name: str,
|
||||||
node1: RiverNode = None,
|
node1: RiverNode = None,
|
||||||
node2: RiverNode = None,
|
node2: RiverNode = None,
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(RiverReach, self).__init__(
|
super(RiverReach, self).__init__(
|
||||||
id=id, name=name,
|
id, name,
|
||||||
node1=node1, node2=node2,
|
node1, node2,
|
||||||
status=status,
|
status=status
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self._reach = Reach(status=self._status, parent=self)
|
self._reach = Reach(status=self._status, parent=self)
|
||||||
self._frictions = FrictionList(status=self._status)
|
self._frictions = FrictionList(status=self._status)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE river_reach{ext} (
|
CREATE TABLE river_reach(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
enabled BOOLEAN NOT NULL,
|
enable BOOLEAN NOT NULL,
|
||||||
node1 INTEGER,
|
node1 INTEGER,
|
||||||
node2 INTEGER,
|
node2 INTEGER,
|
||||||
{Scenario.create_db_add_scenario()},
|
FOREIGN KEY(node1) REFERENCES river_node(id),
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
FOREIGN KEY(node2) REFERENCES river_node(id)
|
||||||
FOREIGN KEY(node1) REFERENCES river_node(pamhyr_id),
|
|
||||||
FOREIGN KEY(node2) REFERENCES river_node(pamhyr_id),
|
|
||||||
PRIMARY KEY(pamhyr_id, scenario)
|
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
if ext == "_tmp":
|
cls._create_submodel(execute)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
return cls._update_submodel(execute, version)
|
||||||
|
|
||||||
if major == minor == "0":
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
if major == "0" and minor == "1":
|
|
||||||
if int(release) < 2:
|
|
||||||
execute(
|
|
||||||
"ALTER TABLE river_reach " +
|
|
||||||
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
|
|
||||||
)
|
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data=None):
|
|
||||||
table = "river_reach"
|
|
||||||
nodes = data['id2pid']['river_node']
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
# Copy table
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
f"(pamhyr_id, name, enabled, node1, node2, scenario) " +
|
|
||||||
"SELECT pamhyr_id, name, enable, node1, node2, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
cls._db_update_to_0_2_0_set_node_pid(execute, table, nodes)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_node_pid(cls, execute, table, nodes):
|
|
||||||
bcs = execute(
|
|
||||||
f"SELECT pamhyr_id, node1, node2 FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in bcs:
|
|
||||||
it = iter(row)
|
|
||||||
|
|
||||||
pid = next(it)
|
|
||||||
node1_id = next(it)
|
|
||||||
node2_id = next(it)
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE {table} " +
|
|
||||||
f"SET node1 = {nodes[node1_id]}, " +
|
|
||||||
f"node2 = {nodes[node2_id]} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
reachs = []
|
reachs = []
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
if data is None:
|
||||||
return reachs
|
data = {}
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, deleted, name, enabled, " +
|
"SELECT id, name, enable, node1, node2 FROM river_reach"
|
||||||
"node1, node2, scenario " +
|
|
||||||
"FROM river_reach " +
|
|
||||||
f"WHERE scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
# Update id counter
|
||||||
|
cls._id_cnt = max(cls._id_cnt, row[0])
|
||||||
pid = next(it)
|
# Create new reach
|
||||||
deleted = (next(it) == 1)
|
id = row[0]
|
||||||
name = next(it)
|
name = row[1]
|
||||||
enabled = (next(it) == 1)
|
enable = (row[2] == 1)
|
||||||
node1_pid = next(it)
|
|
||||||
node2_pid = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
# Get nodes corresponding to db foreign key id
|
# Get nodes corresponding to db foreign key id
|
||||||
node1 = next(
|
node1 = next(filter(lambda n: n.id == row[3], data["nodes"]))
|
||||||
filter(
|
node2 = next(filter(lambda n: n.id == row[4], data["nodes"]))
|
||||||
lambda n: n.pamhyr_id == node1_pid, data["nodes"]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
node2 = next(
|
|
||||||
filter(
|
|
||||||
lambda n: n.pamhyr_id == node2_pid, data["nodes"]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
new = cls(
|
new = cls(id, name, node1, node2, status=data["status"])
|
||||||
id=pid, name=name, node1=node1, node2=node2,
|
new.enable(enable=enable)
|
||||||
status=data["status"],
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
new.enable(enable=enabled)
|
|
||||||
if deleted:
|
|
||||||
nd.set_as_deleted()
|
|
||||||
|
|
||||||
data["reach"] = new
|
data["reach"] = new
|
||||||
|
|
||||||
new._reach = Reach._db_load(execute, data)
|
new._reach = Reach._db_load(execute, data)
|
||||||
new._frictions = FrictionList._db_load(execute, data)
|
new._frictions = FrictionList._db_load(execute, data)
|
||||||
|
|
||||||
loaded.add(pid)
|
|
||||||
reachs.append(new)
|
reachs.append(new)
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
reachs += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return reachs
|
return reachs
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute(
|
sql = (
|
||||||
"INSERT OR REPLACE INTO " +
|
"INSERT OR REPLACE INTO " +
|
||||||
"river_reach(" +
|
"river_reach(id, name, enable, node1, node2) " +
|
||||||
"pamhyr_id, deleted, name, enabled, " +
|
|
||||||
"node1, node2, scenario" +
|
|
||||||
") " +
|
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.pamhyr_id}, {self._db_format(self.is_deleted())}, " +
|
f"{self.id}, '{self._db_format(self._name)}', " +
|
||||||
f"'{self._db_format(self._name)}', " +
|
|
||||||
f"{self._db_format(self.is_enable())},"
|
f"{self._db_format(self.is_enable())},"
|
||||||
f"{self.node1.pamhyr_id}, {self.node2.pamhyr_id}, " +
|
f"{self.node1.id}, {self.node2.id}" +
|
||||||
f"{self._status.scenario_id}" +
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
|
if data is None:
|
||||||
|
data = {}
|
||||||
|
|
||||||
data["reach"] = self
|
data["reach"] = self
|
||||||
|
|
||||||
objs = [self._reach, self._frictions]
|
objs = [self._reach, self._frictions]
|
||||||
return self._save_submodel(execute, objs, data)
|
return self._save_submodel(execute, objs, data)
|
||||||
|
|
||||||
def _data_traversal(self,
|
|
||||||
predicate=lambda obj, data: True,
|
|
||||||
modifier=lambda obj, data: None,
|
|
||||||
data={}):
|
|
||||||
if predicate(self, data):
|
|
||||||
modifier(self, data)
|
|
||||||
|
|
||||||
self._reach._data_traversal(predicate, modifier, data)
|
|
||||||
self._frictions._data_traversal(predicate, modifier, data)
|
|
||||||
|
|
||||||
def is_deleted(self):
|
|
||||||
return self._deleted
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def reach(self):
|
def reach(self):
|
||||||
return self._reach
|
return self._reach
|
||||||
@@ -418,46 +224,17 @@ class RiverReach(Edge):
|
|||||||
def frictions(self):
|
def frictions(self):
|
||||||
return self._frictions
|
return self._frictions
|
||||||
|
|
||||||
def _split(self, profile, new_node):
|
|
||||||
name = self.name
|
|
||||||
node1 = self.node1
|
|
||||||
node2 = self.node2
|
|
||||||
|
|
||||||
new_reach1 = RiverReach(
|
class River(Graph, SQLSubModel):
|
||||||
id=-1, name=name + "(part 1)",
|
|
||||||
node1=node1, node2=new_node,
|
|
||||||
status=self._status,
|
|
||||||
)
|
|
||||||
|
|
||||||
new_reach2 = RiverReach(
|
|
||||||
id=-1, name=name + "(part 2)",
|
|
||||||
node1=new_node, node2=node2,
|
|
||||||
status=self._status,
|
|
||||||
)
|
|
||||||
|
|
||||||
nr1, nr2 = self._reach._split(profile, new_reach1, new_reach2)
|
|
||||||
|
|
||||||
new_reach1._reach = nr1
|
|
||||||
new_reach1._frictions = self._frictions._splited_for(new_reach1)
|
|
||||||
|
|
||||||
new_reach2._reach = nr2
|
|
||||||
new_reach2._frictions = self._frictions._splited_for(new_reach2)
|
|
||||||
|
|
||||||
self.disable()
|
|
||||||
|
|
||||||
return new_reach1, new_reach2
|
|
||||||
|
|
||||||
|
|
||||||
class River(Graph):
|
|
||||||
_sub_classes = [
|
_sub_classes = [
|
||||||
StricklersList,
|
|
||||||
SedimentLayerList,
|
|
||||||
RiverNode,
|
RiverNode,
|
||||||
RiverReach,
|
RiverReach,
|
||||||
BoundaryConditionList,
|
BoundaryConditionList,
|
||||||
LateralContributionList,
|
LateralContributionList,
|
||||||
InitialConditionsDict,
|
InitialConditionsDict,
|
||||||
|
StricklersList,
|
||||||
SolverParametersList,
|
SolverParametersList,
|
||||||
|
SedimentLayerList,
|
||||||
ReservoirList,
|
ReservoirList,
|
||||||
HydraulicStructureList,
|
HydraulicStructureList,
|
||||||
AddFileList,
|
AddFileList,
|
||||||
@@ -469,14 +246,10 @@ class River(Graph):
|
|||||||
LateralContributionsAdisTSList,
|
LateralContributionsAdisTSList,
|
||||||
D90AdisTSList,
|
D90AdisTSList,
|
||||||
DIFAdisTSList,
|
DIFAdisTSList,
|
||||||
GeoTIFFList,
|
|
||||||
Results
|
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, status=None):
|
def __init__(self, status=None):
|
||||||
super(River, self).__init__(
|
super(River, self).__init__(status=status)
|
||||||
status=status
|
|
||||||
)
|
|
||||||
|
|
||||||
# Replace Node and Edge ctor by custom ctor
|
# Replace Node and Edge ctor by custom ctor
|
||||||
self._node_ctor = RiverNode
|
self._node_ctor = RiverNode
|
||||||
@@ -507,22 +280,19 @@ class River(Graph):
|
|||||||
self._D90AdisTS = D90AdisTSList(status=self._status)
|
self._D90AdisTS = D90AdisTSList(status=self._status)
|
||||||
self._DIFAdisTS = DIFAdisTSList(status=self._status)
|
self._DIFAdisTS = DIFAdisTSList(status=self._status)
|
||||||
|
|
||||||
self._geotiff = GeoTIFFList(status=self._status)
|
|
||||||
|
|
||||||
self._results = {}
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute):
|
||||||
cls._create_submodel(execute)
|
cls._create_submodel(execute)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
return cls._update_submodel(execute, version, data)
|
cls._update_submodel(execute, version)
|
||||||
|
return True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = cls(status=data["status"])
|
new = cls(data["status"])
|
||||||
|
|
||||||
# Stricklers (Stricklers is load in first because it's needed
|
# Stricklers (Stricklers is load in first because it's needed
|
||||||
# for reachs)
|
# for reachs)
|
||||||
@@ -530,84 +300,65 @@ class River(Graph):
|
|||||||
execute, data
|
execute, data
|
||||||
)
|
)
|
||||||
data["stricklers"] = new._stricklers
|
data["stricklers"] = new._stricklers
|
||||||
data['loaded_pid'] = set()
|
|
||||||
|
|
||||||
# Initial conditions
|
# Initial conditions
|
||||||
new._sediment_layers = SedimentLayerList._db_load(
|
new._sediment_layers = SedimentLayerList._db_load(
|
||||||
execute, data
|
execute, data
|
||||||
)
|
)
|
||||||
data["sediment_layers_list"] = new._sediment_layers
|
data["sediment_layers_list"] = new._sediment_layers
|
||||||
data['loaded_pid'] = set()
|
|
||||||
|
|
||||||
# Network
|
# Network
|
||||||
new._nodes = RiverNode._db_load(
|
new._nodes = RiverNode._db_load(
|
||||||
execute, data
|
execute, data
|
||||||
)
|
)
|
||||||
data["nodes"] = new.nodes()
|
data["nodes"] = new.nodes()
|
||||||
data['loaded_pid'] = set()
|
|
||||||
|
|
||||||
new._edges = RiverReach._db_load(
|
new._edges = RiverReach._db_load(
|
||||||
execute, data
|
execute, data
|
||||||
)
|
)
|
||||||
data["edges"] = new.edges()
|
data["edges"] = new.edges()
|
||||||
data['loaded_pid'] = set()
|
|
||||||
|
|
||||||
# Boundary Condition
|
# Boundary Condition
|
||||||
new._boundary_condition = BoundaryConditionList._db_load(
|
new._boundary_condition = BoundaryConditionList._db_load(
|
||||||
execute, data
|
execute, data
|
||||||
)
|
)
|
||||||
|
|
||||||
data['loaded_pid'] = set()
|
|
||||||
|
|
||||||
# Lateral Contribution
|
# Lateral Contribution
|
||||||
new._lateral_contribution = LateralContributionList._db_load(
|
new._lateral_contribution = LateralContributionList._db_load(
|
||||||
execute, data
|
execute, data
|
||||||
)
|
)
|
||||||
|
|
||||||
data['loaded_pid'] = set()
|
|
||||||
|
|
||||||
# Initial conditions
|
# Initial conditions
|
||||||
new._initial_conditions = InitialConditionsDict._db_load(
|
new._initial_conditions = InitialConditionsDict._db_load(
|
||||||
execute, data
|
execute, data
|
||||||
)
|
)
|
||||||
|
|
||||||
data['loaded_pid'] = set()
|
|
||||||
|
|
||||||
# Reservoir
|
# Reservoir
|
||||||
new._reservoir = ReservoirList._db_load(
|
new._reservoir = ReservoirList._db_load(
|
||||||
execute, data
|
execute, data
|
||||||
)
|
)
|
||||||
|
|
||||||
data['loaded_pid'] = set()
|
|
||||||
|
|
||||||
# Hydraulic Structures
|
# Hydraulic Structures
|
||||||
new._hydraulic_structures = HydraulicStructureList._db_load(
|
new._hydraulic_structures = HydraulicStructureList._db_load(
|
||||||
execute, data
|
execute, data
|
||||||
)
|
)
|
||||||
|
|
||||||
data['loaded_pid'] = set()
|
|
||||||
|
|
||||||
# Parameters
|
# Parameters
|
||||||
new._parameters = SolverParametersList._db_load(
|
new._parameters = SolverParametersList._db_load(
|
||||||
execute, data
|
execute, data
|
||||||
)
|
)
|
||||||
|
|
||||||
data['loaded_pid'] = set()
|
|
||||||
|
|
||||||
# Additional Files
|
# Additional Files
|
||||||
new._additional_files = AddFileList._db_load(
|
new._additional_files = AddFileList._db_load(
|
||||||
execute, data
|
execute, data
|
||||||
)
|
)
|
||||||
new._rep_lines = REPLineList._db_load(execute, data)
|
new._rep_lines = REPLineList._db_load(execute, data)
|
||||||
|
|
||||||
data['loaded_pid'] = set()
|
|
||||||
|
|
||||||
new._Pollutants = PollutantsList._db_load(execute, data)
|
|
||||||
|
|
||||||
new._Output_rk_adists = OutputRKAdistsList._db_load(
|
new._Output_rk_adists = OutputRKAdistsList._db_load(
|
||||||
execute, data
|
execute, data
|
||||||
)
|
)
|
||||||
|
|
||||||
|
new._Pollutants = PollutantsList._db_load(execute, data)
|
||||||
|
|
||||||
new._InitialConditionsAdisTS = InitialConditionsAdisTSList._db_load(
|
new._InitialConditionsAdisTS = InitialConditionsAdisTSList._db_load(
|
||||||
execute, data)
|
execute, data)
|
||||||
|
|
||||||
@@ -621,16 +372,8 @@ class River(Graph):
|
|||||||
|
|
||||||
new._DIFAdisTS = DIFAdisTSList._db_load(execute, data)
|
new._DIFAdisTS = DIFAdisTSList._db_load(execute, data)
|
||||||
|
|
||||||
new._geotiff = GeoTIFFList._db_load(execute, data)
|
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_load_results(self, execute, data=None):
|
|
||||||
results_lst = Results._db_load(execute, data)
|
|
||||||
|
|
||||||
for solv_type, results in results_lst:
|
|
||||||
self._results[solv_type] = results
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
self._db_save_delete_artefact(execute, data)
|
self._db_save_delete_artefact(execute, data)
|
||||||
|
|
||||||
@@ -656,11 +399,6 @@ class River(Graph):
|
|||||||
objs.append(self._D90AdisTS)
|
objs.append(self._D90AdisTS)
|
||||||
objs.append(self._DIFAdisTS)
|
objs.append(self._DIFAdisTS)
|
||||||
|
|
||||||
objs.append(self._geotiff)
|
|
||||||
|
|
||||||
for solv_type in self.results:
|
|
||||||
objs.append(self.results[solv_type])
|
|
||||||
|
|
||||||
self._save_submodel(execute, objs, data)
|
self._save_submodel(execute, objs, data)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -684,64 +422,15 @@ class River(Graph):
|
|||||||
execute(
|
execute(
|
||||||
f"DELETE FROM {table} " +
|
f"DELETE FROM {table} " +
|
||||||
"WHERE " +
|
"WHERE " +
|
||||||
f"scenario = {self._status.scenario_id} AND (" +
|
|
||||||
" OR ".join(
|
" OR ".join(
|
||||||
map(
|
map(
|
||||||
lambda n: f"( pamhyr_id <> {n.pamhyr_id} )",
|
lambda n: f"( id <> {n.id} )", els
|
||||||
els
|
|
||||||
)
|
)
|
||||||
) + ")"
|
)
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger_exception(e)
|
logger_exception(e)
|
||||||
|
|
||||||
def _data_traversal(self,
|
|
||||||
predicate=lambda obj, data: True,
|
|
||||||
modifier=lambda obj, data: None,
|
|
||||||
data={}):
|
|
||||||
"""Traversal data and execute modifier fonction if predicate
|
|
||||||
true
|
|
||||||
|
|
||||||
Args:
|
|
||||||
predicate: Function predicate, take current obj and data as input
|
|
||||||
modifier: Function modifier, take current obj and data as input
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
"""
|
|
||||||
if predicate(self, data):
|
|
||||||
modifier(self, data)
|
|
||||||
|
|
||||||
for obj in self._get_objs_list():
|
|
||||||
obj._data_traversal(predicate, modifier, data)
|
|
||||||
|
|
||||||
def _get_objs_list(self):
|
|
||||||
objs = (self._nodes + self._edges)
|
|
||||||
objs += [
|
|
||||||
self._boundary_condition,
|
|
||||||
self._initial_conditions,
|
|
||||||
self._lateral_contribution,
|
|
||||||
self._sediment_layers,
|
|
||||||
self._stricklers,
|
|
||||||
self._reservoir,
|
|
||||||
self._hydraulic_structures,
|
|
||||||
self._additional_files,
|
|
||||||
self._rep_lines,
|
|
||||||
self._Output_rk_adists,
|
|
||||||
self._Pollutants,
|
|
||||||
self._InitialConditionsAdisTS,
|
|
||||||
self._BoundaryConditionsAdisTS,
|
|
||||||
self._LateralContributionsAdisTS,
|
|
||||||
self._D90AdisTS, self._DIFAdisTS,
|
|
||||||
self._geotiff,
|
|
||||||
]
|
|
||||||
|
|
||||||
for solver in self._parameters:
|
|
||||||
objs.append(self._parameters[solver])
|
|
||||||
|
|
||||||
return objs
|
|
||||||
|
|
||||||
def init_default(self):
|
def init_default(self):
|
||||||
self.init_default_network()
|
self.init_default_network()
|
||||||
self.init_default_sediment()
|
self.init_default_sediment()
|
||||||
@@ -779,9 +468,6 @@ execution...
|
|||||||
|
|
||||||
Last export at: @date."""
|
Last export at: @date."""
|
||||||
|
|
||||||
def reachs(self):
|
|
||||||
return self.edges()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def boundary_condition(self):
|
def boundary_condition(self):
|
||||||
return self._boundary_condition
|
return self._boundary_condition
|
||||||
@@ -827,10 +513,6 @@ Last export at: @date."""
|
|||||||
def additional_files(self):
|
def additional_files(self):
|
||||||
return self._additional_files
|
return self._additional_files
|
||||||
|
|
||||||
@property
|
|
||||||
def geotiff(self):
|
|
||||||
return self._geotiff
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def rep_lines(self):
|
def rep_lines(self):
|
||||||
return self._rep_lines
|
return self._rep_lines
|
||||||
@@ -907,36 +589,3 @@ Last export at: @date."""
|
|||||||
)
|
)
|
||||||
|
|
||||||
return has
|
return has
|
||||||
|
|
||||||
@property
|
|
||||||
def results(self):
|
|
||||||
results = self._results
|
|
||||||
if isinstance(results, types.GeneratorType):
|
|
||||||
self._results = next(results)
|
|
||||||
|
|
||||||
return self._results
|
|
||||||
|
|
||||||
@results.setter
|
|
||||||
def results(self, results):
|
|
||||||
solv_type = results.get("solver_type")
|
|
||||||
|
|
||||||
self._results[solv_type] = results
|
|
||||||
|
|
||||||
def _split_reach(self, reach, profile):
|
|
||||||
node1 = reach.node1
|
|
||||||
node2 = reach.node2
|
|
||||||
|
|
||||||
x1, y1 = node1.x, node1.y
|
|
||||||
x2, y2 = node2.x, node2.y
|
|
||||||
|
|
||||||
center_x = (x1 + x2) / 2
|
|
||||||
center_y = (y1 + y2) / 2
|
|
||||||
|
|
||||||
new_node = self.add_node(center_x, center_y)
|
|
||||||
|
|
||||||
r1, r2 = reach._split(profile, new_node)
|
|
||||||
|
|
||||||
self._add_edge(r1)
|
|
||||||
self._add_edge(r2)
|
|
||||||
|
|
||||||
return r1, r2
|
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# Saved.py -- Pamhyr model status class
|
||||||
|
# Copyright (C) 2023-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 <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
class SavedStatus(object):
|
||||||
|
def __init__(self, version=0):
|
||||||
|
super(SavedStatus, self).__init__()
|
||||||
|
self._version = version
|
||||||
|
self._saved = True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def version(self):
|
||||||
|
return self._version
|
||||||
|
|
||||||
|
@version.setter
|
||||||
|
def version(self, version):
|
||||||
|
self._version = version
|
||||||
|
|
||||||
|
def is_saved(self):
|
||||||
|
return self._saved
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
logger.debug("model status set as saved")
|
||||||
|
self._saved = True
|
||||||
|
|
||||||
|
def modified(self):
|
||||||
|
if self._saved:
|
||||||
|
self._version += 1
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
"STATUS: Model status set as modified " +
|
||||||
|
f"at version {self._version}"
|
||||||
|
)
|
||||||
|
self._saved = False
|
||||||
@@ -1,282 +0,0 @@
|
|||||||
# Scenario.py -- Pamhyr
|
|
||||||
# Copyright (C) 2024-2025 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 <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
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 = "",
|
|
||||||
x: int = 1000.0, y: int = 1000.0,
|
|
||||||
revision: int = 0,
|
|
||||||
parent=None):
|
|
||||||
super(Scenario, self).__init__()
|
|
||||||
|
|
||||||
self._set_id(id)
|
|
||||||
|
|
||||||
self._x, self._y = x, y
|
|
||||||
self._name = name
|
|
||||||
self._description = description
|
|
||||||
self._revision = revision
|
|
||||||
self._parent = parent
|
|
||||||
|
|
||||||
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,
|
|
||||||
x REAL NOT NULL DEFAULT 1000,
|
|
||||||
y REAL NOT NULL DEFAULT 1000,
|
|
||||||
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, x, y, name, description, revision, parent_id) " +
|
|
||||||
"VALUES (\n" +
|
|
||||||
" 0, 1000, 1000, 'default', 'Default scenario',\n" +
|
|
||||||
" 0, NULL\n" +
|
|
||||||
")"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create_db_add_scenario(cls):
|
|
||||||
return "scenario INTEGER NOT NULL DEFAULT 0"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create_db_add_scenario_fk(cls):
|
|
||||||
return "FOREIGN KEY(scenario) REFERENCES scenario(id)"
|
|
||||||
|
|
||||||
@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)
|
|
||||||
cls._db_add_default(execute)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def update_db_add_scenario(cls, execute, table):
|
|
||||||
execute(
|
|
||||||
f"ALTER TABLE {table} " +
|
|
||||||
"ADD COLUMN scenario INTEGER NOT NULL DEFAULT 0"
|
|
||||||
)
|
|
||||||
|
|
||||||
# execute(
|
|
||||||
# f"ALTER TABLE {table} " +
|
|
||||||
# "ADD CONSTRAINT fk_scenario FOREIGN KEY (scenario) " +
|
|
||||||
# "REFERENCES scenario(id)"
|
|
||||||
# )
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_load(cls, execute, data=None):
|
|
||||||
scenarios = {}
|
|
||||||
|
|
||||||
table = execute(
|
|
||||||
"SELECT id, x, y, name, description, revision, parent_id " +
|
|
||||||
"FROM scenario " +
|
|
||||||
"ORDER BY id ASC"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in table:
|
|
||||||
it = iter(row)
|
|
||||||
|
|
||||||
id = next(it)
|
|
||||||
x, y = next(it), 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, x=x, y=y,
|
|
||||||
name=name, description=desc,
|
|
||||||
revision=revi, parent=parent
|
|
||||||
)
|
|
||||||
scenarios[id] = new
|
|
||||||
|
|
||||||
return scenarios
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
|
||||||
if self.is_deleted():
|
|
||||||
return self.drop_all(execute)
|
|
||||||
|
|
||||||
parent = 'NULL'
|
|
||||||
if self.parent is not None:
|
|
||||||
parent = self.parent._id
|
|
||||||
|
|
||||||
execute(
|
|
||||||
"INSERT OR REPLACE INTO " +
|
|
||||||
"scenario(id, x, y, name, description, revision, parent_id) " +
|
|
||||||
"VALUES (" +
|
|
||||||
f"{self._id}, " +
|
|
||||||
f"{self.x}, {self.y}, " +
|
|
||||||
f"'{self._db_format(self.name)}', " +
|
|
||||||
f"'{self._db_format(self.description)}', " +
|
|
||||||
f"{self._revision}, " +
|
|
||||||
f"{parent}" +
|
|
||||||
")"
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def drop_all(self, execute):
|
|
||||||
execute(f"DELETE FROM scenario WHERE id = {self.id}")
|
|
||||||
|
|
||||||
tables = [
|
|
||||||
# Adists
|
|
||||||
"output_rk_adists",
|
|
||||||
"boundary_condition_adists", "boundary_condition_data_adists",
|
|
||||||
"lateral_contribution_adists", "lateral_contribution_data_adists",
|
|
||||||
"initial_conditions_adists",
|
|
||||||
"d90_adists", "dif_adists",
|
|
||||||
"pollutants", "pollutants_characteristics",
|
|
||||||
# Hydraulic
|
|
||||||
"additional_files",
|
|
||||||
"boundary_condition", "boundary_condition_data",
|
|
||||||
"lateral_contribution", "lateral_contribution_data",
|
|
||||||
"friction", "stricklers",
|
|
||||||
"hydraulic_structures",
|
|
||||||
"hydraulic_structures_basic", "hydraulic_structures_basic_value",
|
|
||||||
"initial_conditions",
|
|
||||||
"sedimentary_layer", "sedimentary_layer_layer",
|
|
||||||
"reservoir", "reservoir_data",
|
|
||||||
"rep_lines",
|
|
||||||
"solver_parameter",
|
|
||||||
"geometry_pointXYZ", "geometry_profileXYZ",
|
|
||||||
"river_reach", "river_node",
|
|
||||||
]
|
|
||||||
|
|
||||||
for table in tables:
|
|
||||||
execute(
|
|
||||||
f"DELETE FROM {table} " +
|
|
||||||
f"WHERE scenario = {self.id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
@property
|
|
||||||
def id(self):
|
|
||||||
return self._id
|
|
||||||
|
|
||||||
@property
|
|
||||||
def x(self):
|
|
||||||
return self._x
|
|
||||||
|
|
||||||
@property
|
|
||||||
def y(self):
|
|
||||||
return self._y
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
if self._name == "":
|
|
||||||
return f"Child of '{self._parent.name}' ({self.id})"
|
|
||||||
|
|
||||||
return self._name
|
|
||||||
|
|
||||||
@name.setter
|
|
||||||
def name(self, name):
|
|
||||||
self._name = name
|
|
||||||
|
|
||||||
@property
|
|
||||||
def description(self):
|
|
||||||
if self._description == "":
|
|
||||||
return f"Child of '{self._parent.name}' ({self.id})"
|
|
||||||
|
|
||||||
return self._description
|
|
||||||
|
|
||||||
@description.setter
|
|
||||||
def description(self, description):
|
|
||||||
self._description = description
|
|
||||||
|
|
||||||
@property
|
|
||||||
def revision(self):
|
|
||||||
return self._revision
|
|
||||||
|
|
||||||
@revision.setter
|
|
||||||
def revision(self, revision):
|
|
||||||
self._revision = revision
|
|
||||||
|
|
||||||
@property
|
|
||||||
def parent(self):
|
|
||||||
return self._parent
|
|
||||||
|
|
||||||
def set_pos(self, x, y):
|
|
||||||
self._x = x
|
|
||||||
self._y = y
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
if key == "name":
|
|
||||||
self.name = value
|
|
||||||
elif key == "description":
|
|
||||||
self.description = value
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
|
||||||
if key == "id":
|
|
||||||
return self.id
|
|
||||||
if key == "name":
|
|
||||||
return self.name
|
|
||||||
if key == "description":
|
|
||||||
return self.description
|
|
||||||
if key == "parent":
|
|
||||||
return self.parent
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def workdir(self):
|
|
||||||
srep = ""
|
|
||||||
if self.id != 0:
|
|
||||||
srep = os.path.join(
|
|
||||||
self.parent.workdir(),
|
|
||||||
"scenario_" + str(self.id)
|
|
||||||
)
|
|
||||||
|
|
||||||
return srep
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
# Scenarios.py -- Pamhyr
|
|
||||||
# Copyright (C) 2024-2025 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 <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
# -*- 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)
|
|
||||||
self.set(new._id, new)
|
|
||||||
return new
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
return len(self.lst)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def lst(self):
|
|
||||||
return list(
|
|
||||||
map(
|
|
||||||
lambda k: self.get(k),
|
|
||||||
self._dict
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def delete(self, key):
|
|
||||||
if key not in self._dict:
|
|
||||||
return
|
|
||||||
|
|
||||||
self._dict[key].set_as_deleted()
|
|
||||||
self._status.modified()
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# SedimentLayer.py -- Pamhyr
|
# SedimentLayer.py -- Pamhyr
|
||||||
# Copyright (C) 2024-2025 INRAE
|
# Copyright (C) 2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -21,26 +21,23 @@ from functools import reduce
|
|||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Scenario import Scenario
|
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
|
|
||||||
class Layer(SQLSubModel):
|
class Layer(SQLSubModel):
|
||||||
_sub_classes = []
|
_sub_classes = []
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
id: int = -1, name: str = "",
|
id: int = -1, name: str = "",
|
||||||
type="",
|
type="",
|
||||||
height=0.0, d50=0.0, sigma=0.0,
|
height=0.0, d50=0.0, sigma=0.0,
|
||||||
critical_constraint=0.0,
|
critical_constraint=0.0,
|
||||||
sl=None, status=None,
|
sl=None, status=None):
|
||||||
owner_scenario=-1):
|
super(Layer, self).__init__()
|
||||||
super(Layer, self).__init__(
|
|
||||||
id=id, status=status,
|
self._status = status
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._sl = sl
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self._type = type
|
self._type = type
|
||||||
|
|
||||||
@@ -49,17 +46,23 @@ class Layer(SQLSubModel):
|
|||||||
self._sigma = sigma
|
self._sigma = sigma
|
||||||
self._critical_constraint = critical_constraint
|
self._critical_constraint = critical_constraint
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = Layer._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
Layer._id_cnt = max(self.id + 1, Layer._id_cnt + 1)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
if self._name == "":
|
if self._name == "":
|
||||||
return f"Layer #{self.pamhyr_id}"
|
return f"Layer{self.id + 1}"
|
||||||
|
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@name.setter
|
@name.setter
|
||||||
def name(self, name):
|
def name(self, name):
|
||||||
self._name = name
|
self._name = name
|
||||||
self.modified()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self):
|
def type(self):
|
||||||
@@ -68,7 +71,6 @@ class Layer(SQLSubModel):
|
|||||||
@type.setter
|
@type.setter
|
||||||
def type(self, type):
|
def type(self, type):
|
||||||
self._type = type
|
self._type = type
|
||||||
self.modified()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def height(self):
|
def height(self):
|
||||||
@@ -77,7 +79,6 @@ class Layer(SQLSubModel):
|
|||||||
@height.setter
|
@height.setter
|
||||||
def height(self, height):
|
def height(self, height):
|
||||||
self._height = float(height)
|
self._height = float(height)
|
||||||
self.modified()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def d50(self):
|
def d50(self):
|
||||||
@@ -86,7 +87,6 @@ class Layer(SQLSubModel):
|
|||||||
@d50.setter
|
@d50.setter
|
||||||
def d50(self, d50):
|
def d50(self, d50):
|
||||||
self._d50 = float(d50)
|
self._d50 = float(d50)
|
||||||
self.modified()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sigma(self):
|
def sigma(self):
|
||||||
@@ -95,7 +95,6 @@ class Layer(SQLSubModel):
|
|||||||
@sigma.setter
|
@sigma.setter
|
||||||
def sigma(self, sigma):
|
def sigma(self, sigma):
|
||||||
self._sigma = float(sigma)
|
self._sigma = float(sigma)
|
||||||
self.modified()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def critical_constraint(self):
|
def critical_constraint(self):
|
||||||
@@ -104,14 +103,12 @@ class Layer(SQLSubModel):
|
|||||||
@critical_constraint.setter
|
@critical_constraint.setter
|
||||||
def critical_constraint(self, critical_constraint):
|
def critical_constraint(self, critical_constraint):
|
||||||
self._critical_constraint = float(critical_constraint)
|
self._critical_constraint = float(critical_constraint)
|
||||||
self.modified()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE sedimentary_layer_layer{ext} (
|
CREATE TABLE sedimentary_layer_layer(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
ind INTEGER NOT NULL,
|
ind INTEGER NOT NULL,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
type TEXT NOT NULL,
|
type TEXT NOT NULL,
|
||||||
@@ -120,177 +117,92 @@ class Layer(SQLSubModel):
|
|||||||
sigma REAL NOT NULL,
|
sigma REAL NOT NULL,
|
||||||
critical_constraint REAL NOT NULL,
|
critical_constraint REAL NOT NULL,
|
||||||
sl INTEGER,
|
sl INTEGER,
|
||||||
{Scenario.create_db_add_scenario()},
|
FOREIGN KEY(sl) REFERENCES sedimentary_layer(id)
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
|
||||||
FOREIGN KEY(sl) REFERENCES sedimentary_layer(pamhyr_id),
|
|
||||||
PRIMARY KEY(pamhyr_id, scenario)
|
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
if major == minor == "0":
|
if major == minor == "0":
|
||||||
if int(release) < 2:
|
if int(release) < 2:
|
||||||
cls._db_create(execute)
|
cls._db_create(execute)
|
||||||
return True
|
|
||||||
else:
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
if major == "0" and minor == "1":
|
return True
|
||||||
if int(release) < 2:
|
|
||||||
execute(
|
|
||||||
"ALTER TABLE sedimentary_layer_layer " +
|
|
||||||
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
|
|
||||||
)
|
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "sedimentary_layer_layer"
|
|
||||||
sl = data['id2pid']['sedimentary_layer']
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
"(pamhyr_id, ind, name, type, height, " +
|
|
||||||
"d50, sigma, critical_constraint, sl, scenario) " +
|
|
||||||
"SELECT pamhyr_id, ind, name, type, height, " +
|
|
||||||
"d50, sigma, critical_constraint, sl, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
cls._db_update_to_0_2_0_set_sl_pid(execute, table, sl)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_sl_pid(cls, execute, table, sl):
|
|
||||||
els = execute(
|
|
||||||
f"SELECT pamhyr_id, sl FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if els is None:
|
|
||||||
return True
|
|
||||||
|
|
||||||
for row in els:
|
|
||||||
it = iter(row)
|
|
||||||
pid = next(it)
|
|
||||||
sl_id = next(it)
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE {table} " +
|
|
||||||
f"SET sl = {sl[sl_id]} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
new = []
|
||||||
sl = data["sl"]
|
sl = data["sl"]
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, deleted, name, type, height, " +
|
"SELECT id, ind, name, type, height, " +
|
||||||
"d50, sigma, critical_constraint, scenario " +
|
"d50, sigma, critical_constraint " +
|
||||||
"FROM sedimentary_layer_layer " +
|
"FROM sedimentary_layer_layer " +
|
||||||
f"WHERE sl = {sl.pamhyr_id} " +
|
f"WHERE sl = {sl}"
|
||||||
f"AND scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) " +
|
|
||||||
"ORDER BY ind ASC"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for row in table:
|
for _ in table:
|
||||||
it = iter(row)
|
new.append(None)
|
||||||
|
|
||||||
pid = next(it)
|
for row in table:
|
||||||
deleted = (next(it) == 1)
|
ind = row[1]
|
||||||
name = next(it)
|
|
||||||
type = next(it)
|
|
||||||
height = next(it)
|
|
||||||
d50 = next(it)
|
|
||||||
sigma = next(it)
|
|
||||||
critical_constraint = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
layer = cls(
|
layer = cls(
|
||||||
id=pid, name=name,
|
id=row[0], name=row[2],
|
||||||
type=type, height=height,
|
type=row[3], height=row[4],
|
||||||
d50=d50, sigma=sigma,
|
d50=row[5], sigma=row[6],
|
||||||
critical_constraint=critical_constraint,
|
critical_constraint=row[7],
|
||||||
sl=sl, status=data['status'],
|
sl=sl, status=data['status']
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
if deleted:
|
|
||||||
layer.set_as_deleted()
|
|
||||||
|
|
||||||
loaded.add(pid)
|
new[ind] = layer
|
||||||
new.append(layer)
|
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
if not self.must_be_saved():
|
|
||||||
return True
|
|
||||||
|
|
||||||
ind = data["ind"]
|
ind = data["ind"]
|
||||||
sl = data["sl"]
|
sl = data["sl"]
|
||||||
|
|
||||||
execute(
|
sql = (
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"sedimentary_layer_layer(" +
|
"sedimentary_layer_layer(id, ind, name, type, height, " +
|
||||||
"pamhyr_id, deleted, ind, name, type, height, " +
|
"d50, sigma, critical_constraint, sl) " +
|
||||||
"d50, sigma, critical_constraint, sl, scenario" +
|
|
||||||
") " +
|
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.pamhyr_id}, {self._db_format(self.is_deleted())}, " +
|
f"{self.id}, {ind}, '{self._db_format(self._name)}', " +
|
||||||
f"{ind}, '{self._db_format(self._name)}', " +
|
|
||||||
f"'{self._db_format(self._type)}', {self._height}, " +
|
f"'{self._db_format(self._type)}', {self._height}, " +
|
||||||
f"{self._d50}, {self._sigma}, {self._critical_constraint}, " +
|
f"{self._d50}, {self._sigma}, {self._critical_constraint}, " +
|
||||||
f"{sl.pamhyr_id}, " +
|
f"{sl.id}" +
|
||||||
f"{self._status.scenario_id}" +
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def modified(self):
|
|
||||||
super(Layer, self).modified()
|
|
||||||
|
|
||||||
self._sl.modified()
|
|
||||||
|
|
||||||
|
|
||||||
class SedimentLayer(SQLSubModel):
|
class SedimentLayer(SQLSubModel):
|
||||||
_sub_classes = [Layer]
|
_sub_classes = [Layer]
|
||||||
|
_id_cnt = 0
|
||||||
|
|
||||||
def __init__(self, id: int = -1,
|
def __init__(self, id: int = -1,
|
||||||
name: str = "", comment: str = "",
|
name: str = "", comment: str = "",
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(SedimentLayer, self).__init__(
|
super(SedimentLayer, self).__init__()
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
|
self._status = status
|
||||||
self._name = name
|
self._name = name
|
||||||
self._comment = comment
|
self._comment = comment
|
||||||
self._layers = []
|
self._layers = []
|
||||||
|
|
||||||
|
if id == -1:
|
||||||
|
self.id = SedimentLayer._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
SedimentLayer._id_cnt = max(id, SedimentLayer._id_cnt+1)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
s = f"{self.name} ({len(self)})"
|
s = f"{self.name} ({len(self)})"
|
||||||
|
|
||||||
@@ -300,48 +212,31 @@ class SedimentLayer(SQLSubModel):
|
|||||||
return s
|
return s
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(
|
return len(self._layers)
|
||||||
list(
|
|
||||||
filter(
|
|
||||||
lambda el: not el.is_deleted(),
|
|
||||||
self._layers
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def layers(self):
|
def layers(self):
|
||||||
return list(
|
return self._layers.copy()
|
||||||
filter(
|
|
||||||
lambda el: not el.is_deleted(),
|
|
||||||
self._layers
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def height(self):
|
def height(self):
|
||||||
return list(
|
return list(
|
||||||
map(lambda layer: layer.height,
|
map(lambda layer: layer.height, self._layers)
|
||||||
self.layers)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
if self._name == "":
|
if self._name == "":
|
||||||
return f"SL #{self.pamhyr_id}"
|
return f"SL{self.id + 1}"
|
||||||
|
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@name.setter
|
@name.setter
|
||||||
def name(self, name):
|
def name(self, name):
|
||||||
self._name = name
|
self._name = name
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def names(self):
|
def names(self):
|
||||||
return list(
|
return list(
|
||||||
map(
|
map(lambda layer: layer.name, self._layers)
|
||||||
lambda layer: layer.name,
|
|
||||||
self.layers
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -351,128 +246,63 @@ class SedimentLayer(SQLSubModel):
|
|||||||
@comment.setter
|
@comment.setter
|
||||||
def comment(self, comment):
|
def comment(self, comment):
|
||||||
self._comment = comment
|
self._comment = comment
|
||||||
self.modified()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE sedimentary_layer{ext} (
|
CREATE TABLE sedimentary_layer(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
comment TEXT NOT NULL,
|
comment TEXT NOT NULL
|
||||||
{Scenario.create_db_add_scenario()},
|
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
|
||||||
PRIMARY KEY(pamhyr_id, scenario)
|
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
if ext == "_tmp":
|
|
||||||
return True
|
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
if major == minor == "0":
|
if major == minor == "0":
|
||||||
if int(release) < 2:
|
if int(release) < 2:
|
||||||
cls._db_create(execute)
|
cls._db_create(execute)
|
||||||
return True
|
|
||||||
else:
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
if major == "0" and minor == "1":
|
return True
|
||||||
if int(release) < 2:
|
|
||||||
execute(
|
|
||||||
"ALTER TABLE sedimentary_layer " +
|
|
||||||
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
|
|
||||||
)
|
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "sedimentary_layer"
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
"(pamhyr_id, name, comment, scenario) " +
|
|
||||||
"SELECT pamhyr_id, name, comment, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = []
|
new = []
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, deleted, name, comment, scenario " +
|
"SELECT id, name, comment " +
|
||||||
"FROM sedimentary_layer " +
|
"FROM sedimentary_layer "
|
||||||
f"WHERE scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) "
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if table is not None:
|
if table is not None:
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
|
||||||
|
|
||||||
pid = next(it)
|
|
||||||
deleted = (next(it) == 1)
|
|
||||||
name = next(it)
|
|
||||||
comment = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
sl = cls(
|
sl = cls(
|
||||||
id=pid,
|
id=row[0],
|
||||||
name=name,
|
name=row[1],
|
||||||
comment=comment,
|
comment=row[2],
|
||||||
status=data['status'],
|
status=data['status']
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
)
|
||||||
if deleted:
|
|
||||||
sl.set_as_deleted()
|
|
||||||
|
|
||||||
data["sl"] = sl
|
data["sl"] = sl.id
|
||||||
sl._layers = Layer._db_load(execute, data)
|
sl._layers = Layer._db_load(execute, data)
|
||||||
|
|
||||||
loaded.add(pid)
|
|
||||||
new.append(sl)
|
new.append(sl)
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
if not self.must_be_saved():
|
if data is None:
|
||||||
return True
|
data = {}
|
||||||
|
|
||||||
execute(
|
sql = (
|
||||||
"INSERT INTO sedimentary_layer (" +
|
"INSERT INTO sedimentary_layer (id, name, comment) " +
|
||||||
"pamhyr_id, deleted, name, comment, scenario" +
|
f"VALUES ({self.id}, '{self._db_format(self._name)}', " +
|
||||||
") " +
|
f"'{self._db_format(self._comment)}')"
|
||||||
"VALUES (" +
|
|
||||||
f"{self._pamhyr_id}, {self._db_format(self.is_deleted())}, " +
|
|
||||||
f"'{self._db_format(self._name)}', " +
|
|
||||||
f"'{self._db_format(self._comment)}', " +
|
|
||||||
f"{self._status.scenario_id}" +
|
|
||||||
")"
|
|
||||||
)
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
data["sl"] = self
|
data["sl"] = self
|
||||||
|
|
||||||
@@ -484,56 +314,39 @@ class SedimentLayer(SQLSubModel):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _data_traversal(self,
|
|
||||||
predicate=lambda obj, data: True,
|
|
||||||
modifier=lambda obj, data: None,
|
|
||||||
data={}):
|
|
||||||
if predicate(self, data):
|
|
||||||
modifier(self, data)
|
|
||||||
|
|
||||||
for layer in self._layers:
|
|
||||||
layer._data_traversal(predicate, modifier, data)
|
|
||||||
|
|
||||||
def get(self, index):
|
def get(self, index):
|
||||||
return self.layers[index]
|
return self._layers[index]
|
||||||
|
|
||||||
# def set(self, index, new):
|
def set(self, index, new):
|
||||||
# self._layers[index] = new
|
self._layers[index] = new
|
||||||
# self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def insert(self, index, new):
|
def insert(self, index, new):
|
||||||
self._layers.insert(index, new)
|
self._layers.insert(index, new)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def new(self, index):
|
def new(self, index):
|
||||||
n = Layer(sl=self, status=self._status)
|
n = Layer(sl=self, status=self._status)
|
||||||
self.insert(index, n)
|
self.insert(index, n)
|
||||||
self.modified()
|
self._status.modified()
|
||||||
return n
|
return n
|
||||||
|
|
||||||
def undelete(self, els):
|
|
||||||
for el in els:
|
|
||||||
el.set_as_not_deleted()
|
|
||||||
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def delete(self, els):
|
def delete(self, els):
|
||||||
for el in els:
|
for el in els:
|
||||||
el.set_as_deleted()
|
self._layers.remove(el)
|
||||||
|
self._status.modified()
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def delete_i(self, indexes):
|
def delete_i(self, indexes):
|
||||||
list(
|
els = list(
|
||||||
map(
|
map(
|
||||||
lambda e: e[1].set_as_deleted(),
|
lambda x: x[1],
|
||||||
filter(
|
filter(
|
||||||
lambda e: e[0] in indexes,
|
lambda x: x[0] in indexes,
|
||||||
enumerate(self._layers)
|
enumerate(self._layers)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.modified()
|
self.delete(els)
|
||||||
|
|
||||||
def move_up(self, index):
|
def move_up(self, index):
|
||||||
if index >= 0:
|
if index >= 0:
|
||||||
@@ -542,7 +355,7 @@ class SedimentLayer(SQLSubModel):
|
|||||||
lst = self._layers
|
lst = self._layers
|
||||||
lst[index], lst[next] = lst[next], lst[index]
|
lst[index], lst[next] = lst[next], lst[index]
|
||||||
|
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def move_down(self, index):
|
def move_down(self, index):
|
||||||
if index + 1 < len(self._layers):
|
if index + 1 < len(self._layers):
|
||||||
@@ -551,7 +364,7 @@ class SedimentLayer(SQLSubModel):
|
|||||||
lst = self._layers
|
lst = self._layers
|
||||||
lst[index], lst[prev] = lst[prev], lst[index]
|
lst[index], lst[prev] = lst[prev], lst[index]
|
||||||
|
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def compute_height_from_bottom(self, bottom_elevation: list):
|
def compute_height_from_bottom(self, bottom_elevation: list):
|
||||||
sl_height = self.height()
|
sl_height = self.height()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# SedimentLayerList.py -- Pamhyr
|
# SedimentLayerList.py -- Pamhyr
|
||||||
# Copyright (C) 2024-2025 INRAE
|
# Copyright (C) 2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
|
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
from Model.Tools.PamhyrListExt import PamhyrModelList
|
from Model.Tools.PamhyrList import PamhyrModelList
|
||||||
from Model.SedimentLayer.SedimentLayer import SedimentLayer
|
from Model.SedimentLayer.SedimentLayer import SedimentLayer
|
||||||
|
|
||||||
|
|
||||||
@@ -35,17 +35,12 @@ class SedimentLayerList(PamhyrModelList):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute(
|
|
||||||
"DELETE FROM sedimentary_layer " +
|
|
||||||
f"WHERE scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
execute(
|
|
||||||
"DELETE FROM sedimentary_layer_layer " +
|
|
||||||
f"WHERE scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
ok = True
|
ok = True
|
||||||
|
|
||||||
|
# Delete previous data
|
||||||
|
execute("DELETE FROM sedimentary_layer")
|
||||||
|
execute("DELETE FROM sedimentary_layer_layer")
|
||||||
|
|
||||||
for sl in self._lst:
|
for sl in self._lst:
|
||||||
ok &= sl._db_save(execute, data)
|
ok &= sl._db_save(execute, data)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Serializable.py -- Pamhyr pickle abstract class
|
# Serializable.py -- Pamhyr pickle abstract class
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# SolverParametersList.py -- Pamhyr
|
# SolverParametersList.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -20,221 +20,18 @@ from copy import copy
|
|||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
from Solver.Solvers import solver_type_list
|
from Solver.Solvers import solver_type_list
|
||||||
|
|
||||||
from Model.Scenario import Scenario
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
|
||||||
from Model.Tools.PamhyrList import PamhyrModelList
|
from Model.Tools.PamhyrList import PamhyrModelList
|
||||||
|
|
||||||
|
|
||||||
class Parameter(SQLSubModel):
|
class Parameter():
|
||||||
def __init__(self, id: int = -1,
|
def __init__(self,
|
||||||
name="", value="",
|
name="", value="",
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(Parameter, self).__init__(
|
self._status = status
|
||||||
id=id, status=status,
|
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
|
||||||
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self._value = value
|
self._value = value
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_create(cls, execute, ext=""):
|
|
||||||
execute(f"""
|
|
||||||
CREATE TABLE solver_parameter{ext} (
|
|
||||||
pamhyr_id INTEGER NOT NULL,
|
|
||||||
ind INTEGER NOT NULL,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
value TEXT NOT NULL,
|
|
||||||
solver TEXT 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 == minor == "0":
|
|
||||||
release = int(release)
|
|
||||||
if release < 3:
|
|
||||||
execute(
|
|
||||||
"UPDATE solver_parameter SET name='mage_implicitation' " +
|
|
||||||
"WHERE name='mage_implication'"
|
|
||||||
)
|
|
||||||
execute(
|
|
||||||
"UPDATE solver_parameter SET name='mage_iteration_type' " +
|
|
||||||
"WHERE name='mage_iter_type'"
|
|
||||||
)
|
|
||||||
|
|
||||||
def insert(name, value, ind):
|
|
||||||
return execute(
|
|
||||||
"INSERT INTO " +
|
|
||||||
"solver_parameter(ind, name, value, solver) " +
|
|
||||||
"VALUES (" +
|
|
||||||
f"{ind}, " +
|
|
||||||
f"'{cls._db_format(name)}', " +
|
|
||||||
f"'{cls._db_format(value)}', " +
|
|
||||||
f"'{cls._db_format(solver)}'" +
|
|
||||||
")"
|
|
||||||
)
|
|
||||||
|
|
||||||
if release < 4:
|
|
||||||
cls._db_update_to_0_0_4(execute, insert)
|
|
||||||
|
|
||||||
if release < 7:
|
|
||||||
cls._db_update_to_0_0_7(execute, insert)
|
|
||||||
|
|
||||||
if release < 14:
|
|
||||||
cls._db_update_to_0_0_14(execute, data)
|
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_0_4(cls, execute, insert):
|
|
||||||
solvers = execute(
|
|
||||||
"SELECT DISTINCT solver FROM solver_parameter"
|
|
||||||
)
|
|
||||||
|
|
||||||
for solver in solvers:
|
|
||||||
if solver != "mage8":
|
|
||||||
continue
|
|
||||||
|
|
||||||
data = execute("SELECT ind, name, value " +
|
|
||||||
"FROM solver_parameter " +
|
|
||||||
f"WHERE solver = '{solver}' " +
|
|
||||||
"ORDER BY ind ACS")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
"DELETE FROM solver_parameter " +
|
|
||||||
f"WHERE solver = '{solver}'"
|
|
||||||
)
|
|
||||||
|
|
||||||
ind = 0
|
|
||||||
lst = []
|
|
||||||
for v in data:
|
|
||||||
insert(v[0], v[1], ind)
|
|
||||||
ind += 1
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_0_7(cls, execute, insert):
|
|
||||||
solvers = execute(
|
|
||||||
"SELECT DISTINCT solver FROM solver_parameter"
|
|
||||||
)
|
|
||||||
|
|
||||||
for solver in solvers:
|
|
||||||
if solver != "mage8":
|
|
||||||
continue
|
|
||||||
|
|
||||||
insert("mage_init_internal", "N", ind)
|
|
||||||
ind += 1
|
|
||||||
|
|
||||||
new = [
|
|
||||||
("mage_sediment_masse_volumique", "2650.0"),
|
|
||||||
("mage_sediment_angle_repos", "40.0"),
|
|
||||||
("mage_sediment_porosity", "0.40"),
|
|
||||||
("mage_distance_han", "0.0"),
|
|
||||||
("mage_distance_chargement_d50", "100.0"),
|
|
||||||
("mage_distance_chargement_sigma", "100.0"),
|
|
||||||
("mage_methode_modification_geometrie", "1"),
|
|
||||||
("mage_shields_critique", "1"),
|
|
||||||
("mage_shields_correction", "1"),
|
|
||||||
("mage_capacite_solide", "1"),
|
|
||||||
("mage_pas_de_temps_charriage", "1"),
|
|
||||||
("mage_facteur_multiplicateur", "1.0"),
|
|
||||||
]
|
|
||||||
|
|
||||||
for v in new:
|
|
||||||
insert(v[0], v[1], ind)
|
|
||||||
ind += 1
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_0_14(cls, execute, data):
|
|
||||||
table = "solver_parameter"
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
"(pamhyr_id, ind, name, value, solver, scenario) " +
|
|
||||||
"SELECT pamhyr_id, ind, name, value, solver, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_load(cls, execute, data=None):
|
|
||||||
status = data["status"]
|
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
solver = data['solver']
|
|
||||||
|
|
||||||
new = []
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
table = execute(
|
|
||||||
"SELECT pamhyr_id, ind, name, value, scenario " +
|
|
||||||
"FROM solver_parameter " +
|
|
||||||
f"WHERE solver = '{solver}' " +
|
|
||||||
f"AND scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in table:
|
|
||||||
it = iter(row)
|
|
||||||
|
|
||||||
pid = next(it)
|
|
||||||
ind = next(it)
|
|
||||||
name = next(it)
|
|
||||||
value = next(it)
|
|
||||||
owner = next(it)
|
|
||||||
|
|
||||||
new.append(
|
|
||||||
(
|
|
||||||
ind,
|
|
||||||
cls(
|
|
||||||
id=pid, name=name, value=value,
|
|
||||||
status=status, owner_scenario=owner
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
loaded.add(pid)
|
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
new += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return new
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
|
||||||
ind = data["ind"]
|
|
||||||
solver = data["solver"]
|
|
||||||
|
|
||||||
execute(
|
|
||||||
"INSERT INTO " +
|
|
||||||
"solver_parameter(pamhyr_id, ind, " +
|
|
||||||
"name, value, solver, scenario) " +
|
|
||||||
"VALUES (" +
|
|
||||||
f"{self.pamhyr_id}, " +
|
|
||||||
f"{ind}, " +
|
|
||||||
f"'{self._db_format(self.name)}', " +
|
|
||||||
f"'{self._db_format(self.value)}', " +
|
|
||||||
f"'{self._db_format(solver)}', " +
|
|
||||||
f"{self._status.scenario_id}" +
|
|
||||||
")"
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
return self._name
|
return self._name
|
||||||
@@ -257,7 +54,7 @@ class Parameter(SQLSubModel):
|
|||||||
elif key == "value":
|
elif key == "value":
|
||||||
self._value = str(value)
|
self._value = str(value)
|
||||||
|
|
||||||
self.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_tuple(cls, data, status):
|
def from_tuple(cls, data, status):
|
||||||
@@ -269,8 +66,6 @@ class Parameter(SQLSubModel):
|
|||||||
|
|
||||||
|
|
||||||
class SolverParametersList(PamhyrModelList):
|
class SolverParametersList(PamhyrModelList):
|
||||||
_sub_classes = [Parameter]
|
|
||||||
|
|
||||||
def __init__(self, solver_type=None, status=None):
|
def __init__(self, solver_type=None, status=None):
|
||||||
super(SolverParametersList, self).__init__()
|
super(SolverParametersList, self).__init__()
|
||||||
|
|
||||||
@@ -284,21 +79,100 @@ class SolverParametersList(PamhyrModelList):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
|
execute("""
|
||||||
|
CREATE TABLE solver_parameter(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
ind INTEGER NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
value TEXT NOT NULL,
|
||||||
|
solver TEXT NOT NULL
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_update(cls, execute, version):
|
||||||
|
major, minor, release = version.strip().split(".")
|
||||||
|
|
||||||
|
if major == minor == "0":
|
||||||
|
if int(release) < 3:
|
||||||
|
execute(
|
||||||
|
"UPDATE solver_parameter SET name='mage_implicitation' " +
|
||||||
|
"WHERE name='mage_implication'"
|
||||||
|
)
|
||||||
|
execute(
|
||||||
|
"UPDATE solver_parameter SET name='mage_iteration_type' " +
|
||||||
|
"WHERE name='mage_iter_type'"
|
||||||
|
)
|
||||||
|
|
||||||
|
if int(release) < 4:
|
||||||
|
solvers = execute(
|
||||||
|
"SELECT DISTINCT solver FROM solver_parameter")
|
||||||
|
|
||||||
|
for solver in solvers:
|
||||||
|
if solver != "mage8":
|
||||||
|
continue
|
||||||
|
|
||||||
|
data = execute("SELECT ind, name, value " +
|
||||||
|
"FROM solver_parameter " +
|
||||||
|
f"WHERE solver = '{solver}' " +
|
||||||
|
"ORDER BY ind ACS")
|
||||||
|
|
||||||
|
execute(
|
||||||
|
"DELETE FROM solver_parameter " +
|
||||||
|
f"WHERE solver = '{solver}'"
|
||||||
|
)
|
||||||
|
|
||||||
|
def insert(name, value, ind): return execute(
|
||||||
|
"INSERT INTO " +
|
||||||
|
"solver_parameter(ind, name, value, solver) " +
|
||||||
|
"VALUES (" +
|
||||||
|
f"{ind}, " +
|
||||||
|
f"'{cls._db_format(name)}', " +
|
||||||
|
f"'{cls._db_format(value)}', " +
|
||||||
|
f"'{cls._db_format(solver)}'" +
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
|
||||||
|
ind = 0
|
||||||
|
lst = []
|
||||||
|
for v in data:
|
||||||
|
insert(v[0], v[1], ind)
|
||||||
|
ind += 1
|
||||||
|
|
||||||
|
if int(release) < 7:
|
||||||
|
insert("mage_init_internal", "N", ind)
|
||||||
|
ind += 1
|
||||||
|
|
||||||
|
new = [
|
||||||
|
("mage_sediment_masse_volumique", "2650.0"),
|
||||||
|
("mage_sediment_angle_repos", "40.0"),
|
||||||
|
("mage_sediment_porosity", "0.40"),
|
||||||
|
("mage_distance_han", "0.0"),
|
||||||
|
("mage_distance_chargement_d50", "100.0"),
|
||||||
|
("mage_distance_chargement_sigma", "100.0"),
|
||||||
|
("mage_methode_modification_geometrie", "1"),
|
||||||
|
("mage_shields_critique", "1"),
|
||||||
|
("mage_shields_correction", "1"),
|
||||||
|
("mage_capacite_solide", "1"),
|
||||||
|
("mage_pas_de_temps_charriage", "1"),
|
||||||
|
("mage_facteur_multiplicateur", "1.0"),
|
||||||
|
]
|
||||||
|
|
||||||
|
for v in new:
|
||||||
|
insert(v[0], v[1], ind)
|
||||||
|
ind += 1
|
||||||
|
|
||||||
|
return cls._update_submodel(execute, version)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
status = data["status"]
|
status = data["status"]
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
solvers = execute("SELECT DISTINCT solver FROM solver_parameter")
|
solvers = execute("SELECT DISTINCT solver FROM solver_parameter")
|
||||||
new = {}
|
new = {}
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return new
|
|
||||||
|
|
||||||
for solver in solvers:
|
for solver in solvers:
|
||||||
solver = solver[0]
|
solver = solver[0]
|
||||||
if solver not in solver_type_list:
|
if solver not in solver_type_list:
|
||||||
@@ -307,10 +181,18 @@ class SolverParametersList(PamhyrModelList):
|
|||||||
st = solver_type_list[solver]
|
st = solver_type_list[solver]
|
||||||
n = cls(solver_type=st, status=status)
|
n = cls(solver_type=st, status=status)
|
||||||
|
|
||||||
data['solver'] = solver
|
table = execute(
|
||||||
lst = Parameter._db_load(execute, data)
|
"SELECT ind, name, value " +
|
||||||
|
"FROM solver_parameter " +
|
||||||
|
f"WHERE solver = '{solver}'"
|
||||||
|
)
|
||||||
|
|
||||||
n._lst = list(map(lambda t: t[1], sorted(lst)))
|
for row in table:
|
||||||
|
ind = row[0]
|
||||||
|
name = row[1]
|
||||||
|
value = row[2]
|
||||||
|
|
||||||
|
n.set_value(name, value)
|
||||||
|
|
||||||
new[solver] = n
|
new[solver] = n
|
||||||
|
|
||||||
@@ -320,16 +202,22 @@ class SolverParametersList(PamhyrModelList):
|
|||||||
t = self._solver._type
|
t = self._solver._type
|
||||||
execute(
|
execute(
|
||||||
"DELETE FROM solver_parameter " +
|
"DELETE FROM solver_parameter " +
|
||||||
f"WHERE solver = '{t}' " +
|
f"WHERE solver = '{t}'"
|
||||||
f"AND scenario = {self._status.scenario_id}"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
data["solver"] = t
|
|
||||||
|
|
||||||
ind = 0
|
ind = 0
|
||||||
for param in self._lst:
|
for param in self._lst:
|
||||||
data["ind"] = ind
|
sql = (
|
||||||
param._db_save(execute, data)
|
"INSERT INTO " +
|
||||||
|
"solver_parameter(ind, name, value, solver) " +
|
||||||
|
"VALUES (" +
|
||||||
|
f"{ind}, " +
|
||||||
|
f"'{self._db_format(param.name)}', " +
|
||||||
|
f"'{self._db_format(param.value)}', " +
|
||||||
|
f"'{self._db_format(t)}'" +
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
execute(sql)
|
||||||
ind += 1
|
ind += 1
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@@ -354,9 +242,9 @@ class SolverParametersList(PamhyrModelList):
|
|||||||
|
|
||||||
def set(self, index, new):
|
def set(self, index, new):
|
||||||
self._lst[index] = new
|
self._lst[index] = new
|
||||||
new.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def set_value(self, pid, key, value, owner):
|
def set_value(self, key, value):
|
||||||
for p in self._lst:
|
for p in self._lst:
|
||||||
if p["name"] == key:
|
if p["name"] == key:
|
||||||
p["value"] = value
|
p["value"] = value
|
||||||
@@ -365,11 +253,9 @@ class SolverParametersList(PamhyrModelList):
|
|||||||
|
|
||||||
self._lst.append(
|
self._lst.append(
|
||||||
Parameter(
|
Parameter(
|
||||||
id=-1,
|
|
||||||
name=key,
|
name=key,
|
||||||
value=value,
|
value=value,
|
||||||
status=self._status,
|
status=self._status
|
||||||
owner_scenario=self._status.scenario_id
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self._status.modified()
|
self._status.modified()
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
# Status.py -- Pamhyr model status class
|
|
||||||
# Copyright (C) 2023-2025 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 <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from enum import Enum, auto
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
|
||||||
|
|
||||||
|
|
||||||
class Status(Enum):
|
|
||||||
EDIT = auto()
|
|
||||||
READ_ONLY = auto()
|
|
||||||
|
|
||||||
|
|
||||||
class StudyStatus(object):
|
|
||||||
def __init__(self, scenario=None):
|
|
||||||
super(StudyStatus, self).__init__()
|
|
||||||
self._status = Status.EDIT
|
|
||||||
self._scenario = scenario
|
|
||||||
self._saved = True
|
|
||||||
|
|
||||||
@property
|
|
||||||
def scenario_id(self):
|
|
||||||
if self._scenario is None:
|
|
||||||
return -1
|
|
||||||
|
|
||||||
return self._scenario.id
|
|
||||||
|
|
||||||
def is_editable(self):
|
|
||||||
return self._status == Status.EDIT
|
|
||||||
|
|
||||||
def set_as_editable(self):
|
|
||||||
self._status = Status.EDIT
|
|
||||||
|
|
||||||
def is_read_only(self):
|
|
||||||
return self._status == Status.READ_ONLY
|
|
||||||
|
|
||||||
def set_as_read_only(self):
|
|
||||||
self._status = Status.READ_ONLY
|
|
||||||
|
|
||||||
@property
|
|
||||||
def scenario(self):
|
|
||||||
return self._scenario
|
|
||||||
|
|
||||||
@scenario.setter
|
|
||||||
def scenario(self, scenario):
|
|
||||||
logger.debug(f"Set scenario to {scenario}")
|
|
||||||
self._scenario = scenario
|
|
||||||
|
|
||||||
@property
|
|
||||||
def version(self):
|
|
||||||
if self._scenario is None:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
return self._scenario.revision
|
|
||||||
|
|
||||||
@version.setter
|
|
||||||
def version(self, version):
|
|
||||||
if self._scenario is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
self._scenario.revision = version
|
|
||||||
|
|
||||||
def str_display(self):
|
|
||||||
if self._scenario is None:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
return f"{self._scenario.name}"
|
|
||||||
|
|
||||||
def is_saved(self):
|
|
||||||
return self._saved
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
logger.debug("model status set as saved")
|
|
||||||
self._saved = True
|
|
||||||
|
|
||||||
def _set_as_not_saved(self):
|
|
||||||
self._saved = False
|
|
||||||
|
|
||||||
def _set_as_saved(self):
|
|
||||||
self._saved = True
|
|
||||||
|
|
||||||
def modified(self):
|
|
||||||
if self._saved:
|
|
||||||
self.version += 1
|
|
||||||
|
|
||||||
self._set_as_not_saved()
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# Stricklers.py -- Pamhyr
|
# Stricklers.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,10 +19,10 @@
|
|||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLSubModel
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Scenario import Scenario
|
|
||||||
|
|
||||||
|
|
||||||
class Stricklers(SQLSubModel):
|
class Stricklers(SQLSubModel):
|
||||||
|
_id_cnt = 0
|
||||||
_sub_classes = []
|
_sub_classes = []
|
||||||
|
|
||||||
def __init__(self, id: int = -1,
|
def __init__(self, id: int = -1,
|
||||||
@@ -30,11 +30,16 @@ class Stricklers(SQLSubModel):
|
|||||||
comment: str = "",
|
comment: str = "",
|
||||||
minor: float = 35.0,
|
minor: float = 35.0,
|
||||||
medium: float = 15.0,
|
medium: float = 15.0,
|
||||||
status=None, owner_scenario=-1):
|
status=None):
|
||||||
super(Stricklers, self).__init__(
|
super(Stricklers, self).__init__()
|
||||||
id=id, status=status,
|
self._status = status
|
||||||
owner_scenario=owner_scenario
|
|
||||||
)
|
if id == -1:
|
||||||
|
self.id = Stricklers._id_cnt
|
||||||
|
else:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
Stricklers._id_cnt = max(Stricklers._id_cnt + 1, id + 1)
|
||||||
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self._comment = comment
|
self._comment = comment
|
||||||
@@ -43,123 +48,67 @@ class Stricklers(SQLSubModel):
|
|||||||
self._medium = medium
|
self._medium = medium
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute, ext=""):
|
def _db_create(cls, execute):
|
||||||
execute(f"""
|
execute("""
|
||||||
CREATE TABLE stricklers{ext} (
|
CREATE TABLE stricklers(
|
||||||
{cls.create_db_add_pamhyr_id()},
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
name TEXT,
|
name TEXT,
|
||||||
comment TEXT,
|
comment TEXT,
|
||||||
minor REAL NOT NULL,
|
minor REAL NOT NULL,
|
||||||
medium REAL NOT NULL,
|
medium REAL NOT NULL
|
||||||
{Scenario.create_db_add_scenario()},
|
|
||||||
{Scenario.create_db_add_scenario_fk()},
|
|
||||||
PRIMARY KEY(pamhyr_id, scenario)
|
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
major, minor, release = version.strip().split(".")
|
return cls._update_submodel(execute, version)
|
||||||
if major == minor == "0":
|
|
||||||
cls._db_update_to_0_2_0(execute, data)
|
|
||||||
|
|
||||||
if major == "0" and minor == "1":
|
|
||||||
if int(release) < 2:
|
|
||||||
execute(
|
|
||||||
"ALTER TABLE stricklers " +
|
|
||||||
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
|
|
||||||
)
|
|
||||||
|
|
||||||
return cls._update_submodel(execute, version, data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0(cls, execute, data):
|
|
||||||
table = "stricklers"
|
|
||||||
|
|
||||||
cls.update_db_add_pamhyr_id(execute, table, data)
|
|
||||||
Scenario.update_db_add_scenario(execute, table)
|
|
||||||
|
|
||||||
cls._db_create(execute, ext="_tmp")
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"INSERT INTO {table}_tmp " +
|
|
||||||
"(pamhyr_id, name, comment, minor, medium, scenario) " +
|
|
||||||
"SELECT pamhyr_id, name, comment, minor, medium, scenario " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
execute(f"DROP TABLE {table}")
|
|
||||||
execute(f"ALTER TABLE {table}_tmp RENAME TO {table}")
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
stricklers = []
|
stricklers = []
|
||||||
status = data["status"]
|
status = data["status"]
|
||||||
scenario = data["scenario"]
|
|
||||||
loaded = data['loaded_pid']
|
|
||||||
|
|
||||||
if scenario is None:
|
|
||||||
return []
|
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT pamhyr_id, deleted, " +
|
"SELECT id, name, comment, minor, medium " +
|
||||||
"name, comment, minor, medium, scenario " +
|
"FROM stricklers"
|
||||||
"FROM stricklers " +
|
|
||||||
f"WHERE scenario = {scenario.id} " +
|
|
||||||
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if table is None:
|
if table is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
for row in table:
|
for row in table:
|
||||||
it = iter(row)
|
id = row[0]
|
||||||
|
name = row[1]
|
||||||
pid = next(it)
|
comment = row[2]
|
||||||
deleted = (next(it) == 1)
|
minor = row[3]
|
||||||
name = next(it)
|
medium = row[4]
|
||||||
comment = next(it)
|
|
||||||
minor = next(it)
|
|
||||||
medium = next(it)
|
|
||||||
owner_scenario = next(it)
|
|
||||||
|
|
||||||
new = cls(
|
new = cls(
|
||||||
id=pid, name=name, comment=comment,
|
id=id,
|
||||||
|
name=name,
|
||||||
|
comment=comment,
|
||||||
minor=minor, medium=medium,
|
minor=minor, medium=medium,
|
||||||
status=status, owner_scenario=owner_scenario
|
status=status
|
||||||
)
|
)
|
||||||
if deleted:
|
|
||||||
new.set_as_deleted()
|
|
||||||
|
|
||||||
stricklers.append(new)
|
stricklers.append(new)
|
||||||
loaded.add(pid)
|
|
||||||
|
|
||||||
data["scenario"] = scenario.parent
|
|
||||||
stricklers += cls._db_load(execute, data)
|
|
||||||
data["scenario"] = scenario
|
|
||||||
|
|
||||||
return stricklers
|
return stricklers
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
if not self.must_be_saved():
|
sql = (
|
||||||
return True
|
|
||||||
|
|
||||||
execute(
|
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"stricklers(pamhyr_id, deleted, " +
|
"stricklers(id, name, comment, minor, medium) " +
|
||||||
"name, comment, minor, medium, scenario) " +
|
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.pamhyr_id}, " +
|
f"{self.id}, " +
|
||||||
f"{self._db_format(self.is_deleted())}, " +
|
|
||||||
f"'{self._db_format(self.name)}', " +
|
f"'{self._db_format(self.name)}', " +
|
||||||
f"'{self._db_format(self.comment)}', " +
|
f"'{self._db_format(self.comment)}', " +
|
||||||
f"{float(self.minor)}, {float(self.medium)}, " +
|
f"{float(self.minor)}, {float(self.medium)}" +
|
||||||
f"{self._status.scenario_id}"
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
execute(sql)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -169,13 +118,12 @@ class Stricklers(SQLSubModel):
|
|||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
if self._name == "":
|
if self._name == "":
|
||||||
return f"K #{self.pamhyr_id}"
|
return f"K{self.id + 1}"
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@name.setter
|
@name.setter
|
||||||
def name(self, name):
|
def name(self, name):
|
||||||
self._name = name
|
self._name = name
|
||||||
self.modified()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def comment(self):
|
def comment(self):
|
||||||
@@ -184,7 +132,6 @@ class Stricklers(SQLSubModel):
|
|||||||
@comment.setter
|
@comment.setter
|
||||||
def comment(self, comment):
|
def comment(self, comment):
|
||||||
self._comment = comment
|
self._comment = comment
|
||||||
self.modified()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def minor(self):
|
def minor(self):
|
||||||
@@ -193,7 +140,6 @@ class Stricklers(SQLSubModel):
|
|||||||
@minor.setter
|
@minor.setter
|
||||||
def minor(self, minor):
|
def minor(self, minor):
|
||||||
self._minor = float(minor)
|
self._minor = float(minor)
|
||||||
self.modified()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def medium(self):
|
def medium(self):
|
||||||
@@ -202,4 +148,3 @@ class Stricklers(SQLSubModel):
|
|||||||
@medium.setter
|
@medium.setter
|
||||||
def medium(self, medium):
|
def medium(self, medium):
|
||||||
self._medium = float(medium)
|
self._medium = float(medium)
|
||||||
self.modified()
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# StricklersList.py -- Pamhyr
|
# StricklersList.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,7 +18,8 @@
|
|||||||
|
|
||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
|
|
||||||
from Model.Tools.PamhyrListExt import PamhyrModelList
|
from Model.Saved import SavedStatus
|
||||||
|
from Model.Tools.PamhyrList import PamhyrModelList
|
||||||
from Model.Stricklers.Stricklers import Stricklers
|
from Model.Stricklers.Stricklers import Stricklers
|
||||||
|
|
||||||
|
|
||||||
@@ -32,8 +33,8 @@ class StricklersList(PamhyrModelList):
|
|||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
return cls._update_submodel(execute, version, data)
|
return cls._update_submodel(execute, version)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
@@ -47,10 +48,7 @@ class StricklersList(PamhyrModelList):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute(
|
execute("DELETE FROM stricklers")
|
||||||
"DELETE FROM stricklers " +
|
|
||||||
f"WHERE scenario = {self._status.scenario_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
objs = self._lst
|
objs = self._lst
|
||||||
return self._save_submodel(execute, objs, data)
|
return self._save_submodel(execute, objs, data)
|
||||||
@@ -60,12 +58,8 @@ class StricklersList(PamhyrModelList):
|
|||||||
return self.lst
|
return self.lst
|
||||||
|
|
||||||
def new(self, index):
|
def new(self, index):
|
||||||
s = Stricklers(
|
s = Stricklers(status=self._status)
|
||||||
status=self._status,
|
|
||||||
owner_scenario=self._status.scenario_id
|
|
||||||
)
|
|
||||||
self.insert(index, s)
|
self.insert(index, s)
|
||||||
self.modified()
|
|
||||||
return s
|
return s
|
||||||
|
|
||||||
@timer
|
@timer
|
||||||
@@ -80,4 +74,5 @@ class StricklersList(PamhyrModelList):
|
|||||||
reverse=reverse,
|
reverse=reverse,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.modified()
|
if self._status is not None:
|
||||||
|
self._status.modified()
|
||||||
+32
-257
@@ -1,5 +1,5 @@
|
|||||||
# Study.py -- Pamhyr Study class
|
# Study.py -- Pamhyr Study class
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -22,12 +22,10 @@ import logging
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from tools import timer, timestamp
|
from tools import timer, timestamp
|
||||||
from functools import reduce
|
|
||||||
|
|
||||||
from Model.Tools.PamhyrDB import SQLModel
|
from Model.Tools.PamhyrDB import SQLModel
|
||||||
from Model.Scenarios import Scenarios
|
from Model.Saved import SavedStatus
|
||||||
from Model.Scenario import Scenario
|
from Model.Serializable import Serializable
|
||||||
from Model.Status import StudyStatus
|
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
from Model.River import River
|
from Model.River import River
|
||||||
|
|
||||||
@@ -37,18 +35,13 @@ logger = logging.getLogger()
|
|||||||
|
|
||||||
|
|
||||||
class Study(SQLModel):
|
class Study(SQLModel):
|
||||||
_version = "0.2.3"
|
|
||||||
|
|
||||||
_sub_classes = [
|
_sub_classes = [
|
||||||
Scenario,
|
|
||||||
River,
|
River,
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, filename=None, init_new=True, copy=False):
|
def __init__(self, filename=None, init_new=True):
|
||||||
if copy:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Metadata
|
# Metadata
|
||||||
|
self._version = "0.0.11"
|
||||||
self.creation_date = datetime.now()
|
self.creation_date = datetime.now()
|
||||||
self.last_modification_date = datetime.now()
|
self.last_modification_date = datetime.now()
|
||||||
self.last_save_date = datetime.now()
|
self.last_save_date = datetime.now()
|
||||||
@@ -56,7 +49,7 @@ class Study(SQLModel):
|
|||||||
self._filename = filename
|
self._filename = filename
|
||||||
super(Study, self).__init__(filename=filename)
|
super(Study, self).__init__(filename=filename)
|
||||||
|
|
||||||
self.status = StudyStatus()
|
self.status = SavedStatus()
|
||||||
|
|
||||||
# Study general information
|
# Study general information
|
||||||
self._name = ""
|
self._name = ""
|
||||||
@@ -67,22 +60,12 @@ class Study(SQLModel):
|
|||||||
|
|
||||||
if init_new:
|
if init_new:
|
||||||
# Study data
|
# Study data
|
||||||
s0 = Scenario(
|
|
||||||
id=0, name='default',
|
|
||||||
description='Default scenario',
|
|
||||||
)
|
|
||||||
|
|
||||||
self.scenarios = Scenarios(status=self.status)
|
|
||||||
self.scenarios[0] = s0
|
|
||||||
self.status.scenario = s0
|
|
||||||
self._river = River(status=self.status)
|
self._river = River(status=self.status)
|
||||||
else:
|
else:
|
||||||
self._init_db_file(filename, is_new=False)
|
self._init_db_file(filename, is_new=False)
|
||||||
|
|
||||||
self._old_save_id = 0
|
self._old_save_id = 0
|
||||||
|
|
||||||
self._river_scenario_cache = {}
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def checkers(cls):
|
def checkers(cls):
|
||||||
lst = [
|
lst = [
|
||||||
@@ -101,12 +84,6 @@ class Study(SQLModel):
|
|||||||
def river(self):
|
def river(self):
|
||||||
return self._river
|
return self._river
|
||||||
|
|
||||||
def is_editable(self):
|
|
||||||
return self.status.is_editable()
|
|
||||||
|
|
||||||
def is_read_only(self):
|
|
||||||
return self.status.is_read_only()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_saved(self):
|
def is_saved(self):
|
||||||
return self.status.is_saved()
|
return self.status.is_saved()
|
||||||
@@ -130,11 +107,15 @@ class Study(SQLModel):
|
|||||||
|
|
||||||
is_new = False
|
is_new = False
|
||||||
fname = fname + "." + str(self._old_save_id)
|
fname = fname + "." + str(self._old_save_id)
|
||||||
if os.path.exists(self.filename):
|
if os.path.exists(self.filename) and ".backup" not in self.filename:
|
||||||
filename = os.path.join(fdir, "_PAMHYR_", "__old__", fname)
|
filename = os.path.join(fdir, "_PAMHYR_", "__old__", fname)
|
||||||
logger.debug(f"Backup previous version copy: {filename}")
|
logger.debug(f"Backup previous version copy: {filename}")
|
||||||
shutil.copy(self.filename, filename)
|
shutil.copy(self.filename, filename)
|
||||||
self._old_save_id += 1
|
self._old_save_id += 1
|
||||||
|
is_new = True
|
||||||
|
|
||||||
|
if ".backup" in self.filename:
|
||||||
|
is_new = True
|
||||||
|
|
||||||
if not os.path.exists(self.filename):
|
if not os.path.exists(self.filename):
|
||||||
is_new = True
|
is_new = True
|
||||||
@@ -231,10 +212,11 @@ class Study(SQLModel):
|
|||||||
def _create(self):
|
def _create(self):
|
||||||
# Info (metadata)
|
# Info (metadata)
|
||||||
self.execute(
|
self.execute(
|
||||||
"CREATE TABLE info(key TEXT NOT NULL UNIQUE, value TEXT NOT NULL)"
|
"INSERT INTO info VALUES ('study_release', '0')"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.execute(
|
self.execute(
|
||||||
"INSERT INTO info VALUES ('current_scenario', '0')"
|
"CREATE TABLE info(key TEXT NOT NULL UNIQUE, value TEXT NOT NULL)"
|
||||||
)
|
)
|
||||||
self.execute(
|
self.execute(
|
||||||
"INSERT INTO info VALUES ('version', " +
|
"INSERT INTO info VALUES ('version', " +
|
||||||
@@ -270,7 +252,7 @@ class Study(SQLModel):
|
|||||||
if version[0] == self._version:
|
if version[0] == self._version:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
logger.info(f"Update database from {version[0]} to {self._version}")
|
logger.debug("Update database")
|
||||||
|
|
||||||
major, minor, release = version[0].split('.')
|
major, minor, release = version[0].split('.')
|
||||||
if major == "0" and minor == "0" and int(release) < 10:
|
if major == "0" and minor == "0" and int(release) < 10:
|
||||||
@@ -278,53 +260,19 @@ class Study(SQLModel):
|
|||||||
"INSERT INTO info VALUES ('study_release', '0')"
|
"INSERT INTO info VALUES ('study_release', '0')"
|
||||||
)
|
)
|
||||||
|
|
||||||
if major == "0" and int(minor) < 2:
|
if self._update_submodel(version[0]):
|
||||||
self._add_into_info_if_not_exists('current_scenario', '0')
|
|
||||||
|
|
||||||
if major == "0" and int(minor) < 2:
|
|
||||||
# Need to temporary disable the sqlite foreign keys
|
|
||||||
# checking to update db dans change the table id fk to
|
|
||||||
# table pamhyr_id fk
|
|
||||||
self.execute(
|
|
||||||
"PRAGMA foreign_keys = OFF;"
|
|
||||||
)
|
|
||||||
|
|
||||||
ok = self._update_submodel(version[0], data={})
|
|
||||||
|
|
||||||
if major == "0" and int(minor) < 2:
|
|
||||||
# Reactivate foreign keys checking
|
|
||||||
self.execute(
|
|
||||||
"PRAGMA foreign_keys = ON;"
|
|
||||||
)
|
|
||||||
|
|
||||||
if ok:
|
|
||||||
self.execute(
|
self.execute(
|
||||||
f"UPDATE info SET value='{self._version}' WHERE key='version'"
|
f"UPDATE info SET value='{self._version}' WHERE key='version'"
|
||||||
)
|
)
|
||||||
logger.info("Update database done.")
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
logger.info("Update failed!")
|
logger.info("TODO: update failed")
|
||||||
raise NotImplementedMethodeError(self, self._update)
|
raise NotImplementedMethodeError(self, self._update)
|
||||||
|
|
||||||
def _add_into_info_if_not_exists(self, key, value):
|
|
||||||
rows = self.execute(f"SELECT value FROM info WHERE key='{key}'")
|
|
||||||
|
|
||||||
if rows is None or len(rows) == 0:
|
|
||||||
self.execute(
|
|
||||||
f"INSERT INTO info VALUES ('{key}', '{value}')"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _load(cls, filename):
|
def _load(cls, filename):
|
||||||
new = cls(init_new=False, filename=filename)
|
new = cls(init_new=False, filename=filename)
|
||||||
|
|
||||||
def sql_exec(sql):
|
|
||||||
return new.execute(
|
|
||||||
sql, fetch_one=False, commit=True
|
|
||||||
)
|
|
||||||
|
|
||||||
version = new.execute(
|
version = new.execute(
|
||||||
"SELECT value FROM info WHERE key='study_release'"
|
"SELECT value FROM info WHERE key='study_release'"
|
||||||
)
|
)
|
||||||
@@ -342,65 +290,31 @@ class Study(SQLModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
new.creation_date = datetime.fromtimestamp(
|
new.creation_date = datetime.fromtimestamp(
|
||||||
float(
|
float(new.execute(
|
||||||
new.execute(
|
"SELECT value FROM info WHERE key='creation_date'")[0])
|
||||||
"SELECT value FROM info WHERE key='creation_date'"
|
|
||||||
)[0]
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
new.last_save_date = datetime.fromtimestamp(
|
new.last_save_date = datetime.fromtimestamp(
|
||||||
float(
|
float(new.execute(
|
||||||
new.execute(
|
"SELECT value FROM info WHERE key='last_save_date'")[0])
|
||||||
"SELECT value FROM info WHERE key='last_save_date'"
|
|
||||||
)[0]
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
data = {
|
|
||||||
"study": new,
|
|
||||||
"status": new.status
|
|
||||||
}
|
|
||||||
|
|
||||||
# Scenarios
|
|
||||||
new.scenarios = Scenarios._db_load(
|
|
||||||
sql_exec,
|
|
||||||
data=data
|
|
||||||
)
|
|
||||||
|
|
||||||
scenario_id = new.execute(
|
|
||||||
"SELECT value FROM info WHERE key='current_scenario'"
|
|
||||||
)
|
|
||||||
logger.debug(f"Load with scenario {scenario_id[0]}")
|
|
||||||
|
|
||||||
scenario = new.scenarios[int(scenario_id[0])]
|
|
||||||
new.status.scenario = scenario
|
|
||||||
|
|
||||||
if reduce(
|
|
||||||
lambda a, s: a or (s.parent is scenario),
|
|
||||||
new.scenarios.lst,
|
|
||||||
False
|
|
||||||
):
|
|
||||||
new.status.set_as_read_only()
|
|
||||||
|
|
||||||
data["scenario"] = scenario
|
|
||||||
data["loaded_pid"] = set()
|
|
||||||
|
|
||||||
# Load river data
|
# Load river data
|
||||||
new._river = River._db_load(
|
new._river = River._db_load(
|
||||||
sql_exec, data=data
|
lambda sql: new.execute(
|
||||||
|
sql,
|
||||||
|
fetch_one=False,
|
||||||
|
commit=True
|
||||||
|
),
|
||||||
|
data={"status": new.status}
|
||||||
)
|
)
|
||||||
|
|
||||||
new._river._db_load_results(sql_exec, data=data)
|
|
||||||
|
|
||||||
new.status._set_as_saved()
|
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _save(self, progress=None):
|
def _save(self, progress=None):
|
||||||
progress = progress if progress is not None else lambda: None
|
progress = progress if progress is not None else lambda: None
|
||||||
|
|
||||||
self.execute(
|
self.execute(
|
||||||
"INSERT OR REPLACE INTO info VALUES ('study_release', " +
|
"INSERT INTO info VALUES ('study_release', " +
|
||||||
f"'{self.status.version}')"
|
f"'{self.status.version}')"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -438,28 +352,16 @@ class Study(SQLModel):
|
|||||||
)
|
)
|
||||||
progress()
|
progress()
|
||||||
|
|
||||||
self.execute(
|
self._save_submodel([self._river], data=progress)
|
||||||
f"UPDATE info SET " +
|
|
||||||
f"value='{self.status.scenario_id}' " +
|
|
||||||
"WHERE key='current_scenario'"
|
|
||||||
)
|
|
||||||
progress()
|
|
||||||
|
|
||||||
self._save_submodel(
|
|
||||||
[self.scenarios, self._river],
|
|
||||||
data=progress
|
|
||||||
)
|
|
||||||
self.commit()
|
self.commit()
|
||||||
|
|
||||||
def sql_save_request_count(self, *args, **kargs):
|
def sql_save_request_count(self):
|
||||||
return self._count()
|
return self._count()
|
||||||
|
|
||||||
def _count(self):
|
def _count(self):
|
||||||
cnt = self._save_count(
|
cnt = self._save_count([self._river])
|
||||||
[self.scenarios, self._river]
|
|
||||||
)
|
|
||||||
logger.debug(cnt)
|
logger.debug(cnt)
|
||||||
return cnt + 7
|
return cnt + 6
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
"""Close db connection
|
"""Close db connection
|
||||||
@@ -468,130 +370,3 @@ class Study(SQLModel):
|
|||||||
Nothing.
|
Nothing.
|
||||||
"""
|
"""
|
||||||
self._close()
|
self._close()
|
||||||
|
|
||||||
def new_scenario_from_current(self, switch=True):
|
|
||||||
new = self.scenarios.new(self.status.scenario)
|
|
||||||
|
|
||||||
if switch:
|
|
||||||
self.status.scenario = new
|
|
||||||
|
|
||||||
self.status.set_as_editable()
|
|
||||||
return new
|
|
||||||
|
|
||||||
def reload_from_scenario(self, scenario):
|
|
||||||
river = self.load_scenario(scenario)
|
|
||||||
self._river = river
|
|
||||||
|
|
||||||
self.status.scenario = scenario
|
|
||||||
|
|
||||||
if reduce(
|
|
||||||
lambda a, s: a or (s.parent is scenario),
|
|
||||||
self.scenarios.lst,
|
|
||||||
False
|
|
||||||
):
|
|
||||||
self.status.set_as_read_only()
|
|
||||||
else:
|
|
||||||
self.status.set_as_editable()
|
|
||||||
|
|
||||||
def load_scenario(self, scenario):
|
|
||||||
if scenario in self._river_scenario_cache:
|
|
||||||
return self._river_scenario_cache[scenario]
|
|
||||||
|
|
||||||
def sql_exec(sql):
|
|
||||||
return self.execute(
|
|
||||||
sql, fetch_one=False, commit=True
|
|
||||||
)
|
|
||||||
|
|
||||||
old_scenario = self.status.scenario
|
|
||||||
self.status.scenario = scenario
|
|
||||||
|
|
||||||
data = {
|
|
||||||
"status": self.status,
|
|
||||||
"loaded_pid": set(),
|
|
||||||
"scenario": scenario
|
|
||||||
}
|
|
||||||
|
|
||||||
# Load river data
|
|
||||||
river = River._db_load(
|
|
||||||
sql_exec, data=data
|
|
||||||
)
|
|
||||||
data["study"] = self
|
|
||||||
river._db_load_results(sql_exec, data=data)
|
|
||||||
|
|
||||||
self._river_scenario_cache[scenario] = river
|
|
||||||
self.status.scenario = old_scenario
|
|
||||||
|
|
||||||
return river
|
|
||||||
|
|
||||||
def copy_from_scenario(self, scenario):
|
|
||||||
new = self._copy()
|
|
||||||
|
|
||||||
new._river = self.load_scenario(scenario)
|
|
||||||
|
|
||||||
def set_status(obj):
|
|
||||||
obj._status = new.status
|
|
||||||
|
|
||||||
new.river._data_traversal(
|
|
||||||
modifier=lambda obj, data: set_status(obj),
|
|
||||||
)
|
|
||||||
|
|
||||||
return new
|
|
||||||
|
|
||||||
def _copy(self):
|
|
||||||
"""
|
|
||||||
This method make a copy of current study. This copy is like an
|
|
||||||
empty shell, it's not fully functional. Study object use
|
|
||||||
SQLite connection to file, this copy as no valid connection.
|
|
||||||
|
|
||||||
(!) Please use this copy as read only object!
|
|
||||||
"""
|
|
||||||
new = Study(copy=True)
|
|
||||||
|
|
||||||
new._filename = ""
|
|
||||||
new.creation_date = self.creation_date
|
|
||||||
new.last_modification_date = self.last_modification_date
|
|
||||||
new.last_save_date = self.last_save_date
|
|
||||||
|
|
||||||
new._name = self._name
|
|
||||||
new.description = self.description
|
|
||||||
|
|
||||||
new._time_system = self._time_system
|
|
||||||
new._date = self._date
|
|
||||||
|
|
||||||
new.status = StudyStatus()
|
|
||||||
new.scenarios = self.scenarios
|
|
||||||
new.status.scenario = self.status.scenario
|
|
||||||
|
|
||||||
new._river = self._river
|
|
||||||
new._river_scenario_cache = self._river_scenario_cache
|
|
||||||
|
|
||||||
return new
|
|
||||||
|
|
||||||
def duplicate_current_scenario(self):
|
|
||||||
source = self.status.scenario_id
|
|
||||||
new = self.scenarios.new(
|
|
||||||
self.status.scenario.parent
|
|
||||||
)
|
|
||||||
|
|
||||||
new.set_pos(self.status.scenario.x + 100,
|
|
||||||
self.status.scenario.y + 100)
|
|
||||||
|
|
||||||
new.name = self.status.scenario.name + " (copy)"
|
|
||||||
|
|
||||||
self.status.scenario = new
|
|
||||||
self.river._data_traversal(
|
|
||||||
predicate=lambda obj, data: obj._owner_scenario == source,
|
|
||||||
modifier=lambda obj, data: obj.set_owner_scenario(),
|
|
||||||
data={}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.status.set_as_editable()
|
|
||||||
return new
|
|
||||||
|
|
||||||
@property
|
|
||||||
def results(self):
|
|
||||||
return self._river.results
|
|
||||||
|
|
||||||
@results.setter
|
|
||||||
def results(self, results):
|
|
||||||
self._river.results = results
|
|
||||||
+18
-224
@@ -1,5 +1,5 @@
|
|||||||
# PamhyrDB.py -- Pamhyr abstract model database classes
|
# PamhyrDB.py -- Pamhyr abstract model database classes
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -26,8 +26,6 @@ from functools import reduce
|
|||||||
from SQL import SQL
|
from SQL import SQL
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
from Model.Tools.PamhyrID import PamhyrID
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
# Top level model class
|
# Top level model class
|
||||||
@@ -44,7 +42,7 @@ class SQLModel(SQL):
|
|||||||
if exists and is_new:
|
if exists and is_new:
|
||||||
os.remove(db)
|
os.remove(db)
|
||||||
|
|
||||||
self._db = sqlite3.connect(db, check_same_thread=False)
|
self._db = sqlite3.connect(db)
|
||||||
self._cur = self._db.cursor()
|
self._cur = self._db.cursor()
|
||||||
|
|
||||||
if is_new:
|
if is_new:
|
||||||
@@ -73,9 +71,10 @@ class SQLModel(SQL):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def _create(self):
|
def _create(self):
|
||||||
|
|
||||||
raise NotImplementedMethodeError(self, self._create)
|
raise NotImplementedMethodeError(self, self._create)
|
||||||
|
|
||||||
def _update_submodel(self, version, data=None):
|
def _update_submodel(self, version):
|
||||||
def fn(sql): return self.execute(
|
def fn(sql): return self.execute(
|
||||||
sql,
|
sql,
|
||||||
fetch_one=False,
|
fetch_one=False,
|
||||||
@@ -84,7 +83,7 @@ class SQLModel(SQL):
|
|||||||
|
|
||||||
ok = True
|
ok = True
|
||||||
for cls in self._sub_classes:
|
for cls in self._sub_classes:
|
||||||
ok &= cls._db_update(fn, version, data=data)
|
ok &= cls._db_update(fn, version)
|
||||||
|
|
||||||
self.commit()
|
self.commit()
|
||||||
return ok
|
return ok
|
||||||
@@ -95,22 +94,18 @@ class SQLModel(SQL):
|
|||||||
def _save_submodel(self, objs, data=None):
|
def _save_submodel(self, objs, data=None):
|
||||||
progress = data if data is not None else lambda: None
|
progress = data if data is not None else lambda: None
|
||||||
|
|
||||||
def fn(sql, *args, **kargs):
|
def fn(sql):
|
||||||
if "fetch_one" not in kargs:
|
|
||||||
kargs["fetch_one"] = False
|
|
||||||
|
|
||||||
if "commit" not in kargs:
|
|
||||||
kargs["commit"] = False
|
|
||||||
|
|
||||||
res = self.execute(
|
res = self.execute(
|
||||||
sql, *args, **kargs
|
sql,
|
||||||
|
fetch_one=False,
|
||||||
|
commit=False
|
||||||
)
|
)
|
||||||
progress()
|
progress()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
ok = True
|
ok = True
|
||||||
for obj in objs:
|
for obj in objs:
|
||||||
ok &= obj._db_save(fn, data={})
|
ok &= obj._db_save(fn)
|
||||||
|
|
||||||
self.commit()
|
self.commit()
|
||||||
return ok
|
return ok
|
||||||
@@ -121,7 +116,7 @@ class SQLModel(SQL):
|
|||||||
def _count(self):
|
def _count(self):
|
||||||
raise NotImplementedMethodeError(self, self._count)
|
raise NotImplementedMethodeError(self, self._count)
|
||||||
|
|
||||||
def _save_count(self, objs, *args, data={}, **kargs):
|
def _save_count(self, objs, data=None):
|
||||||
counter = {
|
counter = {
|
||||||
"insert": 0,
|
"insert": 0,
|
||||||
"update": 0,
|
"update": 0,
|
||||||
@@ -129,7 +124,7 @@ class SQLModel(SQL):
|
|||||||
"other": 0,
|
"other": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
def fn(sql, *args, **kargs):
|
def fn(sql):
|
||||||
if "insert" in sql.lower():
|
if "insert" in sql.lower():
|
||||||
counter["insert"] = counter["insert"] + 1
|
counter["insert"] = counter["insert"] + 1
|
||||||
elif "update" in sql.lower():
|
elif "update" in sql.lower():
|
||||||
@@ -142,7 +137,7 @@ class SQLModel(SQL):
|
|||||||
|
|
||||||
ok = True
|
ok = True
|
||||||
for obj in objs:
|
for obj in objs:
|
||||||
ok &= obj._db_save(fn, data=data)
|
ok &= obj._db_save(fn)
|
||||||
|
|
||||||
logger.debug(counter)
|
logger.debug(counter)
|
||||||
|
|
||||||
@@ -159,103 +154,15 @@ class SQLModel(SQL):
|
|||||||
# Sub model class
|
# Sub model class
|
||||||
|
|
||||||
|
|
||||||
class SQLSubModel(PamhyrID):
|
class SQLSubModel(object):
|
||||||
_sub_classes = []
|
_sub_classes = []
|
||||||
|
|
||||||
def __init__(self, id: int = -1, status=None,
|
|
||||||
owner_scenario=-1, **kwargs):
|
|
||||||
super(SQLSubModel, self).__init__(id=id, **kwargs)
|
|
||||||
|
|
||||||
self._status = status
|
|
||||||
|
|
||||||
# Deletion status of the model object. This status MUST be set
|
|
||||||
# to True if an object that exists in a parent scenario is
|
|
||||||
# deleted in current scenario.
|
|
||||||
self._deleted = False
|
|
||||||
|
|
||||||
# The 'owner_scenario' is the id of the scenario to which the
|
|
||||||
# object belongs. This id CAN be different to current
|
|
||||||
# scenario, but in case of object modification, this id MUST
|
|
||||||
# be set to current scenario id. (This action is made in
|
|
||||||
# 'modified' method.)
|
|
||||||
self._owner_scenario = 0
|
|
||||||
if owner_scenario == -1:
|
|
||||||
if status is not None:
|
|
||||||
self._owner_scenario = self._status.scenario_id
|
|
||||||
else:
|
|
||||||
self._owner_scenario = owner_scenario
|
|
||||||
|
|
||||||
def must_be_saved(self):
|
|
||||||
"""Return True if this object MUST be save in the save file.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
True if this object MUST be save, otherelse False
|
|
||||||
"""
|
|
||||||
return self._owner_scenario == self._status.scenario_id
|
|
||||||
|
|
||||||
def modified(self):
|
|
||||||
"""Set study status to modified and update the object
|
|
||||||
owner_scenario to current scenario
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Nothing
|
|
||||||
"""
|
|
||||||
if self._status is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
self._owner_scenario = self._status.scenario_id
|
|
||||||
self._status.modified()
|
|
||||||
|
|
||||||
def set_owner_scenario(self):
|
|
||||||
"""Set study status the object owner_scenario to current
|
|
||||||
scenario if predicate(scenario_id) is true
|
|
||||||
|
|
||||||
Args:
|
|
||||||
predicate: Function predicate on owner_scenario id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Nothing
|
|
||||||
"""
|
|
||||||
if self._status is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
self._owner_scenario = self._status.scenario_id
|
|
||||||
self._status.modified()
|
|
||||||
|
|
||||||
def is_deleted(self):
|
|
||||||
"""This object is deleted?
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
True if this object is deleted, otherelse False
|
|
||||||
"""
|
|
||||||
return self._deleted
|
|
||||||
|
|
||||||
def set_as_deleted(self):
|
|
||||||
"""Set object deleted flag to True.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Nothing
|
|
||||||
"""
|
|
||||||
self._deleted = True
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def set_as_not_deleted(self):
|
|
||||||
"""Set object deleted flag to False.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Nothing
|
|
||||||
"""
|
|
||||||
self._deleted = False
|
|
||||||
self.modified()
|
|
||||||
|
|
||||||
def _db_format(self, value):
|
def _db_format(self, value):
|
||||||
# Replace ''' by ''' to preserve SQL injection
|
# Replace ''' by ''' to preserve SQL injection
|
||||||
if type(value) is str:
|
if type(value) is str:
|
||||||
value = value.replace("'", "'")
|
value = value.replace("'", "'")
|
||||||
elif type(value) is bool:
|
elif type(value) is bool:
|
||||||
value = 'TRUE' if value else 'FALSE'
|
value = 'TRUE' if value else 'FALSE'
|
||||||
elif value is None:
|
|
||||||
value = "NULL"
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -276,16 +183,12 @@ class SQLSubModel(PamhyrID):
|
|||||||
raise NotImplementedMethodeError(cls, cls._db_create)
|
raise NotImplementedMethodeError(cls, cls._db_create)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _update_submodel(cls, execute, version, data=None):
|
def _update_submodel(cls, execute, version):
|
||||||
ok = True
|
|
||||||
|
|
||||||
for sc in cls._sub_classes:
|
for sc in cls._sub_classes:
|
||||||
ok &= sc._db_update(execute, version, data)
|
sc._db_update(execute, version)
|
||||||
|
|
||||||
return ok
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
"""Update data base scheme
|
"""Update data base scheme
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -297,93 +200,6 @@ class SQLSubModel(PamhyrID):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedMethodeError(cls, cls._db_update)
|
raise NotImplementedMethodeError(cls, cls._db_update)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_node_pid(cls, execute, table, nodes):
|
|
||||||
els = execute(
|
|
||||||
f"SELECT pamhyr_id, node FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in els:
|
|
||||||
it = iter(row)
|
|
||||||
pid = next(it)
|
|
||||||
node_id = next(it)
|
|
||||||
|
|
||||||
if node_id == -1:
|
|
||||||
continue
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE {table} " +
|
|
||||||
f"SET node = {nodes[node_id]} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_2_0_set_reach_pid(cls, execute, table, reachs):
|
|
||||||
els = execute(
|
|
||||||
f"SELECT pamhyr_id, reach FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if els is None:
|
|
||||||
return True
|
|
||||||
|
|
||||||
for row in els:
|
|
||||||
it = iter(row)
|
|
||||||
pid = next(it)
|
|
||||||
reach_id = next(it)
|
|
||||||
|
|
||||||
if reach_id == -1:
|
|
||||||
continue
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE {table} " +
|
|
||||||
f"SET reach = {reachs[reach_id]} " +
|
|
||||||
f"WHERE pamhyr_id = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _db_update_to_0_1_1_assoc_section_from_rk(
|
|
||||||
cls, execute, table,
|
|
||||||
reach_column="reach",
|
|
||||||
rk_column="rk",
|
|
||||||
section_column="section",
|
|
||||||
origin_version="0.1.0"):
|
|
||||||
kid = "pamhyr_id"
|
|
||||||
if origin_version == "0.0.*":
|
|
||||||
kid = "id"
|
|
||||||
|
|
||||||
els = execute(
|
|
||||||
"SELECT " +
|
|
||||||
f"{kid}, {reach_column}, {rk_column} " +
|
|
||||||
f"FROM {table}"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in els:
|
|
||||||
it = iter(row)
|
|
||||||
|
|
||||||
pid = next(it)
|
|
||||||
reach_id = next(it)
|
|
||||||
rk = next(it)
|
|
||||||
|
|
||||||
if reach_id == -1 or reach_id is None:
|
|
||||||
continue
|
|
||||||
|
|
||||||
section_id = -1
|
|
||||||
section = execute(
|
|
||||||
f"SELECT pamhyr_id FROM geometry_profileXYZ " +
|
|
||||||
f"WHERE reach == {reach_id} AND rk == {rk}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(section) != 0:
|
|
||||||
section_id = section[0][0]
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE {table} " +
|
|
||||||
f"SET {section_column} = {section_id} " +
|
|
||||||
f"WHERE {kid} = {pid}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
"""Load instance of this class from SQL data base
|
"""Load instance of this class from SQL data base
|
||||||
@@ -398,12 +214,8 @@ class SQLSubModel(PamhyrID):
|
|||||||
raise NotImplementedMethodeError(cls, cls._db_load)
|
raise NotImplementedMethodeError(cls, cls._db_load)
|
||||||
|
|
||||||
def _save_submodel(self, execute, objs, data=None):
|
def _save_submodel(self, execute, objs, data=None):
|
||||||
ok = True
|
|
||||||
|
|
||||||
for o in objs:
|
for o in objs:
|
||||||
ok &= o._db_save(execute, data=data)
|
o._db_save(execute, data=data)
|
||||||
|
|
||||||
return ok
|
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
"""Save class data to data base
|
"""Save class data to data base
|
||||||
@@ -417,21 +229,3 @@ class SQLSubModel(PamhyrID):
|
|||||||
save
|
save
|
||||||
"""
|
"""
|
||||||
raise NotImplementedMethodeError(self, self._db_save)
|
raise NotImplementedMethodeError(self, self._db_save)
|
||||||
|
|
||||||
def _data_traversal(self,
|
|
||||||
predicate=lambda obj, data: True,
|
|
||||||
modifier=lambda obj, data: None,
|
|
||||||
data={}):
|
|
||||||
"""Traversal data and execute modifier fonction if predicate
|
|
||||||
true
|
|
||||||
|
|
||||||
Args:
|
|
||||||
predicate: Function predicate, take current obj and data as input
|
|
||||||
modifier: Function modifier, take current obj and data as input
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
"""
|
|
||||||
if predicate(self, data):
|
|
||||||
modifier(self, data)
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# PamhyrDict.py -- Pamhyr abstract dict for model classes
|
# PamhyrDict.py -- Pamhyr abstract dict for model classes
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -32,9 +32,9 @@ class PamhyrModelDict(SQLSubModel):
|
|||||||
_sub_classes = []
|
_sub_classes = []
|
||||||
|
|
||||||
def __init__(self, status=None):
|
def __init__(self, status=None):
|
||||||
super(PamhyrModelDict, self).__init__(
|
super(PamhyrModelDict, self).__init__()
|
||||||
status=status
|
|
||||||
)
|
self._status = status
|
||||||
|
|
||||||
self._dict = {}
|
self._dict = {}
|
||||||
|
|
||||||
@@ -43,8 +43,8 @@ class PamhyrModelDict(SQLSubModel):
|
|||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
return cls._update_submodel(execute, version, data)
|
return cls._update_submodel(execute, version)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
@@ -59,12 +59,6 @@ class PamhyrModelDict(SQLSubModel):
|
|||||||
def __contains__(self, key):
|
def __contains__(self, key):
|
||||||
return key in self._dict
|
return key in self._dict
|
||||||
|
|
||||||
def __getitem__(self, key):
|
|
||||||
return self.get(key)
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
self.set(key, value)
|
|
||||||
|
|
||||||
def set(self, key, new):
|
def set(self, key, new):
|
||||||
self._dict[key] = new
|
self._dict[key] = new
|
||||||
self._status.modified()
|
self._status.modified()
|
||||||
@@ -82,24 +76,5 @@ class PamhyrModelDict(SQLSubModel):
|
|||||||
self.set(key, new)
|
self.set(key, new)
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _data_traversal(self,
|
|
||||||
predicate=lambda obj, data: True,
|
|
||||||
modifier=lambda obj, data: None,
|
|
||||||
data={}):
|
|
||||||
"""Traversal data and execute modifier fonction if predicate
|
|
||||||
true
|
|
||||||
|
|
||||||
Args:
|
|
||||||
predicate: Function predicate, take current obj and data as input
|
|
||||||
modifier: Function modifier, take current obj and data as input
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
"""
|
|
||||||
for key in self._dict:
|
|
||||||
self._dict[key]\
|
|
||||||
._data_traversal(predicate, modifier, data)
|
|
||||||
|
|
||||||
def new(self, key):
|
def new(self, key):
|
||||||
raise NotImplementedMethodeError(self, self.new)
|
raise NotImplementedMethodeError(self, self.new)
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
# PamhyrID.py -- Pamhyr
|
|
||||||
# Copyright (C) 2024-2025 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 <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from tools import trace, timer
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
|
||||||
|
|
||||||
|
|
||||||
class PamhyrID(object):
|
|
||||||
_pamhyr_id_cnt = 0
|
|
||||||
|
|
||||||
def __init__(self, id: int = -1, **kwargs):
|
|
||||||
super(PamhyrID, self).__init__(**kwargs)
|
|
||||||
|
|
||||||
self._pamhyr_id = self.get_new_pamhyr_id(id)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_new_pamhyr_id(cls, id):
|
|
||||||
pid = int(id)
|
|
||||||
|
|
||||||
if pid < 0:
|
|
||||||
pid = PamhyrID._pamhyr_id_cnt
|
|
||||||
|
|
||||||
PamhyrID._pamhyr_id_cnt = max(
|
|
||||||
id + 1,
|
|
||||||
PamhyrID._pamhyr_id_cnt + 1
|
|
||||||
)
|
|
||||||
|
|
||||||
return pid
|
|
||||||
|
|
||||||
@property
|
|
||||||
def id(self):
|
|
||||||
return self._pamhyr_id
|
|
||||||
|
|
||||||
@property
|
|
||||||
def pamhyr_id(self):
|
|
||||||
return self._pamhyr_id
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def update_db_add_pamhyr_id(cls, execute, table,
|
|
||||||
data=None):
|
|
||||||
execute(
|
|
||||||
f"ALTER TABLE {table} " +
|
|
||||||
f"ADD COLUMN pamhyr_id INTEGER NOT NULL DEFAULT -1"
|
|
||||||
)
|
|
||||||
|
|
||||||
id2pid = cls.update_db_add_pamhyr_id_init_id2pid(table, data)
|
|
||||||
rows = execute(f"SELECT id FROM {table}")
|
|
||||||
|
|
||||||
if rows is None:
|
|
||||||
return True
|
|
||||||
|
|
||||||
for row in rows:
|
|
||||||
id = row[0]
|
|
||||||
pid = cls.get_new_pamhyr_id(-1)
|
|
||||||
|
|
||||||
id2pid[table][id] = pid
|
|
||||||
|
|
||||||
execute(
|
|
||||||
f"UPDATE {table} " +
|
|
||||||
f"SET pamhyr_id = {pid} " +
|
|
||||||
f"WHERE id = {id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def update_db_add_pamhyr_id_init_id2pid(cls, table, data):
|
|
||||||
id2pid = {}
|
|
||||||
if "id2pid" in data:
|
|
||||||
id2pid = data["id2pid"]
|
|
||||||
else:
|
|
||||||
data["id2pid"] = id2pid
|
|
||||||
|
|
||||||
if table not in id2pid:
|
|
||||||
id2pid[table] = {}
|
|
||||||
|
|
||||||
return id2pid
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create_db_add_pamhyr_id(cls):
|
|
||||||
return "pamhyr_id INTEGER NOT NULL"
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# PamhyrList.py -- Pamhyr Abstract List object for the Model
|
# PamhyrList.py -- Pamhyr Abstract List object for the Model
|
||||||
# Copyright (C) 2023-2025 INRAE
|
# Copyright (C) 2023-2024 INRAE
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -28,7 +28,8 @@ logger = logging.getLogger()
|
|||||||
|
|
||||||
|
|
||||||
class PamhyrModelList(SQLSubModel):
|
class PamhyrModelList(SQLSubModel):
|
||||||
_sub_classes = []
|
_sub_classes = [
|
||||||
|
]
|
||||||
|
|
||||||
def __init__(self, status=None):
|
def __init__(self, status=None):
|
||||||
super(PamhyrModelList, self).__init__()
|
super(PamhyrModelList, self).__init__()
|
||||||
@@ -45,8 +46,8 @@ class PamhyrModelList(SQLSubModel):
|
|||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
return cls._update_submodel(execute, version, data)
|
return cls._update_submodel(execute, version)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
@@ -85,24 +86,6 @@ class PamhyrModelList(SQLSubModel):
|
|||||||
if self._status is not None:
|
if self._status is not None:
|
||||||
self._status.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def _data_traversal(self,
|
|
||||||
predicate=lambda obj, data: True,
|
|
||||||
modifier=lambda obj, data: None,
|
|
||||||
data={}):
|
|
||||||
"""Traversal data and execute modifier fonction if predicate
|
|
||||||
true
|
|
||||||
|
|
||||||
Args:
|
|
||||||
predicate: Function predicate, take current obj and data as input
|
|
||||||
modifier: Function modifier, take current obj and data as input
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
"""
|
|
||||||
for el in self._lst:
|
|
||||||
el._data_traversal(predicate, modifier, data)
|
|
||||||
|
|
||||||
def new(self, index):
|
def new(self, index):
|
||||||
"""Create new elements and add it to list
|
"""Create new elements and add it to list
|
||||||
|
|
||||||
@@ -184,7 +167,8 @@ class PamhyrModelList(SQLSubModel):
|
|||||||
|
|
||||||
class PamhyrModelListWithTab(SQLSubModel):
|
class PamhyrModelListWithTab(SQLSubModel):
|
||||||
_tabs_list = []
|
_tabs_list = []
|
||||||
_sub_classes = []
|
_sub_classes = [
|
||||||
|
]
|
||||||
|
|
||||||
def __init__(self, status=None):
|
def __init__(self, status=None):
|
||||||
super(PamhyrModelListWithTab, self).__init__()
|
super(PamhyrModelListWithTab, self).__init__()
|
||||||
@@ -203,8 +187,8 @@ class PamhyrModelListWithTab(SQLSubModel):
|
|||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version, data=None):
|
def _db_update(cls, execute, version):
|
||||||
return cls._update_submodel(execute, version, data)
|
return cls._update_submodel(execute, version)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
@@ -213,24 +197,6 @@ class PamhyrModelListWithTab(SQLSubModel):
|
|||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
raise NotImplementedMethodeError(self, self._db_save)
|
raise NotImplementedMethodeError(self, self._db_save)
|
||||||
|
|
||||||
def _data_traversal(self,
|
|
||||||
predicate=lambda obj, data: True,
|
|
||||||
modifier=lambda obj, data: None,
|
|
||||||
data={}):
|
|
||||||
"""Traversal data and execute modifier fonction if predicate
|
|
||||||
true
|
|
||||||
|
|
||||||
Args:
|
|
||||||
predicate: Function predicate, take current obj and data as input
|
|
||||||
modifier: Function modifier, take current obj and data as input
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Nothing
|
|
||||||
"""
|
|
||||||
for tab in self._tabs:
|
|
||||||
for el in self._tabs[tab]:
|
|
||||||
el._data_traversal(predicate, modifier, data)
|
|
||||||
|
|
||||||
################
|
################
|
||||||
# MODEL METHOD #
|
# MODEL METHOD #
|
||||||
################
|
################
|
||||||
|
|||||||
Loaded 100 of 370 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user