mirror of
https://gitlab.com/pamhyr/pamhyr2.git
synced 2026-10-30 03:20:41 +01:00
Compare commits
428
Commits
v1.2.3
...
test_runner
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0994dd801 | ||
|
|
d002eebd49 | ||
|
|
e3ffac9ce0 | ||
|
|
e8a4d5eeed | ||
|
|
fc090dadce | ||
|
|
25e4786484 | ||
|
|
81e85ab488 | ||
|
|
201b1099bd | ||
|
|
b6054dd633 | ||
|
|
912843be25 | ||
|
|
bf49a7fc87 | ||
|
|
d77be00bcc | ||
|
|
ff0fa8ae8e | ||
|
|
92f4793181 | ||
|
|
5eaac204d5 | ||
|
|
5357eb9735 | ||
|
|
0946212c4b | ||
|
|
d6ab44d18e | ||
|
|
1db1a543c7 | ||
|
|
3e92a0c40d | ||
|
|
f3c461cbd9 | ||
|
|
012408d19e | ||
|
|
f7e563e71a | ||
|
|
81c6d39c1a | ||
|
|
d682443e1d | ||
|
|
c065b19ef8 | ||
|
|
b20a4c3c3d | ||
|
|
d66e75c183 | ||
|
|
31b4c47379 | ||
|
|
71bba499e0 | ||
|
|
7a08fa42d9 | ||
|
|
18a008462a | ||
|
|
d81da59398 | ||
|
|
3ab1eabe75 | ||
|
|
cf4f860db8 | ||
|
|
c68e3337ef | ||
|
|
db1ae613d6 | ||
|
|
a800816cda | ||
|
|
d4ef06c7c1 | ||
|
|
95d1d231c9 | ||
|
|
10b278e26f | ||
|
|
0550eef2eb | ||
|
|
73eb9919eb | ||
|
|
9116009b0b | ||
|
|
4e4ceb5074 | ||
|
|
d79e2ff187 | ||
|
|
d4a2594239 | ||
|
|
b666162bdf | ||
|
|
d705accaed | ||
|
|
ceff5f3083 | ||
|
|
97762756f8 | ||
|
|
2c49e991b4 | ||
|
|
c2a8f38ef5 | ||
|
|
f8a41fce08 | ||
|
|
624ae826eb | ||
|
|
d47dc0687e | ||
|
|
117e5222e4 | ||
|
|
b1c7a77f37 | ||
|
|
7b833390f1 | ||
|
|
ae08642116 | ||
|
|
c1905aa7e1 | ||
|
|
97ece018aa | ||
|
|
7f0102a881 | ||
|
|
786923bdbf | ||
|
|
deb9b2069f | ||
|
|
9308a73e8e | ||
|
|
869e116ad0 | ||
|
|
a2f3d22001 | ||
|
|
2e360943b2 | ||
|
|
8976f054c7 | ||
|
|
7bce725c63 | ||
|
|
5c83d67865 | ||
|
|
6e52b1681e | ||
|
|
f27b2cc586 | ||
|
|
1ed5d69bf4 | ||
|
|
5bb6cc40fe | ||
|
|
4c0a12dcf9 | ||
|
|
a308af41e0 | ||
|
|
02db91f3d7 | ||
|
|
4cf4015579 | ||
|
|
291b97ac9b | ||
|
|
b04e367e72 | ||
|
|
81d58122d6 | ||
|
|
874f592cf4 | ||
|
|
9b0bdd1e63 | ||
|
|
d934b5e381 | ||
|
|
14549330b9 | ||
|
|
2487bec6d6 | ||
|
|
4e1acfecdc | ||
|
|
c63c776989 | ||
|
|
44a8be6be3 | ||
|
|
a5a64f2080 | ||
|
|
16ee5a90e4 | ||
|
|
7ca6e69526 | ||
|
|
279fa0bf5e | ||
|
|
abe24611b3 | ||
|
|
17a18dde18 | ||
|
|
044818072e | ||
|
|
8bd791b2c4 | ||
|
|
c8d0d0c212 | ||
|
|
d31be61af8 | ||
|
|
a8a408b5d5 | ||
|
|
ff9d25412c | ||
|
|
de7f3e63b1 | ||
|
|
879c151524 | ||
|
|
7990a60d8b | ||
|
|
8aa0e25da2 | ||
|
|
1e6a578890 | ||
|
|
1dca9b98df | ||
|
|
de180b3635 | ||
|
|
5ca763ac8c | ||
|
|
188486f33a | ||
|
|
bc08d06bb1 | ||
|
|
05e039011a | ||
|
|
7640123299 | ||
|
|
e6dd983f97 | ||
|
|
002ebc5928 | ||
|
|
84486c03ed | ||
|
|
dc7dfb2414 | ||
|
|
ee01beb844 | ||
|
|
68b62a5630 | ||
|
|
990d44dd07 | ||
|
|
819ae41c00 | ||
|
|
bf95b7f7e6 | ||
|
|
861b78b928 | ||
|
|
08c414a66e | ||
|
|
57a80d00aa | ||
|
|
70eae5f7d2 | ||
|
|
e2a65e2cdd | ||
|
|
f03b74614e | ||
|
|
532b3fd7fd | ||
|
|
64df4129d4 | ||
|
|
f6e35e2045 | ||
|
|
a97717a624 | ||
|
|
3f650de52f | ||
|
|
1f4bda7976 | ||
|
|
a4423e45f8 | ||
|
|
1386c592bf | ||
|
|
016f3fc73c | ||
|
|
a42c46f4d2 | ||
|
|
fb09cf254e | ||
|
|
315b913fd0 | ||
|
|
0383dc18ae | ||
|
|
f16856d23b | ||
|
|
e640fa1bc0 | ||
|
|
df3b3b604d | ||
|
|
21e6c410e2 | ||
|
|
19045329dc | ||
|
|
4586ef79f7 | ||
|
|
f97d4ce3c5 | ||
|
|
cd4f626218 | ||
|
|
c1eb2cf447 | ||
|
|
5fec95cc4d | ||
|
|
46b943c800 | ||
|
|
fa8fe508a7 | ||
|
|
2a926a8d73 | ||
|
|
d222842313 | ||
|
|
2942b6e442 | ||
|
|
926cade437 | ||
|
|
13b4413b36 | ||
|
|
f516db0e48 | ||
|
|
8744e1d73e | ||
|
|
6113d9f4cd | ||
|
|
1dd92cb089 | ||
|
|
8f6255c2cd | ||
|
|
6c4f6e269c | ||
|
|
82b0837d8c | ||
|
|
137489cc1f | ||
|
|
4dc4dfdda0 | ||
|
|
ae15311471 | ||
|
|
dbcdace348 | ||
|
|
9375827949 | ||
|
|
739f401fac | ||
|
|
5f5a47679d | ||
|
|
26d1d63edb | ||
|
|
381bc37d13 | ||
|
|
936bfc753a | ||
|
|
f0ccf1f4f7 | ||
|
|
3a3c4d9d73 | ||
|
|
9c69f2ce6d | ||
|
|
e7a27c798a | ||
|
|
ddc612328e | ||
|
|
d05d1e6914 | ||
|
|
8bcf776813 | ||
|
|
589e591d3b | ||
|
|
e2e8c5ec9e | ||
|
|
d9aea9eb3c | ||
|
|
4cddd43f5a | ||
|
|
becc1696cb | ||
|
|
cf4957551f | ||
|
|
794b06d55a | ||
|
|
f51f742e00 | ||
|
|
40cc188815 | ||
|
|
303f6bb35b | ||
|
|
9043b6e2a3 | ||
|
|
0608b9175e | ||
|
|
ea7fda6b28 | ||
|
|
090326b75c | ||
|
|
78e6f7e5a7 | ||
|
|
8173eb8de9 | ||
|
|
591cd8950c | ||
|
|
3a78d6d6f7 | ||
|
|
bbf85a6048 | ||
|
|
6dfa876809 | ||
|
|
aff86a2ffa | ||
|
|
1eb925f3dd | ||
|
|
701d76240e | ||
|
|
37ea9e9083 | ||
|
|
6457b3ad25 | ||
|
|
cba5b948de | ||
|
|
4ba232a7e4 | ||
|
|
05f3f0fc7e | ||
|
|
89a5d78ced | ||
|
|
1b70e22a58 | ||
|
|
22aeda105e | ||
|
|
7181122a3a | ||
|
|
76ce34c4ea | ||
|
|
c58620fdc2 | ||
|
|
a6341dedd2 | ||
|
|
ba7171326c | ||
|
|
78b4269a69 | ||
|
|
a30bf8e364 | ||
|
|
bd1edbaf30 | ||
|
|
bdf9264963 | ||
|
|
befe1a550d | ||
|
|
f4d3f2b28f | ||
|
|
2a1c8c1e57 | ||
|
|
a3da09cf5b | ||
|
|
017f26c03d | ||
|
|
1e2a123693 | ||
|
|
2916afb974 | ||
|
|
37963b7b21 | ||
|
|
e5b4c998cb | ||
|
|
4bf0ad40b1 | ||
|
|
420d766ae5 | ||
|
|
645a76b0e8 | ||
|
|
6288f039d2 | ||
|
|
3585b29b03 | ||
|
|
96bac8ee1f | ||
|
|
e7d14e7aa3 | ||
|
|
6b14ed7efe | ||
|
|
3d40f599d6 | ||
|
|
6ec677a047 | ||
|
|
7bb0c9d57e | ||
|
|
ec2a1da6b6 | ||
|
|
a072a28929 | ||
|
|
fc7d732e0a | ||
|
|
cc5d86328b | ||
|
|
e3cbe3cf1f | ||
|
|
9482aaf523 | ||
|
|
e0cee0fbef | ||
|
|
f9a8512477 | ||
|
|
1457db721b | ||
|
|
7774d2d7af | ||
|
|
e6d3564d9b | ||
|
|
d62e703439 | ||
|
|
0346080e16 | ||
|
|
76559f6af8 | ||
|
|
04ce79462c | ||
|
|
ffa065a3e6 | ||
|
|
0e1e3665a6 | ||
|
|
c64bcd1ff7 | ||
|
|
dd84b83302 | ||
|
|
084223037c | ||
|
|
072840a18c | ||
|
|
3ef13666dc | ||
|
|
e344678ac5 | ||
|
|
1f79e6510d | ||
|
|
b94c37dd3b | ||
|
|
b61c4da8f1 | ||
|
|
ba60585550 | ||
|
|
0ed7660e72 | ||
|
|
8b4ff41b96 | ||
|
|
8769fa260d | ||
|
|
2667737490 | ||
|
|
7cb3982a1f | ||
|
|
2336dd22f3 | ||
|
|
f13ab03192 | ||
|
|
0df813b0d2 | ||
|
|
2faebfd402 | ||
|
|
7094ff3e62 | ||
|
|
c6ab2647c1 | ||
|
|
b62d1429c0 | ||
|
|
23a0bc33fa | ||
|
|
93340a48b1 | ||
|
|
0379a8ee02 | ||
|
|
6cce02650b | ||
|
|
6e79fe4cd5 | ||
|
|
f66d8c7a34 | ||
|
|
826e532605 | ||
|
|
fc35f2fbcc | ||
|
|
216239bfce | ||
|
|
739eb56006 | ||
|
|
468405d4ea | ||
|
|
31af83ba02 | ||
|
|
45fddb9459 | ||
|
|
05dcd3bcca | ||
|
|
89a898410f | ||
|
|
4a0456cbce | ||
|
|
a525d8a2b0 | ||
|
|
d7fcf62e12 | ||
|
|
4ac86b9acb | ||
|
|
00c9ecde52 | ||
|
|
ae49908a42 | ||
|
|
37cf42c524 | ||
|
|
c43a26870e | ||
|
|
5a6574921a | ||
|
|
7fa86c29e7 | ||
|
|
a3f20796d7 | ||
|
|
835d73789e | ||
|
|
f0ac170d2f | ||
|
|
b154e3d03b | ||
|
|
633e5a53dc | ||
|
|
3f98cf8625 | ||
|
|
36fe9a51c4 | ||
|
|
74ab3aa522 | ||
|
|
cfdf175f9d | ||
|
|
1b8f9b2abc | ||
|
|
f3ad4414b6 | ||
|
|
8930c39fbe | ||
|
|
7c2908569e | ||
|
|
5598464081 | ||
|
|
40d90424a3 | ||
|
|
bb33e8077a | ||
|
|
2859d0f4ff | ||
|
|
cab1bceae8 | ||
|
|
48c389a27c | ||
|
|
4199eb6ac5 | ||
|
|
02c6430ed2 | ||
|
|
05ee0e86b1 | ||
|
|
1ed6274f8b | ||
|
|
7857a2ea61 | ||
|
|
c01effbf4c | ||
|
|
a2ff3ecd9a | ||
|
|
a5ecc2383c | ||
|
|
e73f76024e | ||
|
|
cd7a81d828 | ||
|
|
81264fa63e | ||
|
|
5e3dbc18c3 | ||
|
|
e43c20c1e6 | ||
|
|
f65039d748 | ||
|
|
d0e70ddddc | ||
|
|
3de96a8d9d | ||
|
|
b3fd673f33 | ||
|
|
fb12f498a7 | ||
|
|
fd54116d55 | ||
|
|
83ecdc9a88 | ||
|
|
be1e868fc4 | ||
|
|
a1e6e646f9 | ||
|
|
29df56c8b4 | ||
|
|
064b3efa13 | ||
|
|
5cc5628956 | ||
|
|
8afa3d1e30 | ||
|
|
8f0535dc5c | ||
|
|
a412f2f18f | ||
|
|
8f4d233965 | ||
|
|
cc84b96024 | ||
|
|
4ff81a0b63 | ||
|
|
9b40b7eb39 | ||
|
|
5c3464a303 | ||
|
|
71bea2eea4 | ||
|
|
ed0a549027 | ||
|
|
267f30849f | ||
|
|
b8ed3d8954 | ||
|
|
b634acec67 | ||
|
|
3035ea8d8b | ||
|
|
abe7046d4f | ||
|
|
b60b922fef | ||
|
|
85d3345043 | ||
|
|
60ee889794 | ||
|
|
9ec21d815d | ||
|
|
ac32995e01 | ||
|
|
8c99268bb8 | ||
|
|
237b2670f6 | ||
|
|
d50f617a85 | ||
|
|
1cfc7e6638 | ||
|
|
8b9bb68ac5 | ||
|
|
71b631fbe4 | ||
|
|
7274ff5e2d | ||
|
|
770ecdfa99 | ||
|
|
4023ae73ba | ||
|
|
7f87d22249 | ||
|
|
6225f8940e | ||
|
|
6a5183cc0c | ||
|
|
037f5ecfa5 | ||
|
|
63657151ea | ||
|
|
c4e1a4c485 | ||
|
|
4222503961 | ||
|
|
af779a1880 | ||
|
|
84a5bed66d | ||
|
|
fa9ffccb19 | ||
|
|
4a333ff988 | ||
|
|
ce8c4ebd91 | ||
|
|
bc157fcde7 | ||
|
|
9a0476aa46 | ||
|
|
c7b5b40eda | ||
|
|
74c61262b7 | ||
|
|
2290e2eb6d | ||
|
|
0d33072a7e | ||
|
|
19d9a29459 | ||
|
|
d2e5eecf22 | ||
|
|
b9ca6bf4a9 | ||
|
|
aa68acea3d | ||
|
|
bf7b18f158 | ||
|
|
3675dc5b9d | ||
|
|
1dfdcb0413 | ||
|
|
32cda58064 | ||
|
|
4081f96d68 | ||
|
|
16350b4dec | ||
|
|
d8fe1d8b81 | ||
|
|
5525fff1fe | ||
|
|
328f5656bf | ||
|
|
9ec080a107 | ||
|
|
1bae755f1c | ||
|
|
9deccb465e | ||
|
|
2ff635d0cd | ||
|
|
9f4b297d23 | ||
|
|
aea308a5a2 | ||
|
|
5c4948bd6d | ||
|
|
d78d9167a0 | ||
|
|
9c9fcc3dd8 | ||
|
|
4271fbb85f | ||
|
|
ba4603e850 | ||
|
|
f2bc31c97b | ||
|
|
62a1726ef1 | ||
|
|
f19941f6da | ||
|
|
3917b84b56 | ||
|
|
3ecf97ac82 |
No files matched your search
+9
-3
@@ -41,8 +41,14 @@ __old__
|
|||||||
|
|
||||||
*-venv
|
*-venv
|
||||||
|
|
||||||
mage8
|
mage8/
|
||||||
adists
|
adists/
|
||||||
|
rubarbe/
|
||||||
|
|
||||||
|
linux/
|
||||||
|
windows/
|
||||||
|
|
||||||
|
install/
|
||||||
|
|
||||||
### END CUSTOM ###
|
### END CUSTOM ###
|
||||||
|
|
||||||
@@ -624,7 +630,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
|
||||||
|
|||||||
+87
-8
@@ -1,5 +1,5 @@
|
|||||||
# .gitlab-ci.yml -- Pamhyr gitlab-ci
|
# .gitlab-ci.yml -- Pamhyr gitlab-ci
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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,6 +24,13 @@ stages:
|
|||||||
- antivirus
|
- antivirus
|
||||||
- release
|
- release
|
||||||
|
|
||||||
|
#########################
|
||||||
|
# PYINSTALLER VARIABLES #
|
||||||
|
#########################
|
||||||
|
|
||||||
|
variables:
|
||||||
|
HIDDEN_IMPORTS: "--hiddenimport rasterio --collect-submodules rasterio"
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# DOWNLOADS #
|
# DOWNLOADS #
|
||||||
#############
|
#############
|
||||||
@@ -31,6 +38,7 @@ stages:
|
|||||||
variables:
|
variables:
|
||||||
MAGE_8_VERSION: "permalink/latest"
|
MAGE_8_VERSION: "permalink/latest"
|
||||||
ADISTS_VERSION: "permalink/latest"
|
ADISTS_VERSION: "permalink/latest"
|
||||||
|
RUBAR_VERSION: "permalink/latest"
|
||||||
|
|
||||||
dl-mage8-doc:
|
dl-mage8-doc:
|
||||||
stage: downloads
|
stage: downloads
|
||||||
@@ -104,6 +112,36 @@ dl-adists-windows:
|
|||||||
paths:
|
paths:
|
||||||
- adists-windows/adists.exe
|
- adists-windows/adists.exe
|
||||||
|
|
||||||
|
dl-rubar-linux:
|
||||||
|
stage: downloads
|
||||||
|
tags:
|
||||||
|
- linux
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
||||||
|
script:
|
||||||
|
- mkdir -p rubar-linux
|
||||||
|
- cd rubar-linux
|
||||||
|
- curl -L -o rubar.tgz https://forge.inrae.fr/river-hydraulics/rubarbe/-/releases/$RUBAR_VERSION/downloads/packages/rubar_linux.tgz
|
||||||
|
- tar xf rubar.tgz
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- rubar-linux/rubarbe
|
||||||
|
|
||||||
|
dl-rubar-windows:
|
||||||
|
stage: downloads
|
||||||
|
tags:
|
||||||
|
- linux
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
|
||||||
|
script:
|
||||||
|
- mkdir -p 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
|
||||||
|
- tar xf rubar.tgz
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- rubar-windows/rubarbe.exe
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# CONFIGURE #
|
# CONFIGURE #
|
||||||
#############
|
#############
|
||||||
@@ -142,7 +180,7 @@ unittest:
|
|||||||
- job: set-version
|
- job: set-version
|
||||||
artifacts: true
|
artifacts: true
|
||||||
script:
|
script:
|
||||||
- python3 -m venv venv
|
- python3.12 -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
|
||||||
@@ -160,7 +198,7 @@ test-pep8:
|
|||||||
- job: unittest
|
- job: unittest
|
||||||
script:
|
script:
|
||||||
# Setup virtual env
|
# Setup virtual env
|
||||||
- python3 -m venv venv
|
- python3.12 -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
|
||||||
@@ -238,7 +276,7 @@ build-developers-doc:
|
|||||||
- ./build.sh
|
- ./build.sh
|
||||||
# Create html documentation
|
# Create html documentation
|
||||||
- cd ../../src
|
- cd ../../src
|
||||||
- python3 -m venv doc-venv
|
- python3.12 -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
|
||||||
@@ -266,6 +304,8 @@ build-linux:
|
|||||||
needs:
|
needs:
|
||||||
- job: dl-adists-linux
|
- job: dl-adists-linux
|
||||||
artifacts: true
|
artifacts: true
|
||||||
|
- job: dl-rubar-linux
|
||||||
|
artifacts: true
|
||||||
- job: dl-mage8-linux
|
- job: dl-mage8-linux
|
||||||
artifacts: true
|
artifacts: true
|
||||||
- job: dl-mage8-doc
|
- job: dl-mage8-doc
|
||||||
@@ -286,13 +326,13 @@ build-linux:
|
|||||||
- mkdir -p linux
|
- mkdir -p linux
|
||||||
- cd linux
|
- cd linux
|
||||||
# Setup virtual env
|
# Setup virtual env
|
||||||
- python3 -m venv linux-venv
|
- python3.12 -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 --paths linux-venv/lib/python3.8/site-packages ../src/pamhyr.py
|
- pyinstaller -y $HIDDEN_IMPORTS --paths linux-venv/lib/python3.12/site-packages ../src/pamhyr.py
|
||||||
# Create directory
|
# Create directory
|
||||||
- mkdir -p pamhyr
|
- mkdir -p pamhyr
|
||||||
- mkdir -p pamhyr/_internal
|
- mkdir -p pamhyr/_internal
|
||||||
@@ -307,6 +347,9 @@ build-linux:
|
|||||||
# Copy adists
|
# Copy adists
|
||||||
- mkdir -p pamhyr/adists
|
- mkdir -p pamhyr/adists
|
||||||
- cp -v ../adists-linux/* pamhyr/adists/
|
- cp -v ../adists-linux/* pamhyr/adists/
|
||||||
|
# Copy rubar
|
||||||
|
- mkdir -p pamhyr/rubar
|
||||||
|
- cp -v ../rubar-linux/* pamhyr/rubar/
|
||||||
# Copy Pamhyr
|
# Copy Pamhyr
|
||||||
- cp -r dist/pamhyr/* pamhyr/
|
- cp -r dist/pamhyr/* pamhyr/
|
||||||
# Pamhyr script to force x11
|
# Pamhyr script to force x11
|
||||||
@@ -372,6 +415,8 @@ build-windows:
|
|||||||
artifacts: true
|
artifacts: true
|
||||||
- job: dl-adists-windows
|
- job: dl-adists-windows
|
||||||
artifacts: true
|
artifacts: true
|
||||||
|
- job: dl-rubar-windows
|
||||||
|
artifacts: true
|
||||||
- job: dl-mage8-doc
|
- job: dl-mage8-doc
|
||||||
artifacts: true
|
artifacts: true
|
||||||
- job: set-version
|
- job: set-version
|
||||||
@@ -395,7 +440,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 --hide-console hide-early -y ..\src\pamhyr.py
|
- pyinstaller -i ../src/View/ui/ressources/icon.ico $HIDDEN_IMPORTS --hide-console hide-early -y ..\src\pamhyr.py
|
||||||
# Create directory
|
# Create directory
|
||||||
- mkdir pamhyr
|
- mkdir pamhyr
|
||||||
- dir
|
- dir
|
||||||
@@ -574,6 +619,40 @@ 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:
|
||||||
@@ -593,7 +672,7 @@ tag-release:
|
|||||||
- job: pkg-hash
|
- job: pkg-hash
|
||||||
artifacts: true
|
artifacts: true
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_TAG
|
- if: $CI_COMMIT_BRANCH == 'master' && $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 - 2024
|
Pierre-Antoine ROUBY, INRAE, 2023 - 2025
|
||||||
@@ -18,6 +18,7 @@ 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.irstea.fr/theophile.terraz/pamhyr/-/wikis/home)
|
||||||
|
|||||||
+20
-15
@@ -1,18 +1,23 @@
|
|||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
|
|
||||||
echo " PDF documentation.org"
|
FILES="documentation.org scenario.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
|
|
||||||
|
|
||||||
echo " HTML documentation.org"
|
for file in $FILES
|
||||||
emacs -nw --batch \
|
do
|
||||||
--eval "(require 'org)" \
|
echo " PDF $file"
|
||||||
--eval "(require 'ox-latex)" \
|
emacs -nw --batch \
|
||||||
--eval "(require 'ox-html)" \
|
--eval "(require 'org)" \
|
||||||
--eval "(load-file \"../tools/setup.el\")" \
|
--eval "(require 'ox-latex)" \
|
||||||
--file ./documentation.org \
|
--eval "(load-file \"../tools/setup.el\")" \
|
||||||
-f org-html-export-to-html
|
--file ./$file \
|
||||||
|
-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
|
||||||
@@ -0,0 +1,661 @@
|
|||||||
|
# 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.
@@ -0,0 +1,525 @@
|
|||||||
|
# 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.
|
After Width: | Height: | Size: 70 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 196 KiB |
@@ -1,10 +1,10 @@
|
|||||||
PyQt5==5.15.4
|
PyQt5==5.15.11
|
||||||
PyQt5-Qt5==5.15.2
|
PyQt5-Qt5==5.15.2
|
||||||
PyQt5-sip==12.12.2
|
PyQt5-sip==12.18.0
|
||||||
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.7.1
|
matplotlib==3.8.3
|
||||||
numpy>=1.24.2
|
numpy>=1.24.2
|
||||||
colorama>=0.4.3
|
colorama>=0.4.3
|
||||||
pyinstaller>=5.11.0
|
pyinstaller>=5.11.0
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
guix shell --manifest=manifest.scm -- python3 ./src/pamhyr.py $@
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
;; 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"))
|
||||||
@@ -46,6 +46,10 @@ copy /y ..\mage8-windows\mage_extraire.exe pamhyr\mage8\
|
|||||||
copy /y ..\mage8-windows\mailleurTT.exe pamhyr\mage8\
|
copy /y ..\mage8-windows\mailleurTT.exe pamhyr\mage8\
|
||||||
copy /y ..\mage8-windows\libbief.dll pamhyr\mage8\
|
copy /y ..\mage8-windows\libbief.dll pamhyr\mage8\
|
||||||
|
|
||||||
|
rem rubar
|
||||||
|
mkdir pamhyr\rubar
|
||||||
|
copy /y ..\rubar-windows\rubarbe.exe pamhyr\rubar\
|
||||||
|
|
||||||
rem adists
|
rem adists
|
||||||
mkdir pamhyr\adists
|
mkdir pamhyr\adists
|
||||||
copy /y ..\adists-windows\adists.exe pamhyr\adists\
|
copy /y ..\adists-windows\adists.exe pamhyr\adists\
|
||||||
|
|||||||
+3
-3
@@ -1,10 +1,10 @@
|
|||||||
PyQt5==5.15.4
|
PyQt5==5.15.11
|
||||||
PyQt5-Qt5==5.15.2
|
PyQt5-Qt5==5.15.2
|
||||||
PyQt5-sip==12.12.2
|
PyQt5-sip==12.18.0
|
||||||
#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.7.1
|
matplotlib==3.8.3
|
||||||
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-2024 INRAE
|
# Copyright (C) 2023-2025 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-2024 INRAE
|
# Copyright (C) 2023-2025 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-2024 INRAE
|
# Copyright (C) 2023-2025 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-2024 INRAE
|
# Copyright (C) 2023-2025 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-2024 INRAE
|
# Copyright (C) 2023-2025 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
|
||||||
|
|||||||
+135
-107
@@ -17,7 +17,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from numpy import sign
|
from numpy import sign, interp
|
||||||
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,110 +40,96 @@ 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
|
|
||||||
|
|
||||||
if limites[0] > limites[1]:
|
# pre process
|
||||||
lim = limites[1]
|
|
||||||
limites[1] = limites[0]
|
|
||||||
limites[0] = lim
|
|
||||||
elif limites[0] == limites[1]:
|
|
||||||
return reach
|
|
||||||
|
|
||||||
self.st_to_m(reach, limites)
|
profiles = self.get_profiles(reach, limites)
|
||||||
self.interpolate_transversal_step(reach,
|
if profiles is None:
|
||||||
limites,
|
return []
|
||||||
step)
|
|
||||||
self.purge_aligned_points(reach)
|
|
||||||
|
|
||||||
return reach
|
for p in profiles:
|
||||||
|
if len(p) < 1:
|
||||||
|
return []
|
||||||
|
|
||||||
def st_to_m(self, reach, limites):
|
guide_list = self.get_guide_list(reach, profiles)
|
||||||
|
if guide_list is None:
|
||||||
|
return []
|
||||||
|
|
||||||
gl = reach.compute_guidelines()
|
# we never modify original profiles, we work on the copies
|
||||||
profiles = reach.profiles[limites[0]:limites[1]+1]
|
m_profiles = self.st_to_m(profiles, guide_list)
|
||||||
|
new_profiles = self.interpolate_transversal_step(m_profiles, step)
|
||||||
|
|
||||||
print(profiles)
|
for new_profiles2 in new_profiles:
|
||||||
|
for p in new_profiles2:
|
||||||
|
p.hard_purge_aligned_points()
|
||||||
|
|
||||||
# we make sure that the lines are in the left-to-right order
|
return new_profiles
|
||||||
guide_list = [
|
|
||||||
x.name
|
|
||||||
for x in profiles[0].named_points()
|
|
||||||
if x.name in gl[0]
|
|
||||||
]
|
|
||||||
|
|
||||||
gl1 = [""] + guide_list
|
def st_to_m(self, profiles, guide_list):
|
||||||
gl2 = guide_list + [""]
|
|
||||||
max_values = [0] * (len(guide_list) + 1)
|
guide_list = ["un"] + guide_list + ["np"]
|
||||||
max_values_index = [0] * (len(guide_list) + 1)
|
max_values = [0] * (len(guide_list) - 1)
|
||||||
|
max_values_index = [0] * (len(guide_list) - 1)
|
||||||
# we work between each couple of guidelines
|
# we work between each couple of guidelines
|
||||||
for j, p in enumerate(profiles):
|
for j, p in enumerate(profiles):
|
||||||
index1 = 0
|
|
||||||
index2 = p.named_point_index(guide_list[0])
|
|
||||||
if index2 - index1 > max_values[0]:
|
|
||||||
max_values[0] = index2 - index1
|
|
||||||
max_values_index[0] = j
|
|
||||||
for i in range(len(guide_list) - 1):
|
for i in range(len(guide_list) - 1):
|
||||||
index1 = p.named_point_index(guide_list[i])
|
index1 = p.named_point_index(guide_list[i])
|
||||||
index2 = p.named_point_index(guide_list[i+1])
|
index2 = p.named_point_index(guide_list[i+1])
|
||||||
if index2 - index1 > max_values[i + 1]:
|
if index2 - index1 > max_values[i]:
|
||||||
max_values[i + 1] = index2 - index1
|
max_values[i] = index2 - index1
|
||||||
max_values_index[i + 1] = j
|
max_values_index[i] = j
|
||||||
index1 = p.named_point_index(guide_list[-1])
|
|
||||||
index2 = len(p) - 1
|
|
||||||
if index2 - index1 > max_values[-1]:
|
|
||||||
max_values[-1] = index2 - index1
|
|
||||||
max_values_index[-1] = j
|
|
||||||
|
|
||||||
for i in range(len(max_values)):
|
for i in range(len(max_values)):
|
||||||
for isect in range(max_values_index[i], len(profiles)-1):
|
for isect in range(max_values_index[i], len(profiles)-1):
|
||||||
self.compl_sect(profiles[isect],
|
self.compl_sect(profiles[isect],
|
||||||
profiles[isect+1],
|
profiles[isect+1],
|
||||||
gl1[i], gl2[i])
|
guide_list[i],
|
||||||
|
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],
|
||||||
gl1[i], gl2[i])
|
guide_list[i],
|
||||||
|
guide_list[i+1])
|
||||||
|
return profiles
|
||||||
|
|
||||||
def interpolate_transversal_step(self,
|
def interpolate_transversal_step(self,
|
||||||
reach,
|
profiles,
|
||||||
limites,
|
|
||||||
step):
|
step):
|
||||||
# calcul number of intermediate profiles
|
# calcul number of intermediate profiles
|
||||||
np = []
|
np = []
|
||||||
for i in range(limites[0], limites[1]):
|
for i in range(len(profiles)-1):
|
||||||
np.append(int((reach.profiles[i+1].rk -
|
np.append(int((profiles[i+1].rk -
|
||||||
reach.profiles[i].rk) / step) - 1)
|
profiles[i].rk) / step) - 1)
|
||||||
if np[-1] < 0:
|
if np[-1] < 0:
|
||||||
np[-1] = 0
|
np[-1] = 0
|
||||||
|
|
||||||
d = [] # ratios
|
new_profiles = []
|
||||||
p = [] # new profiles
|
for i in range(len(profiles)-1):
|
||||||
ptr = int(limites[0])
|
new_profiles2 = []
|
||||||
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 = reach.profiles[ptr0].copy()
|
p = profiles[i].copy()
|
||||||
# RATIO entre les deux sections initiales
|
# RATIO entre les deux sections initiales
|
||||||
dj = float(j+1)*d
|
dj = float(j+1)*d
|
||||||
ptr += 1 # next profile, original
|
pt1 = profiles[i].points
|
||||||
for k in range(len(reach.profiles[ptr0].points)):
|
pt2 = profiles[i+1].points
|
||||||
p.points[k].x = reach.profiles[ptr0].points[k].x + \
|
for k in range(len(pt1)):
|
||||||
dj*(reach.profiles[ptr].points[k].x -
|
p.points[k].x = pt1[k].x + \
|
||||||
reach.profiles[ptr0].points[k].x)
|
dj*(pt2[k].x -
|
||||||
p.points[k].y = reach.profiles[ptr0].points[k].y + \
|
pt1[k].x)
|
||||||
dj*(reach.profiles[ptr].points[k].y -
|
p.points[k].y = pt1[k].y + \
|
||||||
reach.profiles[ptr0].points[k].y)
|
dj*(pt2[k].y -
|
||||||
p.points[k].z = reach.profiles[ptr0].points[k].z + \
|
pt1[k].y)
|
||||||
dj*(reach.profiles[ptr].points[k].z -
|
p.points[k].z = pt1[k].z + \
|
||||||
reach.profiles[ptr0].points[k].z)
|
dj*(pt2[k].z -
|
||||||
p.rk = reach.profiles[ptr0].rk + \
|
pt1[k].z)
|
||||||
dj*(reach.profiles[ptr].rk -
|
p.rk = profiles[i].rk + \
|
||||||
reach.profiles[ptr0].rk)
|
dj*(profiles[i+1].rk -
|
||||||
|
profiles[i].rk)
|
||||||
p.name = f'interpol{p.rk}'
|
p.name = f'interpol{p.rk}'
|
||||||
reach.insert_profile(ptr, p)
|
new_profiles2.append(p)
|
||||||
ptr += 1 # next profile, original
|
new_profiles.append(new_profiles2)
|
||||||
|
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
|
||||||
@@ -153,26 +139,17 @@ class InternalMeshing(AMeshingTool):
|
|||||||
# len1 and 2: number of intervals
|
# len1 and 2: number of intervals
|
||||||
# len1: target len
|
# len1: target len
|
||||||
|
|
||||||
if tag1 == '': # left end point
|
start1 = sect1.named_point_index(tag1)
|
||||||
start1 = 0
|
start2 = sect2.named_point_index(tag1)
|
||||||
start2 = 0
|
|
||||||
else:
|
|
||||||
start1 = sect1.named_point_index(tag1)
|
|
||||||
start2 = sect2.named_point_index(tag1)
|
|
||||||
|
|
||||||
if tag2 == '': # right end point
|
end1 = sect1.named_point_index(tag2)
|
||||||
end1 = sect1.nb_points-1
|
end2 = sect2.named_point_index(tag2)
|
||||||
end2 = sect2.nb_points-1
|
|
||||||
else:
|
|
||||||
end1 = sect1.named_point_index(tag2)
|
|
||||||
end2 = sect2.named_point_index(tag2)
|
|
||||||
|
|
||||||
len1 = end1 - start1
|
len1 = end1 - start1
|
||||||
len2 = end2 - start2
|
len2 = end2 - start2
|
||||||
|
|
||||||
if len1 < len2:
|
if len1 < len2:
|
||||||
self.compl_sect(sect2, sect1, start2, start1,
|
self.compl_sect(sect2, sect1, tag1, tag2)
|
||||||
len2, len1, tag1, tag2)
|
|
||||||
return
|
return
|
||||||
elif len1 == len2:
|
elif len1 == len2:
|
||||||
return
|
return
|
||||||
@@ -191,25 +168,21 @@ 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
|
||||||
for i in range(len1-len2):
|
for i in range(len1-len2):
|
||||||
p = sect2.point(start2).copy()
|
p = sect2.point(start2).copy()
|
||||||
sect2.insert_point(start2, p)
|
sect2.insert_point(start2, p)
|
||||||
if tag1 == '': # left end point
|
|
||||||
sect2.point(start2).name = ''
|
|
||||||
if tag2 == '': # left end point
|
|
||||||
sect2.point(start2+1).name = ''
|
|
||||||
len2 += 1
|
len2 += 1
|
||||||
elif ltot1 < 0.0001:
|
elif ltot1 < 0.0001:
|
||||||
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
|
||||||
@@ -268,11 +241,13 @@ class InternalMeshing(AMeshingTool):
|
|||||||
p = sect2.point(start2+len2).copy()
|
p = sect2.point(start2+len2).copy()
|
||||||
sect2.insert_point(start2+len2, p)
|
sect2.insert_point(start2+len2, p)
|
||||||
sect2.point(start2+len2).name = ''
|
sect2.point(start2+len2).name = ''
|
||||||
sect2.point(start2).name = tag1
|
if tag1 != "un":
|
||||||
sect2.point(start2+len2).name = tag2
|
sect2.point(start2).name = tag1
|
||||||
|
if tag1 != "np":
|
||||||
|
sect2.point(start2+len2).name = tag2
|
||||||
sect2.modified()
|
sect2.modified()
|
||||||
|
|
||||||
def update_rk(self, reach,
|
def update_rk(self, reach, begin_rk, end_rk,
|
||||||
step: float = 50,
|
step: float = 50,
|
||||||
limites=[-1, -1],
|
limites=[-1, -1],
|
||||||
origin=0,
|
origin=0,
|
||||||
@@ -283,27 +258,40 @@ 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 reach
|
return []
|
||||||
|
|
||||||
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
|
return reach.get_rk()
|
||||||
|
|
||||||
# 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 = sign(reach.profiles[-1].rk - reach.profiles[0].rk)
|
sgn = old_orientation
|
||||||
|
if abs(sgn) < 0.5:
|
||||||
|
sgn = 1.0
|
||||||
|
|
||||||
reach.profiles[origin].rk = origin_value
|
new_rk = [0.0]*nprof
|
||||||
|
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):
|
||||||
@@ -314,8 +302,7 @@ 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]))
|
||||||
reach.profiles[i].rk = reach.profiles[i-1].rk \
|
new_rk[i] = new_rk[i-1] + (sgn * (d1 + d2) / 2)
|
||||||
+ (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
|
||||||
@@ -325,9 +312,50 @@ 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]))
|
||||||
reach.profiles[i].rk = reach.profiles[i+1].rk \
|
new_rk[i] = new_rk[i+1] - (sgn * (d1 + d2) / 2)
|
||||||
- (sgn * (d1 + d2) / 2)
|
|
||||||
|
|
||||||
def purge_aligned_points(self, reach):
|
new_begin_rk = interp(old_begin_rk, old_rk, new_rk)
|
||||||
for p in reach.profiles:
|
new_end_rk = interp(old_end_rk, old_rk, new_rk)
|
||||||
p.purge_aligned_points()
|
if abs(sgn - old_orientation) > 0.1: # orientation changed
|
||||||
|
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
|
||||||
+7
-3
@@ -1,5 +1,5 @@
|
|||||||
# Mage.py -- Pamhyr
|
# Mage.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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,7 +344,9 @@ 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(
|
||||||
@@ -353,7 +355,7 @@ class MeshingWithMageMailleurTT(AMeshingTool):
|
|||||||
f"{logger_color_reset()}"
|
f"{logger_color_reset()}"
|
||||||
)
|
)
|
||||||
outputs = proc.readAllStandardOutput()
|
outputs = proc.readAllStandardOutput()
|
||||||
logger.debug(outputs)
|
logger.warning(outputs)
|
||||||
|
|
||||||
errors = proc.readAllStandardError()
|
errors = proc.readAllStandardError()
|
||||||
logger.error(
|
logger.error(
|
||||||
@@ -419,7 +421,9 @@ 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(
|
||||||
@@ -428,7 +432,7 @@ class MeshingWithMageMailleurTT(AMeshingTool):
|
|||||||
f"{logger_color_reset()}"
|
f"{logger_color_reset()}"
|
||||||
)
|
)
|
||||||
outputs = proc.readAllStandardOutput()
|
outputs = proc.readAllStandardOutput()
|
||||||
logger.debug(outputs)
|
logger.warning(outputs)
|
||||||
|
|
||||||
errors = proc.readAllStandardError()
|
errors = proc.readAllStandardError()
|
||||||
logger.error(
|
logger.error(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# __init__.py -- Pamhyr
|
# __init__.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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-2024 INRAE
|
# Copyright (C) 2023-2025 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 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 INRAE
|
# Copyright (C) 2024-2025 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,31 +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
|
||||||
|
|
||||||
|
|
||||||
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):
|
status=None, owner_scenario=-1):
|
||||||
super(AddFile, self).__init__()
|
super(AddFile, self).__init__(
|
||||||
|
id=id, status=status,
|
||||||
if id == -1:
|
owner_scenario=owner_scenario
|
||||||
self.id = AddFile._id_cnt
|
)
|
||||||
else:
|
|
||||||
self.id = id
|
|
||||||
|
|
||||||
self._status = status
|
|
||||||
|
|
||||||
self._enabled = enabled
|
self._enabled = enabled
|
||||||
self._name = f"File {self.id}" if name == "" else name
|
self._name = f"File #{self._pamhyr_id}" if name == "" else name
|
||||||
self._path = path
|
self._path = path
|
||||||
self._text = text
|
self._text = text
|
||||||
|
|
||||||
AddFile._id_cnt = max(id, AddFile._id_cnt+1)
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
value = None
|
value = None
|
||||||
|
|
||||||
@@ -71,7 +65,7 @@ class AddFile(SQLSubModel):
|
|||||||
elif key == "text":
|
elif key == "text":
|
||||||
self._text = value
|
self._text = value
|
||||||
|
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def enabled(self):
|
def enabled(self):
|
||||||
@@ -80,7 +74,7 @@ class AddFile(SQLSubModel):
|
|||||||
@enabled.setter
|
@enabled.setter
|
||||||
def enabled(self, enabled):
|
def enabled(self, enabled):
|
||||||
self._enabled = enabled
|
self._enabled = enabled
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
def is_enabled(self):
|
def is_enabled(self):
|
||||||
return self._enabled
|
return self._enabled
|
||||||
@@ -92,7 +86,7 @@ class AddFile(SQLSubModel):
|
|||||||
@name.setter
|
@name.setter
|
||||||
def name(self, name):
|
def name(self, name):
|
||||||
self._name = name
|
self._name = name
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path(self):
|
def path(self):
|
||||||
@@ -101,7 +95,7 @@ class AddFile(SQLSubModel):
|
|||||||
@path.setter
|
@path.setter
|
||||||
def path(self, path):
|
def path(self, path):
|
||||||
self._path = path
|
self._path = path
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def text(self):
|
def text(self):
|
||||||
@@ -110,38 +104,80 @@ class AddFile(SQLSubModel):
|
|||||||
@text.setter
|
@text.setter
|
||||||
def text(self, text):
|
def text(self, text):
|
||||||
self._text = text
|
self._text = text
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE additional_files(
|
CREATE TABLE additional_files{ext} (
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
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):
|
def _db_update(cls, execute, version, data=None):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
|
release = int(release)
|
||||||
|
|
||||||
if major == minor == "0":
|
if major == minor == "0":
|
||||||
if int(release) < 8:
|
if 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 id, enabled, name, path, text " +
|
"SELECT pamhyr_id, enabled, deleted, name, path, text, scenario " +
|
||||||
"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:
|
||||||
@@ -149,30 +185,45 @@ 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']
|
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):
|
||||||
sql = (
|
if not self.must_be_saved():
|
||||||
|
return True
|
||||||
|
|
||||||
|
execute(
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"additional_files(id, enabled, name, path, text) " +
|
"additional_files(pamhyr_id, enabled, deleted, " +
|
||||||
|
"name, path, text, scenario) " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, {self._enabled}, " +
|
f"{self._pamhyr_id}, " +
|
||||||
|
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 INRAE
|
# Copyright (C) 2024-2025 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.PamhyrList import PamhyrModelList
|
from Model.Tools.PamhyrListExt import PamhyrModelList
|
||||||
from Model.AdditionalFile.AddFile import AddFile
|
from Model.AdditionalFile.AddFile import AddFile
|
||||||
|
|
||||||
|
|
||||||
@@ -38,7 +38,10 @@ class AddFileList(PamhyrModelList):
|
|||||||
ok = True
|
ok = True
|
||||||
|
|
||||||
# Delete previous data
|
# Delete previous data
|
||||||
execute("DELETE FROM additional_files")
|
execute(
|
||||||
|
"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-2024 INRAE
|
# Copyright (C) 2023-2025 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,24 +27,193 @@ 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 BoundaryCondition(SQLSubModel):
|
class Data(SQLSubModel):
|
||||||
_sub_classes = []
|
_sub_classes = []
|
||||||
_id_cnt = 0
|
|
||||||
|
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, data0, data1,
|
||||||
status=None):
|
id: int = -1,
|
||||||
super(BoundaryCondition, self).__init__()
|
types=[float, float],
|
||||||
|
status=None,
|
||||||
|
owner_scenario=-1):
|
||||||
|
super(Data, self).__init__(
|
||||||
|
id=id, status=status,
|
||||||
|
owner_scenario=owner_scenario
|
||||||
|
)
|
||||||
|
|
||||||
self._status = status
|
self._types = types
|
||||||
|
self._data = [data0, data1]
|
||||||
|
|
||||||
if id == -1:
|
@classmethod
|
||||||
self.id = BoundaryCondition._id_cnt
|
def _db_create(cls, execute, ext=""):
|
||||||
else:
|
execute(f"""
|
||||||
self.id = id
|
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):
|
||||||
|
_sub_classes = [Data]
|
||||||
|
|
||||||
|
def __init__(self, id: int = -1,
|
||||||
|
name: str = "",
|
||||||
|
status=None,
|
||||||
|
owner_scenario=-1):
|
||||||
|
super(BoundaryCondition, self).__init__(
|
||||||
|
id=id, status=status,
|
||||||
|
owner_scenario=owner_scenario
|
||||||
|
)
|
||||||
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self._type = ""
|
self._type = ""
|
||||||
@@ -53,37 +222,66 @@ 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)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE boundary_condition(
|
CREATE TABLE boundary_condition{ext}(
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
|
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,
|
||||||
FOREIGN KEY(node) REFERENCES river_node(id)
|
{Scenario.create_db_add_scenario()},
|
||||||
|
{Scenario.create_db_add_scenario_fk()},
|
||||||
|
FOREIGN KEY(node) REFERENCES river_node(pamhyr_id),
|
||||||
|
PRIMARY KEY(pamhyr_id, scenario)
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
execute("""
|
if ext == "_tmp":
|
||||||
CREATE TABLE boundary_condition_data(
|
return True
|
||||||
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):
|
def _db_update(cls, execute, version, data=None):
|
||||||
return True
|
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 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):
|
||||||
@@ -110,83 +308,117 @@ 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 id, name, type, node " +
|
"SELECT pamhyr_id, deleted, name, type, node, scenario " +
|
||||||
"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:
|
||||||
t = row[2]
|
it = iter(row)
|
||||||
|
|
||||||
|
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=row[0],
|
id=pid, name=name,
|
||||||
name=row[1],
|
status=data['status'],
|
||||||
status=data['status']
|
owner_scenario=owner_scenario
|
||||||
)
|
)
|
||||||
|
if deleted:
|
||||||
|
bc.set_as_deleted()
|
||||||
|
|
||||||
bc.node = None
|
bc.node = None
|
||||||
if row[3] != -1:
|
if node != -1:
|
||||||
bc.node = next(filter(lambda n: n.id == row[3], data["nodes"]),
|
bc.node = next(filter(lambda n: n.id == node, nodes), None)
|
||||||
None)
|
|
||||||
|
|
||||||
values = execute(
|
data["bc"] = bc
|
||||||
"SELECT ind, data0, data1 FROM boundary_condition_data " +
|
bc._data = Data._db_load(execute, data=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(f"DELETE FROM boundary_condition WHERE id = {self.id}")
|
execute(
|
||||||
execute(f"DELETE FROM boundary_condition_data WHERE bc = {self.id}")
|
"DELETE FROM boundary_condition_data " +
|
||||||
|
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
|
||||||
|
|
||||||
sql = (
|
execute(
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"boundary_condition(id, name, type, tab, node) " +
|
"boundary_condition(" +
|
||||||
|
"pamhyr_id, deleted, name, type, tab, node, scenario" +
|
||||||
|
") " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, '{self._db_format(self._name)}', " +
|
f"{self._pamhyr_id}, {self._db_format(self.is_deleted())}, " +
|
||||||
f"'{self._db_format(self._type)}', '{tab}', {node}" +
|
f"'{self._db_format(self._name)}', " +
|
||||||
|
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:
|
||||||
data0 = self._db_format(str(d[0]))
|
data["ind"] = ind
|
||||||
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(self._data)
|
return len(
|
||||||
|
list(
|
||||||
|
filter(
|
||||||
|
lambda el: not el.is_deleted(),
|
||||||
|
self._data
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def compatibility(cls):
|
def compatibility(cls):
|
||||||
@@ -211,14 +443,14 @@ class BoundaryCondition(SQLSubModel):
|
|||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
if self._name == "":
|
if self._name == "":
|
||||||
return f"B{self.id + 1}"
|
return f"B #{self.pamhyr_id}"
|
||||||
|
|
||||||
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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bctype(self):
|
def bctype(self):
|
||||||
@@ -231,7 +463,7 @@ class BoundaryCondition(SQLSubModel):
|
|||||||
@node.setter
|
@node.setter
|
||||||
def node(self, node):
|
def node(self, node):
|
||||||
self._node = node
|
self._node = node
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
def has_node(self):
|
def has_node(self):
|
||||||
return self._node is not None
|
return self._node is not None
|
||||||
@@ -242,7 +474,12 @@ class BoundaryCondition(SQLSubModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def data(self):
|
def data(self):
|
||||||
return self._data.copy()
|
return list(
|
||||||
|
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:
|
||||||
@@ -276,51 +513,79 @@ 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 (new_0, new_1)
|
return Data(
|
||||||
|
new_0, new_1,
|
||||||
|
status=self._status
|
||||||
|
)
|
||||||
|
|
||||||
def add(self, index: int):
|
def add(self, index: int):
|
||||||
value = (self._default_0, self._default_1)
|
value = Data(
|
||||||
|
self._default_0, self._default_1,
|
||||||
|
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):
|
def insert(self, index: int, value):
|
||||||
self._data.insert(index, value)
|
if value in self._data:
|
||||||
self._status.modified()
|
value.set_as_not_deleted()
|
||||||
|
else:
|
||||||
|
self._data.insert(index, value)
|
||||||
|
|
||||||
def delete_i(self, indexes):
|
self.modified()
|
||||||
|
|
||||||
|
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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
def delete(self, els):
|
def delete(self, els):
|
||||||
self._data = list(
|
for el in els:
|
||||||
filter(
|
el.set_as_deleted()
|
||||||
lambda e: e not in els,
|
|
||||||
self.data
|
self.modified()
|
||||||
)
|
|
||||||
)
|
def undelete(self, els):
|
||||||
self._status.modified()
|
for el in els:
|
||||||
|
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._status.modified()
|
self.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):
|
||||||
return self.data[index]
|
if len(self._data) == 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self._data[index]
|
||||||
|
|
||||||
def get_range(self, _range):
|
def get_range(self, _range):
|
||||||
lst = []
|
lst = []
|
||||||
@@ -329,10 +594,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 = list(self._data[index])
|
v = self._data[index]
|
||||||
v[column] = self._types[column](value)
|
v[column] = self._types[column](value)
|
||||||
self._data[index] = tuple(v)
|
self._data[index] = v
|
||||||
self._status.modified()
|
self.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)
|
||||||
@@ -364,14 +629,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._status.modified()
|
self.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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
def reach(self, river):
|
def reach(self, river):
|
||||||
r = []
|
r = []
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# BoundaryConditionList.py -- Pamhyr
|
# BoundaryConditionList.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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.PamhyrList import PamhyrModelListWithTab
|
from Model.Tools.PamhyrListExt 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,9 +41,6 @@ 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(
|
||||||
@@ -53,10 +50,10 @@ class BoundaryConditionList(PamhyrModelListWithTab):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute("DELETE FROM boundary_condition")
|
execute(
|
||||||
|
"DELETE FROM boundary_condition " +
|
||||||
if data is None:
|
f"WHERE scenario = {self._status.scenario_id}"
|
||||||
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-2024 INRAE
|
# Copyright (C) 2023-2025 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,8 +22,12 @@ from Model.BoundaryCondition.BoundaryCondition import BoundaryCondition
|
|||||||
|
|
||||||
|
|
||||||
class NotDefined(BoundaryCondition):
|
class NotDefined(BoundaryCondition):
|
||||||
def __init__(self, id: int = -1, name: str = "", status=None):
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
super(NotDefined, self).__init__(id=id, name=name, status=status)
|
status=None, owner_scenario=-1):
|
||||||
|
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"]
|
||||||
@@ -34,9 +38,12 @@ class NotDefined(BoundaryCondition):
|
|||||||
|
|
||||||
|
|
||||||
class PonctualContribution(BoundaryCondition):
|
class PonctualContribution(BoundaryCondition):
|
||||||
def __init__(self, id: int = -1, name: str = "", status=None):
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
|
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"]
|
||||||
@@ -48,8 +55,12 @@ class PonctualContribution(BoundaryCondition):
|
|||||||
|
|
||||||
|
|
||||||
class TimeOverZ(BoundaryCondition):
|
class TimeOverZ(BoundaryCondition):
|
||||||
def __init__(self, id: int = -1, name: str = "", status=None):
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
super(TimeOverZ, self).__init__(id=id, name=name, status=status)
|
status=None, owner_scenario=-1):
|
||||||
|
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"]
|
||||||
@@ -61,9 +72,12 @@ class TimeOverZ(BoundaryCondition):
|
|||||||
|
|
||||||
|
|
||||||
class TimeOverDischarge(BoundaryCondition):
|
class TimeOverDischarge(BoundaryCondition):
|
||||||
def __init__(self, id: int = -1, name: str = "", status=None):
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
|
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"]
|
||||||
@@ -75,8 +89,12 @@ class TimeOverDischarge(BoundaryCondition):
|
|||||||
|
|
||||||
|
|
||||||
class ZOverDischarge(BoundaryCondition):
|
class ZOverDischarge(BoundaryCondition):
|
||||||
def __init__(self, id: int = -1, name: str = "", status=None):
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
super(ZOverDischarge, self).__init__(id=id, name=name, status=status)
|
status=None, owner_scenario=-1):
|
||||||
|
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"]
|
||||||
@@ -92,8 +110,12 @@ class ZOverDischarge(BoundaryCondition):
|
|||||||
|
|
||||||
|
|
||||||
class Solid(BoundaryCondition):
|
class Solid(BoundaryCondition):
|
||||||
def __init__(self, id: int = -1, name: str = "", status=None):
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
super(Solid, self).__init__(id=id, name=name, status=status)
|
status=None, owner_scenario=-1):
|
||||||
|
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-2024 INRAE
|
# Copyright (C) 2023-2025 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,25 +27,193 @@ 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 BoundaryConditionAdisTS(SQLSubModel):
|
class Data(SQLSubModel):
|
||||||
_sub_classes = []
|
_sub_classes = []
|
||||||
_id_cnt = 0
|
|
||||||
|
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):
|
||||||
|
_sub_classes = [Data]
|
||||||
|
|
||||||
def __init__(self, id: int = -1,
|
def __init__(self, id: int = -1,
|
||||||
pollutant: int = -1, status=None):
|
pollutant: int = -1, status=None,
|
||||||
super(BoundaryConditionAdisTS, self).__init__()
|
owner_scenario=-1):
|
||||||
|
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
|
||||||
@@ -53,117 +221,210 @@ 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)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE boundary_condition_adists(
|
CREATE TABLE boundary_condition_adists{ext}(
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
|
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,
|
||||||
FOREIGN KEY(pollutant) REFERENCES Pollutants(id),
|
{Scenario.create_db_add_scenario()},
|
||||||
FOREIGN KEY(node) REFERENCES river_node(id)
|
{Scenario.create_db_add_scenario_fk()},
|
||||||
|
FOREIGN KEY(pollutant) REFERENCES Pollutants(pamhyr_id),
|
||||||
|
FOREIGN KEY(node) REFERENCES river_node(pamhyr_id)
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
execute("""
|
if ext != "":
|
||||||
CREATE TABLE boundary_condition_data_adists(
|
return True
|
||||||
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):
|
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
|
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 id, pollutant, type, node " +
|
"SELECT pamhyr_id, deleted, pollutant, type, node, scenario " +
|
||||||
"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:
|
||||||
bc = cls(
|
it = iter(row)
|
||||||
id=row[0],
|
|
||||||
pollutant=row[1],
|
|
||||||
status=data['status']
|
|
||||||
)
|
|
||||||
|
|
||||||
bc.type = row[2]
|
pid = next(it)
|
||||||
|
deleted = (next(it) == 1)
|
||||||
|
pollutant = next(it)
|
||||||
|
bc_type = next(it)
|
||||||
|
node = next(it)
|
||||||
|
owner_scenario = next(it)
|
||||||
|
|
||||||
|
bc = cls(
|
||||||
|
id=pid,
|
||||||
|
pollutant=pollutant,
|
||||||
|
status=status,
|
||||||
|
owner_scenario=owner_scenario
|
||||||
|
)
|
||||||
|
if deleted:
|
||||||
|
bc.set_as_deleted()
|
||||||
|
|
||||||
|
bc.type = bc_type
|
||||||
|
|
||||||
bc.node = None
|
bc.node = None
|
||||||
if row[3] != -1:
|
if row[3] != -1:
|
||||||
tmp = next(filter(
|
tmp = next(
|
||||||
lambda n: n.id == row[3], data["nodes"]),
|
filter(
|
||||||
None)
|
lambda n: n.id == node, nodes
|
||||||
|
),
|
||||||
|
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
|
||||||
|
|
||||||
values = execute(
|
data["bca"] = bc
|
||||||
"SELECT data0, data1 FROM " +
|
bc._data = Data._db_load(execute, data=data)
|
||||||
"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(f"DELETE FROM boundary_condition_adists WHERE id = {self.id}")
|
execute(
|
||||||
execute(f"DELETE FROM boundary_condition_data_adists" +
|
"DELETE FROM boundary_condition_adists " +
|
||||||
f" WHERE bc = {self.id}")
|
f"WHERE pamhyr_id = {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
|
||||||
|
|
||||||
sql = (
|
execute(
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"boundary_condition_adists(id, pollutant, type, node) " +
|
"boundary_condition_adists(" +
|
||||||
|
"pamhyr_id, deleted, pollutant, type, " +
|
||||||
|
"node, scenario) " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, {self._pollutant}, " +
|
f"{self.id}, {self._db_format(self.is_deleted())}, " +
|
||||||
f"'{self._db_format(self._type)}', {node}" +
|
f"{self._pollutant}, " +
|
||||||
|
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:
|
||||||
data0 = self._db_format(str(d[0]))
|
data["ind"] = ind
|
||||||
data1 = self._db_format(str(d[1]))
|
|
||||||
|
|
||||||
sql = (
|
d._db_save(execute, data)
|
||||||
"INSERT INTO " +
|
|
||||||
"boundary_condition_data_adists(data0, data1, bc) " +
|
ind += 1
|
||||||
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-2024 INRAE
|
# Copyright (C) 2023-2025 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.PamhyrList import PamhyrModelList
|
from Model.Tools.PamhyrListExt import PamhyrModelList
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
from Model.BoundaryConditionsAdisTS.BoundaryConditionAdisTS \
|
from Model.BoundaryConditionsAdisTS.BoundaryConditionAdisTS \
|
||||||
@@ -45,14 +45,20 @@ class BoundaryConditionsAdisTSList(PamhyrModelList):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute("DELETE FROM boundary_condition_adists")
|
execute(
|
||||||
execute("DELETE FROM boundary_condition_data_adists")
|
"DELETE FROM boundary_condition_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 bc in self._lst:
|
for bca in self._lst:
|
||||||
bc._db_save(execute, data=data)
|
bca._db_save(execute, data=data)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# D90AdisTS.py -- Pamhyr
|
# D90AdisTS.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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,6 +23,7 @@ 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
|
||||||
|
|
||||||
@@ -33,107 +34,162 @@ 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):
|
status=None, owner_scenario=-1):
|
||||||
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 = []
|
||||||
|
|
||||||
D90AdisTS._id_cnt = max(
|
|
||||||
D90AdisTS._id_cnt + 1,
|
|
||||||
self.id
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE d90_adists(
|
CREATE TABLE d90_adists{ext}(
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
name TEXT NOT NULL,
|
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
d90 REAL NOT NULL,
|
name TEXT NOT NULL,
|
||||||
enabled BOOLEAN NOT NULL
|
d90 REAL NOT NULL,
|
||||||
)
|
enabled BOOLEAN NOT NULL,
|
||||||
""")
|
{Scenario.create_db_add_scenario()},
|
||||||
|
{Scenario.create_db_add_scenario_fk()}
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
if ext != "":
|
||||||
|
return True
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version):
|
def _db_update(cls, execute, version, data=None):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
if major == 0 and minor < 1:
|
created = False
|
||||||
|
|
||||||
|
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 id, name, d90, enabled " +
|
"SELECT pamhyr_id, deleted, name, d90, enabled, scenario " +
|
||||||
"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:
|
||||||
d90_id = row[0]
|
it = iter(row)
|
||||||
name = row[1]
|
|
||||||
d90 = row[2]
|
|
||||||
enabled = (row[3] == 1)
|
|
||||||
|
|
||||||
D90 = cls(
|
pid = next(it)
|
||||||
id=d90_id,
|
deleted = (next(it) == 1)
|
||||||
|
name = next(it)
|
||||||
|
value_d90 = next(it)
|
||||||
|
enabled = (next(it) == 1)
|
||||||
|
owner_scenario = next(it)
|
||||||
|
|
||||||
|
d90 = cls(
|
||||||
|
id=pid,
|
||||||
name=name,
|
name=name,
|
||||||
status=data['status']
|
status=status,
|
||||||
|
owner_scenario=owner_scenario
|
||||||
)
|
)
|
||||||
|
if deleted:
|
||||||
|
d90.set_as_deleted()
|
||||||
|
|
||||||
D90.d90 = d90
|
d90.d90 = value_d90
|
||||||
D90.enabled = enabled
|
d90.enabled = enabled
|
||||||
|
|
||||||
data['d90_default_id'] = d90_id
|
data['d90_default_id'] = pid
|
||||||
D90._data = D90AdisTSSpec._db_load(execute, data)
|
d90._data = D90AdisTSSpec._db_load(execute, data)
|
||||||
|
|
||||||
new.append(D90)
|
loaded.add(pid)
|
||||||
|
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):
|
||||||
execute(f"DELETE FROM d90_adists WHERE id = {self.id}")
|
if not self.must_be_saved():
|
||||||
|
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
|
||||||
|
|
||||||
sql = (
|
execute(
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"d90_adists(" +
|
"d90_adists(" +
|
||||||
"id, name, d90, enabled" +
|
"pamhyr_id, deleted, " +
|
||||||
|
"name, d90, enabled, scenario" +
|
||||||
") " +
|
") " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, '{self._db_format(self._name)}', " +
|
f"{self.id}, {self._db_format(self.is_deleted())}, " +
|
||||||
f"{d90}, {self._enabled}" +
|
f"'{self._db_format(self._name)}', " +
|
||||||
|
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_spec " +
|
"DELETE FROM d90_adists_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:
|
||||||
@@ -141,6 +197,16 @@ 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)
|
||||||
|
|
||||||
@@ -151,7 +217,7 @@ class D90AdisTS(SQLSubModel):
|
|||||||
@name.setter
|
@name.setter
|
||||||
def name(self, name):
|
def name(self, name):
|
||||||
self._name = name
|
self._name = name
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def d90(self):
|
def d90(self):
|
||||||
@@ -160,7 +226,7 @@ class D90AdisTS(SQLSubModel):
|
|||||||
@d90.setter
|
@d90.setter
|
||||||
def d90(self, d90):
|
def d90(self, d90):
|
||||||
self._d90 = d90
|
self._d90 = d90
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def enabled(self):
|
def enabled(self):
|
||||||
@@ -169,28 +235,45 @@ class D90AdisTS(SQLSubModel):
|
|||||||
@enabled.setter
|
@enabled.setter
|
||||||
def enabled(self, enabled):
|
def enabled(self, enabled):
|
||||||
self._enabled = enabled
|
self._enabled = enabled
|
||||||
self._status.modified()
|
self.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._status.modified()
|
self.modified()
|
||||||
return n
|
return n
|
||||||
|
|
||||||
def delete(self, data):
|
def delete(self, data):
|
||||||
self._data = list(
|
list(
|
||||||
filter(
|
map(
|
||||||
lambda x: x not in data,
|
lambda x: x.set_as_deleted(),
|
||||||
self._data
|
data
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
def delete_i(self, indexes):
|
def delete_i(self, indexes):
|
||||||
for ind in indexes:
|
list(
|
||||||
del self._data[ind]
|
map(
|
||||||
self._status.modified()
|
lambda e: e[1].set_as_deleted(),
|
||||||
|
filter(
|
||||||
|
lambda e: e[0] in indexes,
|
||||||
|
enumerate(self._data)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.modified()
|
||||||
|
|
||||||
def insert(self, index, data):
|
def insert(self, index, data):
|
||||||
self._data.insert(index, data)
|
if data in self._data:
|
||||||
self._status.modified()
|
self.undelete([data])
|
||||||
|
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-2024 INRAE
|
# Copyright (C) 2023-2025 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.PamhyrList import PamhyrModelList
|
from Model.Tools.PamhyrListExt import PamhyrModelList
|
||||||
from Model.D90AdisTS.D90AdisTS import D90AdisTS
|
from Model.D90AdisTS.D90AdisTS import D90AdisTS
|
||||||
|
|
||||||
|
|
||||||
@@ -42,7 +42,10 @@ class D90AdisTSList(PamhyrModelList):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute("DELETE FROM d90_adists")
|
execute(
|
||||||
|
"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-2024 INRAE
|
# Copyright (C) 2023-2025 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,21 @@ 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):
|
status=None, owner_scenario=-1):
|
||||||
super(D90AdisTSSpec, self).__init__()
|
super(D90AdisTSSpec, self).__init__(
|
||||||
|
id=id, status=status,
|
||||||
self._status = status
|
owner_scenario=owner_scenario
|
||||||
|
)
|
||||||
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
|
||||||
@@ -49,62 +45,129 @@ 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)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE d90_spec(
|
CREATE TABLE d90_adists_spec{ext}(
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
d90_default INTEGER NOT NULL,
|
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
name TEXT NOT NULL,
|
d90_default INTEGER NOT NULL,
|
||||||
reach INTEGER NOT NULL,
|
name TEXT NOT NULL,
|
||||||
start_rk REAL NOT NULL,
|
reach INTEGER NOT NULL,
|
||||||
end_rk REAL NOT NULL,
|
start_rk REAL NOT NULL,
|
||||||
d90 REAL NOT NULL,
|
end_rk REAL NOT NULL,
|
||||||
enabled BOOLEAN NOT NULL,
|
d90 REAL NOT NULL,
|
||||||
FOREIGN KEY(d90_default) REFERENCES d90_adists(id),
|
enabled BOOLEAN NOT NULL,
|
||||||
FOREIGN KEY(reach) REFERENCES river_reach(id)
|
{Scenario.create_db_add_scenario()},
|
||||||
)
|
{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):
|
def _db_update(cls, execute, version, data=None):
|
||||||
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 id, d90_default, name, reach, start_rk, end_rk, " +
|
"SELECT pamhyr_id, deleted, " +
|
||||||
"d90, enabled " +
|
f"name, reach, start_rk, end_rk, " +
|
||||||
"FROM d90_spec " +
|
"d90, enabled, scenario " +
|
||||||
f"WHERE d90_default = {data['d90_default_id']} "
|
"FROM d90_adists_spec " +
|
||||||
|
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:
|
||||||
id = row[0]
|
it = iter(row)
|
||||||
name = row[2]
|
|
||||||
reach = row[3]
|
pid = next(it)
|
||||||
start_rk = row[4]
|
deleted = (next(it) == 1)
|
||||||
end_rk = row[5]
|
name = next(it)
|
||||||
d90 = row[6]
|
reach = next(it)
|
||||||
enabled = (row[7] == 1)
|
start_rk = next(it)
|
||||||
|
end_rk = next(it)
|
||||||
|
d90 = next(it)
|
||||||
|
enabled = (next(it) == 1)
|
||||||
|
owner_scenario = next(it)
|
||||||
|
|
||||||
new_spec = cls(
|
new_spec = cls(
|
||||||
id=id,
|
id=pid,
|
||||||
name=name,
|
name=name,
|
||||||
status=data['status']
|
status=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
|
||||||
@@ -112,29 +175,36 @@ 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']
|
||||||
|
|
||||||
sql = (
|
execute(
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"d90_spec(id, d90_default, name, reach, " +
|
"d90_adists_spec(pamhyr_id, deleted, " +
|
||||||
"start_rk, end_rk, d90, enabled) " +
|
"d90_default, name, reach, " +
|
||||||
|
"start_rk, end_rk, d90, enabled, scenario) " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, " +
|
f"{self.id}, {self._db_format(self.is_deleted())}" +
|
||||||
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}, " +
|
f"{self._start_rk}, {self._end_rk}, " +
|
||||||
f"{self._end_rk}, " +
|
f"{self._d90}, {self._enabled}" +
|
||||||
f"{self._d90}, " +
|
f"{self._status.scenario_id}" +
|
||||||
f"{self._enabled}" +
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
execute(sql)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -145,7 +215,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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def reach(self):
|
def reach(self):
|
||||||
@@ -154,7 +224,7 @@ class D90AdisTSSpec(SQLSubModel):
|
|||||||
@reach.setter
|
@reach.setter
|
||||||
def reach(self, reach):
|
def reach(self, reach):
|
||||||
self._reach = reach
|
self._reach = reach
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def start_rk(self):
|
def start_rk(self):
|
||||||
@@ -163,7 +233,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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def end_rk(self):
|
def end_rk(self):
|
||||||
@@ -172,7 +242,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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def d90(self):
|
def d90(self):
|
||||||
@@ -181,7 +251,7 @@ class D90AdisTSSpec(SQLSubModel):
|
|||||||
@d90.setter
|
@d90.setter
|
||||||
def d90(self, d90):
|
def d90(self, d90):
|
||||||
self._d90 = d90
|
self._d90 = d90
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def enabled(self):
|
def enabled(self):
|
||||||
@@ -190,4 +260,4 @@ class D90AdisTSSpec(SQLSubModel):
|
|||||||
@enabled.setter
|
@enabled.setter
|
||||||
def enabled(self, enabled):
|
def enabled(self, enabled):
|
||||||
self._enabled = enabled
|
self._enabled = enabled
|
||||||
self._status.modified()
|
self.modified()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# DIFAdisTS.py -- Pamhyr
|
# DIFAdisTS.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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,6 +23,7 @@ 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
|
||||||
|
|
||||||
@@ -33,19 +34,16 @@ 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):
|
status=None, owner_scenario=-1):
|
||||||
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
|
||||||
@@ -55,76 +53,129 @@ class DIFAdisTS(SQLSubModel):
|
|||||||
self._types = ["iwasa", "fisher", "elder", "constante", "generique"]
|
self._types = ["iwasa", "fisher", "elder", "constante", "generique"]
|
||||||
self._data = []
|
self._data = []
|
||||||
|
|
||||||
DIFAdisTS._id_cnt = max(
|
|
||||||
DIFAdisTS._id_cnt + 1,
|
|
||||||
self.id
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE dif_adists(
|
CREATE TABLE dif_adists{ext}(
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
method TEXT NOT NULL,
|
method TEXT 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,
|
||||||
)
|
{Scenario.create_db_add_scenario()},
|
||||||
""")
|
{Scenario.create_db_add_scenario_fk()}
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
if ext != "":
|
||||||
|
return True
|
||||||
|
|
||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version):
|
def _db_update(cls, execute, version, data=None):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
if major == 0 and minor < 1:
|
created = False
|
||||||
|
|
||||||
|
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 id, name, method, dif, b, c, enabled " +
|
"SELECT pamhyr_id, name, method, dif, b, c, enabled, scenario " +
|
||||||
"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:
|
||||||
dif_id = row[0]
|
it = iter(row)
|
||||||
name = row[1]
|
|
||||||
method = row[2]
|
|
||||||
dif = row[3]
|
|
||||||
b = row[4]
|
|
||||||
c = row[5]
|
|
||||||
enabled = (row[6] == 1)
|
|
||||||
|
|
||||||
DIF = cls(
|
pid = next(it)
|
||||||
id=dif_id,
|
name = next(it)
|
||||||
|
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=data['status']
|
status=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'] = dif_id
|
data['dif_default_id'] = pid
|
||||||
DIF._data = DIFAdisTSSpec._db_load(execute, data)
|
dif._data = DIFAdisTSSpec._db_load(execute, data)
|
||||||
|
|
||||||
new.append(DIF)
|
loaded.add(pid)
|
||||||
|
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):
|
||||||
execute(f"DELETE FROM dif_adists WHERE id = {self.id}")
|
if not self.must_be_saved():
|
||||||
|
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:
|
||||||
@@ -145,12 +196,13 @@ class DIFAdisTS(SQLSubModel):
|
|||||||
sql = (
|
sql = (
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"dif_adists(" +
|
"dif_adists(" +
|
||||||
"id, name, method, dif, b, c, enabled" +
|
"pamhyr_id, name, method, dif, b, c, enabled, scenario" +
|
||||||
") " +
|
") " +
|
||||||
"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}, {b}, {c}, {self._enabled}" +
|
f"{dif.id}, {b}, {c}, {self._enabled}, " +
|
||||||
|
f"{self._status.scenario_id}" +
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -158,8 +210,9 @@ class DIFAdisTS(SQLSubModel):
|
|||||||
|
|
||||||
data['dif_default_id'] = self.id
|
data['dif_default_id'] = self.id
|
||||||
execute(
|
execute(
|
||||||
"DELETE FROM dif_spec " +
|
"DELETE FROM dif_adists_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:
|
||||||
@@ -167,6 +220,16 @@ 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)
|
||||||
|
|
||||||
@@ -177,7 +240,7 @@ class DIFAdisTS(SQLSubModel):
|
|||||||
@name.setter
|
@name.setter
|
||||||
def name(self, name):
|
def name(self, name):
|
||||||
self._name = name
|
self._name = name
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def method(self):
|
def method(self):
|
||||||
@@ -186,7 +249,7 @@ class DIFAdisTS(SQLSubModel):
|
|||||||
@method.setter
|
@method.setter
|
||||||
def method(self, method):
|
def method(self, method):
|
||||||
self._method = method
|
self._method = method
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def types(self):
|
def types(self):
|
||||||
@@ -199,7 +262,7 @@ class DIFAdisTS(SQLSubModel):
|
|||||||
@dif.setter
|
@dif.setter
|
||||||
def dif(self, dif):
|
def dif(self, dif):
|
||||||
self._dif = dif
|
self._dif = dif
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def b(self):
|
def b(self):
|
||||||
@@ -208,7 +271,7 @@ class DIFAdisTS(SQLSubModel):
|
|||||||
@b.setter
|
@b.setter
|
||||||
def b(self, b):
|
def b(self, b):
|
||||||
self._b = b
|
self._b = b
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def c(self):
|
def c(self):
|
||||||
@@ -217,7 +280,7 @@ class DIFAdisTS(SQLSubModel):
|
|||||||
@c.setter
|
@c.setter
|
||||||
def c(self, c):
|
def c(self, c):
|
||||||
self._c = c
|
self._c = c
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def enabled(self):
|
def enabled(self):
|
||||||
@@ -226,28 +289,45 @@ class DIFAdisTS(SQLSubModel):
|
|||||||
@enabled.setter
|
@enabled.setter
|
||||||
def enabled(self, enabled):
|
def enabled(self, enabled):
|
||||||
self._enabled = enabled
|
self._enabled = enabled
|
||||||
self._status.modified()
|
self.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._status.modified()
|
self.modified()
|
||||||
return n
|
return n
|
||||||
|
|
||||||
def delete(self, data):
|
def delete(self, data):
|
||||||
self._data = list(
|
list(
|
||||||
filter(
|
map(
|
||||||
lambda x: x not in data,
|
lambda x: x.set_as_deleted(),
|
||||||
self._data
|
data
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
def delete_i(self, indexes):
|
def delete_i(self, indexes):
|
||||||
for ind in indexes:
|
list(
|
||||||
del self._data[ind]
|
map(
|
||||||
self._status.modified()
|
lambda e: e[1].set_as_deleted(),
|
||||||
|
filter(
|
||||||
|
lambda e: e[0] in indexes,
|
||||||
|
enumerate(self._data)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.modified()
|
||||||
|
|
||||||
def insert(self, index, data):
|
def insert(self, index, data):
|
||||||
self._data.insert(index, data)
|
if data in self._data:
|
||||||
self._status.modified()
|
self.undelete([data])
|
||||||
|
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-2024 INRAE
|
# Copyright (C) 2023-2025 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.PamhyrList import PamhyrModelList
|
from Model.Tools.PamhyrListExt import PamhyrModelList
|
||||||
from Model.DIFAdisTS.DIFAdisTS import DIFAdisTS
|
from Model.DIFAdisTS.DIFAdisTS import DIFAdisTS
|
||||||
|
|
||||||
|
|
||||||
@@ -42,7 +42,10 @@ class DIFAdisTSList(PamhyrModelList):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute("DELETE FROM dif_adists")
|
execute(
|
||||||
|
"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-2024 INRAE
|
# Copyright (C) 2023-2025 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,6 +22,7 @@ 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()
|
||||||
|
|
||||||
@@ -54,63 +55,137 @@ class DIFAdisTSSpec(SQLSubModel):
|
|||||||
DIFAdisTSSpec._id_cnt = max(DIFAdisTSSpec._id_cnt + 1, self.id)
|
DIFAdisTSSpec._id_cnt = max(DIFAdisTSSpec._id_cnt + 1, self.id)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE dif_spec(
|
CREATE TABLE dif_adists_spec{ext}(
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
dif_default INTEGER NOT NULL,
|
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
method TEXT NOT NULL,
|
dif_default INTEGER NOT NULL,
|
||||||
reach INTEGER NOT NULL,
|
method TEXT NOT NULL,
|
||||||
start_rk REAL NOT NULL,
|
reach INTEGER NOT NULL,
|
||||||
end_rk REAL NOT NULL,
|
start_rk REAL NOT NULL,
|
||||||
dif REAL NOT NULL,
|
end_rk REAL NOT NULL,
|
||||||
b REAL,
|
dif REAL NOT NULL,
|
||||||
c REAL,
|
b REAL,
|
||||||
enabled BOOLEAN NOT NULL,
|
c REAL,
|
||||||
FOREIGN KEY(dif_default) REFERENCES dif_adists(id),
|
enabled BOOLEAN NOT NULL,
|
||||||
FOREIGN KEY(reach) REFERENCES river_reach(id)
|
{Scenario.create_db_add_scenario()},
|
||||||
)
|
{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):
|
def _db_update(cls, execute, version, data=None):
|
||||||
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 id, dif_default, method, reach, start_rk, end_rk, " +
|
"SELECT pamhyr_id, deleted, method, reach, start_rk, end_rk, " +
|
||||||
"dif, b, c, enabled " +
|
"dif, b, c, enabled, scenario " +
|
||||||
"FROM dif_spec " +
|
"FROM dif_adists_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:
|
||||||
id = row[0]
|
it = iter(row)
|
||||||
method = row[2]
|
|
||||||
reach = row[3]
|
id = next(it)
|
||||||
start_rk = row[4]
|
deleted = (next(it) == 1)
|
||||||
end_rk = row[5]
|
method = next(it)
|
||||||
dif = row[6]
|
reach = next(it)
|
||||||
b = row[7]
|
start_rk = next(it)
|
||||||
c = row[8]
|
end_rk = next(it)
|
||||||
enabled = (row[9] == 1)
|
dif = next(it)
|
||||||
|
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=data['status']
|
status=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
|
||||||
@@ -120,31 +195,37 @@ 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']
|
||||||
|
|
||||||
sql = (
|
execute(
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"dif_spec(id, dif_default, method, reach, " +
|
"dif_adists_spec(pamhyr_id, deleted, " +
|
||||||
"start_rk, end_rk, dif, b, c, enabled) " +
|
"dif_default, method, reach, " +
|
||||||
|
"start_rk, end_rk, dif, b, c, enabled, scenario) " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, " +
|
f"{self.id}, {self._db_format(self.is_deleted())}" +
|
||||||
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}, " +
|
f"{self._start_rk}, {self._end_rk}, " +
|
||||||
f"{self._end_rk}, " +
|
f"{self._dif}, {self._b}, {self._c}, " +
|
||||||
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-2024 INRAE
|
# Copyright (C) 2023-2025 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
|
||||||
|
|||||||
+187
-84
@@ -1,5 +1,5 @@
|
|||||||
# Friction.py -- Pamhyr
|
# Friction.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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 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
|
||||||
|
|
||||||
@@ -28,49 +29,49 @@ logger = logging.getLogger()
|
|||||||
|
|
||||||
|
|
||||||
class Friction(SQLSubModel):
|
class Friction(SQLSubModel):
|
||||||
_id_cnt = 0
|
def __init__(self, id: int = -1,
|
||||||
|
reach=None,
|
||||||
|
status=None,
|
||||||
|
owner_scenario=-1):
|
||||||
|
super(Friction, self).__init__(
|
||||||
|
id=id, status=status,
|
||||||
|
owner_scenario=owner_scenario
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
self._reach = reach
|
||||||
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, 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):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE friction(
|
CREATE TABLE friction{ext} (
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
|
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,
|
||||||
FOREIGN KEY(reach) REFERENCES river_reach(id),
|
{Scenario.create_db_add_scenario()},
|
||||||
FOREIGN KEY(begin_strickler) REFERENCES stricklers(id),
|
{Scenario.create_db_add_scenario_fk()},
|
||||||
FOREIGN KEY(end_strickler) REFERENCES stricklers(id)
|
FOREIGN KEY(reach) REFERENCES river_reach(pamhyr_id),
|
||||||
|
FOREIGN KEY(begin_strickler) REFERENCES stricklers(pamhyr_id),
|
||||||
|
FOREIGN KEY(end_strickler) REFERENCES stricklers(pamhyr_id),
|
||||||
|
PRIMARY KEY(pamhyr_id, scenario)
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
return True
|
if ext != "":
|
||||||
|
return True
|
||||||
|
|
||||||
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version):
|
def _db_update(cls, execute, version, data=None):
|
||||||
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:
|
||||||
@@ -78,49 +79,164 @@ 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")
|
||||||
|
|
||||||
return True
|
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 = "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 id, ind, begin_rk, end_rk, " +
|
"SELECT pamhyr_id, deleted, begin_rk, end_rk, " +
|
||||||
"begin_strickler, end_strickler " +
|
"begin_strickler, end_strickler, scenario " +
|
||||||
f"FROM friction WHERE reach = {reach.id}"
|
"FROM friction " +
|
||||||
|
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:
|
||||||
id = row[0]
|
it = iter(row)
|
||||||
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
|
||||||
if int(row[4]) == -1:
|
begin_strickler = None
|
||||||
bs = None
|
if begin_strickler_pid != -1:
|
||||||
else:
|
begin_strickler = next(
|
||||||
bs = next(filter(lambda s: s.id == row[4], stricklers))
|
filter(
|
||||||
|
lambda s: s.id == begin_strickler_pid,
|
||||||
|
stricklers
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if int(row[5]) == -1:
|
end_strickler = None
|
||||||
es = None
|
if end_strickler_pid != -1:
|
||||||
else:
|
end_strickler = next(
|
||||||
es = next(filter(lambda s: s.id == row[5], stricklers))
|
filter(
|
||||||
|
lambda s: s.id == begin_strickler_pid,
|
||||||
|
stricklers
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# Create friction
|
new_friction = cls(
|
||||||
sec = cls(status=status)
|
id=pid, status=data['status'],
|
||||||
sec.edge = reach
|
owner_scenario=owner_scenario
|
||||||
sec.begin_rk = row[2]
|
)
|
||||||
sec.end_rk = row[3]
|
if deleted:
|
||||||
sec.begin_strickler = bs
|
f.set_as_deleted()
|
||||||
sec.end_strickler = es
|
|
||||||
|
|
||||||
new.append((ind, sec))
|
new_friction.reach = reach
|
||||||
|
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
|
||||||
@@ -133,47 +249,34 @@ class Friction(SQLSubModel):
|
|||||||
|
|
||||||
execute(
|
execute(
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"friction(id, ind, begin_rk, end_rk, " +
|
"friction(pamhyr_id, deleted, ind, begin_rk, end_rk, " +
|
||||||
"reach, begin_strickler, end_strickler) " +
|
"reach, begin_strickler, end_strickler, scenario) " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, {ind}, " +
|
f"{self.id}, {self._db_format(self.is_deleted())}, " +
|
||||||
f"{self._begin_rk}, {self._end_rk}, " +
|
f"{ind}, {self._begin_rk}, {self._end_rk}, " +
|
||||||
f"{self._edge.id}, " +
|
f"{self._reach.id}, {b_s_id}, {e_s_id}, " +
|
||||||
f"{b_s_id}, {e_s_id}" +
|
f"{self._status.scenario_id}" +
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
return self._name
|
|
||||||
|
|
||||||
@name.setter
|
|
||||||
def name(self, name):
|
|
||||||
self._name = name
|
|
||||||
self._status.modified()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def edge(self):
|
|
||||||
return self._edge
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def reach(self):
|
def reach(self):
|
||||||
return self._edge
|
return self._reach
|
||||||
|
|
||||||
@edge.setter
|
@reach.setter
|
||||||
def edge(self, edge):
|
def reach(self, reach):
|
||||||
self._edge = edge
|
self._reach = reach
|
||||||
if (edge is not None and
|
if (reach is not None and
|
||||||
self._begin_rk == 0.0 and
|
self._begin_rk == 0.0 and
|
||||||
self._end_rk == 0.0):
|
self._end_rk == 0.0):
|
||||||
self._begin_rk = self._edge.reach.get_rk_min()
|
self._begin_rk = self._reach.reach.get_rk_min()
|
||||||
self._end_rk = self._edge.reach.get_rk_max()
|
self._end_rk = self._reach.reach.get_rk_max()
|
||||||
self._status.modified()
|
self._status.modified()
|
||||||
|
|
||||||
def has_edge(self):
|
def has_reach(self):
|
||||||
return self._edge is not None
|
return self._reach is not None
|
||||||
|
|
||||||
def has_coefficient(self):
|
def has_coefficient(self):
|
||||||
return (
|
return (
|
||||||
@@ -181,7 +284,7 @@ class Friction(SQLSubModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def is_full_defined(self):
|
def is_full_defined(self):
|
||||||
return self.has_edge() and self.has_coefficient()
|
return self.has_reach() and self.has_coefficient()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def begin_rk(self):
|
def begin_rk(self):
|
||||||
@@ -189,11 +292,11 @@ class Friction(SQLSubModel):
|
|||||||
|
|
||||||
@begin_rk.setter
|
@begin_rk.setter
|
||||||
def begin_rk(self, begin_rk):
|
def begin_rk(self, begin_rk):
|
||||||
if self._edge is None:
|
if self._reach is None:
|
||||||
self._begin_rk = begin_rk
|
self._begin_rk = begin_rk
|
||||||
else:
|
else:
|
||||||
_min = self._edge.reach.get_rk_min()
|
_min = self._reach.reach.get_rk_min()
|
||||||
_max = self._edge.reach.get_rk_max()
|
_max = self._reach.reach.get_rk_max()
|
||||||
|
|
||||||
if _min <= begin_rk <= _max:
|
if _min <= begin_rk <= _max:
|
||||||
self._begin_rk = begin_rk
|
self._begin_rk = begin_rk
|
||||||
@@ -206,11 +309,11 @@ class Friction(SQLSubModel):
|
|||||||
|
|
||||||
@end_rk.setter
|
@end_rk.setter
|
||||||
def end_rk(self, end_rk):
|
def end_rk(self, end_rk):
|
||||||
if self._edge is None:
|
if self._reach is None:
|
||||||
self._end_rk = end_rk
|
self._end_rk = end_rk
|
||||||
else:
|
else:
|
||||||
_min = self._edge.reach.get_rk_min()
|
_min = self._reach.reach.get_rk_min()
|
||||||
_max = self._edge.reach.get_rk_max()
|
_max = self._reach.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-2024 INRAE
|
# Copyright (C) 2023-2025 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,9 +20,10 @@ 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.PamhyrList import PamhyrModelList
|
from Model.Tools.PamhyrListExt import PamhyrModelList
|
||||||
|
|
||||||
from Model.Friction.Friction import Friction
|
from Model.Friction.Friction import Friction
|
||||||
|
|
||||||
@@ -39,30 +40,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):
|
def _db_update(cls, execute, version, data=None):
|
||||||
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)
|
return cls._update_submodel(execute, version, data)
|
||||||
|
|
||||||
@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'])
|
||||||
|
|
||||||
ilst = Friction._db_load(
|
new._lst = 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(f"DELETE FROM friction WHERE reach = {reach.id}")
|
execute(
|
||||||
|
f"DELETE FROM friction WHERE reach = {reach.id} " +
|
||||||
|
f"AND scenario = {self._status.scenario_id}"
|
||||||
|
)
|
||||||
|
|
||||||
ok = True
|
ok = True
|
||||||
ind = 0
|
ind = 0
|
||||||
@@ -86,19 +87,38 @@ 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, index):
|
def new(self, reach, index):
|
||||||
n = Friction(status=self._status)
|
n = Friction(reach=reach, 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
|
||||||
@@ -0,0 +1,368 @@
|
|||||||
|
# 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
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# 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-2024 INRAE
|
# Copyright (C) 2023-2025 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,14 +16,18 @@
|
|||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from Model.Tools.PamhyrDB import SQLSubModel
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
|
|
||||||
class Point(object):
|
class Point(SQLSubModel):
|
||||||
def __init__(self, name: str = "", profile=None, status=None):
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
super(Point, self).__init__()
|
profile=None, status=None,
|
||||||
|
owner_scenario=-1):
|
||||||
self._status = status
|
super(Point, self).__init__(
|
||||||
|
id=id, status=status,
|
||||||
|
owner_scenario=owner_scenario
|
||||||
|
)
|
||||||
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self._profile = profile
|
self._profile = profile
|
||||||
@@ -43,6 +47,8 @@ class Point(object):
|
|||||||
else:
|
else:
|
||||||
self._sl = sl
|
self._sl = sl
|
||||||
|
|
||||||
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
return self._name
|
return self._name
|
||||||
@@ -50,9 +56,9 @@ class Point(object):
|
|||||||
@name.setter
|
@name.setter
|
||||||
def name(self, name):
|
def name(self, name):
|
||||||
self._name = name
|
self._name = name
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
def point_is_named(self):
|
def 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-2024 INRAE
|
# Copyright (C) 2023-2025 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
|
||||||
|
|||||||
+206
-50
@@ -1,5 +1,5 @@
|
|||||||
# PointXYZ.py -- Pamhyr
|
# PointXYZ.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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,30 +16,40 @@
|
|||||||
|
|
||||||
# -*- 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, x: float = 0.0, y: float = 0.0, z: float = 0.0,
|
def __init__(self, id: int = -1,
|
||||||
name: str = "", profile=None, status=None):
|
x: float = 0.0, y: float = 0.0, z: float = 0.0,
|
||||||
|
name: str = "", profile=None,
|
||||||
|
status=None, owner_scenario=-1):
|
||||||
super(PointXYZ, self).__init__(
|
super(PointXYZ, self).__init__(
|
||||||
name=name, profile=profile, status=status)
|
id=id, 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):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE geometry_pointXYZ(
|
CREATE TABLE geometry_pointXYZ{ext} (
|
||||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
|
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,
|
||||||
@@ -47,17 +57,19 @@ class PointXYZ(Point, SQLSubModel):
|
|||||||
z INTEGER NOT NULL,
|
z INTEGER NOT NULL,
|
||||||
profile INTEGER NOT NULL,
|
profile INTEGER NOT NULL,
|
||||||
sl INTEGER,
|
sl INTEGER,
|
||||||
FOREIGN KEY(profile) REFERENCES geometry_profileXYZ(id),
|
{Scenario.create_db_add_scenario()},
|
||||||
FOREIGN KEY(sl) REFERENCES sedimentary_layer(id)
|
{Scenario.create_db_add_scenario_fk()},
|
||||||
|
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):
|
def _db_update(cls, execute, version, data=None):
|
||||||
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:
|
||||||
@@ -68,63 +80,174 @@ class PointXYZ(Point, SQLSubModel):
|
|||||||
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 ind, name, x, y, z, sl " +
|
"SELECT pamhyr_id, ind, deleted, " +
|
||||||
|
"name, x, y, z, sl, scenario " +
|
||||||
"FROM geometry_pointXYZ " +
|
"FROM geometry_pointXYZ " +
|
||||||
f"WHERE profile = {profile.id}"
|
f"WHERE profile = {profile.pamhyr_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:
|
||||||
ind = row[0]
|
it = iter(row)
|
||||||
name = row[1]
|
|
||||||
x = row[2]
|
|
||||||
y = row[3]
|
|
||||||
z = row[4]
|
|
||||||
sl = row[5]
|
|
||||||
|
|
||||||
new = cls(
|
pid = next(it)
|
||||||
|
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:
|
||||||
new._sl = None
|
point._sl = None
|
||||||
else:
|
else:
|
||||||
new._sl = next(
|
point._sl = next(
|
||||||
filter(
|
filter(
|
||||||
lambda s: s.id == sl,
|
lambda s: s.pamhyr_id == sl,
|
||||||
data["sediment_layers_list"].sediment_layers
|
data["sediment_layers_list"].sediment_layers
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
yield ind, new
|
loaded.add(pid)
|
||||||
|
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.id if self._sl is not None else -1
|
sl = self._sl.pamhyr_id if self._sl is not None else -1
|
||||||
|
|
||||||
sql = (
|
execute(
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"geometry_pointXYZ(ind, name, x, y, z, profile, sl) " +
|
"geometry_pointXYZ(pamhyr_id, deleted, ind, name, " +
|
||||||
|
"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.id}, {sl}" +
|
f"{profile.pamhyr_id}, {sl}, " +
|
||||||
|
f"{self._status.scenario_id}" +
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
execute(sql)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -150,9 +273,44 @@ class PointXYZ(Point, SQLSubModel):
|
|||||||
|
|
||||||
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
|
||||||
@@ -160,7 +318,7 @@ class PointXYZ(Point, SQLSubModel):
|
|||||||
@x.setter
|
@x.setter
|
||||||
def x(self, value):
|
def x(self, value):
|
||||||
self._x = float(value)
|
self._x = float(value)
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def y(self):
|
def y(self):
|
||||||
@@ -169,7 +327,7 @@ class PointXYZ(Point, SQLSubModel):
|
|||||||
@y.setter
|
@y.setter
|
||||||
def y(self, value):
|
def y(self, value):
|
||||||
self._y = float(value)
|
self._y = float(value)
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def z(self):
|
def z(self):
|
||||||
@@ -178,7 +336,10 @@ class PointXYZ(Point, SQLSubModel):
|
|||||||
@z.setter
|
@z.setter
|
||||||
def z(self, value):
|
def z(self, value):
|
||||||
self._z = float(value)
|
self._z = float(value)
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
|
def get_coordinate(self):
|
||||||
|
return (self._x, self._y, self._z)
|
||||||
|
|
||||||
def is_nan(self):
|
def is_nan(self):
|
||||||
"""
|
"""
|
||||||
@@ -226,8 +387,13 @@ class PointXYZ(Point, SQLSubModel):
|
|||||||
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
|
|
||||||
return (s*(s-a) * (s-b)*(s-c)) ** 0.5
|
s = float((a + b + c) / 2)
|
||||||
|
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)
|
||||||
@@ -240,13 +406,3 @@ class PointXYZ(Point, SQLSubModel):
|
|||||||
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-2024 INRAE
|
# Copyright (C) 2023-2025 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,23 +28,15 @@ 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):
|
status=None, owner_scenario=-1):
|
||||||
super(Profile, self).__init__()
|
super(Profile, self).__init__(
|
||||||
|
id=id, status=status,
|
||||||
self._status = status
|
owner_scenario=owner_scenario
|
||||||
|
)
|
||||||
if id == -1:
|
|
||||||
self.id = Profile._id_cnt
|
|
||||||
else:
|
|
||||||
self.id = id
|
|
||||||
|
|
||||||
Profile._id_cnt = max(self.id, Profile._id_cnt+1)
|
|
||||||
|
|
||||||
self._num = int(num)
|
self._num = int(num)
|
||||||
self._code1 = int(code1)
|
self._code1 = int(code1)
|
||||||
@@ -66,35 +58,50 @@ class Profile(object):
|
|||||||
return len(self.points)
|
return len(self.points)
|
||||||
|
|
||||||
def _get_points_list(self):
|
def _get_points_list(self):
|
||||||
# Points list generator is type (int, Point) with the first
|
return list(self._points)
|
||||||
# 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 self._points
|
return list(
|
||||||
|
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]
|
||||||
|
|
||||||
def named_point(self, name):
|
def named_point(self, name):
|
||||||
return next((p for p in self.points if p.name == name), None)
|
if name == "un":
|
||||||
|
return self.points[0]
|
||||||
|
elif name == "np":
|
||||||
|
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:
|
||||||
|
return next((p for p in self.points if p.name == name), None)
|
||||||
|
|
||||||
def named_point_index(self, name):
|
def named_point_index(self, name):
|
||||||
return next(
|
if name == "un":
|
||||||
(p for p in enumerate(self.points) if p[1].name == name), None
|
return 0
|
||||||
)[0]
|
elif name == "np":
|
||||||
|
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:
|
||||||
|
return next(
|
||||||
|
(p for p in enumerate(self.points) if p[1].name == name),
|
||||||
|
None
|
||||||
|
)[0]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def reach(self):
|
def reach(self):
|
||||||
@@ -111,7 +118,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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def code1(self):
|
def code1(self):
|
||||||
@@ -124,7 +131,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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def code2(self):
|
def code2(self):
|
||||||
@@ -137,7 +144,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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def nb_points(self):
|
def nb_points(self):
|
||||||
@@ -154,7 +161,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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
@@ -167,7 +174,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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sl(self):
|
def sl(self):
|
||||||
@@ -180,7 +187,7 @@ class Profile(object):
|
|||||||
@sl.setter
|
@sl.setter
|
||||||
def sl(self, value):
|
def sl(self, value):
|
||||||
self._sl = value
|
self._sl = value
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def profile_type(self):
|
def profile_type(self):
|
||||||
@@ -193,7 +200,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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
def point(self, i: int):
|
def point(self, i: int):
|
||||||
if i < len(self.points):
|
if i < len(self.points):
|
||||||
@@ -208,7 +215,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.point_is_named()]
|
if 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.
|
||||||
@@ -220,21 +227,25 @@ class Profile(object):
|
|||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
"""
|
"""
|
||||||
self.points.insert(index, point)
|
if point in self._points:
|
||||||
self._status.modified()
|
point.set_as_not_deleted()
|
||||||
|
else:
|
||||||
|
self._points.insert(index, point)
|
||||||
|
|
||||||
|
self.modified()
|
||||||
|
|
||||||
def delete_i(self, indexes: list):
|
def delete_i(self, indexes: list):
|
||||||
self._points = list(
|
list(
|
||||||
map(
|
map(
|
||||||
lambda e: e[1],
|
lambda e: e[1].set_as_deleted(),
|
||||||
filter(
|
filter(
|
||||||
lambda e: e[0] not in indexes,
|
lambda e: e[0] in indexes,
|
||||||
enumerate(self.points)
|
enumerate(self.points)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
def delete_points(self, points):
|
def delete_points(self, points):
|
||||||
"""Delete some elements in profile list
|
"""Delete some elements in profile list
|
||||||
@@ -245,31 +256,31 @@ class Profile(object):
|
|||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
"""
|
"""
|
||||||
self._points = list(
|
list(
|
||||||
filter(
|
map(
|
||||||
lambda p: p not in points,
|
lambda p: p.set_as_deleted(),
|
||||||
self.points
|
points
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self._status.modified()
|
self.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._status.modified()
|
self.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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
# Sort
|
# Sort
|
||||||
|
|
||||||
@@ -282,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._status.modified()
|
self.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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@timer
|
@timer
|
||||||
def reverse(self):
|
def reverse(self):
|
||||||
self._points.reverse()
|
self._points.reverse()
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
# Sediment Layers
|
# Sediment Layers
|
||||||
|
|
||||||
|
|||||||
+343
-131
@@ -1,5 +1,5 @@
|
|||||||
# ProfileXYZ.py -- Pamhyr
|
# ProfileXYZ.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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,17 +16,20 @@
|
|||||||
|
|
||||||
# -*- 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
|
from tools import timer, flatten
|
||||||
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
|
||||||
@@ -48,13 +51,14 @@ 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):
|
status=None, owner_scenario=-1):
|
||||||
"""ProfileXYZ constructor
|
"""ProfileXYZ constructor
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -69,15 +73,16 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
"""
|
"""
|
||||||
super(ProfileXYZ, self).__init__(
|
super(ProfileXYZ, self).__init__(
|
||||||
id=id,
|
id=id,
|
||||||
num=num,
|
|
||||||
name=name,
|
name=name,
|
||||||
rk=rk,
|
num=num, 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
|
||||||
@@ -87,10 +92,11 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
self.station_up_to_date = False
|
self.station_up_to_date = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE geometry_profileXYZ(
|
CREATE TABLE geometry_profileXYZ{ext} (
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
|
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,
|
||||||
@@ -99,15 +105,21 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
code1 INTEGER NOT NULL,
|
code1 INTEGER NOT NULL,
|
||||||
code2 INTEGER NOT NULL,
|
code2 INTEGER NOT NULL,
|
||||||
sl INTEGER,
|
sl INTEGER,
|
||||||
FOREIGN KEY(reach) REFERENCES river_reach(id),
|
{Scenario.create_db_add_scenario()},
|
||||||
FOREIGN KEY(sl) REFERENCES sedimentary_layer(id)
|
{Scenario.create_db_add_scenario_fk()},
|
||||||
|
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):
|
def _db_update(cls, execute, version, data=None):
|
||||||
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)
|
||||||
@@ -128,83 +140,215 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
return cls._update_submodel(execute, version)
|
cls._db_update_to_0_2_0(execute, data)
|
||||||
|
|
||||||
|
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):
|
||||||
profiles = []
|
new = []
|
||||||
|
|
||||||
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 id, ind, name, rk, num, code1, code2, sl " +
|
"SELECT pamhyr_id, ind, deleted, name, rk, num, " +
|
||||||
|
"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:
|
||||||
id = row[0]
|
it = iter(row)
|
||||||
ind = row[1]
|
|
||||||
name = row[2]
|
|
||||||
rk = row[3]
|
|
||||||
num = row[5]
|
|
||||||
code1 = row[5]
|
|
||||||
code2 = row[6]
|
|
||||||
sl = row[7]
|
|
||||||
|
|
||||||
new = cls(
|
pid = next(it)
|
||||||
id=id, num=num,
|
ind = next(it)
|
||||||
|
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:
|
||||||
new._sl = None
|
profile._sl = None
|
||||||
else:
|
else:
|
||||||
new._sl = next(
|
profile._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
|
||||||
|
)
|
||||||
|
|
||||||
data["profile"] = new
|
loaded_save = data['loaded_pid']
|
||||||
new._points = PointXYZ._db_load(execute, data.copy())
|
data['loaded_pid'] = set()
|
||||||
|
data["profile"] = profile
|
||||||
|
|
||||||
yield ind, new
|
profile._points = list(
|
||||||
|
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.id if self._sl is not None else -1
|
sl = self._sl.pamhyr_id if self._sl is not None else -1
|
||||||
|
|
||||||
sql = (
|
execute(
|
||||||
"INSERT OR REPLACE INTO " +
|
"INSERT OR REPLACE INTO " +
|
||||||
"geometry_profileXYZ(id, ind, name, reach, " +
|
"geometry_profileXYZ(pamhyr_id, deleted, ind, name, reach, " +
|
||||||
"rk, num, code1, code2, sl) " +
|
"rk, num, code1, code2, sl, scenario) " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, {ind}, '{self._db_format(self._name)}', " +
|
f"{self.pamhyr_id}, {self._db_format(self.is_deleted())}, " +
|
||||||
f"{self.reach.id}, {self.rk}, {self.num}, " +
|
f"{ind}, '{self._db_format(self._name)}', " +
|
||||||
f"{self.code1}, {self.code1}, {sl}" +
|
f"{self.reach.pamhyr_id}, {self.rk}, {self.num}, " +
|
||||||
|
f"{self.code1}, {self.code1}, {sl}, {self._status.scenario_id}" +
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
execute(sql)
|
|
||||||
|
|
||||||
points = self.points
|
|
||||||
|
|
||||||
data["profile"] = self
|
data["profile"] = self
|
||||||
execute(f"DELETE FROM geometry_pointXYZ WHERE profile = {self.id}")
|
execute(
|
||||||
|
"DELETE FROM geometry_pointXYZ " +
|
||||||
|
f"WHERE profile = {self.pamhyr_id} " +
|
||||||
|
f"AND scenario = {self._status.scenario_id}"
|
||||||
|
)
|
||||||
|
|
||||||
ind = 0
|
ind = 0
|
||||||
for point in points:
|
for point in self._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
|
||||||
@@ -237,6 +381,47 @@ 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(
|
||||||
@@ -277,6 +462,12 @@ 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]
|
||||||
|
|
||||||
@@ -317,11 +508,20 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
Nothing.
|
Nothing.
|
||||||
"""
|
"""
|
||||||
for point in list_points:
|
for point in list_points:
|
||||||
pt = PointXYZ(*point, profile=self, status=self._status)
|
named_args = {
|
||||||
self.points.append(pt)
|
"x": point[0],
|
||||||
self._status.modified()
|
"y": point[1],
|
||||||
self.tab_up_to_date = False
|
"z": point[2],
|
||||||
self.station_up_to_date = False
|
"name": point[3],
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -380,11 +580,13 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
"""
|
"""
|
||||||
point_xyz = PointXYZ(0., 0., 0., profile=self, status=self._status)
|
point_xyz = PointXYZ(
|
||||||
|
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.tab_up_to_date = False
|
self.modified()
|
||||||
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.
|
||||||
@@ -395,11 +597,16 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
Returns:
|
Returns:
|
||||||
The new point.
|
The new point.
|
||||||
"""
|
"""
|
||||||
point = PointXYZ(0., 0., 0., profile=self, status=self._status)
|
x, y, z = (0., 0., 0.)
|
||||||
self.points.insert(index, point)
|
|
||||||
self._status.modified()
|
if 0 < len(self._points) >= index:
|
||||||
self.tab_up_to_date = False
|
x, y, z = self._points[index - 1].get_coordinate()
|
||||||
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):
|
||||||
@@ -542,8 +749,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], zz[i+1]],
|
[zz[i+1], zz[i]],
|
||||||
[station[i], station[i+1]]
|
[station[i+1], station[i]]
|
||||||
)
|
)
|
||||||
line.append([y, z])
|
line.append([y, z])
|
||||||
|
|
||||||
@@ -632,11 +839,8 @@ 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:
|
||||||
y = np.interp(
|
fact = (z - zz[i]) / (zz[i+1] - zz[i])
|
||||||
z,
|
y = station[i] + fact * (station[i+1] - station[i])
|
||||||
[zz[i], zz[i+1]],
|
|
||||||
[station[i], station[i+1]]
|
|
||||||
)
|
|
||||||
start.append(y)
|
start.append(y)
|
||||||
|
|
||||||
end = []
|
end = []
|
||||||
@@ -645,11 +849,8 @@ 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:
|
||||||
y = np.interp(
|
fact = (z - zz[i]) / (zz[i+1] - zz[i])
|
||||||
z,
|
y = station[i] + fact * (station[i+1] - station[i])
|
||||||
[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):
|
||||||
@@ -680,33 +881,31 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
|
|
||||||
# Interpolate points at river left side
|
# Interpolate points at river left side
|
||||||
if (i_left > 0):
|
if (i_left > 0):
|
||||||
x = np.interp(
|
if abs(self.point(i_left - 1).z - self.point(i_left).z) < 1e-20:
|
||||||
z,
|
pt_left = self.point(i_left)
|
||||||
[self.point(i_left).z, self.point(i_left - 1).z],
|
else:
|
||||||
[self.point(i_left).x, self.point(i_left - 1).x]
|
fact = (z - self.point(i_left).z) / \
|
||||||
)
|
(self.point(i_left - 1).z - self.point(i_left).z)
|
||||||
y = np.interp(
|
x = self.point(i_left).x + fact * \
|
||||||
z,
|
(self.point(i_left - 1).x - self.point(i_left).x)
|
||||||
[self.point(i_left).z, self.point(i_left - 1).z],
|
y = self.point(i_left).y + fact * \
|
||||||
[self.point(i_left).y, self.point(i_left - 1).y]
|
(self.point(i_left - 1).y - self.point(i_left).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):
|
||||||
x = np.interp(
|
if abs(self.point(i_right + 1).z - self.point(i_right).z) < 1e-20:
|
||||||
z,
|
pt_right = self.point(i_right)
|
||||||
[self.point(i_right).z, self.point(i_right + 1).z],
|
else:
|
||||||
[self.point(i_right).x, self.point(i_right + 1).x]
|
fact = (z - self.point(i_right).z) / \
|
||||||
)
|
(self.point(i_right + 1).z - self.point(i_right).z)
|
||||||
y = np.interp(
|
x = self.point(i_right).x + fact * \
|
||||||
z,
|
(self.point(i_right + 1).x - self.point(i_right).x)
|
||||||
[self.point(i_right).z, self.point(i_right + 1).z],
|
y = self.point(i_right).y + fact * \
|
||||||
[self.point(i_right).y, self.point(i_right + 1).y]
|
(self.point(i_right + 1).y - self.point(i_right).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)
|
||||||
|
|
||||||
@@ -754,13 +953,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.point_is_named():
|
if 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.point_is_named():
|
if point.is_named():
|
||||||
last_named_point = point
|
last_named_point = point
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -831,54 +1030,56 @@ 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 = 2 # we consider the first and last point as named
|
nb_named = 0 # we consider the first and last point as named
|
||||||
area = [0.0]
|
area = []
|
||||||
|
|
||||||
for i in range(1, self.nb_points-1):
|
for i, p in enumerate(self.points):
|
||||||
if self.point(i).point_is_named():
|
if p.is_named() or i == 0 or i == self.nb_points - 1:
|
||||||
area.append(9999999.999)
|
area.append((9999999.999, p))
|
||||||
nb_named += 1
|
nb_named += 1
|
||||||
else:
|
else:
|
||||||
area.append(
|
area.append((
|
||||||
PointXYZ.areatriangle3d(
|
PointXYZ.areatriangle3d(
|
||||||
self.point(i-1),
|
self.point(i-1),
|
||||||
self.point(i),
|
p,
|
||||||
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):
|
||||||
to_rm = np.argmin(area[1:self.nb_points - 1]) + 1
|
ind, (min_area, p) = min(
|
||||||
|
enumerate(area),
|
||||||
|
key=lambda t: t[1][0]
|
||||||
|
)
|
||||||
|
|
||||||
self.delete_i([to_rm])
|
p.set_as_deleted()
|
||||||
area.pop(to_rm)
|
area.pop(ind)
|
||||||
|
|
||||||
for i in [to_rm-1, to_rm]:
|
for i in [ind-1, ind]:
|
||||||
if (i == 0):
|
p = self.point(i)
|
||||||
continue
|
|
||||||
|
|
||||||
if (i == self.nb_points - 1):
|
if p.is_named() or i == 0 or i == self.nb_points - 1:
|
||||||
continue
|
area[i] = (9999999.999, p)
|
||||||
|
|
||||||
if self.point(i).point_is_named():
|
|
||||||
area[i] = 9999999.999
|
|
||||||
else:
|
else:
|
||||||
area[i] = PointXYZ.areatriangle3d(
|
area[i] = (
|
||||||
self.point(i-1),
|
PointXYZ.areatriangle3d(
|
||||||
self.point(i),
|
self.point(i-1), p, self.point(i+1)
|
||||||
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
|
||||||
@@ -900,14 +1101,10 @@ class ProfileXYZ(Profile, SQLSubModel):
|
|||||||
self.point(i+1).z = 0.5 * self.point(i).z + 0.5 * self.point(i+2).z
|
self.point(i+1).z = 0.5 * self.point(i).z + 0.5 * self.point(i+2).z
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
p = ProfileXYZ(self.id,
|
p = ProfileXYZ(name=self.name,
|
||||||
self.name,
|
rk=self.rk,
|
||||||
self.rk,
|
reach=self.reach,
|
||||||
self.reach,
|
status=self._status)
|
||||||
self.num,
|
|
||||||
0,
|
|
||||||
0, 0,
|
|
||||||
self._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())
|
||||||
|
|
||||||
@@ -925,3 +1122,18 @@ 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()
|
||||||
+146
-84
@@ -1,5 +1,5 @@
|
|||||||
# Reach.py -- Pamhyr
|
# Reach.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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,7 +45,6 @@ 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] = []
|
||||||
@@ -53,41 +52,59 @@ 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):
|
def _db_update(cls, execute, version, data=None):
|
||||||
return cls._update_submodel(execute, version)
|
return cls._update_submodel(execute, version, data)
|
||||||
|
|
||||||
@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 = ProfileXYZ._db_load(
|
new._profiles = sorted(
|
||||||
execute,
|
ProfileXYZ._db_load(
|
||||||
data=data.copy()
|
execute,
|
||||||
|
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):
|
||||||
profiles = self.profiles
|
ok = True
|
||||||
|
|
||||||
# Delete old data
|
execute(
|
||||||
execute(f"DELETE FROM geometry_profileXYZ WHERE reach = {self.id}")
|
"DELETE FROM geometry_profileXYZ " +
|
||||||
|
f"WHERE reach = {self.pamhyr_id} " +
|
||||||
if data is None:
|
f"AND scenario = {self._status.scenario_id}"
|
||||||
data = {}
|
)
|
||||||
|
|
||||||
ind = 0
|
ind = 0
|
||||||
for profile in profiles:
|
for profile in self._profiles:
|
||||||
data["ind"] = ind
|
data["ind"] = ind
|
||||||
profile._db_save(execute, data)
|
ok &= profile._db_save(execute, data)
|
||||||
ind += 1
|
ind += 1
|
||||||
|
|
||||||
return True
|
return ok
|
||||||
|
|
||||||
|
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
|
||||||
@@ -111,31 +128,18 @@ 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(
|
return list(self._profiles)
|
||||||
map(
|
|
||||||
lambda p: p[1],
|
|
||||||
sorted(
|
|
||||||
self._profiles,
|
|
||||||
key=lambda p: p[0]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
if not isinstance(self._profiles, list):
|
return len(self.profiles)
|
||||||
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 self._profiles
|
return list(filter(lambda p: not p.is_deleted(), self._profiles))
|
||||||
|
|
||||||
@profiles.setter
|
@profiles.setter
|
||||||
def profiles(self, profiles):
|
def profiles(self, profiles):
|
||||||
@@ -163,15 +167,6 @@ 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
|
||||||
|
|
||||||
@@ -183,10 +178,9 @@ 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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
return profile
|
return profile
|
||||||
|
|
||||||
@@ -199,9 +193,16 @@ class Reach(SQLSubModel):
|
|||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
"""
|
"""
|
||||||
self.profiles.insert(index, profile)
|
if profile in self._profiles:
|
||||||
self._update_profile_numbers()
|
self.undelete([profile])
|
||||||
self._status.modified()
|
profile.num = index
|
||||||
|
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
|
||||||
@@ -212,24 +213,38 @@ class Reach(SQLSubModel):
|
|||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
"""
|
"""
|
||||||
profiles = set(
|
gi = [self.get_global_profil_index(i) for i in indexes]
|
||||||
|
list(
|
||||||
map(
|
map(
|
||||||
lambda e: e[1],
|
lambda p: p[1].set_as_deleted(),
|
||||||
filter(
|
filter(
|
||||||
lambda e: e[0] in indexes,
|
lambda e: e[0] in gi,
|
||||||
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(
|
||||||
filter(
|
map(
|
||||||
lambda p: p not in profiles,
|
lambda p: p[1],
|
||||||
self.profiles
|
filter(
|
||||||
|
lambda e: e[0] not in gi,
|
||||||
|
enumerate(self._profiles)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self._update_profile_numbers()
|
self.modified()
|
||||||
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
|
||||||
@@ -240,35 +255,41 @@ class Reach(SQLSubModel):
|
|||||||
Returns:
|
Returns:
|
||||||
Nothing.
|
Nothing.
|
||||||
"""
|
"""
|
||||||
self._profiles = list(
|
list(
|
||||||
filter(
|
map(
|
||||||
lambda p: p not in profiles,
|
lambda p: p.set_as_deleted(),
|
||||||
self.profiles
|
profiles
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self._update_profile_numbers()
|
self.modified()
|
||||||
self._status.modified()
|
|
||||||
|
def undelete(self, lst):
|
||||||
|
for el in lst:
|
||||||
|
el.set_as_not_deleted()
|
||||||
|
|
||||||
|
self.modified()
|
||||||
|
|
||||||
def purge(self):
|
def purge(self):
|
||||||
self._profiles = []
|
for el in self._profiles:
|
||||||
self._update_profile_numbers()
|
el.set_as_deleted()
|
||||||
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._status.modified()
|
self.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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
def get_x(self):
|
def get_x(self):
|
||||||
return list(
|
return list(
|
||||||
@@ -496,7 +517,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._status.modified()
|
self.modified()
|
||||||
return (complete, incomplete)
|
return (complete, incomplete)
|
||||||
|
|
||||||
def _map_guidelines_points(self, func, full=False):
|
def _map_guidelines_points(self, func, full=False):
|
||||||
@@ -537,28 +558,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._status.modified()
|
self.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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
# Import/Export
|
# Import/Export
|
||||||
|
|
||||||
@@ -614,8 +635,7 @@ 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
|
||||||
@@ -633,6 +653,8 @@ 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",
|
||||||
@@ -645,12 +667,15 @@ class Reach(SQLSubModel):
|
|||||||
d[profile_header[i]] = data
|
d[profile_header[i]] = data
|
||||||
|
|
||||||
prof = ProfileXYZ(
|
prof = ProfileXYZ(
|
||||||
**d, reach=self, status=self._status
|
id=-1,
|
||||||
|
**d,
|
||||||
|
reach=self,
|
||||||
|
status=self._status
|
||||||
)
|
)
|
||||||
prof.import_points(profile)
|
prof.import_points(profile)
|
||||||
|
|
||||||
imported_profiles.append(prof)
|
imported_profiles.append(prof)
|
||||||
self._status.modified()
|
self.modified()
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
exception_message_box(e)
|
exception_message_box(e)
|
||||||
@@ -658,9 +683,8 @@ class Reach(SQLSubModel):
|
|||||||
logger.error(e)
|
logger.error(e)
|
||||||
e.alert()
|
e.alert()
|
||||||
finally:
|
finally:
|
||||||
self.profiles = imported_profiles + self.profiles
|
self._profiles = self._profiles + imported_profiles
|
||||||
|
|
||||||
self._update_profile_numbers()
|
|
||||||
return imported_profiles
|
return imported_profiles
|
||||||
|
|
||||||
@timer
|
@timer
|
||||||
@@ -894,3 +918,41 @@ 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-2024 INRAE
|
# Copyright (C) 2023-2025 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-2024 INRAE
|
# Copyright (C) 2023-2025 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 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 (
|
||||||
@@ -34,49 +35,97 @@ 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):
|
status=None, owner_scenario=-1):
|
||||||
super(BasicHS, self).__init__()
|
super(BasicHS, self).__init__(
|
||||||
|
id=id, status=status,
|
||||||
self._status = status
|
owner_scenario=owner_scenario
|
||||||
|
)
|
||||||
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)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE hydraulic_structures_basic(
|
CREATE TABLE hydraulic_structures_basic{ext} (
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
|
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,
|
||||||
FOREIGN KEY(hs) REFERENCES hydraulic_structures(id)
|
{Scenario.create_db_add_scenario()},
|
||||||
|
{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):
|
def _db_update(cls, execute, version, data=None):
|
||||||
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 True
|
if major == "0" and minor == "1":
|
||||||
|
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):
|
||||||
@@ -94,58 +143,85 @@ 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 id, name, type, enabled, hs " +
|
"SELECT pamhyr_id, deleted, name, type, enabled, hs, scenario " +
|
||||||
"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:
|
||||||
bhs_id = row[0]
|
it = iter(row)
|
||||||
name = row[1]
|
|
||||||
type = row[2]
|
bhs_pid = next(it)
|
||||||
enabled = (row[3] == 1)
|
deleted = (next(it) == 1)
|
||||||
hs_id = row[4]
|
name = next(it)
|
||||||
|
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_id,
|
id=bhs_pid, name=name,
|
||||||
name=name,
|
status=status, owner_scenario=owner_scenario
|
||||||
status=data['status']
|
|
||||||
)
|
)
|
||||||
|
if deleted:
|
||||||
|
bhs.set_as_deleted()
|
||||||
|
|
||||||
bhs.enabled = enabled
|
bhs.enabled = enabled
|
||||||
|
|
||||||
data['bhs_id'] = bhs_id
|
data['bhs_id'] = bhs_pid
|
||||||
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']
|
||||||
|
|
||||||
sql = (
|
execute(
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"hydraulic_structures_basic(id, name, type, enabled, hs) " +
|
"hydraulic_structures_basic(pamhyr_id, deleted, " +
|
||||||
|
"name, type, enabled, hs, scenario) " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, " +
|
f"{self.pamhyr_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.id
|
data['bhs_id'] = self.pamhyr_id
|
||||||
execute(
|
execute(
|
||||||
"DELETE FROM hydraulic_structures_basic_value " +
|
"DELETE FROM hydraulic_structures_basic_value " +
|
||||||
f"WHERE bhs = {self.id}"
|
f"WHERE bhs = {self.pamhyr_id} " +
|
||||||
|
f"AND scenario = {self._status.scenario_id}"
|
||||||
)
|
)
|
||||||
|
|
||||||
for values in self._data:
|
for values in self._data:
|
||||||
@@ -153,20 +229,30 @@ 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.id + 1}"
|
return f"{self._type} #{self.pamhyr_id}"
|
||||||
|
|
||||||
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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self):
|
def type(self):
|
||||||
@@ -175,7 +261,7 @@ class BasicHS(SQLSubModel):
|
|||||||
@type.setter
|
@type.setter
|
||||||
def type(self, type):
|
def type(self, type):
|
||||||
self._type = type
|
self._type = type
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def enabled(self):
|
def enabled(self):
|
||||||
@@ -184,7 +270,7 @@ class BasicHS(SQLSubModel):
|
|||||||
@enabled.setter
|
@enabled.setter
|
||||||
def enabled(self, enabled):
|
def enabled(self, enabled):
|
||||||
self._enabled = enabled
|
self._enabled = enabled
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def parameters(self):
|
def parameters(self):
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Types.py -- Pamhyr
|
# Types.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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,10 +27,11 @@ 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):
|
status=None, owner_scenario=-1):
|
||||||
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"
|
||||||
@@ -39,197 +40,208 @@ 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):
|
status=None, owner_scenario=-1):
|
||||||
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("width", float, 1.0, status=status),
|
BHSValue(-1, "width", float, 1.0, status=status),
|
||||||
BHSValue("elevation", float, 1.0, status=status),
|
BHSValue(-1, "elevation", float, 1.0, status=status),
|
||||||
BHSValue("discharge_coefficient", float, 0.4, status=status),
|
BHSValue(-1, "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):
|
status=None, owner_scenario=-1):
|
||||||
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("width", float, 1.0, status=status),
|
BHSValue(-1, "width", float, 1.0, status=status),
|
||||||
BHSValue("elevation", float, 1.0, status=status),
|
BHSValue(-1, "elevation", float, 1.0, status=status),
|
||||||
BHSValue("loading_elevation", float, 9999.999, status=status),
|
BHSValue(-1, "loading_elevation", float, 9999.999, status=status),
|
||||||
BHSValue("discharge_coefficient", float, 0.4, status=status),
|
BHSValue(-1, "discharge_coefficient", float, 0.4, status=status),
|
||||||
BHSValue("half-angle_tangent", float, 0.0, status=status),
|
BHSValue(-1, "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):
|
status=None, owner_scenario=-1):
|
||||||
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("elevation", float, 1.0, status=status),
|
BHSValue(-1, "elevation", float, 1.0, status=status),
|
||||||
BHSValue("loading_elevation", float, 9999.999, status=status),
|
BHSValue(-1, "loading_elevation", float, 9999.999, status=status),
|
||||||
BHSValue("discharge_coefficient", float, 0.31, status=status),
|
BHSValue(-1, "discharge_coefficient", float, 0.31, status=status),
|
||||||
BHSValue("half-angle_tangent", float, 1.0, status=status),
|
BHSValue(-1, "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):
|
status=None, owner_scenario=-1):
|
||||||
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("width", float, 0.0, status=status),
|
BHSValue(-1, "width", float, 0.0, status=status),
|
||||||
BHSValue("elevation", float, 0.0, status=status),
|
BHSValue(-1, "elevation", float, 0.0, status=status),
|
||||||
BHSValue("loading_elevation", float, 9999.999,
|
BHSValue(-1, "loading_elevation", float, 9999.999,
|
||||||
status=status),
|
status=status),
|
||||||
BHSValue("discharge_coefficient", float, 0.4, status=status),
|
BHSValue(-1, "discharge_coefficient", float, 0.4, status=status),
|
||||||
BHSValue("maximal_loading_elevation", float, 9999.999,
|
BHSValue(-1, "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):
|
status=None, owner_scenario=-1):
|
||||||
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("diameter", float, 0.0, status=status),
|
BHSValue(-1, "diameter", float, 0.0, status=status),
|
||||||
BHSValue("elevation", float, 0.0, status=status),
|
BHSValue(-1, "elevation", float, 0.0, status=status),
|
||||||
BHSValue("siltation_height", float, 0.0, status=status),
|
BHSValue(-1, "siltation_height", float, 0.0, status=status),
|
||||||
BHSValue("discharge_coefficient", float, 0.4, status=status),
|
BHSValue(-1, "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):
|
status=None, owner_scenario=-1):
|
||||||
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("elevation", float, 1.0, status=status),
|
BHSValue(-1, "elevation", float, 1.0, status=status),
|
||||||
BHSValue("width", float, 1.0, status=status),
|
BHSValue(-1, "width", float, 1.0, status=status),
|
||||||
BHSValue("top_of_the_vault", float, 0.0, status=status),
|
BHSValue(-1, "top_of_the_vault", float, 0.0, status=status),
|
||||||
BHSValue("bottom_of_the_vault", float, 0.0, status=status),
|
BHSValue(-1, "bottom_of_the_vault", float, 0.0, status=status),
|
||||||
BHSValue("discharge_coefficient", float, 0.4, status=status),
|
BHSValue(-1, "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):
|
status=None, owner_scenario=-1):
|
||||||
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("width", float, 1.0, status=status),
|
BHSValue(-1, "width", float, 1.0, status=status),
|
||||||
BHSValue("elevation", float, 0.0, status=status),
|
BHSValue(-1, "elevation", float, 0.0, status=status),
|
||||||
BHSValue("discharge coefficient", float, 0.4, status=status),
|
BHSValue(-1, "discharge coefficient", float, 0.4, status=status),
|
||||||
BHSValue("opening", float, 1.0, status=status),
|
BHSValue(-1, "opening", float, 1.0, status=status),
|
||||||
BHSValue("maximal_opening", float, 1.0, status=status),
|
BHSValue(-1, "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):
|
status=None, owner_scenario=-1):
|
||||||
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("width", float, 1.0, status=status),
|
BHSValue(-1, "width", float, 1.0, status=status),
|
||||||
BHSValue("elevation", float, 0.0, status=status),
|
BHSValue(-1, "elevation", float, 0.0, status=status),
|
||||||
BHSValue("discharge coefficient", float, 0.4, status=status),
|
BHSValue(-1, "discharge coefficient", float, 0.4, status=status),
|
||||||
BHSValue("opening", float, 1.0, status=status),
|
BHSValue(-1, "opening", float, 1.0, status=status),
|
||||||
BHSValue("maximal_opening", float, 1.0, status=status),
|
BHSValue(-1, "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):
|
status=None, owner_scenario=-1):
|
||||||
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("step_space", float, 0.1, status=status),
|
BHSValue(-1, "step_space", float, 0.1, status=status),
|
||||||
BHSValue("weir", float, 0.15, status=status),
|
BHSValue(-1, "weir", float, 0.15, status=status),
|
||||||
BHSValue("coefficient", float, 0.4, status=status),
|
BHSValue(-1, "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):
|
status=None, owner_scenario=-1):
|
||||||
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("width", float, 0.0, status=status),
|
BHSValue(-1, "width", float, 0.0, status=status),
|
||||||
BHSValue("elevation", float, 0.0, status=status),
|
BHSValue(-1, "elevation", float, 0.0, status=status),
|
||||||
BHSValue("loading_elevation", float, 9999.999,
|
BHSValue(-1, "loading_elevation", float, 9999.999,
|
||||||
status=status),
|
status=status),
|
||||||
BHSValue("discharge_coefficient", float, 0.4, status=status),
|
BHSValue(-1, "discharge_coefficient", float, 0.4, status=status),
|
||||||
BHSValue("maximal_loading_elevation", float, 9999.999,
|
BHSValue(-1, "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):
|
status=None, owner_scenario=-1):
|
||||||
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("parameter_1", float, 0.0, status=status),
|
BHSValue(-1, "parameter_1", float, 0.0, status=status),
|
||||||
BHSValue("parameter_2", float, 0.0, status=status),
|
BHSValue(-1, "parameter_2", float, 0.0, status=status),
|
||||||
BHSValue("parameter_3", float, 0.0, status=status),
|
BHSValue(-1, "parameter_3", float, 0.0, status=status),
|
||||||
BHSValue("parameter_4", float, 0.0, status=status),
|
BHSValue(-1, "parameter_4", float, 0.0, status=status),
|
||||||
BHSValue("parameter_5", float, 0.0, status=status),
|
BHSValue(-1, "parameter_5", float, 0.0, status=status),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Value.py -- Pamhyr
|
# Value.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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,45 +17,92 @@
|
|||||||
# -*- 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, name: str = "", type=float, value=0.0,
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
status=None):
|
type=float, value=0.0,
|
||||||
super(BHSValue, self).__init__()
|
status=None, owner_scenario=-1):
|
||||||
|
super(BHSValue, self).__init__(
|
||||||
self._status = status
|
id=id, 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):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE hydraulic_structures_basic_value(
|
CREATE TABLE hydraulic_structures_basic_value{ext} (
|
||||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
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,
|
||||||
FOREIGN KEY(bhs) REFERENCES hydraulic_structures_basic(id)
|
{Scenario.create_db_add_scenario()},
|
||||||
|
{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):
|
def _db_update(cls, execute, version, data=None):
|
||||||
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 True
|
return cls._update_submodel(execute, version, data)
|
||||||
|
|
||||||
|
@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):
|
||||||
@@ -86,44 +133,62 @@ 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 name, type, value " +
|
"SELECT pamhyr_id, name, type, value, scenario " +
|
||||||
"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:
|
||||||
name = row[0]
|
it = iter(row)
|
||||||
type = cls._str_to_type(row[1])
|
|
||||||
value = row[2]
|
pid = next(it)
|
||||||
|
name = next(it)
|
||||||
|
type = cls._str_to_type(next(it))
|
||||||
|
value = next(it)
|
||||||
|
owner_scenario = next(it)
|
||||||
|
|
||||||
val = cls(
|
val = cls(
|
||||||
name=name,
|
id=pid, name=name,
|
||||||
type=type,
|
type=type, value=value,
|
||||||
value=value,
|
status=status, owner_scenario=owner_scenario
|
||||||
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"]
|
||||||
|
|
||||||
sql = (
|
execute(
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"hydraulic_structures_basic_value(name, type, value, bhs) " +
|
"hydraulic_structures_basic_value" +
|
||||||
|
"(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
|
||||||
|
|
||||||
@@ -142,4 +207,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._status.modified()
|
self.modified()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# HydraulicStructures.py -- Pamhyr
|
# HydraulicStructures.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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,6 +22,7 @@ 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
|
||||||
@@ -36,162 +37,289 @@ 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):
|
status=None, owner_scenario=-1):
|
||||||
super(HydraulicStructure, self).__init__()
|
super(HydraulicStructure, self).__init__(
|
||||||
|
id=id, status=status,
|
||||||
self._status = status
|
owner_scenario=owner_scenario
|
||||||
|
)
|
||||||
if id == -1:
|
|
||||||
self.id = HydraulicStructure._id_cnt
|
|
||||||
else:
|
|
||||||
self.id = id
|
|
||||||
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self._input_rk = None
|
self._input_section = None
|
||||||
self._output_rk = None
|
self._output_section = 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
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute, ext=""):
|
||||||
|
execute(f"""
|
||||||
execute("""
|
CREATE TABLE hydraulic_structures{ext} (
|
||||||
CREATE TABLE hydraulic_structures(
|
{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,
|
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,
|
||||||
FOREIGN KEY(input_reach) REFERENCES river_reach(id),
|
input_section INTEGER,
|
||||||
FOREIGN KEY(output_reach) REFERENCES river_reach(id)
|
output_section INTEGER,
|
||||||
|
{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):
|
def _db_update(cls, execute, version, data=None):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
if major == minor == "0":
|
rl = int(release)
|
||||||
rl = int(release)
|
|
||||||
|
|
||||||
|
if major == minor == "0":
|
||||||
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 " +
|
||||||
ALTER TABLE hydraulic_structures
|
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
|
||||||
RENAME COLUMN input_kp TO input_rk
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
execute(
|
|
||||||
"""
|
|
||||||
ALTER TABLE hydraulic_structures
|
|
||||||
RENAME COLUMN output_kp TO output_rk
|
|
||||||
"""
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return True
|
return cls._update_submodel(execute, version, data)
|
||||||
|
|
||||||
|
@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 id, name, enabled, " +
|
"SELECT pamhyr_id, deleted, name, enabled, " +
|
||||||
"input_rk, output_rk, " +
|
"input_section, output_section, " +
|
||||||
"input_reach, output_reach " +
|
"input_reach, output_reach, scenario " +
|
||||||
"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:
|
||||||
it = iter(row)
|
new.append(cls._db_load_new(execute, data, row))
|
||||||
|
|
||||||
hs_id = next(it)
|
data["scenario"] = scenario.parent
|
||||||
name = next(it)
|
new += cls._db_load(execute, data)
|
||||||
enabled = (next(it) == 1)
|
data["scenario"] = scenario
|
||||||
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(f"DELETE FROM hydraulic_structures WHERE id = {self.id}")
|
execute(
|
||||||
|
"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.id
|
input_reach_id = self._input_reach.pamhyr_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.id
|
output_reach_id = self._output_reach.pamhyr_id
|
||||||
|
|
||||||
input_rk = -1
|
input_section = 'NULL'
|
||||||
if self.input_rk is not None:
|
if self.input_section is not None:
|
||||||
input_rk = self.input_rk
|
input_section = self.input_section.pamhyr_id
|
||||||
|
|
||||||
output_rk = -1
|
output_section = 'NULL'
|
||||||
if self.output_rk is not None:
|
if self.output_section is not None:
|
||||||
output_rk = self.output_rk
|
output_section = self.output_section.pamhyr_id
|
||||||
|
|
||||||
sql = (
|
execute(
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"hydraulic_structures(" +
|
"hydraulic_structures(" +
|
||||||
" id, name, enabled, input_rk, output_rk, " +
|
" pamhyr_id, deleted, name, enabled, " +
|
||||||
" input_reach, output_reach" +
|
" input_section, output_section, " +
|
||||||
|
" input_reach, output_reach, " +
|
||||||
|
" scenario" +
|
||||||
") " +
|
") " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, '{self._db_format(self._name)}', " +
|
f"{self.pamhyr_id}, " +
|
||||||
|
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_rk}, {output_rk}, " +
|
f"{input_section}, {output_section}, " +
|
||||||
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.id
|
data['hs_id'] = self.pamhyr_id
|
||||||
execute(
|
execute(
|
||||||
"DELETE FROM hydraulic_structures_basic " +
|
"DELETE FROM hydraulic_structures_basic " +
|
||||||
f"WHERE hs = {self.id}"
|
f"WHERE hs = {self.pamhyr_id} " +
|
||||||
|
f"AND scenario = {self._status.scenario_id}"
|
||||||
)
|
)
|
||||||
|
|
||||||
for basic in self._data:
|
for basic in self._data:
|
||||||
@@ -199,41 +327,71 @@ 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._data)
|
return len(self.lst)
|
||||||
|
|
||||||
|
@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.id + 1}"
|
return f"HS #{self.pamhyr_id}"
|
||||||
|
|
||||||
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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def input_rk(self):
|
def input_rk(self):
|
||||||
return self._input_rk
|
if self._input_section is None:
|
||||||
|
return None
|
||||||
|
|
||||||
@input_rk.setter
|
return self._input_section
|
||||||
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):
|
||||||
return self._output_rk
|
if self._output_section is None:
|
||||||
|
return None
|
||||||
|
|
||||||
@output_rk.setter
|
return self._output_section
|
||||||
def output_rk(self, output_rk):
|
|
||||||
self._output_rk = output_rk
|
@property
|
||||||
self._status.modified()
|
def input_section(self):
|
||||||
|
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):
|
||||||
@@ -242,7 +400,7 @@ class HydraulicStructure(SQLSubModel):
|
|||||||
@enabled.setter
|
@enabled.setter
|
||||||
def enabled(self, enabled):
|
def enabled(self, enabled):
|
||||||
self._enabled = enabled
|
self._enabled = enabled
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def input_reach(self):
|
def input_reach(self):
|
||||||
@@ -251,7 +409,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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def output_reach(self):
|
def output_reach(self):
|
||||||
@@ -260,26 +418,34 @@ 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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def basic_structures(self):
|
def basic_structures(self):
|
||||||
return self._data.copy()
|
return self.lst.copy()
|
||||||
|
|
||||||
def basic_structure(self, index: int):
|
def basic_structure(self, index: int):
|
||||||
return self._data[index]
|
if len(self._data) == 0:
|
||||||
|
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):
|
||||||
self._data.insert(index, value)
|
if value in self._data:
|
||||||
self._status.modified()
|
value.set_as_not_deleted()
|
||||||
|
else:
|
||||||
|
self._data.insert(index, value)
|
||||||
|
|
||||||
def delete_i(self, indexes):
|
self.modified()
|
||||||
|
|
||||||
|
def hard_delete_i(self, indexes):
|
||||||
self._data = list(
|
self._data = list(
|
||||||
map(
|
map(
|
||||||
lambda e: e[1],
|
lambda e: e[1],
|
||||||
@@ -289,20 +455,30 @@ class HydraulicStructure(SQLSubModel):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
def delete(self, els):
|
def delete_i(self, indexes):
|
||||||
self._data = list(
|
list(
|
||||||
filter(
|
map(
|
||||||
lambda e: e not in els,
|
lambda e: e[1].set_as_deleted(),
|
||||||
self._data
|
filter(
|
||||||
|
lambda e: e[0] in indexes,
|
||||||
|
enumerate(self.lst)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
|
def delete(self, els):
|
||||||
|
for el in els:
|
||||||
|
el.set_as_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._status.modified()
|
|
||||||
|
self.modified()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# HydraulicStructuresList.py -- Pamhyr
|
# HydraulicStructuresList.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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.PamhyrList import PamhyrModelList
|
from Model.Tools.PamhyrListExt 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("DELETE FROM hydraulic_structures")
|
execute(
|
||||||
|
"DELETE FROM hydraulic_structures " +
|
||||||
if data is None:
|
f"WHERE scenario = {self._status.scenario_id}"
|
||||||
data = {}
|
)
|
||||||
|
|
||||||
for hs in self._lst:
|
for hs in self._lst:
|
||||||
hs._db_save(execute, data=data)
|
hs._db_save(execute, data=data)
|
||||||
@@ -53,9 +53,12 @@ class HydraulicStructureList(PamhyrModelList):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def new(self, lst, index):
|
def new(self, lst, index):
|
||||||
n = HydraulicStructure(status=self._status)
|
n = HydraulicStructure(
|
||||||
|
status=self._status,
|
||||||
|
owner_scenario=self._status.scenario_id
|
||||||
|
)
|
||||||
self._lst.insert(index, n)
|
self._lst.insert(index, n)
|
||||||
self._status.modified()
|
self.modified()
|
||||||
return n
|
return n
|
||||||
|
|
||||||
def __copy__(self):
|
def __copy__(self):
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# InitialConditions.py -- Pamhyr
|
# InitialConditions.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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,32 +24,34 @@ 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, name: str = "",
|
def __init__(self, id: int = -1,
|
||||||
comment: str = "", reach=None,
|
name: str = "", comment: str = "",
|
||||||
rk: float = 0.0, discharge: float = 0.0,
|
reach=None, section=None,
|
||||||
|
discharge: float = 0.0,
|
||||||
height: float = 0.0,
|
height: float = 0.0,
|
||||||
status=None):
|
status=None,
|
||||||
super(Data, self).__init__()
|
owner_scenario=-1):
|
||||||
|
super(Data, self).__init__(
|
||||||
self._status = status
|
id=id, 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._rk = rk
|
self._section = section
|
||||||
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._rk != 0.0:
|
if self._section is not None:
|
||||||
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()
|
||||||
@@ -57,25 +59,29 @@ class Data(SQLSubModel):
|
|||||||
self._update_from_discharge()
|
self._update_from_discharge()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE initial_conditions(
|
CREATE TABLE initial_conditions{ext} (
|
||||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
|
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,
|
||||||
rk REAL NOT NULL,
|
section INTEGER,
|
||||||
discharge REAL NOT NULL,
|
discharge REAL NOT NULL,
|
||||||
height REAL NOT NULL,
|
height REAL NOT NULL,
|
||||||
FOREIGN KEY(reach) REFERENCES river_reach(id)
|
{Scenario.create_db_add_scenario()},
|
||||||
|
{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):
|
def _db_update(cls, execute, version, data=None):
|
||||||
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:
|
||||||
@@ -83,41 +89,138 @@ class Data(SQLSubModel):
|
|||||||
"ALTER TABLE initial_conditions RENAME COLUMN kp TO rk"
|
"ALTER TABLE initial_conditions RENAME COLUMN kp TO rk"
|
||||||
)
|
)
|
||||||
|
|
||||||
return cls._update_submodel(execute, version)
|
cls._db_update_to_0_2_0(execute, data)
|
||||||
|
|
||||||
|
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):
|
||||||
id = data["reach"].id
|
new = []
|
||||||
|
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 ind, name, comment, rk, discharge, height " +
|
"SELECT pamhyr_id, deleted, ind, name, comment, " +
|
||||||
|
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
new = []
|
ok = True
|
||||||
|
|
||||||
for _ in table:
|
|
||||||
new.append(None)
|
|
||||||
|
|
||||||
for row in table:
|
for row in table:
|
||||||
ind = row[0]
|
it = iter(row)
|
||||||
name = row[1]
|
|
||||||
comment = row[2]
|
pid = next(it)
|
||||||
rk = row[3]
|
deleted = (next(it) == 1)
|
||||||
discharge = row[4]
|
ind = next(it)
|
||||||
height = row[5]
|
name = next(it)
|
||||||
|
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,
|
||||||
rk=rk,
|
section=section,
|
||||||
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})"
|
||||||
)
|
)
|
||||||
|
|
||||||
new[ind] = d
|
# Recution only if no data found
|
||||||
|
if len(new) == 0:
|
||||||
|
data["scenario"] = scenario.parent
|
||||||
|
new += cls._db_load(execute, data)
|
||||||
|
data["scenario"] = scenario
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
@@ -126,13 +229,14 @@ class Data(SQLSubModel):
|
|||||||
|
|
||||||
execute(
|
execute(
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"initial_conditions(ind, name, comment, rk, " +
|
"initial_conditions(pamhyr_id, deleted, ind, name, comment, " +
|
||||||
"discharge, height, reach) " +
|
"section, discharge, height, reach, 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._db_format(self._comment)}', " +
|
f"'{self._db_format(self._comment)}', " +
|
||||||
f"{self._rk}, {self._discharge}, {self._height}, " +
|
f"{self._section.id}, {self._discharge}, {self._height}, " +
|
||||||
f"{self._reach.id}" +
|
f"{self._reach.id}, {self._status.scenario_id}" +
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -142,7 +246,7 @@ class Data(SQLSubModel):
|
|||||||
new = Data(
|
new = Data(
|
||||||
name=self.name,
|
name=self.name,
|
||||||
comment=self._comment,
|
comment=self._comment,
|
||||||
rk=self._rk,
|
section=self._section,
|
||||||
discharge=self._discharge,
|
discharge=self._discharge,
|
||||||
height=self._height,
|
height=self._height,
|
||||||
reach=self._reach,
|
reach=self._reach,
|
||||||
@@ -161,8 +265,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":
|
elif key == "rk" or key == "section":
|
||||||
val = self._rk
|
val = self._section
|
||||||
elif key == "speed":
|
elif key == "speed":
|
||||||
val = self._speed
|
val = self._speed
|
||||||
elif key == "discharge":
|
elif key == "discharge":
|
||||||
@@ -175,9 +279,9 @@ class Data(SQLSubModel):
|
|||||||
return val
|
return val
|
||||||
|
|
||||||
def _update_get_min(self):
|
def _update_get_min(self):
|
||||||
profile = self._reach.reach.get_profiles_from_rk(self._rk)
|
profile = self._section
|
||||||
if len(profile) > 0:
|
if profile is not None:
|
||||||
min = profile[0].z_min()
|
min = profile.z_min()
|
||||||
else:
|
else:
|
||||||
min = 0.0
|
min = 0.0
|
||||||
|
|
||||||
@@ -204,8 +308,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":
|
elif key == "rk" or key == "section":
|
||||||
self._rk = float(value)
|
self._section = 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
|
||||||
@@ -220,7 +324,7 @@ class Data(SQLSubModel):
|
|||||||
self._height = float(value)
|
self._height = float(value)
|
||||||
self._update_from_height()
|
self._update_from_height()
|
||||||
|
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
|
|
||||||
class InitialConditions(SQLSubModel):
|
class InitialConditions(SQLSubModel):
|
||||||
@@ -241,8 +345,8 @@ class InitialConditions(SQLSubModel):
|
|||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version):
|
def _db_update(cls, execute, version, data=None):
|
||||||
return cls._update_submodel(execute, version)
|
return cls._update_submodel(execute, version, data)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
@@ -270,8 +374,18 @@ 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
|
||||||
@@ -283,49 +397,69 @@ class InitialConditions(SQLSubModel):
|
|||||||
@reach.setter
|
@reach.setter
|
||||||
def reach(self, new):
|
def reach(self, new):
|
||||||
self._reach = reach
|
self._reach = reach
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def data(self):
|
def data(self):
|
||||||
return self._data.copy()
|
return list(
|
||||||
|
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._status.modified()
|
self.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._status.modified()
|
self.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)
|
||||||
|
|
||||||
n['rk'] = rk
|
section = reduce(
|
||||||
|
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):
|
||||||
self._data.insert(index, data)
|
if data in self._data:
|
||||||
self._status.modified()
|
data.set_as_not_deleted()
|
||||||
|
else:
|
||||||
|
self._data.insert(index, data)
|
||||||
|
|
||||||
|
self.modified()
|
||||||
|
|
||||||
def delete(self, data):
|
def delete(self, data):
|
||||||
self._data = list(
|
list(
|
||||||
filter(
|
map(
|
||||||
lambda x: x not in data,
|
lambda x: x.set_as_deleted(),
|
||||||
self._data
|
filter(
|
||||||
|
lambda x: x in data,
|
||||||
|
self._data
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
def delete_i(self, indexes):
|
def delete_i(self, indexes):
|
||||||
data = list(
|
data = list(
|
||||||
@@ -341,18 +475,23 @@ 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._status.modified()
|
self.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 self._data_get("rk")
|
return list(
|
||||||
|
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")
|
||||||
@@ -360,12 +499,13 @@ 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 = {}
|
||||||
@@ -375,12 +515,14 @@ 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"]] = data["discharge"]
|
data_discharge[data["rk"].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 = []
|
|
||||||
for profile in profiles:
|
self.clear_data()
|
||||||
|
|
||||||
|
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)
|
||||||
@@ -395,7 +537,10 @@ class InitialConditions(SQLSubModel):
|
|||||||
strickler = 25.0
|
strickler = 25.0
|
||||||
|
|
||||||
if not compute_discharge:
|
if not compute_discharge:
|
||||||
discharge = data_discharge[profile.rk]
|
if profile.rk in data_discharge:
|
||||||
|
discharge = data_discharge[profile.rk]
|
||||||
|
else:
|
||||||
|
discharge = 0.0
|
||||||
else:
|
else:
|
||||||
discharge = (
|
discharge = (
|
||||||
# ((width * 0.8)
|
# ((width * 0.8)
|
||||||
@@ -418,21 +563,17 @@ 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.rk
|
new["rk"] = profile
|
||||||
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
|
||||||
|
|
||||||
@@ -443,33 +584,43 @@ 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"]] = data["height"]
|
data_height[data["rk"].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 = []
|
|
||||||
for profile in profiles:
|
self.clear_data()
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
if not compute_height:
|
if not compute_height:
|
||||||
height = data_height[profile.rk]
|
if profile.rk in data_height:
|
||||||
|
height = data_height[profile.rk]
|
||||||
|
else:
|
||||||
|
height = 0.0
|
||||||
else:
|
else:
|
||||||
if abs(incline) <= 0:
|
if abs(incline) <= 0:
|
||||||
height = 0.0
|
height = 0.0
|
||||||
else:
|
else:
|
||||||
height = (
|
height = (
|
||||||
discharge
|
discharge
|
||||||
/
|
/ ((width * 0.8)
|
||||||
((width * 0.8) * strickler * (abs(incline) ** (0.5)))
|
* strickler
|
||||||
|
* (abs(incline) ** (0.5)))
|
||||||
) ** (0.6)
|
) ** (0.6)
|
||||||
|
|
||||||
elevation = max(
|
elevation = max(
|
||||||
@@ -483,15 +634,13 @@ 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["rk"] = profile.rk
|
new["section"] = profile
|
||||||
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,
|
||||||
@@ -507,20 +656,23 @@ 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"]] = data["discharge"]
|
data_discharge[data["rk"].rk] = data["discharge"]
|
||||||
|
|
||||||
self._data = []
|
self.clear_data()
|
||||||
for profile in profiles:
|
for profile in profiles:
|
||||||
|
|
||||||
if not compute_discharge:
|
if not compute_discharge:
|
||||||
d = data_discharge[profile.rk]
|
if profile.rk in data_discharge:
|
||||||
|
d = data_discharge[profile.rk]
|
||||||
|
else:
|
||||||
|
d = 0.0
|
||||||
else:
|
else:
|
||||||
d = discharge
|
d = discharge
|
||||||
elevation = interp(profile.rk,
|
elevation = interp(profile.rk,
|
||||||
[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.rk
|
new["rk"] = profile
|
||||||
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-2024 INRAE
|
# Copyright (C) 2023-2025 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,7 +64,8 @@ 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-2024 INRAE
|
# Copyright (C) 2023-2025 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,6 +23,7 @@ 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
|
||||||
@@ -34,19 +35,17 @@ 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,
|
||||||
super(InitialConditionsAdisTS, self).__init__()
|
owner_scenario=-1):
|
||||||
|
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
|
||||||
@@ -56,81 +55,159 @@ class InitialConditionsAdisTS(SQLSubModel):
|
|||||||
self._enabled = True
|
self._enabled = True
|
||||||
self._data = []
|
self._data = []
|
||||||
|
|
||||||
InitialConditionsAdisTS._id_cnt = max(
|
|
||||||
InitialConditionsAdisTS._id_cnt + 1,
|
|
||||||
self.id
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE initial_conditions_adists(
|
CREATE TABLE initial_conditions_adists{ext}(
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
pollutant INTEGER NOT NULL,
|
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
name TEXT NOT NULL,
|
pollutant INTEGER NOT NULL,
|
||||||
concentration REAL NOT NULL,
|
name TEXT NOT NULL,
|
||||||
eg REAL NOT NULL,
|
concentration REAL NOT NULL,
|
||||||
em REAL NOT NULL,
|
eg REAL NOT NULL,
|
||||||
ed REAL NOT NULL,
|
em REAL NOT NULL,
|
||||||
enabled BOOLEAN NOT NULL,
|
ed REAL NOT NULL,
|
||||||
FOREIGN KEY(pollutant) REFERENCES Pollutants(id)
|
enabled BOOLEAN NOT NULL,
|
||||||
)
|
{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):
|
def _db_update(cls, execute, version, data=None):
|
||||||
major, minor, release = version.strip().split(".")
|
major, minor, release = version.strip().split(".")
|
||||||
if major == 0 and minor < 1:
|
created = False
|
||||||
|
|
||||||
|
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 id, pollutant, name, concentration, eg, em, ed, " +
|
"SELECT pamhyr_id, deleted, " +
|
||||||
"enabled " +
|
"pollutant, name, concentration, eg, em, ed, " +
|
||||||
"FROM initial_conditions_adists"
|
"enabled, scenario " +
|
||||||
|
"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:
|
||||||
IC_id = row[0]
|
it = iter(row)
|
||||||
pollutant = row[1]
|
|
||||||
name = row[2]
|
|
||||||
concentration = row[3]
|
|
||||||
eg = row[4]
|
|
||||||
em = row[5]
|
|
||||||
ed = row[6]
|
|
||||||
enabled = (row[7] == 1)
|
|
||||||
|
|
||||||
IC = cls(
|
pid = next(it)
|
||||||
id=IC_id,
|
deleted = (next(it) == 1)
|
||||||
|
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=data['status']
|
status=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'] = IC_id
|
data['ic_default_id'] = pid
|
||||||
IC._data = ICAdisTSSpec._db_load(execute, data)
|
ic._data = ICAdisTSSpec._db_load(execute, data)
|
||||||
|
|
||||||
new.append(IC)
|
loaded.add(pid)
|
||||||
|
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):
|
||||||
execute(f"DELETE FROM initial_conditions_adists WHERE id = {self.id}")
|
if not self.must_be_saved():
|
||||||
|
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:
|
||||||
@@ -155,12 +232,14 @@ class InitialConditionsAdisTS(SQLSubModel):
|
|||||||
sql = (
|
sql = (
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"initial_conditions_adists(" +
|
"initial_conditions_adists(" +
|
||||||
"id, pollutant, name, concentration, " +
|
"pamhyr_id, deleted, pollutant, name, concentration, " +
|
||||||
"eg, em, ed, enabled" +
|
"eg, em, ed, enabled, scenario" +
|
||||||
") " +
|
") " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, {pollutant}, '{self._db_format(self._name)}', " +
|
f"{self.id}, {self.is_deleted()}, " +
|
||||||
f"{concentration}, {eg}, {em}, {ed}, {self._enabled}" +
|
f"{pollutant}, '{self._db_format(self._name)}', " +
|
||||||
|
f"{concentration}, {eg}, {em}, {ed}, {self._enabled}, " +
|
||||||
|
f"{self._status.scenario_id}" +
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -168,8 +247,9 @@ class InitialConditionsAdisTS(SQLSubModel):
|
|||||||
|
|
||||||
data['ic_default_id'] = self.id
|
data['ic_default_id'] = self.id
|
||||||
execute(
|
execute(
|
||||||
"DELETE FROM initial_conditions_spec " +
|
"DELETE FROM initial_conditions_adists_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-2024 INRAE
|
# Copyright (C) 2023-2025 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.PamhyrList import PamhyrModelList
|
from Model.Tools.PamhyrListExt import PamhyrModelList
|
||||||
from Model.InitialConditionsAdisTS.InitialConditionsAdisTS \
|
from Model.InitialConditionsAdisTS.InitialConditionsAdisTS \
|
||||||
import InitialConditionsAdisTS
|
import InitialConditionsAdisTS
|
||||||
|
|
||||||
@@ -43,7 +43,10 @@ class InitialConditionsAdisTSList(PamhyrModelList):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute("DELETE FROM initial_conditions_adists")
|
execute(
|
||||||
|
"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-2024 INRAE
|
# Copyright (C) 2023-2025 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,14 +22,13 @@ 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):
|
||||||
@@ -37,11 +36,6 @@ 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
|
||||||
@@ -53,73 +47,142 @@ 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)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE initial_conditions_spec(
|
CREATE TABLE initial_conditions_adists_spec{ext}(
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
ic_default INTEGER NOT NULL,
|
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
name TEXT NOT NULL,
|
ic_default INTEGER NOT NULL,
|
||||||
reach INTEGER NOT NULL,
|
name TEXT NOT NULL,
|
||||||
start_rk REAL NOT NULL,
|
reach INTEGER NOT NULL,
|
||||||
end_rk REAL NOT NULL,
|
start_rk REAL NOT NULL,
|
||||||
concentration REAL NOT NULL,
|
end_rk REAL NOT NULL,
|
||||||
eg REAL NOT NULL,
|
concentration REAL NOT NULL,
|
||||||
em REAL NOT NULL,
|
eg REAL NOT NULL,
|
||||||
ed REAL NOT NULL,
|
em REAL NOT NULL,
|
||||||
rate REAL NOT NULL,
|
ed REAL NOT NULL,
|
||||||
enabled BOOLEAN NOT NULL,
|
rate REAL NOT NULL,
|
||||||
FOREIGN KEY(ic_default) REFERENCES initial_conditions_adists(id),
|
enabled BOOLEAN NOT NULL,
|
||||||
FOREIGN KEY(reach) REFERENCES river_reach(id)
|
{Scenario.create_db_add_scenario()},
|
||||||
)
|
{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):
|
def _db_update(cls, execute, version, data=None):
|
||||||
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 id, ic_default, name, reach, start_rk, end_rk, " +
|
"SELECT pamhyr_id, deleted, name, reach, start_rk, end_rk, " +
|
||||||
"concentration, eg, em, ed, rate, enabled " +
|
"concentration, eg, em, ed, rate, enabled, scenario " +
|
||||||
"FROM initial_conditions_spec " +
|
"FROM initial_conditions_adists_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:
|
||||||
id = row[0]
|
it = iter(row)
|
||||||
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)
|
|
||||||
|
|
||||||
# new_spec = [name, reach, start_rk, end_rk,
|
id = next(it)
|
||||||
# concentration, eg, em, ed, rate, enabled]
|
deleted = (next(it) == 1)
|
||||||
|
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,
|
id=id, name=name,
|
||||||
name=name,
|
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
|
||||||
@@ -131,30 +194,37 @@ 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_spec(id, ic_default, name, reach, " +
|
"initial_conditions_adists_spec(id, deleted, " +
|
||||||
"start_rk, end_rk, concentration, eg, em, ed, rate, enabled) " +
|
"ic_default, name, reach, " +
|
||||||
|
"start_rk, end_rk, concentration, eg, em, ed, rate, " +
|
||||||
|
"enabled, scenario) " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, " +
|
f"{self.id}, {self._db_format(self.is_deleted())}, " +
|
||||||
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}, " +
|
f"{self._start_rk}, {self._end_rk}, " +
|
||||||
f"{self._end_rk}, " +
|
|
||||||
f"{self._concentration}, " +
|
f"{self._concentration}, " +
|
||||||
f"{self._eg}, " +
|
f"{self._eg}, {self._em}, {self._ed}, " +
|
||||||
f"{self._em}, " +
|
f"{self._rate}, {self._enabled}, " +
|
||||||
f"{self._ed}, " +
|
f"{self._status.scenario_id}" +
|
||||||
f"{self._rate}, " +
|
|
||||||
f"{self._enabled}" +
|
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
execute(sql)
|
execute(sql)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# LateralContribution.py -- Pamhyr
|
# LateralContribution.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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,66 +27,226 @@ 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 LateralContribution(SQLSubModel):
|
class Data(SQLSubModel):
|
||||||
_sub_classes = []
|
_sub_classes = []
|
||||||
_id_cnt = 0
|
|
||||||
|
|
||||||
def __init__(self, id: int = -1, name: str = "", status=None):
|
def __init__(self,
|
||||||
super(LateralContribution, self).__init__()
|
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._status = status
|
self._types = types
|
||||||
|
self._data = [data0, data1]
|
||||||
if id == -1:
|
|
||||||
self.id = LateralContribution._id_cnt
|
|
||||||
else:
|
|
||||||
self.id = id
|
|
||||||
|
|
||||||
self._name = name
|
|
||||||
self._type = ""
|
|
||||||
self._edge = None
|
|
||||||
self._begin_rk = 0.0
|
|
||||||
self._end_rk = 0.0
|
|
||||||
self._data = []
|
|
||||||
self._header = []
|
|
||||||
self._types = [float, float]
|
|
||||||
|
|
||||||
LateralContribution._id_cnt = max(
|
|
||||||
LateralContribution._id_cnt + 1, self.id)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE lateral_contribution(
|
CREATE TABLE lateral_contribution_data{ext} (
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
name TEXT NOT NULL,
|
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
type TEXT NOT NULL,
|
|
||||||
tab TEXT NOT NULL,
|
|
||||||
edge INTEGER,
|
|
||||||
begin_rk REAL NOT NULL,
|
|
||||||
end_rk REAL NOT NULL,
|
|
||||||
FOREIGN KEY(edge) REFERENCES river_reach(id)
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
|
|
||||||
execute("""
|
|
||||||
CREATE TABLE lateral_contribution_data(
|
|
||||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
||||||
ind INTEGER NOT NULL,
|
ind INTEGER NOT NULL,
|
||||||
data0 TEXT NOT NULL,
|
data0 TEXT NOT NULL,
|
||||||
data1 TEXT NOT NULL,
|
data1 TEXT NOT NULL,
|
||||||
lc INTEGER,
|
lc INTEGER,
|
||||||
FOREIGN KEY(lc) REFERENCES lateral_contribution(id)
|
{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)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version):
|
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):
|
||||||
|
_sub_classes = [Data]
|
||||||
|
|
||||||
|
def __init__(self, id: int = -1,
|
||||||
|
name: str = "",
|
||||||
|
status=None, owner_scenario=-1):
|
||||||
|
super(LateralContribution, self).__init__(
|
||||||
|
id=id, status=status,
|
||||||
|
owner_scenario=owner_scenario
|
||||||
|
)
|
||||||
|
|
||||||
|
self._name = name
|
||||||
|
self._type = ""
|
||||||
|
self._reach = None
|
||||||
|
self._begin_section = None
|
||||||
|
self._end_section = None
|
||||||
|
self._data = []
|
||||||
|
self._header = []
|
||||||
|
self._types = [float, float]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_create(cls, execute, ext=""):
|
||||||
|
execute(f"""
|
||||||
|
CREATE TABLE lateral_contribution{ext}(
|
||||||
|
{cls.create_db_add_pamhyr_id()},
|
||||||
|
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
type TEXT NOT NULL,
|
||||||
|
tab TEXT NOT NULL,
|
||||||
|
reach INTEGER,
|
||||||
|
begin_section INTEGER,
|
||||||
|
end_section INTEGER,
|
||||||
|
{Scenario.create_db_add_scenario()},
|
||||||
|
{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":
|
||||||
|
return True
|
||||||
|
|
||||||
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_update(cls, execute, version, data=None):
|
||||||
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:
|
||||||
@@ -103,7 +263,45 @@ class LateralContribution(SQLSubModel):
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
return True
|
execute(
|
||||||
|
"""
|
||||||
|
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):
|
||||||
@@ -124,84 +322,142 @@ 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 id, name, type, edge, begin_rk, end_rk " +
|
"SELECT pamhyr_id, deleted, name, type, " +
|
||||||
f"FROM lateral_contribution WHERE tab = '{tab}'"
|
"reach, begin_section, end_section, scenario " +
|
||||||
|
"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:
|
||||||
t = row[2]
|
it = iter(row)
|
||||||
|
|
||||||
|
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=row[0],
|
id=pid, name=name,
|
||||||
name=row[1],
|
status=status,
|
||||||
status=data['status']
|
owner_scenario=owner_scenario
|
||||||
)
|
)
|
||||||
lc.edge = None
|
lc.reach = None
|
||||||
|
lc._begin_section = None
|
||||||
|
lc._end_section = None
|
||||||
|
|
||||||
if row[3] != -1:
|
if row[3] != -1:
|
||||||
lc.edge = next(filter(lambda e: e.id == row[3], data["edges"]))
|
lc.reach = next(
|
||||||
lc._begin_rk = row[4]
|
filter(
|
||||||
lc._end_rk = row[5]
|
lambda e: e.id == reach,
|
||||||
|
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
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
values = execute(
|
data["lc"] = lc
|
||||||
"SELECT ind, data0, data1 FROM lateral_contribution_data " +
|
lc._data = Data._db_load(execute, data=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(f"DELETE FROM lateral_contribution WHERE id = {self.id}")
|
execute(
|
||||||
execute(f"DELETE FROM lateral_contribution_data WHERE lc = {self.id}")
|
"DELETE FROM lateral_contribution_data " +
|
||||||
|
f"WHERE lc = {self._pamhyr_id} " +
|
||||||
|
f"AND scenario = {self._status.scenario_id}"
|
||||||
|
)
|
||||||
|
|
||||||
edge = -1
|
reach = -1
|
||||||
if self._edge is not None:
|
begin_section = -1
|
||||||
edge = self._edge.id
|
end_section = -1
|
||||||
|
|
||||||
sql = (
|
if self._reach is not None:
|
||||||
|
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(id, name, type, tab, " +
|
"lateral_contribution(" +
|
||||||
"edge, begin_rk, end_rk) " +
|
"pamhyr_id, deleted, name, type, tab, " +
|
||||||
|
"reach, begin_section, end_section, scenario) " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, '{self._db_format(self._name)}', " +
|
f"{self.id}, {self._db_format(self.is_deleted())}, " +
|
||||||
f"'{self._db_format(self._type)}', '{tab}', {edge}, " +
|
f"'{self._db_format(self._name)}', " +
|
||||||
f"{self._begin_rk}, {self._end_rk}" +
|
f"'{self._db_format(self._type)}', '{tab}', {reach}, " +
|
||||||
|
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:
|
||||||
data0 = self._db_format(str(d[0]))
|
data["ind"] = ind
|
||||||
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(self._data)
|
return len(
|
||||||
|
list(
|
||||||
|
filter(
|
||||||
|
lambda el: not el.is_deleted(),
|
||||||
|
self._data
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def compatibility(cls):
|
def compatibility(cls):
|
||||||
@@ -226,67 +482,66 @@ class LateralContribution(SQLSubModel):
|
|||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
if self._name == "":
|
if self._name == "":
|
||||||
return f"L{self.id + 1}"
|
return f"LC #{self.id}"
|
||||||
|
|
||||||
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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lctype(self):
|
def lctype(self):
|
||||||
return self._type
|
return self._type
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def edge(self):
|
def reach(self):
|
||||||
return self._edge
|
return self._reach
|
||||||
|
|
||||||
@edge.setter
|
@reach.setter
|
||||||
def edge(self, edge):
|
def reach(self, reach):
|
||||||
self._edge = edge
|
self._reach = reach
|
||||||
if edge is not None:
|
if reach is not None:
|
||||||
self._begin_rk = self._edge.reach.get_rk_min()
|
self._begin_section = self._reach.reach.profiles[0]
|
||||||
self._end_rk = self._edge.reach.get_rk_max()
|
self._end_section = self._reach.reach.profiles[-1]
|
||||||
self._status.modified()
|
|
||||||
|
|
||||||
def has_edge(self):
|
self.modified()
|
||||||
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):
|
||||||
return self._begin_rk
|
if self._begin_section is None:
|
||||||
|
return 0
|
||||||
|
|
||||||
@begin_rk.setter
|
return self._begin_section.rk
|
||||||
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()
|
|
||||||
|
|
||||||
if _min <= begin_rk <= _max:
|
@property
|
||||||
self._begin_rk = begin_rk
|
def begin_section(self):
|
||||||
|
return self._begin_section
|
||||||
|
|
||||||
self._status.modified()
|
@begin_section.setter
|
||||||
|
def begin_section(self, section):
|
||||||
|
self._begin_section = section
|
||||||
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def end_rk(self):
|
def end_rk(self):
|
||||||
return self._end_rk
|
if self._end_section is None:
|
||||||
|
return 0
|
||||||
|
|
||||||
@end_rk.setter
|
return self._end_section.rk
|
||||||
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()
|
|
||||||
|
|
||||||
if _min <= end_rk <= _max:
|
@property
|
||||||
self._end_rk = end_rk
|
def end_section(self):
|
||||||
|
return self._end_section
|
||||||
|
|
||||||
self._status.modified()
|
@end_section.setter
|
||||||
|
def end_section(self, section):
|
||||||
|
self._end_section = section
|
||||||
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def header(self):
|
def header(self):
|
||||||
@@ -294,7 +549,12 @@ class LateralContribution(SQLSubModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def data(self):
|
def data(self):
|
||||||
return self._data.copy()
|
return list(
|
||||||
|
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:
|
||||||
@@ -328,17 +588,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 (new_0, new_1)
|
return Data(new_0, new_1, status=self._status)
|
||||||
|
|
||||||
def add(self, index: int):
|
def add(self, index: int):
|
||||||
value = (self._default_0, self._default_1)
|
value = Data(self._default_0, self._default_1, 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):
|
def insert(self, index: int, value):
|
||||||
self._data.insert(index, value)
|
self._data.insert(index, value)
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
def delete_i(self, indexes):
|
def delete_i(self, indexes):
|
||||||
self._data = list(
|
self._data = list(
|
||||||
@@ -350,7 +610,7 @@ class LateralContribution(SQLSubModel):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
def delete(self, els):
|
def delete(self, els):
|
||||||
self._data = list(
|
self._data = list(
|
||||||
@@ -359,14 +619,14 @@ class LateralContribution(SQLSubModel):
|
|||||||
self.data
|
self.data
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self._status.modified()
|
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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
def get_i(self, index):
|
def get_i(self, index):
|
||||||
return self.data[index]
|
return self.data[index]
|
||||||
@@ -378,10 +638,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 = list(self._data[index])
|
v = self._data[index]
|
||||||
v[column] = self._types[column](value)
|
v[column] = self._types[column](value)
|
||||||
self._data[index] = tuple(v)
|
self._data[index] = v
|
||||||
self._status.modified()
|
self.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)
|
||||||
@@ -392,9 +652,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.edge = self.edge
|
new.reach = self.reach
|
||||||
new.begin_rk = self.begin_rk
|
new.begin_section = self.begin_section
|
||||||
new.end_rk = self.end_rk
|
new.end_section = self.end_section
|
||||||
|
|
||||||
for i, _ in enumerate(self.data):
|
for i, _ in enumerate(self.data):
|
||||||
new.add(i)
|
new.add(i)
|
||||||
@@ -408,7 +668,7 @@ class LateralContribution(SQLSubModel):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.info(e)
|
logger.info(e)
|
||||||
|
|
||||||
self._status.modified()
|
self.modified()
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def move_up(self, index):
|
def move_up(self, index):
|
||||||
@@ -416,11 +676,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._status.modified()
|
self.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._status.modified()
|
self.modified()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# LateralContributionList.py -- Pamhyr
|
# LateralContributionList.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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.PamhyrList import PamhyrModelListWithTab
|
from Model.Tools.PamhyrListExt 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,7 +50,10 @@ class LateralContributionList(PamhyrModelListWithTab):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute("DELETE FROM lateral_contribution")
|
execute(
|
||||||
|
"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-2024 INRAE
|
# Copyright (C) 2023-2025 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,8 +22,12 @@ from Model.LateralContribution.LateralContribution import LateralContribution
|
|||||||
|
|
||||||
|
|
||||||
class NotDefined(LateralContribution):
|
class NotDefined(LateralContribution):
|
||||||
def __init__(self, id: int = -1, name: str = "", status=None):
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
super(NotDefined, self).__init__(id=id, name=name, status=status)
|
status=None, owner_scenario=-1):
|
||||||
|
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"]
|
||||||
@@ -34,8 +38,12 @@ class NotDefined(LateralContribution):
|
|||||||
|
|
||||||
|
|
||||||
class LateralContrib(LateralContribution):
|
class LateralContrib(LateralContribution):
|
||||||
def __init__(self, id: int = -1, name: str = "", status=None):
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
super(LateralContrib, self).__init__(id=id, name=name, status=status)
|
status=None, owner_scenario=-1):
|
||||||
|
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"]
|
||||||
@@ -47,8 +55,12 @@ class LateralContrib(LateralContribution):
|
|||||||
|
|
||||||
|
|
||||||
class Rain(LateralContribution):
|
class Rain(LateralContribution):
|
||||||
def __init__(self, id: int = -1, name: str = "", status=None):
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
super(Rain, self).__init__(id=id, name=name, status=status)
|
status=None, owner_scenario=-1):
|
||||||
|
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"]
|
||||||
@@ -60,8 +72,12 @@ class Rain(LateralContribution):
|
|||||||
|
|
||||||
|
|
||||||
class Evaporation(LateralContribution):
|
class Evaporation(LateralContribution):
|
||||||
def __init__(self, id: int = -1, name: str = "", status=None):
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
super(Evaporation, self).__init__(id=id, name=name, status=status)
|
status=None, owner_scenario=-1):
|
||||||
|
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-2024 INRAE
|
# Copyright (C) 2023-2025 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,137 +27,390 @@ 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 LateralContributionAdisTS(SQLSubModel):
|
class Data(SQLSubModel):
|
||||||
_sub_classes = []
|
_sub_classes = []
|
||||||
_id_cnt = 0
|
|
||||||
|
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):
|
||||||
|
_sub_classes = [Data]
|
||||||
|
|
||||||
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,
|
||||||
super(LateralContributionAdisTS, self).__init__()
|
owner_scenario=-1):
|
||||||
|
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._edge = None
|
self._reach = 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):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE lateral_contribution_adists(
|
CREATE TABLE lateral_contribution_adists{ext}(
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
|
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
pollutant INTEGER NOT NULL,
|
pollutant INTEGER NOT NULL,
|
||||||
edge INTEGER NOT NULL,
|
reach INTEGER NOT NULL,
|
||||||
begin_rk REAL NOT NULL,
|
begin_rk REAL NOT NULL,
|
||||||
end_rk REAL NOT NULL,
|
end_rk REAL NOT NULL,
|
||||||
FOREIGN KEY(pollutant) REFERENCES Pollutants(id),
|
{Scenario.create_db_add_scenario()},
|
||||||
FOREIGN KEY(edge) REFERENCES river_reach(id)
|
{Scenario.create_db_add_scenario_fk()},
|
||||||
|
FOREIGN KEY(pollutant) REFERENCES Pollutants(pamhyr_id),
|
||||||
|
FOREIGN KEY(reach) REFERENCES river_reach(pamhyr_id)
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
execute("""
|
if ext != "":
|
||||||
CREATE TABLE lateral_contribution_data_adists(
|
return True
|
||||||
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):
|
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
|
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 id, pollutant, edge, begin_rk, end_rk " +
|
"SELECT pamhyr_id, deleted, pollutant, reach, " +
|
||||||
"FROM lateral_contribution_adists"
|
"begin_rk, end_rk, scenario " +
|
||||||
|
"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:
|
||||||
lc = cls(
|
it = iter(row)
|
||||||
id=row[0],
|
|
||||||
pollutant=row[1],
|
pid = next(it)
|
||||||
status=data['status']
|
deleted = (next(it) == 1)
|
||||||
|
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()
|
||||||
|
|
||||||
lc.edge = row[2]
|
lca.reach = reach
|
||||||
lc.begin_rk = row[3]
|
lca.begin_rk = brk
|
||||||
lc.end_rk = row[4]
|
lca.end_rk = erk
|
||||||
|
|
||||||
values = execute(
|
data["lca"] = lca
|
||||||
"SELECT data0," +
|
lca._data = Data._db_load(execute, data=data)
|
||||||
" data1 FROM lateral_contribution_data_adists " +
|
|
||||||
f"WHERE lc = '{lc.id}'"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Write data
|
loaded.add(pid)
|
||||||
for v in values:
|
new.append(lca)
|
||||||
data0 = lc._types[0](v[0])
|
|
||||||
data1 = lc._types[1](v[1])
|
|
||||||
# Replace data at pos ind
|
|
||||||
lc._data.append((data0, data1))
|
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
execute(f"DELETE FROM lateral_contribution_adists" +
|
execute(
|
||||||
f" WHERE id = {self.id}")
|
f"DELETE FROM lateral_contribution_adists " +
|
||||||
execute(f"DELETE FROM lateral_contribution_data_adists" +
|
f"WHERE pamhyr_id = {self.id} " +
|
||||||
f" WHERE lc = {self.id}")
|
f"AND scenario = {self._status.scenario_id}"
|
||||||
|
)
|
||||||
|
execute(
|
||||||
|
f"DELETE FROM lateral_contribution_data_adists " +
|
||||||
|
f"WHERE lca = {self.id} " +
|
||||||
|
f"AND scenario = {self._status.scenario_id}"
|
||||||
|
)
|
||||||
|
|
||||||
sql = (
|
execute(
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"lateral_contribution_adists(id, " +
|
"lateral_contribution_adists(id, deleted," +
|
||||||
"pollutant, edge, begin_rk, end_rk) " +
|
"pollutant, reach, begin_rk, end_rk, scenario) " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, {self._pollutant}, {self.edge}, " +
|
f"{self.id}, {self._db_format(self.is_deleted())}, " +
|
||||||
|
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:
|
||||||
data0 = self._db_format(str(d[0]))
|
data["ind"] = ind
|
||||||
data1 = self._db_format(str(d[1]))
|
|
||||||
|
|
||||||
sql = (
|
d._db_save(execute, data)
|
||||||
"INSERT INTO " +
|
|
||||||
"lateral_contribution_data_adists(data0, data1, lc) " +
|
ind += 1
|
||||||
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(
|
||||||
|
list(
|
||||||
|
filter(
|
||||||
|
lambda el: not el.is_deleted(),
|
||||||
|
self._data
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def time_convert(cls, data):
|
def time_convert(cls, data):
|
||||||
@@ -176,13 +429,13 @@ class LateralContributionAdisTS(SQLSubModel):
|
|||||||
return int(data)
|
return int(data)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def edge(self):
|
def reach(self):
|
||||||
return self._edge
|
return self._reach
|
||||||
|
|
||||||
@edge.setter
|
@reach.setter
|
||||||
def edge(self, edge):
|
def reach(self, reach):
|
||||||
self._edge = edge
|
self._reach = reach
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def header(self):
|
def header(self):
|
||||||
@@ -191,7 +444,7 @@ class LateralContributionAdisTS(SQLSubModel):
|
|||||||
@header.setter
|
@header.setter
|
||||||
def header(self, header):
|
def header(self, header):
|
||||||
self._header = header
|
self._header = header
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pollutant(self):
|
def pollutant(self):
|
||||||
@@ -208,7 +461,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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def end_rk(self):
|
def end_rk(self):
|
||||||
@@ -217,4 +470,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._status.modified()
|
self.modified()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# LateralContributionsAdisTSList.py -- Pamhyr
|
# LateralContributionsAdisTSList.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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.PamhyrList import PamhyrModelList
|
from Model.Tools.PamhyrListExt import PamhyrModelList
|
||||||
from Model.Except import NotImplementedMethodeError
|
from Model.Except import NotImplementedMethodeError
|
||||||
|
|
||||||
from Model.LateralContributionsAdisTS.LateralContributionAdisTS \
|
from Model.LateralContributionsAdisTS.LateralContributionAdisTS \
|
||||||
@@ -45,8 +45,14 @@ class LateralContributionsAdisTSList(PamhyrModelList):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute("DELETE FROM lateral_contribution_adists")
|
execute(
|
||||||
execute("DELETE FROM lateral_condition_data_adists")
|
"DELETE FROM lateral_contribution_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 = {}
|
||||||
|
|||||||
+16
-22
@@ -1,5 +1,5 @@
|
|||||||
# Edge.py -- Pamhyr
|
# Edge.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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,28 +16,18 @@
|
|||||||
|
|
||||||
# -*- 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(object):
|
class Edge(SQLSubModel):
|
||||||
_id_cnt = 0
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
|
|
||||||
def __init__(self, id: int, name: str,
|
|
||||||
node1: Node = None,
|
node1: Node = None,
|
||||||
node2: Node = None,
|
node2: Node = None,
|
||||||
status=None):
|
**kwargs):
|
||||||
super(Edge, self).__init__()
|
super(Edge, self).__init__(id=id, **kwargs)
|
||||||
|
|
||||||
self._status = status
|
|
||||||
|
|
||||||
if id == -1:
|
|
||||||
type(self)._id_cnt += 1
|
|
||||||
self.id = type(self)._id_cnt
|
|
||||||
else:
|
|
||||||
self.id = id
|
|
||||||
|
|
||||||
self._name = name
|
self._name = name
|
||||||
|
|
||||||
self.node1 = node1
|
self.node1 = node1
|
||||||
self.node2 = node2
|
self.node2 = node2
|
||||||
|
|
||||||
@@ -49,7 +39,7 @@ class Edge(object):
|
|||||||
if name == "name":
|
if name == "name":
|
||||||
ret = self.name
|
ret = self.name
|
||||||
elif name == "id":
|
elif name == "id":
|
||||||
ret = self.id
|
ret = self.pamhyr_id
|
||||||
elif name == "node1":
|
elif name == "node1":
|
||||||
ret = self.node1
|
ret = self.node1
|
||||||
elif name == "node2":
|
elif name == "node2":
|
||||||
@@ -63,7 +53,7 @@ class Edge(object):
|
|||||||
if name == "name":
|
if name == "name":
|
||||||
self._name = value
|
self._name = value
|
||||||
elif name == "id":
|
elif name == "id":
|
||||||
self.id = value
|
self.pamhyr_id = value
|
||||||
elif name == "node1":
|
elif name == "node1":
|
||||||
self.node1 = value
|
self.node1 = value
|
||||||
elif name == "node2":
|
elif name == "node2":
|
||||||
@@ -71,7 +61,11 @@ class Edge(object):
|
|||||||
elif name == "enable":
|
elif name == "enable":
|
||||||
self._enable = value
|
self._enable = value
|
||||||
|
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
return self.pamhyr_id
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
@@ -85,14 +79,14 @@ class Edge(object):
|
|||||||
|
|
||||||
def enable(self, enable=True):
|
def enable(self, enable=True):
|
||||||
self._enable = enable
|
self._enable = enable
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
def disable(self):
|
def disable(self):
|
||||||
self._enable = False
|
self._enable = False
|
||||||
self._status.modified()
|
self.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._status.modified()
|
self.modified()
|
||||||
+40
-34
@@ -1,5 +1,5 @@
|
|||||||
# Graph.py -- Pamhyr
|
# Graph.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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(object):
|
class Graph(SQLSubModel):
|
||||||
def __init__(self, status=None):
|
def __init__(self, **kwargs):
|
||||||
super(Graph, self).__init__()
|
super(Graph, self).__init__(**kwargs)
|
||||||
|
|
||||||
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(object):
|
|||||||
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 self._nodes
|
return list(filter(lambda n: not n.is_deleted(), 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 self._edges
|
return list(filter(lambda e: not e.is_deleted(), self._edges))
|
||||||
|
|
||||||
def enable_edges(self):
|
def enable_edges(self):
|
||||||
return list(
|
return list(
|
||||||
@@ -54,44 +54,44 @@ class Graph(object):
|
|||||||
def _enable_edges(self):
|
def _enable_edges(self):
|
||||||
"""Return a generator"""
|
"""Return a generator"""
|
||||||
return filter(
|
return filter(
|
||||||
lambda e: e.is_enable(),
|
lambda e: e.is_enable() and not e.is_deleted(),
|
||||||
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(object):
|
|||||||
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(object):
|
|||||||
edge = list(
|
edge = list(
|
||||||
filter(
|
filter(
|
||||||
lambda e: e.name == edge_name,
|
lambda e: e.name == edge_name,
|
||||||
self._edges
|
self.edges()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -121,18 +121,23 @@ class Graph(object):
|
|||||||
|
|
||||||
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(
|
||||||
-1,
|
id=-1, name="", x=x, y=y,
|
||||||
"",
|
|
||||||
x=x, y=y,
|
|
||||||
status=self._status
|
status=self._status
|
||||||
)
|
)
|
||||||
return node
|
return node
|
||||||
|
|
||||||
def _add_node(self, node):
|
def _add_node(self, node):
|
||||||
self._nodes.append(node)
|
if node in self._nodes:
|
||||||
self._status.modified()
|
node.set_as_not_deleted()
|
||||||
|
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):
|
||||||
@@ -143,9 +148,9 @@ class Graph(object):
|
|||||||
return self._add_node(node)
|
return self._add_node(node)
|
||||||
|
|
||||||
def remove_node(self, node):
|
def remove_node(self, node):
|
||||||
self._nodes.remove(node)
|
node.set_as_deleted()
|
||||||
self._remove_associated_edge(node)
|
self._remove_associated_edge(node)
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
def _remove_associated_edge(self, node: str):
|
def _remove_associated_edge(self, node: str):
|
||||||
edges = list(
|
edges = list(
|
||||||
@@ -164,8 +169,7 @@ class Graph(object):
|
|||||||
|
|
||||||
def _create_edge(self, n1: Node, n2: Node):
|
def _create_edge(self, n1: Node, n2: Node):
|
||||||
edge = self._edge_ctor(
|
edge = self._edge_ctor(
|
||||||
-1,
|
node1=n1, node2=n2,
|
||||||
"", n1, n2,
|
|
||||||
status=self._status
|
status=self._status
|
||||||
)
|
)
|
||||||
return edge
|
return edge
|
||||||
@@ -176,10 +180,12 @@ class Graph(object):
|
|||||||
# 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._edges.append(edge)
|
self.modified()
|
||||||
|
|
||||||
self._status.modified()
|
|
||||||
return edge
|
return edge
|
||||||
|
|
||||||
def add_edge(self, n1: Node, n2: Node):
|
def add_edge(self, n1: Node, n2: Node):
|
||||||
@@ -193,8 +199,8 @@ class Graph(object):
|
|||||||
return self._create_edge(n1, n2)
|
return self._create_edge(n1, n2)
|
||||||
|
|
||||||
def remove_edge(self, edge):
|
def remove_edge(self, edge):
|
||||||
self._edges.remove(edge)
|
edge.set_as_deleted()
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
def is_upstream_node(self, node):
|
def is_upstream_node(self, node):
|
||||||
return reduce(
|
return reduce(
|
||||||
|
|||||||
+10
-19
@@ -1,5 +1,5 @@
|
|||||||
# Node.py -- Pamhyr
|
# Node.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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,24 +16,15 @@
|
|||||||
|
|
||||||
# -*- 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(object):
|
class Node(SQLSubModel):
|
||||||
_id_cnt = 0
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
|
|
||||||
def __init__(self, id: int, name: str,
|
|
||||||
x: float = 0.0, y: float = 0.0,
|
x: float = 0.0, y: float = 0.0,
|
||||||
status=None):
|
**kwargs):
|
||||||
super(Node, self).__init__()
|
super(Node, self).__init__(id=id, **kwargs)
|
||||||
|
|
||||||
self._status = status
|
|
||||||
|
|
||||||
if id == -1:
|
|
||||||
type(self)._id_cnt += 1
|
|
||||||
self.id = type(self)._id_cnt
|
|
||||||
else:
|
|
||||||
self.id = id
|
|
||||||
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self.pos = Point(x, y)
|
self.pos = Point(x, y)
|
||||||
@@ -44,7 +35,7 @@ class Node(object):
|
|||||||
if key == "name":
|
if key == "name":
|
||||||
ret = self._name
|
ret = self._name
|
||||||
elif key == "id":
|
elif key == "id":
|
||||||
ret = self.id
|
ret = self.pamhyr_id
|
||||||
elif key == "pos":
|
elif key == "pos":
|
||||||
ret = f"({self.pos.x},{self.pos.y})"
|
ret = f"({self.pos.x},{self.pos.y})"
|
||||||
|
|
||||||
@@ -54,9 +45,9 @@ class Node(object):
|
|||||||
if key == "name":
|
if key == "name":
|
||||||
self._name = value
|
self._name = value
|
||||||
elif key == "id":
|
elif key == "id":
|
||||||
self.id = value
|
self.pamhyr_id = value
|
||||||
|
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
@@ -76,4 +67,4 @@ class Node(object):
|
|||||||
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._status.modified()
|
self.modified()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# Point.py -- Pamhyr
|
# Point.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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-2024 INRAE
|
# Copyright (C) 2023-2025 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-2024 INRAE
|
# Copyright (C) 2023-2025 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.Saved import SavedStatus
|
from Model.Status import StudyStatus
|
||||||
|
|
||||||
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 = SavedStatus()
|
status = StudyStatus()
|
||||||
g = Graph(status=status)
|
g = Graph(status=status)
|
||||||
|
|
||||||
return g
|
return g
|
||||||
@@ -60,6 +60,9 @@ 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-2024 INRAE
|
# Copyright (C) 2023-2025 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,33 +27,27 @@ 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,
|
||||||
super(OutputRKAdists, self).__init__()
|
owner_scenario=-1):
|
||||||
|
super(OutputRKAdists, self).__init__(
|
||||||
self._status = status
|
id=id, 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)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def reach(self):
|
def reach(self):
|
||||||
return self._reach
|
return self._reach
|
||||||
@@ -82,70 +76,129 @@ class OutputRKAdists(SQLSubModel):
|
|||||||
self._status.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute, ext=""):
|
||||||
|
execute(f"""
|
||||||
sql = (
|
CREATE TABLE output_rk_adists{ext}(
|
||||||
"CREATE TABLE OutputRKAdists(" +
|
{cls.create_db_add_pamhyr_id()},
|
||||||
"id INTEGER NOT NULL PRIMARY KEY, " +
|
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
"reach INTEGER NOT NULL, " +
|
reach INTEGER NOT NULL,
|
||||||
"rk REAL NOT NULL, " +
|
rk REAL NOT NULL,
|
||||||
"title TEXT NOT NULL, " +
|
title TEXT NOT NULL,
|
||||||
"FOREIGN KEY(reach) REFERENCES river_reach(id)" +
|
{Scenario.create_db_add_scenario()},
|
||||||
")"
|
{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):
|
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
|
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 id, reach, rk, title " +
|
"SELECT pamhyr_id, deleted, reach, rk, title, scenario " +
|
||||||
f"FROM OutputRKAdists"
|
f"FROM output_rk_adists"
|
||||||
)
|
)
|
||||||
|
|
||||||
if table is not None:
|
if table is not None:
|
||||||
for row in table:
|
for row in table:
|
||||||
id = row[0]
|
it = iter(row)
|
||||||
id_reach = row[1]
|
|
||||||
id_rk = row[2]
|
pid = next(it)
|
||||||
title = row[3]
|
deleted = (next(it) == 1)
|
||||||
|
id_reach = next(it)
|
||||||
|
id_rk = next(it)
|
||||||
|
title = next(it)
|
||||||
|
owner_scenario = next(it)
|
||||||
|
|
||||||
new_output = cls(
|
new_output = cls(
|
||||||
id=id, reach=id_reach,
|
id=pid, 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(f"DELETE FROM OutputRKAdists WHERE id = {self.id}")
|
execute(
|
||||||
|
"DELETE FROM output_rk_adists " +
|
||||||
sql = (
|
f"WHERE pamhyr_id = {self.id} " +
|
||||||
"INSERT INTO " +
|
f"AND scenario = {self._status.scenario_id} "
|
||||||
"OutputRKAdists(id, reach, rk, title) " +
|
|
||||||
"VALUES (" +
|
|
||||||
f"{self.id}, {self._reach}, {self._rk}, " +
|
|
||||||
f"'{self._db_format(self._title)}'" +
|
|
||||||
")"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
execute(sql)
|
execute(
|
||||||
|
"INSERT INTO " +
|
||||||
|
"output_rk_adists(pamhyr_id, deleted, " +
|
||||||
|
"reach, rk, title, scenario) " +
|
||||||
|
"VALUES (" +
|
||||||
|
f"{self.id}, {self._db_format(self.is_deleted())}" +
|
||||||
|
f"{self._reach}, {self._rk}, " +
|
||||||
|
f"'{self._db_format(self._title)}', " +
|
||||||
|
f"{self._status.scenario_id}" +
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# OutputRKListAdists.py -- Pamhyr
|
# OutputRKListAdists.py -- Pamhyr
|
||||||
# Copyright (C) 2024 INRAE
|
# Copyright (C) 2024-2025 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.PamhyrList import PamhyrModelList
|
from Model.Tools.PamhyrListExt import PamhyrModelList
|
||||||
from Model.OutputRKAdists.OutputRKAdists import OutputRKAdists
|
from Model.OutputRKAdists.OutputRKAdists import OutputRKAdists
|
||||||
|
|
||||||
|
|
||||||
@@ -38,7 +38,10 @@ class OutputRKAdistsList(PamhyrModelList):
|
|||||||
ok = True
|
ok = True
|
||||||
|
|
||||||
# Delete previous data
|
# Delete previous data
|
||||||
execute("DELETE FROM OutputRKAdists")
|
execute(
|
||||||
|
"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-2024 INRAE
|
# Copyright (C) 2023-2025 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,23 +27,264 @@ 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 Pollutants(SQLSubModel):
|
class PollutantCharacteristics(SQLSubModel):
|
||||||
_sub_classes = []
|
_sub_classes = []
|
||||||
_id_cnt = 0
|
|
||||||
|
|
||||||
def __init__(self, id: int = -1, name: str = "", status=None):
|
def __init__(self, id: int = -1, type=-1,
|
||||||
super(Pollutants, self).__init__()
|
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._status = status
|
||||||
|
|
||||||
if id == -1:
|
self._type = type
|
||||||
self.id = Pollutants._id_cnt
|
self._diametre = diametre
|
||||||
else:
|
self._rho = rho
|
||||||
self.id = id
|
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):
|
||||||
|
_sub_classes = [PollutantCharacteristics]
|
||||||
|
|
||||||
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
|
status=None, owner_scenario=-1):
|
||||||
|
super(Pollutants, self).__init__(
|
||||||
|
id=id, status=status,
|
||||||
|
owner_scenario=owner_scenario
|
||||||
|
)
|
||||||
|
|
||||||
|
self._status = status
|
||||||
|
|
||||||
if name is None or name == "":
|
if name is None or name == "":
|
||||||
self.name = f"pol{self.id}"
|
self.name = f"pol{self.id}"
|
||||||
@@ -53,9 +294,6 @@ class Pollutants(SQLSubModel):
|
|||||||
|
|
||||||
self._data = []
|
self._data = []
|
||||||
|
|
||||||
Pollutants._id_cnt = max(
|
|
||||||
Pollutants._id_cnt + 1, self.id)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
return self._name
|
return self._name
|
||||||
@@ -67,106 +305,167 @@ class Pollutants(SQLSubModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def data(self):
|
def data(self):
|
||||||
return self._data.copy()
|
return self._data
|
||||||
|
|
||||||
|
@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):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE Pollutants(
|
CREATE TABLE pollutants{ext}(
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
name TEXT NOT NULL UNIQUE
|
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
{Scenario.create_db_add_scenario()},
|
||||||
|
{Scenario.create_db_add_scenario_fk()}
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
execute("""
|
if ext != "":
|
||||||
CREATE TABLE Pollutants_characteristics(
|
return True
|
||||||
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):
|
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
|
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 id, name " +
|
"SELECT pamhyr_id, deleted, name FROM pollutants " +
|
||||||
f"FROM Pollutants"
|
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:
|
||||||
id = row[0]
|
it = iter(row)
|
||||||
name = row[1]
|
|
||||||
|
pid = next(it)
|
||||||
|
deleted = (next(it) == 1)
|
||||||
|
name = next(it)
|
||||||
|
|
||||||
new_pollutant = cls(
|
new_pollutant = cls(
|
||||||
id=id, name=name,
|
id=pid, name=name,
|
||||||
status=status
|
status=status
|
||||||
)
|
)
|
||||||
|
if deleted:
|
||||||
|
new_pollutant.set_as_deleted()
|
||||||
|
|
||||||
new_data = []
|
data["pollutant"] = new_pollutant
|
||||||
table = execute(
|
new_pollutant._data = PollutantCharacteristics._db_load(
|
||||||
"SELECT * " +
|
execute, data=data
|
||||||
"FROM Pollutants_characteristics " +
|
|
||||||
f"WHERE pollutant = {id}"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if table is not None:
|
loaded.add(pid)
|
||||||
for t in table:
|
|
||||||
new_data = t[1:-1]
|
|
||||||
|
|
||||||
new_pollutant._data.append(new_data)
|
|
||||||
|
|
||||||
new.append(new_pollutant)
|
new.append(new_pollutant)
|
||||||
|
|
||||||
|
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(f"DELETE FROM Pollutants WHERE id = {self.id}")
|
ok = True
|
||||||
execute(f"DELETE FROM Pollutants_characteristics" +
|
|
||||||
f" WHERE pollutant = {self.id}")
|
|
||||||
|
|
||||||
sql = (
|
execute(
|
||||||
|
"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(id, name) " +
|
"pollutants(pamhyr_id, deleted, name, scenario) " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, " +
|
f"{self.id}, {self._db_format(self.is_deleted())}, " +
|
||||||
f"'{self._db_format(self._name)}'" +
|
f"'{self._db_format(self._name)}', " +
|
||||||
|
f"{self._status.scenario_id}" +
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
|
||||||
execute(sql)
|
for d in self._data:
|
||||||
|
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:
|
||||||
sql = (
|
d._data_traversal(predicate, modifier, data)
|
||||||
"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):
|
||||||
@@ -181,7 +480,6 @@ 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 INRAE
|
# Copyright (C) 2024-2025 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,7 +38,10 @@ class PollutantsList(PamhyrModelList):
|
|||||||
ok = True
|
ok = True
|
||||||
|
|
||||||
# Delete previous data
|
# Delete previous data
|
||||||
execute("DELETE FROM Pollutants")
|
execute(
|
||||||
|
"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 INRAE
|
# Copyright (C) 2024-2025 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,31 +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
|
||||||
|
|
||||||
|
|
||||||
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):
|
status=None, owner_scenario=-1):
|
||||||
super(REPLine, self).__init__()
|
super(REPLine, self).__init__(
|
||||||
|
id, status=status,
|
||||||
if id == -1:
|
owner_scenario=owner_scenario
|
||||||
self.id = REPLine._id_cnt
|
)
|
||||||
else:
|
|
||||||
self.id = id
|
|
||||||
|
|
||||||
self._status = status
|
|
||||||
|
|
||||||
self._enabled = enabled
|
self._enabled = enabled
|
||||||
self._name = f"Line{self.id}" if name == "" else name
|
self._name = f"Line #{self._pamhyr_id}" if name == "" else name
|
||||||
self._line = line
|
self._line = line
|
||||||
self._solvers = solvers
|
self._solvers = solvers
|
||||||
|
|
||||||
REPLine._id_cnt = max(id, REPLine._id_cnt+1)
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
value = None
|
value = None
|
||||||
|
|
||||||
@@ -113,35 +107,75 @@ class REPLine(SQLSubModel):
|
|||||||
self._status.modified()
|
self._status.modified()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE rep_lines(
|
CREATE TABLE rep_lines{ext} (
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
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):
|
def _db_update(cls, execute, version, data=None):
|
||||||
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 id, enabled, name, line, solvers " +
|
"SELECT pamhyr_id, enabled, deleted, name, line, " +
|
||||||
"FROM rep_lines"
|
"solvers, scenario " +
|
||||||
|
"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:
|
||||||
@@ -149,32 +183,47 @@ 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, name=name, line=line,
|
id=id, enabled=enabled,
|
||||||
solvers=solvers, status=data['status']
|
name=name, line=line, solvers=solvers,
|
||||||
|
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)
|
||||||
|
|
||||||
sql = (
|
execute(
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"rep_lines(id, enabled, name, line, solvers) " +
|
"rep_lines(pamhyr_id, enabled, deleted, " +
|
||||||
|
"name, line, solvers, scenario) " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, {self._enabled}, " +
|
f"{self._pamhyr_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 INRAE
|
# Copyright (C) 2024-2025 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.PamhyrList import PamhyrModelList
|
from Model.Tools.PamhyrListExt import PamhyrModelList
|
||||||
from Model.REPLine.REPLine import REPLine
|
from Model.REPLine.REPLine import REPLine
|
||||||
|
|
||||||
|
|
||||||
@@ -38,7 +38,10 @@ class REPLineList(PamhyrModelList):
|
|||||||
ok = True
|
ok = True
|
||||||
|
|
||||||
# Delete previous data
|
# Delete previous data
|
||||||
execute("DELETE FROM rep_lines")
|
execute(
|
||||||
|
"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-2024 INRAE
|
# Copyright (C) 2023-2025 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,148 +19,386 @@
|
|||||||
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 Reservoir(SQLSubModel):
|
class Data(SQLSubModel):
|
||||||
_sub_classes = []
|
_sub_classes = []
|
||||||
_id_cnt = 0
|
|
||||||
|
|
||||||
def __init__(self, id: int = -1, name: str = "",
|
def __init__(self, id: int = -1,
|
||||||
status=None):
|
elevation: float = 0.,
|
||||||
super(Reservoir, self).__init__()
|
surface: float = 0.,
|
||||||
|
status=None, owner_scenario=-1):
|
||||||
|
super(Data, self).__init__(
|
||||||
|
id=id, status=status,
|
||||||
|
owner_scenario=owner_scenario
|
||||||
|
)
|
||||||
|
|
||||||
self._status = status
|
self._data = [elevation, surface]
|
||||||
|
|
||||||
if id == -1:
|
@classmethod
|
||||||
self.id = Reservoir._id_cnt
|
def _db_create(cls, execute, ext=""):
|
||||||
else:
|
execute(f"""
|
||||||
self.id = id
|
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):
|
||||||
|
_sub_classes = [Data]
|
||||||
|
|
||||||
|
def __init__(self, id: int = -1,
|
||||||
|
name: str = "",
|
||||||
|
status=None, owner_scenario=-1):
|
||||||
|
super(Reservoir, self).__init__(
|
||||||
|
id=id, status=status,
|
||||||
|
owner_scenario=owner_scenario
|
||||||
|
)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE reservoir(
|
CREATE TABLE reservoir{ext} (
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
|
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
node INTEGER,
|
node INTEGER,
|
||||||
FOREIGN KEY(node) REFERENCES river_node(id)
|
{Scenario.create_db_add_scenario()},
|
||||||
|
{Scenario.create_db_add_scenario_fk()},
|
||||||
|
FOREIGN KEY(node) REFERENCES river_node(pamhyr_id),
|
||||||
|
PRIMARY KEY(pamhyr_id, scenario)
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
execute("""
|
if ext == "_tmp":
|
||||||
CREATE TABLE reservoir_data(
|
return True
|
||||||
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):
|
def _db_update(cls, execute, version, data=None):
|
||||||
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
|
||||||
|
|
||||||
return True
|
cls._db_update_to_0_2_0(execute, data)
|
||||||
|
|
||||||
|
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 id, name, node " +
|
"SELECT pamhyr_id, deleted, name, node, scenario " +
|
||||||
"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:
|
||||||
id = row[0]
|
it = iter(row)
|
||||||
name = row[1]
|
|
||||||
node_id = row[2]
|
id = next(it)
|
||||||
new_reservoir = cls(id, name, status=data["status"])
|
deleted = (next(it) == 1)
|
||||||
|
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:
|
if node_id != -1 and node_id is not None:
|
||||||
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"]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
new_data = []
|
data["reservoir"] = new_reservoir
|
||||||
table = execute(
|
new_reservoir._data = Data._db_load(
|
||||||
"SELECT elevation, surface " +
|
execute, data=data
|
||||||
"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(f"DELETE FROM reservoir WHERE id = {self.id}")
|
execute(
|
||||||
execute(f"DELETE FROM reservoir_data WHERE reservoir = {self.id}")
|
"DELETE FROM reservoir " +
|
||||||
|
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 = -1
|
node_id = 'NULL'
|
||||||
if self._node is not None:
|
if self._node is not None:
|
||||||
node_id = self._node.id
|
node_id = self._node.pamhyr_id
|
||||||
|
|
||||||
sql = (
|
sql = (
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"reservoir(id, name, node) " +
|
"reservoir(pamhyr_id, deleted, name, node, scenario) " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, '{self._db_format(self._name)}', " +
|
f"{self.pamhyr_id}, " +
|
||||||
f"{node_id}" +
|
f"{self._db_format(self.is_deleted())}, " +
|
||||||
|
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:
|
||||||
sql = (
|
data["ind"] = ind
|
||||||
"INSERT INTO " +
|
|
||||||
"reservoir_data(ind, elevation, surface, reservoir) " +
|
d._db_save(execute, data)
|
||||||
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"R{self.id + 1}"
|
return f"Reservoir #{self.pamhyr_id}"
|
||||||
|
|
||||||
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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def node(self):
|
def node(self):
|
||||||
@@ -169,7 +407,7 @@ class Reservoir(SQLSubModel):
|
|||||||
@node.setter
|
@node.setter
|
||||||
def node(self, node):
|
def node(self, node):
|
||||||
self._node = node
|
self._node = node
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
def has_node(self):
|
def has_node(self):
|
||||||
return self._node is not None
|
return self._node is not None
|
||||||
@@ -202,42 +440,63 @@ class Reservoir(SQLSubModel):
|
|||||||
return (new_0, new_1)
|
return (new_0, new_1)
|
||||||
|
|
||||||
def add(self, index: int):
|
def add(self, index: int):
|
||||||
value = (self._default_elevation, self._default_surface)
|
data = Data(
|
||||||
self._data.insert(index, value)
|
elevation=self._default_elevation,
|
||||||
self._status.modified()
|
surface=self._default_surface,
|
||||||
return value
|
status=self._status,
|
||||||
|
)
|
||||||
|
|
||||||
|
self._data.insert(index, data)
|
||||||
|
self.modified()
|
||||||
|
return data
|
||||||
|
|
||||||
def insert(self, index: int, value):
|
def insert(self, index: int, value):
|
||||||
self._data.insert(index, value)
|
if type(value) is tuple or type(value) is list:
|
||||||
self._status.modified()
|
value = Data(
|
||||||
|
elevation=value[0],
|
||||||
|
surface=value[1]
|
||||||
|
)
|
||||||
|
|
||||||
def delete_i(self, indexes):
|
self._data.insert(index, value)
|
||||||
|
self.modified()
|
||||||
|
|
||||||
|
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)
|
enumerate(self._data)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
def delete(self, els):
|
def delete_i(self, indexes):
|
||||||
self._data = list(
|
list(
|
||||||
filter(
|
map(
|
||||||
lambda e: e not in els,
|
lambda e: e[1].set_as_deleted(),
|
||||||
self.data
|
filter(
|
||||||
|
lambda e: e[0] in indexes,
|
||||||
|
enumerate(self.lst)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
|
def delete(self, els):
|
||||||
|
for el in els:
|
||||||
|
el.set_as_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, key=lambda d: d[0])
|
||||||
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]
|
||||||
@@ -246,13 +505,12 @@ 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):
|
||||||
v = list(self._data[index])
|
self._data[index][column] = value
|
||||||
v[column] = value
|
self.modified()
|
||||||
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)
|
||||||
@@ -265,11 +523,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._status.modified()
|
self.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._status.modified()
|
self.modified()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# ReservoirList.py -- Pamhyr
|
# ReservoirList.py -- Pamhyr
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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.PamhyrList import PamhyrModelList
|
from Model.Tools.PamhyrListExt import PamhyrModelList
|
||||||
from Model.Reservoir.Reservoir import Reservoir
|
from Model.Reservoir.Reservoir import Reservoir
|
||||||
|
|
||||||
|
|
||||||
@@ -39,8 +39,14 @@ class ReservoirList(PamhyrModelList):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute("DELETE FROM reservoir")
|
execute(
|
||||||
execute("DELETE FROM reservoir_data")
|
"DELETE FROM reservoir " +
|
||||||
|
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-2024 INRAE
|
# Copyright (C) 2023-2025 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,32 +14,174 @@
|
|||||||
# 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 Results(object):
|
class AdditionalData(SQLSubModel):
|
||||||
def __init__(self, study=None, solver=None,
|
_sub_classes = []
|
||||||
repertory="", name="0"):
|
|
||||||
|
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._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"):
|
||||||
|
super(Results, self).__init__(
|
||||||
|
id=id, status=study.status,
|
||||||
|
owner_scenario=study.status.scenario.id
|
||||||
|
)
|
||||||
|
|
||||||
|
self._name = name
|
||||||
|
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"]
|
||||||
@@ -60,6 +202,13 @@ class Results(object):
|
|||||||
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]
|
||||||
|
|
||||||
@@ -69,3 +218,164 @@ class Results(object):
|
|||||||
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-2024 INRAE
|
# Copyright (C) 2023-2025 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,6 +50,9 @@ 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-2024 INRAE
|
# Copyright (C) 2023-2025 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,15 +14,27 @@
|
|||||||
# 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(object):
|
class Profile(SQLSubModel):
|
||||||
def __init__(self, profile, study):
|
def __init__(self, profile, study):
|
||||||
|
super(Profile, self).__init__(
|
||||||
|
id=-1, status=study.status,
|
||||||
|
owner_scenario=study.status.scenario.id
|
||||||
|
)
|
||||||
|
|
||||||
self._study = study
|
self._study = study
|
||||||
self._profile = profile # Source profile in the study
|
self._profile = profile # Source profile in the study
|
||||||
self._data = {} # Dict of dict {<ts>: {<key>: <value>, ...}, ...}
|
self._data = {} # Dict of dict {<ts>: {<key>: <value>, ...}, ...}
|
||||||
@@ -52,9 +64,11 @@ class Profile(object):
|
|||||||
return self._data[timestamp]
|
return self._data[timestamp]
|
||||||
|
|
||||||
def get_key(self, key):
|
def get_key(self, key):
|
||||||
return list(
|
res = list(
|
||||||
map(lambda ts: self._data[ts][key], self._data)
|
map(lambda ts: self._data[ts][key],
|
||||||
|
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:
|
||||||
@@ -65,17 +79,196 @@ class Profile(object):
|
|||||||
def has_sediment(self):
|
def has_sediment(self):
|
||||||
return any(map(lambda ts: "sl" in self._data[ts], self._data))
|
return any(map(lambda ts: "sl" in self._data[ts], self._data))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_create(cls, execute, ext=""):
|
||||||
|
execute(f"""
|
||||||
|
CREATE TABLE results_data{ext}(
|
||||||
|
{cls.create_db_add_pamhyr_id()},
|
||||||
|
result INTEGER NOT NULL,
|
||||||
|
key TEXT NOT NULL,
|
||||||
|
reach INTEGER NOT NULL,
|
||||||
|
section INTEGER NOT NULL,
|
||||||
|
len_data INTEGER NOT NULL,
|
||||||
|
data BLOB NOT NULL,
|
||||||
|
{Scenario.create_db_add_scenario()},
|
||||||
|
{Scenario.create_db_add_scenario_fk()},
|
||||||
|
FOREIGN KEY(result) REFERENCES results(pamhyr_id),
|
||||||
|
FOREIGN KEY(reach) REFERENCES river_reach(pamhyr_id),
|
||||||
|
FOREIGN KEY(section)
|
||||||
|
REFERENCES geometry_profileXYZ(pamhyr_id),
|
||||||
|
PRIMARY KEY(pamhyr_id, result, key, scenario)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
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 = list(
|
self._profiles = []
|
||||||
map(
|
if with_init:
|
||||||
lambda p: Profile(p, self._study),
|
self._profiles = list(
|
||||||
reach.profiles
|
map(
|
||||||
|
lambda p: Profile(p, self._study),
|
||||||
|
reach.profiles
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
self._profile_mask = list(
|
self._profile_mask = list(
|
||||||
map(
|
map(
|
||||||
@@ -111,9 +304,44 @@ class Reach(object):
|
|||||||
def has_sediment(self):
|
def has_sediment(self):
|
||||||
return any(map(lambda profile: profile.has_sediment(), self._profiles))
|
return any(map(lambda profile: profile.has_sediment(), self._profiles))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_create(cls, execute, ext=""):
|
||||||
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
|
@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
|
||||||
@@ -147,3 +375,30 @@ class River(object):
|
|||||||
self._reachs
|
self._reachs
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_create(cls, execute, ext=""):
|
||||||
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_update(cls, execute, version, data=None):
|
||||||
|
return cls._update_submodel(execute, version, data)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _db_load(cls, execute, data=None):
|
||||||
|
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-2024 INRAE
|
# Copyright (C) 2023-2025 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
|
||||||
|
|||||||
+430
-79
@@ -1,5 +1,5 @@
|
|||||||
# River.py -- Pamhyr river model
|
# River.py -- Pamhyr river model
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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,9 +16,13 @@
|
|||||||
|
|
||||||
# -*- 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
|
||||||
@@ -55,64 +59,150 @@ 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, SQLSubModel):
|
class RiverNode(Node):
|
||||||
_sub_classes = []
|
_sub_classes = []
|
||||||
|
|
||||||
def __init__(self, id: str, name: str,
|
def __init__(self, id: int = -1, name: str = "",
|
||||||
x: float, y: float,
|
x: float = 0.0, y: float = 0.0,
|
||||||
status=None):
|
status=None, owner_scenario=-1):
|
||||||
super(RiverNode, self).__init__(
|
super(RiverNode, self).__init__(
|
||||||
id, name,
|
id=id, name=name, x=x, y=y,
|
||||||
x, y,
|
status=status,
|
||||||
status=status
|
owner_scenario=owner_scenario
|
||||||
)
|
)
|
||||||
|
|
||||||
self._locker = None
|
self._locker = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE river_node(
|
CREATE TABLE river_node{ext}(
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
|
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)
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
cls._create_submodel(execute)
|
if ext == "_tmp":
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version):
|
def _db_update(cls, execute, version, data=None):
|
||||||
return True
|
major, minor, release = version.strip().split(".")
|
||||||
|
|
||||||
|
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:
|
||||||
# Update id counter
|
it = iter(row)
|
||||||
cls._id_cnt = max(cls._id_cnt, row[0])
|
|
||||||
# Create new node
|
pid = next(it)
|
||||||
nodes.append(cls(*row, status=data["status"]))
|
deleted = (next(it) == 1)
|
||||||
|
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):
|
||||||
sql = (
|
execute(
|
||||||
"INSERT OR REPLACE INTO river_node(id, name, x, y) VALUES (" +
|
"INSERT OR REPLACE INTO river_node(" +
|
||||||
f"{self.id}, '{self._db_format(self.name)}', " +
|
"pamhyr_id, deleted, name, x, y, scenario" +
|
||||||
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
|
||||||
@@ -122,100 +212,204 @@ class RiverNode(Node, SQLSubModel):
|
|||||||
self._locker = locker
|
self._locker = locker
|
||||||
|
|
||||||
|
|
||||||
class RiverReach(Edge, SQLSubModel):
|
class RiverReach(Edge):
|
||||||
_sub_classes = [
|
_sub_classes = [
|
||||||
Reach,
|
Reach,
|
||||||
FrictionList,
|
FrictionList,
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, id: str, name: str,
|
def __init__(self, id: str = -1, name: str = "",
|
||||||
node1: RiverNode = None,
|
node1: RiverNode = None,
|
||||||
node2: RiverNode = None,
|
node2: RiverNode = None,
|
||||||
status=None):
|
status=None, owner_scenario=-1):
|
||||||
super(RiverReach, self).__init__(
|
super(RiverReach, self).__init__(
|
||||||
id, name,
|
id=id, name=name,
|
||||||
node1, node2,
|
node1=node1, node2=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):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE river_reach(
|
CREATE TABLE river_reach{ext} (
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
|
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
enable BOOLEAN NOT NULL,
|
enabled BOOLEAN NOT NULL,
|
||||||
node1 INTEGER,
|
node1 INTEGER,
|
||||||
node2 INTEGER,
|
node2 INTEGER,
|
||||||
FOREIGN KEY(node1) REFERENCES river_node(id),
|
{Scenario.create_db_add_scenario()},
|
||||||
FOREIGN KEY(node2) REFERENCES river_node(id)
|
{Scenario.create_db_add_scenario_fk()},
|
||||||
|
FOREIGN KEY(node1) REFERENCES river_node(pamhyr_id),
|
||||||
|
FOREIGN KEY(node2) REFERENCES river_node(pamhyr_id),
|
||||||
|
PRIMARY KEY(pamhyr_id, scenario)
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
cls._create_submodel(execute)
|
if ext == "_tmp":
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version):
|
def _db_update(cls, execute, version, data=None):
|
||||||
return cls._update_submodel(execute, version)
|
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 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 data is None:
|
if scenario is None:
|
||||||
data = {}
|
return reachs
|
||||||
|
|
||||||
table = execute(
|
table = execute(
|
||||||
"SELECT id, name, enable, node1, node2 FROM river_reach"
|
"SELECT pamhyr_id, deleted, name, enabled, " +
|
||||||
|
"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:
|
||||||
# Update id counter
|
it = iter(row)
|
||||||
cls._id_cnt = max(cls._id_cnt, row[0])
|
|
||||||
# Create new reach
|
|
||||||
id = row[0]
|
|
||||||
name = row[1]
|
|
||||||
enable = (row[2] == 1)
|
|
||||||
# Get nodes corresponding to db foreign key id
|
|
||||||
node1 = next(filter(lambda n: n.id == row[3], data["nodes"]))
|
|
||||||
node2 = next(filter(lambda n: n.id == row[4], data["nodes"]))
|
|
||||||
|
|
||||||
new = cls(id, name, node1, node2, status=data["status"])
|
pid = next(it)
|
||||||
new.enable(enable=enable)
|
deleted = (next(it) == 1)
|
||||||
|
name = next(it)
|
||||||
|
enabled = (next(it) == 1)
|
||||||
|
node1_pid = next(it)
|
||||||
|
node2_pid = next(it)
|
||||||
|
owner_scenario = next(it)
|
||||||
|
|
||||||
|
# Get nodes corresponding to db foreign key id
|
||||||
|
node1 = next(
|
||||||
|
filter(
|
||||||
|
lambda n: n.pamhyr_id == node1_pid, data["nodes"]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
node2 = next(
|
||||||
|
filter(
|
||||||
|
lambda n: n.pamhyr_id == node2_pid, data["nodes"]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
new = cls(
|
||||||
|
id=pid, name=name, node1=node1, node2=node2,
|
||||||
|
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):
|
||||||
sql = (
|
execute(
|
||||||
"INSERT OR REPLACE INTO " +
|
"INSERT OR REPLACE INTO " +
|
||||||
"river_reach(id, name, enable, node1, node2) " +
|
"river_reach(" +
|
||||||
|
"pamhyr_id, deleted, name, enabled, " +
|
||||||
|
"node1, node2, scenario" +
|
||||||
|
") " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, '{self._db_format(self._name)}', " +
|
f"{self.pamhyr_id}, {self._db_format(self.is_deleted())}, " +
|
||||||
|
f"'{self._db_format(self._name)}', " +
|
||||||
f"{self._db_format(self.is_enable())},"
|
f"{self._db_format(self.is_enable())},"
|
||||||
f"{self.node1.id}, {self.node2.id}" +
|
f"{self.node1.pamhyr_id}, {self.node2.pamhyr_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
|
||||||
@@ -224,17 +418,46 @@ class RiverReach(Edge, SQLSubModel):
|
|||||||
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
|
||||||
|
|
||||||
class River(Graph, SQLSubModel):
|
new_reach1 = RiverReach(
|
||||||
|
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,
|
||||||
@@ -246,10 +469,14 @@ class River(Graph, SQLSubModel):
|
|||||||
LateralContributionsAdisTSList,
|
LateralContributionsAdisTSList,
|
||||||
D90AdisTSList,
|
D90AdisTSList,
|
||||||
DIFAdisTSList,
|
DIFAdisTSList,
|
||||||
|
GeoTIFFList,
|
||||||
|
Results
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, status=None):
|
def __init__(self, status=None):
|
||||||
super(River, self).__init__(status=status)
|
super(River, self).__init__(
|
||||||
|
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
|
||||||
@@ -280,19 +507,22 @@ class River(Graph, SQLSubModel):
|
|||||||
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):
|
def _db_update(cls, execute, version, data=None):
|
||||||
cls._update_submodel(execute, version)
|
return cls._update_submodel(execute, version, data)
|
||||||
return True
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
new = cls(data["status"])
|
new = cls(status=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)
|
||||||
@@ -300,65 +530,84 @@ class River(Graph, SQLSubModel):
|
|||||||
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)
|
||||||
|
|
||||||
@@ -372,8 +621,16 @@ class River(Graph, SQLSubModel):
|
|||||||
|
|
||||||
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)
|
||||||
|
|
||||||
@@ -399,6 +656,11 @@ class River(Graph, SQLSubModel):
|
|||||||
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
|
||||||
|
|
||||||
@@ -422,15 +684,64 @@ class River(Graph, SQLSubModel):
|
|||||||
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"( id <> {n.id} )", els
|
lambda n: f"( pamhyr_id <> {n.pamhyr_id} )",
|
||||||
|
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()
|
||||||
@@ -468,6 +779,9 @@ 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
|
||||||
@@ -513,6 +827,10 @@ 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
|
||||||
@@ -589,3 +907,36 @@ 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
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
# 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
|
|
||||||
@@ -0,0 +1,282 @@
|
|||||||
|
# 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
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
# 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 INRAE
|
# Copyright (C) 2024-2025 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,23 +21,26 @@ 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,
|
||||||
super(Layer, self).__init__()
|
owner_scenario=-1):
|
||||||
|
super(Layer, self).__init__(
|
||||||
self._status = status
|
id=id, status=status,
|
||||||
|
owner_scenario=owner_scenario
|
||||||
|
)
|
||||||
|
|
||||||
|
self._sl = sl
|
||||||
self._name = name
|
self._name = name
|
||||||
self._type = type
|
self._type = type
|
||||||
|
|
||||||
@@ -46,23 +49,17 @@ 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(id, Layer._id_cnt+1)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
if self._name == "":
|
if self._name == "":
|
||||||
return f"Layer{self.id + 1}"
|
return f"Layer #{self.pamhyr_id}"
|
||||||
|
|
||||||
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):
|
||||||
@@ -71,6 +68,7 @@ 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):
|
||||||
@@ -79,6 +77,7 @@ 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):
|
||||||
@@ -87,6 +86,7 @@ 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,6 +95,7 @@ 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):
|
||||||
@@ -103,12 +104,14 @@ 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):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE sedimentary_layer_layer(
|
CREATE TABLE sedimentary_layer_layer{ext} (
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
|
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,
|
||||||
@@ -117,92 +120,177 @@ 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,
|
||||||
FOREIGN KEY(sl) REFERENCES sedimentary_layer(id)
|
{Scenario.create_db_add_scenario()},
|
||||||
|
{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):
|
def _db_update(cls, execute, version, data=None):
|
||||||
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)
|
||||||
|
|
||||||
return True
|
if major == "0" and minor == "1":
|
||||||
|
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 id, ind, name, type, height, " +
|
"SELECT pamhyr_id, deleted, name, type, height, " +
|
||||||
"d50, sigma, critical_constraint " +
|
"d50, sigma, critical_constraint, scenario " +
|
||||||
"FROM sedimentary_layer_layer " +
|
"FROM sedimentary_layer_layer " +
|
||||||
f"WHERE sl = {sl}"
|
f"WHERE sl = {sl.pamhyr_id} " +
|
||||||
|
f"AND scenario = {scenario.id} " +
|
||||||
|
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) " +
|
||||||
|
"ORDER BY ind ASC"
|
||||||
)
|
)
|
||||||
|
|
||||||
for _ in table:
|
|
||||||
new.append(None)
|
|
||||||
|
|
||||||
for row in table:
|
for row in table:
|
||||||
ind = row[1]
|
it = iter(row)
|
||||||
|
|
||||||
|
pid = next(it)
|
||||||
|
deleted = (next(it) == 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=row[0], name=row[2],
|
id=pid, name=name,
|
||||||
type=row[3], height=row[4],
|
type=type, height=height,
|
||||||
d50=row[5], sigma=row[6],
|
d50=d50, sigma=sigma,
|
||||||
critical_constraint=row[7],
|
critical_constraint=critical_constraint,
|
||||||
sl=sl, status=data['status']
|
sl=sl, status=data['status'],
|
||||||
|
owner_scenario=owner_scenario
|
||||||
)
|
)
|
||||||
|
if deleted:
|
||||||
|
layer.set_as_deleted()
|
||||||
|
|
||||||
new[ind] = layer
|
loaded.add(pid)
|
||||||
|
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"]
|
||||||
|
|
||||||
sql = (
|
execute(
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"sedimentary_layer_layer(id, ind, name, type, height, " +
|
"sedimentary_layer_layer(" +
|
||||||
"d50, sigma, critical_constraint, sl) " +
|
"pamhyr_id, deleted, ind, name, type, height, " +
|
||||||
|
"d50, sigma, critical_constraint, sl, scenario" +
|
||||||
|
") " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, {ind}, '{self._db_format(self._name)}', " +
|
f"{self.pamhyr_id}, {self._db_format(self.is_deleted())}, " +
|
||||||
|
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.id}" +
|
f"{sl.pamhyr_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):
|
status=None, owner_scenario=-1):
|
||||||
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)})"
|
||||||
|
|
||||||
@@ -212,31 +300,48 @@ class SedimentLayer(SQLSubModel):
|
|||||||
return s
|
return s
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self._layers)
|
return len(
|
||||||
|
list(
|
||||||
|
filter(
|
||||||
|
lambda el: not el.is_deleted(),
|
||||||
|
self._layers
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def layers(self):
|
def layers(self):
|
||||||
return self._layers.copy()
|
return list(
|
||||||
|
filter(
|
||||||
|
lambda el: not el.is_deleted(),
|
||||||
|
self._layers
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def height(self):
|
def height(self):
|
||||||
return list(
|
return list(
|
||||||
map(lambda layer: layer.height, self._layers)
|
map(lambda layer: layer.height,
|
||||||
|
self.layers)
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
if self._name == "":
|
if self._name == "":
|
||||||
return f"SL{self.id + 1}"
|
return f"SL #{self.pamhyr_id}"
|
||||||
|
|
||||||
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(lambda layer: layer.name, self._layers)
|
map(
|
||||||
|
lambda layer: layer.name,
|
||||||
|
self.layers
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -246,63 +351,128 @@ 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):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE sedimentary_layer(
|
CREATE TABLE sedimentary_layer{ext} (
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
|
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):
|
def _db_update(cls, execute, version, data=None):
|
||||||
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)
|
||||||
|
|
||||||
return True
|
if major == "0" and minor == "1":
|
||||||
|
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 id, name, comment " +
|
"SELECT pamhyr_id, deleted, name, comment, scenario " +
|
||||||
"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:
|
||||||
sl = cls(
|
it = iter(row)
|
||||||
id=row[0],
|
|
||||||
name=row[1],
|
|
||||||
comment=row[2],
|
|
||||||
status=data['status']
|
|
||||||
)
|
|
||||||
|
|
||||||
data["sl"] = sl.id
|
pid = next(it)
|
||||||
|
deleted = (next(it) == 1)
|
||||||
|
name = next(it)
|
||||||
|
comment = next(it)
|
||||||
|
owner_scenario = next(it)
|
||||||
|
|
||||||
|
sl = cls(
|
||||||
|
id=pid,
|
||||||
|
name=name,
|
||||||
|
comment=comment,
|
||||||
|
status=data['status'],
|
||||||
|
owner_scenario=owner_scenario
|
||||||
|
)
|
||||||
|
if deleted:
|
||||||
|
sl.set_as_deleted()
|
||||||
|
|
||||||
|
data["sl"] = sl
|
||||||
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 data is None:
|
if not self.must_be_saved():
|
||||||
data = {}
|
return True
|
||||||
|
|
||||||
sql = (
|
execute(
|
||||||
"INSERT INTO sedimentary_layer (id, name, comment) " +
|
"INSERT INTO sedimentary_layer (" +
|
||||||
f"VALUES ({self.id}, '{self._db_format(self._name)}', " +
|
"pamhyr_id, deleted, name, comment, scenario" +
|
||||||
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
|
||||||
|
|
||||||
@@ -314,39 +484,56 @@ class SedimentLayer(SQLSubModel):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get(self, index):
|
def _data_traversal(self,
|
||||||
return self._layers[index]
|
predicate=lambda obj, data: True,
|
||||||
|
modifier=lambda obj, data: None,
|
||||||
|
data={}):
|
||||||
|
if predicate(self, data):
|
||||||
|
modifier(self, data)
|
||||||
|
|
||||||
def set(self, index, new):
|
for layer in self._layers:
|
||||||
self._layers[index] = new
|
layer._data_traversal(predicate, modifier, data)
|
||||||
self._status.modified()
|
|
||||||
|
def get(self, index):
|
||||||
|
return self.layers[index]
|
||||||
|
|
||||||
|
# def set(self, index, new):
|
||||||
|
# self._layers[index] = new
|
||||||
|
# self.modified()
|
||||||
|
|
||||||
def insert(self, index, new):
|
def insert(self, index, new):
|
||||||
self._layers.insert(index, new)
|
self._layers.insert(index, new)
|
||||||
self._status.modified()
|
self.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._status.modified()
|
self.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:
|
||||||
self._layers.remove(el)
|
el.set_as_deleted()
|
||||||
self._status.modified()
|
|
||||||
|
self.modified()
|
||||||
|
|
||||||
def delete_i(self, indexes):
|
def delete_i(self, indexes):
|
||||||
els = list(
|
list(
|
||||||
map(
|
map(
|
||||||
lambda x: x[1],
|
lambda e: e[1].set_as_deleted(),
|
||||||
filter(
|
filter(
|
||||||
lambda x: x[0] in indexes,
|
lambda e: e[0] in indexes,
|
||||||
enumerate(self._layers)
|
enumerate(self._layers)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.delete(els)
|
self.modified()
|
||||||
|
|
||||||
def move_up(self, index):
|
def move_up(self, index):
|
||||||
if index >= 0:
|
if index >= 0:
|
||||||
@@ -355,7 +542,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._status.modified()
|
self.modified()
|
||||||
|
|
||||||
def move_down(self, index):
|
def move_down(self, index):
|
||||||
if index + 1 < len(self._layers):
|
if index + 1 < len(self._layers):
|
||||||
@@ -364,7 +551,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._status.modified()
|
self.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 INRAE
|
# Copyright (C) 2024-2025 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.PamhyrList import PamhyrModelList
|
from Model.Tools.PamhyrListExt import PamhyrModelList
|
||||||
from Model.SedimentLayer.SedimentLayer import SedimentLayer
|
from Model.SedimentLayer.SedimentLayer import SedimentLayer
|
||||||
|
|
||||||
|
|
||||||
@@ -35,11 +35,16 @@ class SedimentLayerList(PamhyrModelList):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
ok = True
|
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}"
|
||||||
|
)
|
||||||
|
|
||||||
# Delete previous data
|
ok = True
|
||||||
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-2024 INRAE
|
# Copyright (C) 2023-2025 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-2024 INRAE
|
# Copyright (C) 2023-2025 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,18 +20,221 @@ 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():
|
class Parameter(SQLSubModel):
|
||||||
def __init__(self,
|
def __init__(self, id: int = -1,
|
||||||
name="", value="",
|
name="", value="",
|
||||||
status=None):
|
status=None, owner_scenario=-1):
|
||||||
self._status = status
|
super(Parameter, self).__init__(
|
||||||
|
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
|
||||||
@@ -54,7 +257,7 @@ class Parameter():
|
|||||||
elif key == "value":
|
elif key == "value":
|
||||||
self._value = str(value)
|
self._value = str(value)
|
||||||
|
|
||||||
self._status.modified()
|
self.modified()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_tuple(cls, data, status):
|
def from_tuple(cls, data, status):
|
||||||
@@ -66,6 +269,8 @@ class Parameter():
|
|||||||
|
|
||||||
|
|
||||||
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__()
|
||||||
|
|
||||||
@@ -79,100 +284,21 @@ class SolverParametersList(PamhyrModelList):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute, ext=""):
|
||||||
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:
|
||||||
@@ -181,18 +307,10 @@ 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)
|
||||||
|
|
||||||
table = execute(
|
data['solver'] = solver
|
||||||
"SELECT ind, name, value " +
|
lst = Parameter._db_load(execute, data)
|
||||||
"FROM solver_parameter " +
|
|
||||||
f"WHERE solver = '{solver}'"
|
|
||||||
)
|
|
||||||
|
|
||||||
for row in table:
|
n._lst = list(map(lambda t: t[1], sorted(lst)))
|
||||||
ind = row[0]
|
|
||||||
name = row[1]
|
|
||||||
value = row[2]
|
|
||||||
|
|
||||||
n.set_value(name, value)
|
|
||||||
|
|
||||||
new[solver] = n
|
new[solver] = n
|
||||||
|
|
||||||
@@ -202,22 +320,16 @@ 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:
|
||||||
sql = (
|
data["ind"] = ind
|
||||||
"INSERT INTO " +
|
param._db_save(execute, data)
|
||||||
"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
|
||||||
@@ -242,9 +354,9 @@ class SolverParametersList(PamhyrModelList):
|
|||||||
|
|
||||||
def set(self, index, new):
|
def set(self, index, new):
|
||||||
self._lst[index] = new
|
self._lst[index] = new
|
||||||
self._status.modified()
|
new.modified()
|
||||||
|
|
||||||
def set_value(self, key, value):
|
def set_value(self, pid, key, value, owner):
|
||||||
for p in self._lst:
|
for p in self._lst:
|
||||||
if p["name"] == key:
|
if p["name"] == key:
|
||||||
p["value"] = value
|
p["value"] = value
|
||||||
@@ -253,9 +365,11 @@ 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()
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
# 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-2024 INRAE
|
# Copyright (C) 2023-2025 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,16 +30,11 @@ 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):
|
status=None, owner_scenario=-1):
|
||||||
super(Stricklers, self).__init__()
|
super(Stricklers, self).__init__(
|
||||||
self._status = status
|
id=id, 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
|
||||||
@@ -48,67 +43,123 @@ class Stricklers(SQLSubModel):
|
|||||||
self._medium = medium
|
self._medium = medium
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_create(cls, execute):
|
def _db_create(cls, execute, ext=""):
|
||||||
execute("""
|
execute(f"""
|
||||||
CREATE TABLE stricklers(
|
CREATE TABLE stricklers{ext} (
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
{cls.create_db_add_pamhyr_id()},
|
||||||
|
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):
|
def _db_update(cls, execute, version, data=None):
|
||||||
return cls._update_submodel(execute, version)
|
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 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 id, name, comment, minor, medium " +
|
"SELECT pamhyr_id, deleted, " +
|
||||||
"FROM stricklers"
|
"name, comment, minor, medium, scenario " +
|
||||||
|
"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:
|
||||||
id = row[0]
|
it = iter(row)
|
||||||
name = row[1]
|
|
||||||
comment = row[2]
|
pid = next(it)
|
||||||
minor = row[3]
|
deleted = (next(it) == 1)
|
||||||
medium = row[4]
|
name = next(it)
|
||||||
|
comment = next(it)
|
||||||
|
minor = next(it)
|
||||||
|
medium = next(it)
|
||||||
|
owner_scenario = next(it)
|
||||||
|
|
||||||
new = cls(
|
new = cls(
|
||||||
id=id,
|
id=pid, name=name, comment=comment,
|
||||||
name=name,
|
|
||||||
comment=comment,
|
|
||||||
minor=minor, medium=medium,
|
minor=minor, medium=medium,
|
||||||
status=status
|
status=status, owner_scenario=owner_scenario
|
||||||
)
|
)
|
||||||
|
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):
|
||||||
sql = (
|
if not self.must_be_saved():
|
||||||
|
return True
|
||||||
|
|
||||||
|
execute(
|
||||||
"INSERT INTO " +
|
"INSERT INTO " +
|
||||||
"stricklers(id, name, comment, minor, medium) " +
|
"stricklers(pamhyr_id, deleted, " +
|
||||||
|
"name, comment, minor, medium, scenario) " +
|
||||||
"VALUES (" +
|
"VALUES (" +
|
||||||
f"{self.id}, " +
|
f"{self.pamhyr_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
|
||||||
|
|
||||||
@@ -118,12 +169,13 @@ class Stricklers(SQLSubModel):
|
|||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
if self._name == "":
|
if self._name == "":
|
||||||
return f"K{self.id + 1}"
|
return f"K #{self.pamhyr_id}"
|
||||||
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):
|
||||||
@@ -132,6 +184,7 @@ 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):
|
||||||
@@ -140,6 +193,7 @@ 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):
|
||||||
@@ -148,3 +202,4 @@ 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-2024 INRAE
|
# Copyright (C) 2023-2025 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,8 +18,7 @@
|
|||||||
|
|
||||||
from tools import trace, timer
|
from tools import trace, timer
|
||||||
|
|
||||||
from Model.Saved import SavedStatus
|
from Model.Tools.PamhyrListExt import PamhyrModelList
|
||||||
from Model.Tools.PamhyrList import PamhyrModelList
|
|
||||||
from Model.Stricklers.Stricklers import Stricklers
|
from Model.Stricklers.Stricklers import Stricklers
|
||||||
|
|
||||||
|
|
||||||
@@ -33,8 +32,8 @@ class StricklersList(PamhyrModelList):
|
|||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version):
|
def _db_update(cls, execute, version, data=None):
|
||||||
return cls._update_submodel(execute, version)
|
return cls._update_submodel(execute, version, data)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
@@ -48,7 +47,10 @@ class StricklersList(PamhyrModelList):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def _db_save(self, execute, data=None):
|
def _db_save(self, execute, data=None):
|
||||||
execute("DELETE FROM stricklers")
|
execute(
|
||||||
|
"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)
|
||||||
@@ -58,8 +60,12 @@ class StricklersList(PamhyrModelList):
|
|||||||
return self.lst
|
return self.lst
|
||||||
|
|
||||||
def new(self, index):
|
def new(self, index):
|
||||||
s = Stricklers(status=self._status)
|
s = Stricklers(
|
||||||
|
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
|
||||||
@@ -74,5 +80,4 @@ class StricklersList(PamhyrModelList):
|
|||||||
reverse=reverse,
|
reverse=reverse,
|
||||||
)
|
)
|
||||||
|
|
||||||
if self._status is not None:
|
self.modified()
|
||||||
self._status.modified()
|
|
||||||
+258
-33
@@ -1,5 +1,5 @@
|
|||||||
# Study.py -- Pamhyr Study class
|
# Study.py -- Pamhyr Study class
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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,10 +22,12 @@ 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.Saved import SavedStatus
|
from Model.Scenarios import Scenarios
|
||||||
from Model.Serializable import Serializable
|
from Model.Scenario import Scenario
|
||||||
|
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
|
||||||
|
|
||||||
@@ -35,13 +37,18 @@ 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):
|
def __init__(self, filename=None, init_new=True, copy=False):
|
||||||
|
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()
|
||||||
@@ -49,7 +56,7 @@ class Study(SQLModel):
|
|||||||
self._filename = filename
|
self._filename = filename
|
||||||
super(Study, self).__init__(filename=filename)
|
super(Study, self).__init__(filename=filename)
|
||||||
|
|
||||||
self.status = SavedStatus()
|
self.status = StudyStatus()
|
||||||
|
|
||||||
# Study general information
|
# Study general information
|
||||||
self._name = ""
|
self._name = ""
|
||||||
@@ -60,12 +67,22 @@ 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 = [
|
||||||
@@ -84,6 +101,12 @@ 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()
|
||||||
@@ -107,15 +130,11 @@ 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) and ".backup" not in self.filename:
|
if os.path.exists(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
|
||||||
@@ -211,13 +230,12 @@ class Study(SQLModel):
|
|||||||
|
|
||||||
def _create(self):
|
def _create(self):
|
||||||
# Info (metadata)
|
# Info (metadata)
|
||||||
self.execute(
|
|
||||||
"INSERT INTO info VALUES ('study_release', '0')"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.execute(
|
self.execute(
|
||||||
"CREATE TABLE info(key TEXT NOT NULL UNIQUE, value TEXT NOT NULL)"
|
"CREATE TABLE info(key TEXT NOT NULL UNIQUE, value TEXT NOT NULL)"
|
||||||
)
|
)
|
||||||
|
self.execute(
|
||||||
|
"INSERT INTO info VALUES ('current_scenario', '0')"
|
||||||
|
)
|
||||||
self.execute(
|
self.execute(
|
||||||
"INSERT INTO info VALUES ('version', " +
|
"INSERT INTO info VALUES ('version', " +
|
||||||
f"'{self._db_format(self._version)}')",
|
f"'{self._db_format(self._version)}')",
|
||||||
@@ -252,7 +270,7 @@ class Study(SQLModel):
|
|||||||
if version[0] == self._version:
|
if version[0] == self._version:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
logger.debug("Update database")
|
logger.info(f"Update database from {version[0]} to {self._version}")
|
||||||
|
|
||||||
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:
|
||||||
@@ -260,19 +278,53 @@ class Study(SQLModel):
|
|||||||
"INSERT INTO info VALUES ('study_release', '0')"
|
"INSERT INTO info VALUES ('study_release', '0')"
|
||||||
)
|
)
|
||||||
|
|
||||||
if self._update_submodel(version[0]):
|
if major == "0" and int(minor) < 2:
|
||||||
|
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("TODO: update failed")
|
logger.info("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'"
|
||||||
)
|
)
|
||||||
@@ -290,31 +342,65 @@ class Study(SQLModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
new.creation_date = datetime.fromtimestamp(
|
new.creation_date = datetime.fromtimestamp(
|
||||||
float(new.execute(
|
float(
|
||||||
"SELECT value FROM info WHERE key='creation_date'")[0])
|
new.execute(
|
||||||
|
"SELECT value FROM info WHERE key='creation_date'"
|
||||||
|
)[0]
|
||||||
|
)
|
||||||
)
|
)
|
||||||
new.last_save_date = datetime.fromtimestamp(
|
new.last_save_date = datetime.fromtimestamp(
|
||||||
float(new.execute(
|
float(
|
||||||
"SELECT value FROM info WHERE key='last_save_date'")[0])
|
new.execute(
|
||||||
|
"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(
|
||||||
lambda sql: new.execute(
|
sql_exec, data=data
|
||||||
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 INTO info VALUES ('study_release', " +
|
"INSERT OR REPLACE INTO info VALUES ('study_release', " +
|
||||||
f"'{self.status.version}')"
|
f"'{self.status.version}')"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -352,16 +438,28 @@ class Study(SQLModel):
|
|||||||
)
|
)
|
||||||
progress()
|
progress()
|
||||||
|
|
||||||
self._save_submodel([self._river], data=progress)
|
self.execute(
|
||||||
|
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):
|
def sql_save_request_count(self, *args, **kargs):
|
||||||
return self._count()
|
return self._count()
|
||||||
|
|
||||||
def _count(self):
|
def _count(self):
|
||||||
cnt = self._save_count([self._river])
|
cnt = self._save_count(
|
||||||
|
[self.scenarios, self._river]
|
||||||
|
)
|
||||||
logger.debug(cnt)
|
logger.debug(cnt)
|
||||||
return cnt + 6
|
return cnt + 7
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
"""Close db connection
|
"""Close db connection
|
||||||
@@ -370,3 +468,130 @@ 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
|
||||||
+224
-18
@@ -1,5 +1,5 @@
|
|||||||
# PamhyrDB.py -- Pamhyr abstract model database classes
|
# PamhyrDB.py -- Pamhyr abstract model database classes
|
||||||
# Copyright (C) 2023-2024 INRAE
|
# Copyright (C) 2023-2025 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,6 +26,8 @@ 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
|
||||||
@@ -42,7 +44,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)
|
self._db = sqlite3.connect(db, check_same_thread=False)
|
||||||
self._cur = self._db.cursor()
|
self._cur = self._db.cursor()
|
||||||
|
|
||||||
if is_new:
|
if is_new:
|
||||||
@@ -71,10 +73,9 @@ 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):
|
def _update_submodel(self, version, data=None):
|
||||||
def fn(sql): return self.execute(
|
def fn(sql): return self.execute(
|
||||||
sql,
|
sql,
|
||||||
fetch_one=False,
|
fetch_one=False,
|
||||||
@@ -83,7 +84,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)
|
ok &= cls._db_update(fn, version, data=data)
|
||||||
|
|
||||||
self.commit()
|
self.commit()
|
||||||
return ok
|
return ok
|
||||||
@@ -94,18 +95,22 @@ 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):
|
def fn(sql, *args, **kargs):
|
||||||
|
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,
|
sql, *args, **kargs
|
||||||
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)
|
ok &= obj._db_save(fn, data={})
|
||||||
|
|
||||||
self.commit()
|
self.commit()
|
||||||
return ok
|
return ok
|
||||||
@@ -116,7 +121,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, data=None):
|
def _save_count(self, objs, *args, data={}, **kargs):
|
||||||
counter = {
|
counter = {
|
||||||
"insert": 0,
|
"insert": 0,
|
||||||
"update": 0,
|
"update": 0,
|
||||||
@@ -124,7 +129,7 @@ class SQLModel(SQL):
|
|||||||
"other": 0,
|
"other": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
def fn(sql):
|
def fn(sql, *args, **kargs):
|
||||||
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():
|
||||||
@@ -137,7 +142,7 @@ class SQLModel(SQL):
|
|||||||
|
|
||||||
ok = True
|
ok = True
|
||||||
for obj in objs:
|
for obj in objs:
|
||||||
ok &= obj._db_save(fn)
|
ok &= obj._db_save(fn, data=data)
|
||||||
|
|
||||||
logger.debug(counter)
|
logger.debug(counter)
|
||||||
|
|
||||||
@@ -154,15 +159,103 @@ class SQLModel(SQL):
|
|||||||
# Sub model class
|
# Sub model class
|
||||||
|
|
||||||
|
|
||||||
class SQLSubModel(object):
|
class SQLSubModel(PamhyrID):
|
||||||
_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
|
||||||
@@ -183,12 +276,16 @@ class SQLSubModel(object):
|
|||||||
raise NotImplementedMethodeError(cls, cls._db_create)
|
raise NotImplementedMethodeError(cls, cls._db_create)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _update_submodel(cls, execute, version):
|
def _update_submodel(cls, execute, version, data=None):
|
||||||
|
ok = True
|
||||||
|
|
||||||
for sc in cls._sub_classes:
|
for sc in cls._sub_classes:
|
||||||
sc._db_update(execute, version)
|
ok &= sc._db_update(execute, version, data)
|
||||||
|
|
||||||
|
return ok
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version):
|
def _db_update(cls, execute, version, data=None):
|
||||||
"""Update data base scheme
|
"""Update data base scheme
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -200,6 +297,93 @@ class SQLSubModel(object):
|
|||||||
"""
|
"""
|
||||||
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
|
||||||
@@ -214,8 +398,12 @@ class SQLSubModel(object):
|
|||||||
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:
|
||||||
o._db_save(execute, data=data)
|
ok &= 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
|
||||||
@@ -229,3 +417,21 @@ class SQLSubModel(object):
|
|||||||
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-2024 INRAE
|
# Copyright (C) 2023-2025 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):
|
def _db_update(cls, execute, version, data=None):
|
||||||
return cls._update_submodel(execute, version)
|
return cls._update_submodel(execute, version, data)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
@@ -59,6 +59,12 @@ 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()
|
||||||
@@ -76,5 +82,24 @@ 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)
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
# 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-2024 INRAE
|
# Copyright (C) 2023-2025 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,8 +28,7 @@ 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__()
|
||||||
@@ -46,8 +45,8 @@ class PamhyrModelList(SQLSubModel):
|
|||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version):
|
def _db_update(cls, execute, version, data=None):
|
||||||
return cls._update_submodel(execute, version)
|
return cls._update_submodel(execute, version, data)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
@@ -86,6 +85,24 @@ 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
|
||||||
|
|
||||||
@@ -167,8 +184,7 @@ 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__()
|
||||||
@@ -187,8 +203,8 @@ class PamhyrModelListWithTab(SQLSubModel):
|
|||||||
return cls._create_submodel(execute)
|
return cls._create_submodel(execute)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_update(cls, execute, version):
|
def _db_update(cls, execute, version, data=None):
|
||||||
return cls._update_submodel(execute, version)
|
return cls._update_submodel(execute, version, data)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _db_load(cls, execute, data=None):
|
def _db_load(cls, execute, data=None):
|
||||||
@@ -197,6 +213,24 @@ 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 365 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user