weatherdb 1.1.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- docker/Dockerfile +30 -0
- docker/docker-compose.yaml +58 -0
- docker/docker-compose_test.yaml +24 -0
- docker/start-docker-test.sh +6 -0
- docs/requirements.txt +10 -0
- docs/source/Changelog.md +2 -0
- docs/source/License.rst +7 -0
- docs/source/Methode.md +161 -0
- docs/source/_static/custom.css +8 -0
- docs/source/_static/favicon.ico +0 -0
- docs/source/_static/logo.png +0 -0
- docs/source/api/api.rst +15 -0
- docs/source/api/cli.rst +8 -0
- docs/source/api/weatherDB.broker.rst +10 -0
- docs/source/api/weatherDB.config.rst +7 -0
- docs/source/api/weatherDB.db.rst +23 -0
- docs/source/api/weatherDB.rst +22 -0
- docs/source/api/weatherDB.station.rst +56 -0
- docs/source/api/weatherDB.stations.rst +46 -0
- docs/source/api/weatherDB.utils.rst +22 -0
- docs/source/conf.py +137 -0
- docs/source/index.rst +33 -0
- docs/source/setup/Configuration.md +127 -0
- docs/source/setup/Hosting.md +9 -0
- docs/source/setup/Install.md +49 -0
- docs/source/setup/Quickstart.md +183 -0
- docs/source/setup/setup.rst +12 -0
- weatherdb/__init__.py +24 -0
- weatherdb/_version.py +1 -0
- weatherdb/alembic/README.md +8 -0
- weatherdb/alembic/alembic.ini +80 -0
- weatherdb/alembic/config.py +9 -0
- weatherdb/alembic/env.py +100 -0
- weatherdb/alembic/script.py.mako +26 -0
- weatherdb/alembic/versions/V1.0.0_initial_database_creation.py +898 -0
- weatherdb/alembic/versions/V1.0.2_more_charachters_for_settings+term_station_ma_raster.py +88 -0
- weatherdb/alembic/versions/V1.0.5_fix-ma-raster-values.py +152 -0
- weatherdb/alembic/versions/V1.0.6_update-views.py +22 -0
- weatherdb/broker.py +667 -0
- weatherdb/cli.py +214 -0
- weatherdb/config/ConfigParser.py +663 -0
- weatherdb/config/__init__.py +5 -0
- weatherdb/config/config_default.ini +162 -0
- weatherdb/db/__init__.py +3 -0
- weatherdb/db/connections.py +374 -0
- weatherdb/db/fixtures/RichterParameters.json +34 -0
- weatherdb/db/models.py +402 -0
- weatherdb/db/queries/get_quotient.py +155 -0
- weatherdb/db/views.py +165 -0
- weatherdb/station/GroupStation.py +710 -0
- weatherdb/station/StationBases.py +3108 -0
- weatherdb/station/StationET.py +111 -0
- weatherdb/station/StationP.py +807 -0
- weatherdb/station/StationPD.py +98 -0
- weatherdb/station/StationT.py +164 -0
- weatherdb/station/__init__.py +13 -0
- weatherdb/station/constants.py +21 -0
- weatherdb/stations/GroupStations.py +519 -0
- weatherdb/stations/StationsBase.py +1021 -0
- weatherdb/stations/StationsBaseTET.py +30 -0
- weatherdb/stations/StationsET.py +17 -0
- weatherdb/stations/StationsP.py +128 -0
- weatherdb/stations/StationsPD.py +24 -0
- weatherdb/stations/StationsT.py +21 -0
- weatherdb/stations/__init__.py +11 -0
- weatherdb/utils/TimestampPeriod.py +369 -0
- weatherdb/utils/__init__.py +3 -0
- weatherdb/utils/dwd.py +350 -0
- weatherdb/utils/geometry.py +69 -0
- weatherdb/utils/get_data.py +285 -0
- weatherdb/utils/logging.py +126 -0
- weatherdb-1.1.0.dist-info/LICENSE +674 -0
- weatherdb-1.1.0.dist-info/METADATA +765 -0
- weatherdb-1.1.0.dist-info/RECORD +77 -0
- weatherdb-1.1.0.dist-info/WHEEL +5 -0
- weatherdb-1.1.0.dist-info/entry_points.txt +2 -0
- weatherdb-1.1.0.dist-info/top_level.txt +3 -0
@@ -0,0 +1,162 @@
|
|
1
|
+
[database]
|
2
|
+
; These are the main database settings
|
3
|
+
; The database is created with the cli command create-db-schema or the weatherdb.Broker().create_db_schema() method
|
4
|
+
; you can define multiple database connections by adding a new section like [database.connection_name], where you can define your connection name
|
5
|
+
; The connection setting defines which connection is used if not specified
|
6
|
+
connection = main
|
7
|
+
|
8
|
+
[database:main]
|
9
|
+
; setup a database connection
|
10
|
+
; The password should not be stored in the config file, as it is a security risk.
|
11
|
+
; the module will ask for the password on first execution and store it in the keyring.
|
12
|
+
HOST = localhost
|
13
|
+
PORT = 5432
|
14
|
+
DATABASE = weatherdb
|
15
|
+
USER = weatherdb_user
|
16
|
+
|
17
|
+
[database:owner]
|
18
|
+
; You can setup multiple database connections
|
19
|
+
HOST = localhost
|
20
|
+
PORT = 5432
|
21
|
+
DATABASE = weatherdb
|
22
|
+
USER = weatherdb_owner
|
23
|
+
|
24
|
+
[logging]
|
25
|
+
; The logging settings
|
26
|
+
; Should the log be written to a file or to the console
|
27
|
+
; Possible values: file, console
|
28
|
+
HANDLERS =
|
29
|
+
|
30
|
+
; The log level
|
31
|
+
; Possible values: DEBUG, INFO, WARNING, ERROR, CRITICAL
|
32
|
+
LEVEL = DEBUG
|
33
|
+
|
34
|
+
; The log directory
|
35
|
+
; The path can contain the module path as basis with ${main:module_path} or the complete path.
|
36
|
+
; if the log should be written to the console, this setting is ignored
|
37
|
+
DIRECTORY = ${main:module_path}/logs
|
38
|
+
|
39
|
+
; The log file template
|
40
|
+
; The log file will be named with the template and rotated daily with the date appended
|
41
|
+
; The template can contain the computer host name with {host} and the current user name with {user}
|
42
|
+
FILE = weatherDB_{host}_{user}.log
|
43
|
+
|
44
|
+
; Should the older rotated log files be compressed or not?
|
45
|
+
; Possible values: True, False
|
46
|
+
; The default is True
|
47
|
+
COMPRESSION = True
|
48
|
+
|
49
|
+
; The log file format
|
50
|
+
; The format can contain the following variables:
|
51
|
+
; {asctime} - the time of the log entry
|
52
|
+
; {name} - the name of the logger
|
53
|
+
; {levelname} - the log level
|
54
|
+
; {message} - the log message
|
55
|
+
FORMAT = %(asctime)s - %(name)s - %(levelname)s - %(message)s
|
56
|
+
|
57
|
+
[data]
|
58
|
+
; The WeatherDB module needs several data files to work properly.
|
59
|
+
; those files are only used by the main managing module, not by the users.
|
60
|
+
|
61
|
+
; The data files like multi annual and dems rasters are normaly stored in the data directory.
|
62
|
+
; The path can contain the module path as basis with ${main:module_path} or the complete path.
|
63
|
+
; if they are stored somewhere else set the path here
|
64
|
+
BASE_DIR = ${main:module_path}/data
|
65
|
+
|
66
|
+
[data:rasters]
|
67
|
+
; Digital elevation models (DEM)
|
68
|
+
; The paths can contain the module path as basis with ${main:module_path} or the DATA.BASE_DIR with ${data:BASE_DIR} or the complete path.
|
69
|
+
; The module needs at least one DEM to calculate the horizon shadowing angle.
|
70
|
+
; as finer DEM are sometimes not covering the whole area of germany+40km, that is needed, you can add additional DEMs, that gets used if the first one
|
71
|
+
; the DEMs are used in the order of appearance in the list
|
72
|
+
DEMS = ${data:BASE_DIR}/DEM/DEM25.tif,
|
73
|
+
${data:BASE_DIR}/DEM/DEM80.tif
|
74
|
+
|
75
|
+
; Regionalisation Rasters
|
76
|
+
; the module works with regionalised multi-annual rasters from the DWD
|
77
|
+
; the module needs at least the HYRAS and DWD raster, as this is the basis for the regionalisation of the station data
|
78
|
+
; The rasters should be in a cartesian coordinate system with meter as unit to be able to calculate distances (e.g. EPSG:3035)
|
79
|
+
|
80
|
+
[data:rasters:hyras]
|
81
|
+
; multi annual HYRAS raster
|
82
|
+
; The original data can be found on: https://opendata.dwd.de/climate_environment/CDC/grids_germany/multi_annual/hyras_de/precipitation/
|
83
|
+
; to get better results, we refined the 1km rasters to 25m raster, based on a DEM25. (this file is not providen, but can be asked for)
|
84
|
+
FILE = ${data:BASE_DIR}/regionalisation/HYRAS_ma_1991_2020_DGM25.tif
|
85
|
+
|
86
|
+
; This raster file should have at least 3 raster bands for summer, winter and yearly means.
|
87
|
+
; Give the rasters names or band numbers
|
88
|
+
BAND_P_WIHY = n_hyras_wihj
|
89
|
+
BAND_P_SUHY = n_hyras_sohj
|
90
|
+
BAND_P_YEAR = n_hyras_year
|
91
|
+
|
92
|
+
; The EPSG code of the rasters if not defined in the raster file itself
|
93
|
+
SRID = 3035
|
94
|
+
|
95
|
+
; factors to apply to raster value to get to mm or °C
|
96
|
+
; the rasters should be in integer format, as the factors are applied to the raster values to get decimal values
|
97
|
+
FACTOR_P_WIHY = 1
|
98
|
+
FACTOR_P_SUHY = 1
|
99
|
+
FACTOR_P_YEAR = 1
|
100
|
+
|
101
|
+
[data:rasters:dwd]
|
102
|
+
; multi annual DWD raster
|
103
|
+
FILE = ${data:BASE_DIR}/regionalisation/DWD-grid_ma_1991_2020_DGM25.tif
|
104
|
+
BAND_P_WIHY = n_wihj
|
105
|
+
BAND_P_SUHY = n_sohj
|
106
|
+
BAND_P_YEAR = n_year
|
107
|
+
BAND_T_YEAR = t_year
|
108
|
+
BAND_ET_YEAR = et_year
|
109
|
+
|
110
|
+
; The EPSG code of the rasters if not defined in the raster file itself
|
111
|
+
SRID = 3035
|
112
|
+
|
113
|
+
; factors to apply to raster value to get to mm or °C
|
114
|
+
; the rasters should be in integer format, as the factors are applied to the raster values to get decimal values
|
115
|
+
FACTOR_P_WIHY = 1
|
116
|
+
FACTOR_P_SUHY = 1
|
117
|
+
FACTOR_P_YEAR = 1
|
118
|
+
FACTOR_T_YEAR = 0.1
|
119
|
+
FACTOR_ET_YEAR = 1
|
120
|
+
|
121
|
+
[weatherdb]
|
122
|
+
; The WeatherDB module settings to setup different parts of the module
|
123
|
+
|
124
|
+
; The radius in meters to use for the horizon shadowing calculation
|
125
|
+
; The default is 75km, this value got defined because the maximum possible height is around 4000m for germany
|
126
|
+
; --> horizon angle = arctan(4/75) ~ 3°, this is the lowest category in the richter class table
|
127
|
+
HORIZON_RADIUS = 75000
|
128
|
+
|
129
|
+
; The projected coordinate reference system (CRS) to use for the calculation of the distance during the horizon shadowing calculation
|
130
|
+
; This can be different than the DEM CRS, but has to be a projected CRS with meter as unit
|
131
|
+
; The value should be an EPSG code, e.g. 25832 for UTM zone 32N
|
132
|
+
; The default is UTM zone 32N (EPSG:25832)
|
133
|
+
HORIZON_CRS = 25832
|
134
|
+
|
135
|
+
; The projected coordinate reference system (CRS) to use for the calculation of the distance during raster sampling
|
136
|
+
; This can be different than the DEM CRS, but has to be a projected CRS with meter as unit
|
137
|
+
; The value should be an EPSG code, e.g. 25832 for UTM zone 32N
|
138
|
+
; The default is the same as the HORISON_CRS
|
139
|
+
RASTER_BUFFER_CRS = ${weatherdb:HORIZON_CRS}
|
140
|
+
|
141
|
+
; The minimum date to download data from the DWD
|
142
|
+
; The earlier you go the less data is available and the further away the stations have to be to fill the timeseries
|
143
|
+
; The default is 1999-01-01
|
144
|
+
MIN_DATE = 1999-01-01
|
145
|
+
|
146
|
+
|
147
|
+
[weatherdb:max_fillup_distance]
|
148
|
+
; The maximum distance in meters to use for the filling of the station data
|
149
|
+
; For each parameter (P, T, ET) the module uses a different distance
|
150
|
+
; Precipitation (P)
|
151
|
+
P = 110000
|
152
|
+
; Temperature (T)
|
153
|
+
T = 150000
|
154
|
+
; Evapotranspiration (ET)
|
155
|
+
ET = 150000
|
156
|
+
|
157
|
+
; for developpers:
|
158
|
+
; ----------------
|
159
|
+
;
|
160
|
+
; [alembic]
|
161
|
+
; The database connection used by the alembic scripts
|
162
|
+
; connection = main
|
weatherdb/db/__init__.py
ADDED
@@ -0,0 +1,374 @@
|
|
1
|
+
# libraries
|
2
|
+
import sqlalchemy
|
3
|
+
from sqlalchemy import text as sqltxt
|
4
|
+
from sqlalchemy import URL
|
5
|
+
import functools
|
6
|
+
|
7
|
+
from ..config import config
|
8
|
+
|
9
|
+
# DB connection
|
10
|
+
###############
|
11
|
+
class DBEngine:
|
12
|
+
def __init__(self):
|
13
|
+
self._reset_engine()
|
14
|
+
|
15
|
+
# add the listeners for configuration changes
|
16
|
+
self._config_listener_connection = (
|
17
|
+
"database",
|
18
|
+
"connection",
|
19
|
+
self._connection_update)
|
20
|
+
self._config_listener_subsection = (
|
21
|
+
f"database:{config.get('database', 'connection')}",
|
22
|
+
None,
|
23
|
+
self._reset_engine)
|
24
|
+
config.add_listener(*self._config_listener_connection)
|
25
|
+
config.add_listener(*self._config_listener_subsection)
|
26
|
+
|
27
|
+
def __del__(self):
|
28
|
+
config.remove_listener(*self._config_listener_connection)
|
29
|
+
config.remove_listener(*self._config_listener_subsection)
|
30
|
+
|
31
|
+
def _connection_update(self):
|
32
|
+
"""Listener if another database subsection is selected."""
|
33
|
+
# first remove the old section listener
|
34
|
+
config.remove_listener(*self._config_listener_subsection)
|
35
|
+
|
36
|
+
# add the new section listener
|
37
|
+
self._config_listener_subsection = (
|
38
|
+
f"database:{config.get('database', 'connection')}",
|
39
|
+
None,
|
40
|
+
self._reset_engine)
|
41
|
+
config.add_listener(*self._config_listener_subsection)
|
42
|
+
|
43
|
+
# create a new engine
|
44
|
+
self.create_engine()
|
45
|
+
|
46
|
+
# Engine and Session
|
47
|
+
# ##################
|
48
|
+
def _reset_engine(self):
|
49
|
+
"""Reset the engine to None to force a recreation on next usage."""
|
50
|
+
self._engine = None
|
51
|
+
self._session = None
|
52
|
+
self._is_superuser = None
|
53
|
+
self._select_privilege = None
|
54
|
+
self._create_privilege = None
|
55
|
+
self._update_privilege = None
|
56
|
+
self._insert_privilege = None
|
57
|
+
self._delete_privilege = None
|
58
|
+
self._priviledge_to_recheck = set()
|
59
|
+
|
60
|
+
def connect(self, *args, **kwargs):
|
61
|
+
return self.get_engine().connect(*args, **kwargs)
|
62
|
+
|
63
|
+
def get_engine(self):
|
64
|
+
"""Get the sqlalchemy database engine.
|
65
|
+
|
66
|
+
Returns the last created engine if possible or creates a new one.
|
67
|
+
|
68
|
+
Returns
|
69
|
+
-------
|
70
|
+
sqlalchemy.engine.base.Engine
|
71
|
+
_description_
|
72
|
+
"""
|
73
|
+
if self._engine is None:
|
74
|
+
self.create_engine()
|
75
|
+
|
76
|
+
return self._engine
|
77
|
+
|
78
|
+
def create_engine(self):
|
79
|
+
# create the engine
|
80
|
+
con_key = config.get("database", "connection")
|
81
|
+
con_sect_key = f"database:{con_key}"
|
82
|
+
con_section = config[con_sect_key]
|
83
|
+
user, pwd = config.get_db_credentials()
|
84
|
+
self._engine = sqlalchemy.create_engine(
|
85
|
+
URL.create(
|
86
|
+
drivername="postgresql+psycopg2",
|
87
|
+
username=user,
|
88
|
+
password=pwd,
|
89
|
+
host=con_section["HOST"],
|
90
|
+
database=con_section["DATABASE"],
|
91
|
+
port=con_section["PORT"]),
|
92
|
+
connect_args={
|
93
|
+
"options": "-c timezone=utc",
|
94
|
+
'connect_timeout': 10})
|
95
|
+
self._check_is_superuser()
|
96
|
+
|
97
|
+
return self._engine
|
98
|
+
|
99
|
+
@property
|
100
|
+
def engine(self):
|
101
|
+
return self.get_engine()
|
102
|
+
|
103
|
+
@engine.setter
|
104
|
+
def engine(self, value):
|
105
|
+
raise PermissionError("You are not allowed to change the engine of the database connection.\nPlease change the configuration to change the database connection.")
|
106
|
+
|
107
|
+
def create_session(self):
|
108
|
+
"""Create a new session from the engine.
|
109
|
+
|
110
|
+
Returns
|
111
|
+
-------
|
112
|
+
sqlalchemy.orm.session.Session
|
113
|
+
_description_
|
114
|
+
"""
|
115
|
+
self._session = sqlalchemy.orm.sessionmaker(bind=self.get_engine())
|
116
|
+
return self._session
|
117
|
+
|
118
|
+
def get_session(self):
|
119
|
+
"""Get a session from the engine.
|
120
|
+
|
121
|
+
Returns
|
122
|
+
-------
|
123
|
+
sqlalchemy.orm.session.Session
|
124
|
+
_description_
|
125
|
+
"""
|
126
|
+
if self._session is None:
|
127
|
+
return self.create_session()
|
128
|
+
else:
|
129
|
+
return self._session
|
130
|
+
|
131
|
+
@property
|
132
|
+
def session(self):
|
133
|
+
return self.get_session()
|
134
|
+
|
135
|
+
@session.setter
|
136
|
+
def session(self, value):
|
137
|
+
raise PermissionError("You are not allowed to change the session of the database connection.\nPlease change the configuration to change the database connection.")
|
138
|
+
|
139
|
+
def reload_config(self):
|
140
|
+
"""Reload the configuration and create a new engine.
|
141
|
+
"""
|
142
|
+
self.create_engine()
|
143
|
+
|
144
|
+
# Privilege checks
|
145
|
+
##################
|
146
|
+
def _check_is_superuser(self):
|
147
|
+
with self.connect() as con:
|
148
|
+
self._is_superuser = con.execute(sqltxt(
|
149
|
+
"SELECT usesuper FROM pg_user WHERE usename = current_user;"
|
150
|
+
)).first()[0]
|
151
|
+
return self._is_superuser
|
152
|
+
|
153
|
+
@property
|
154
|
+
def is_superuser(self):
|
155
|
+
if self._is_superuser is None:
|
156
|
+
self._check_is_superuser()
|
157
|
+
return self._is_superuser
|
158
|
+
|
159
|
+
@is_superuser.setter
|
160
|
+
def is_superuser(self, value):
|
161
|
+
raise PermissionError("You are not allowed to change the superuser status of the database connection, as this is due to PostGreSQL database privileges of your database user.")
|
162
|
+
|
163
|
+
def _check_privilege(self, privilege):
|
164
|
+
if self._is_superuser:
|
165
|
+
return True
|
166
|
+
else:
|
167
|
+
with self.connect() as con:
|
168
|
+
if privilege == "CREATE":
|
169
|
+
return con.execute(sqltxt(
|
170
|
+
f"SELECT pg_catalog.has_schema_privilege('public', '{privilege}');"
|
171
|
+
)).first()[0]
|
172
|
+
res = con.execute(sqltxt(
|
173
|
+
f"""SELECT bool_and(pg_catalog.has_table_privilege(table_name, '{privilege}'))
|
174
|
+
FROM information_schema.TABLES
|
175
|
+
WHERE table_schema=CURRENT_SCHEMA AND table_type='BASE TABLE';"""
|
176
|
+
)).first()[0]
|
177
|
+
if res is None:
|
178
|
+
if privilege in self._priviledge_to_recheck:
|
179
|
+
self.log.debug(f"Privilege {privilege} not found, probably because database isn't initiated. Setting as True.")
|
180
|
+
else:
|
181
|
+
self._priviledge_to_recheck.add(privilege)
|
182
|
+
return True
|
183
|
+
else:
|
184
|
+
if privilege in self._priviledge_to_recheck:
|
185
|
+
self._priviledge_to_recheck.remove(privilege)
|
186
|
+
return res
|
187
|
+
|
188
|
+
def _check_select_privilege(self):
|
189
|
+
"""Check on the database if the user has the SELECT privilege."""
|
190
|
+
self._select_privilege = self._check_privilege("SELECT")
|
191
|
+
return self._select_privilege
|
192
|
+
|
193
|
+
def _check_update_privilege(self):
|
194
|
+
"""Check on the database if the user has the UPDATE privilege."""
|
195
|
+
self._update_privilege = self._check_privilege("UPDATE")
|
196
|
+
return self._update_privilege
|
197
|
+
|
198
|
+
def _check_insert_privilege(self):
|
199
|
+
"""Check on the database if the user has the INSERT privilege."""
|
200
|
+
self._insert_privilege = self._check_privilege("INSERT")
|
201
|
+
return self._insert_privilege
|
202
|
+
|
203
|
+
def _check_create_privilege(self):
|
204
|
+
"""Check on the database if the user has the CREATE privilege."""
|
205
|
+
self._create_privilege = self._check_privilege("CREATE")
|
206
|
+
return self._create_privilege
|
207
|
+
|
208
|
+
def _check_delete_privilege(self):
|
209
|
+
"""Check on the database if the user has the DELETE privilege."""
|
210
|
+
self._delete_privilege = self._check_privilege("DELETE")
|
211
|
+
return self._delete_privilege
|
212
|
+
|
213
|
+
@property
|
214
|
+
def select_privilege(self):
|
215
|
+
"""Does the user have the PostGreSQL SELECT privilege on the database?"""
|
216
|
+
if self._select_privilege is None or "SELECT" in self._priviledge_to_recheck:
|
217
|
+
self._check_select_privilege()
|
218
|
+
return self._select_privilege
|
219
|
+
|
220
|
+
@property
|
221
|
+
def update_privilege(self):
|
222
|
+
"""Does the user have the PostGreSQL UPDATE privilege on the database?"""
|
223
|
+
if self._update_privilege is None or "UPDATE" in self._priviledge_to_recheck:
|
224
|
+
self._check_update_privilege()
|
225
|
+
return self._update_privilege
|
226
|
+
|
227
|
+
@property
|
228
|
+
def insert_privilege(self):
|
229
|
+
"""Does the user have the PostGreSQL INSERT privilege on the database?"""
|
230
|
+
if self._insert_privilege is None or "INSERT" in self._priviledge_to_recheck:
|
231
|
+
self._check_insert_privilege()
|
232
|
+
return self._insert_privilege
|
233
|
+
|
234
|
+
@property
|
235
|
+
def upsert_privilege(self):
|
236
|
+
"""Does the user have the PostGreSQL INSERT and UPDATE privilege on the database?"""
|
237
|
+
return self.insert_privilege and self.update_privilege
|
238
|
+
|
239
|
+
@property
|
240
|
+
def create_privilege(self):
|
241
|
+
"""Does the user have the PostGreSQL CREATE privilege on the database?"""
|
242
|
+
if self._create_privilege is None or "CREATE" in self._priviledge_to_recheck:
|
243
|
+
self._check_create_privilege()
|
244
|
+
return self._create_privilege
|
245
|
+
|
246
|
+
@property
|
247
|
+
def delete_privilege(self):
|
248
|
+
"""Does the user have the PostGreSQL DELETE privilege on the database?"""
|
249
|
+
if self._delete_privilege is None or "DELETE" in self._priviledge_to_recheck:
|
250
|
+
self._check_delete_privilege()
|
251
|
+
return self._delete_privilege
|
252
|
+
|
253
|
+
@property
|
254
|
+
def all_privileges(self):
|
255
|
+
"""Does the user have all (SELECT, UPDATE, INSERT, DELETE, CREATE) PostGreSQL privileges on the database?"""
|
256
|
+
return self.select_privilege and \
|
257
|
+
self.update_privilege and \
|
258
|
+
self.insert_privilege and \
|
259
|
+
self.create_privilege and \
|
260
|
+
self.delete_privilege
|
261
|
+
|
262
|
+
def _privilege_setters(self, property_name):
|
263
|
+
raise PermissionError(f"You are not allowed to change the value of {property_name} as this is due to PostGreSQL database privileges of your user.")
|
264
|
+
|
265
|
+
@select_privilege.setter
|
266
|
+
def select_privilege(self, value):
|
267
|
+
self._privilege_setters("select_privilege")
|
268
|
+
|
269
|
+
@update_privilege.setter
|
270
|
+
def update_privilege(self, value):
|
271
|
+
self._privilege_setters("update_privilege")
|
272
|
+
|
273
|
+
@insert_privilege.setter
|
274
|
+
def insert_privilege(self, value):
|
275
|
+
self._privilege_setters("insert_privilege")
|
276
|
+
|
277
|
+
@upsert_privilege.setter
|
278
|
+
def upsert_privilege(self, value):
|
279
|
+
self._privilege_setters("upsert_privilege")
|
280
|
+
|
281
|
+
@create_privilege.setter
|
282
|
+
def create_privilege(self, value):
|
283
|
+
self._privilege_setters("create_privilege")
|
284
|
+
|
285
|
+
@delete_privilege.setter
|
286
|
+
def delete_privilege(self, value):
|
287
|
+
self._privilege_setters("delete_privilege")
|
288
|
+
|
289
|
+
@all_privileges.setter
|
290
|
+
def all_privileges(self, value):
|
291
|
+
self._privilege_setters("all_privileges")
|
292
|
+
|
293
|
+
def deco_is_superuser(self, target):
|
294
|
+
"""Decorator to check if the user is a superuser."""
|
295
|
+
@functools.wraps(target)
|
296
|
+
def wrapper(*args, **kwargs):
|
297
|
+
if self.is_superuser:
|
298
|
+
return target(*args, **kwargs)
|
299
|
+
else:
|
300
|
+
raise PermissionError("You are no super user of the Database and therefor this function is not available.")
|
301
|
+
return wrapper
|
302
|
+
|
303
|
+
def deco_select_privilege(self, target):
|
304
|
+
"""Decorator to check if the user has the SELECT privilege."""
|
305
|
+
@functools.wraps(target)
|
306
|
+
def wrapper(*args, **kwargs):
|
307
|
+
if self.select_privilege:
|
308
|
+
return target(*args, **kwargs)
|
309
|
+
else:
|
310
|
+
raise PermissionError("You have no select privilege on the database and therefor this function is not available.")
|
311
|
+
return wrapper
|
312
|
+
|
313
|
+
def deco_update_privilege(self, target):
|
314
|
+
"""Decorator to check if the user has the UPDATE privilege."""
|
315
|
+
@functools.wraps(target)
|
316
|
+
def wrapper(*args, **kwargs):
|
317
|
+
if self.update_privilege:
|
318
|
+
return target(*args, **kwargs)
|
319
|
+
else:
|
320
|
+
raise PermissionError("You have no update privilege on the database and therefor this function is not available.")
|
321
|
+
return wrapper
|
322
|
+
|
323
|
+
def deco_insert_privilege(self, target):
|
324
|
+
"""Decorator to check if the user has the INSERT privilege."""
|
325
|
+
@functools.wraps(target)
|
326
|
+
def wrapper(*args, **kwargs):
|
327
|
+
if self.insert_privilege:
|
328
|
+
return target(*args, **kwargs)
|
329
|
+
else:
|
330
|
+
raise PermissionError("You have no insert privilege on the database and therefor this function is not available.")
|
331
|
+
return wrapper
|
332
|
+
|
333
|
+
def deco_upsert_privilege(self, target):
|
334
|
+
"""Decorator to check if the user has the INSERT and UPDATE privilege."""
|
335
|
+
@functools.wraps(target)
|
336
|
+
def wrapper(*args, **kwargs):
|
337
|
+
if self.upsert_privilege:
|
338
|
+
return target(*args, **kwargs)
|
339
|
+
else:
|
340
|
+
raise PermissionError("You have no upsert privilege on the database and therefor this function is not available.")
|
341
|
+
return wrapper
|
342
|
+
|
343
|
+
def deco_create_privilege(self, target):
|
344
|
+
"""Decorator to check if the user has the CREATE privilege."""
|
345
|
+
@functools.wraps(target)
|
346
|
+
def wrapper(*args, **kwargs):
|
347
|
+
if self.create_privilege:
|
348
|
+
return target(*args, **kwargs)
|
349
|
+
else:
|
350
|
+
raise PermissionError("You are no admin user of the Database and therefor this function is not available.")
|
351
|
+
return wrapper
|
352
|
+
|
353
|
+
def deco_delete_privilege(self, target):
|
354
|
+
"""Decorator to check if the user has the DELETE privilege."""
|
355
|
+
@functools.wraps(target)
|
356
|
+
def wrapper(*args, **kwargs):
|
357
|
+
if self.delete_privilege:
|
358
|
+
return target(*args, **kwargs)
|
359
|
+
else:
|
360
|
+
raise PermissionError("You have no delete privilege on the database and therefor this function is not available.")
|
361
|
+
return wrapper
|
362
|
+
|
363
|
+
def deco_all_privileges(self, target):
|
364
|
+
"""Decorator to check if the user has all (SELECT, UPDATE, INSERT, DELETE, CREATE) privileges."""
|
365
|
+
@functools.wraps(target)
|
366
|
+
def wrapper(*args, **kwargs):
|
367
|
+
if self.all_privileges:
|
368
|
+
return target(*args, **kwargs)
|
369
|
+
else:
|
370
|
+
raise PermissionError("You are no super user of the Database and therefor this function is not available.")
|
371
|
+
return wrapper
|
372
|
+
|
373
|
+
|
374
|
+
db_engine = DBEngine()
|
@@ -0,0 +1,34 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"precipitation_typ": "precip_summer",
|
4
|
+
"e": 0.38,
|
5
|
+
"b_no-protection": 0.345,
|
6
|
+
"b_little-protection": 0.31,
|
7
|
+
"b_protected": 0.28,
|
8
|
+
"b_heavy-protection": 0.245
|
9
|
+
},
|
10
|
+
{
|
11
|
+
"precipitation_typ": "precip_winter",
|
12
|
+
"e": 0.46,
|
13
|
+
"b_no-protection": 0.34,
|
14
|
+
"b_little-protection": 0.28,
|
15
|
+
"b_protected": 0.24,
|
16
|
+
"b_heavy-protection": 0.19
|
17
|
+
},
|
18
|
+
{
|
19
|
+
"precipitation_typ": "mix",
|
20
|
+
"e": 0.55,
|
21
|
+
"b_no-protection": 0.535,
|
22
|
+
"b_little-protection": 0.39,
|
23
|
+
"b_protected": 0.305,
|
24
|
+
"b_heavy-protection": 0.185
|
25
|
+
},
|
26
|
+
{
|
27
|
+
"precipitation_typ": "snow",
|
28
|
+
"e": 0.82,
|
29
|
+
"b_no-protection": 0.72,
|
30
|
+
"b_little-protection": 0.51,
|
31
|
+
"b_protected": 0.33,
|
32
|
+
"b_heavy-protection": 0.21
|
33
|
+
}
|
34
|
+
]
|