weatherdb 1.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. docker/Dockerfile +30 -0
  2. docker/docker-compose.yaml +58 -0
  3. docker/docker-compose_test.yaml +24 -0
  4. docker/start-docker-test.sh +6 -0
  5. docs/requirements.txt +10 -0
  6. docs/source/Changelog.md +2 -0
  7. docs/source/License.rst +7 -0
  8. docs/source/Methode.md +161 -0
  9. docs/source/_static/custom.css +8 -0
  10. docs/source/_static/favicon.ico +0 -0
  11. docs/source/_static/logo.png +0 -0
  12. docs/source/api/api.rst +15 -0
  13. docs/source/api/cli.rst +8 -0
  14. docs/source/api/weatherDB.broker.rst +10 -0
  15. docs/source/api/weatherDB.config.rst +7 -0
  16. docs/source/api/weatherDB.db.rst +23 -0
  17. docs/source/api/weatherDB.rst +22 -0
  18. docs/source/api/weatherDB.station.rst +56 -0
  19. docs/source/api/weatherDB.stations.rst +46 -0
  20. docs/source/api/weatherDB.utils.rst +22 -0
  21. docs/source/conf.py +137 -0
  22. docs/source/index.rst +33 -0
  23. docs/source/setup/Configuration.md +127 -0
  24. docs/source/setup/Hosting.md +9 -0
  25. docs/source/setup/Install.md +49 -0
  26. docs/source/setup/Quickstart.md +183 -0
  27. docs/source/setup/setup.rst +12 -0
  28. weatherdb/__init__.py +24 -0
  29. weatherdb/_version.py +1 -0
  30. weatherdb/alembic/README.md +8 -0
  31. weatherdb/alembic/alembic.ini +80 -0
  32. weatherdb/alembic/config.py +9 -0
  33. weatherdb/alembic/env.py +100 -0
  34. weatherdb/alembic/script.py.mako +26 -0
  35. weatherdb/alembic/versions/V1.0.0_initial_database_creation.py +898 -0
  36. weatherdb/alembic/versions/V1.0.2_more_charachters_for_settings+term_station_ma_raster.py +88 -0
  37. weatherdb/alembic/versions/V1.0.5_fix-ma-raster-values.py +152 -0
  38. weatherdb/alembic/versions/V1.0.6_update-views.py +22 -0
  39. weatherdb/broker.py +667 -0
  40. weatherdb/cli.py +214 -0
  41. weatherdb/config/ConfigParser.py +663 -0
  42. weatherdb/config/__init__.py +5 -0
  43. weatherdb/config/config_default.ini +162 -0
  44. weatherdb/db/__init__.py +3 -0
  45. weatherdb/db/connections.py +374 -0
  46. weatherdb/db/fixtures/RichterParameters.json +34 -0
  47. weatherdb/db/models.py +402 -0
  48. weatherdb/db/queries/get_quotient.py +155 -0
  49. weatherdb/db/views.py +165 -0
  50. weatherdb/station/GroupStation.py +710 -0
  51. weatherdb/station/StationBases.py +3108 -0
  52. weatherdb/station/StationET.py +111 -0
  53. weatherdb/station/StationP.py +807 -0
  54. weatherdb/station/StationPD.py +98 -0
  55. weatherdb/station/StationT.py +164 -0
  56. weatherdb/station/__init__.py +13 -0
  57. weatherdb/station/constants.py +21 -0
  58. weatherdb/stations/GroupStations.py +519 -0
  59. weatherdb/stations/StationsBase.py +1021 -0
  60. weatherdb/stations/StationsBaseTET.py +30 -0
  61. weatherdb/stations/StationsET.py +17 -0
  62. weatherdb/stations/StationsP.py +128 -0
  63. weatherdb/stations/StationsPD.py +24 -0
  64. weatherdb/stations/StationsT.py +21 -0
  65. weatherdb/stations/__init__.py +11 -0
  66. weatherdb/utils/TimestampPeriod.py +369 -0
  67. weatherdb/utils/__init__.py +3 -0
  68. weatherdb/utils/dwd.py +350 -0
  69. weatherdb/utils/geometry.py +69 -0
  70. weatherdb/utils/get_data.py +285 -0
  71. weatherdb/utils/logging.py +126 -0
  72. weatherdb-1.1.0.dist-info/LICENSE +674 -0
  73. weatherdb-1.1.0.dist-info/METADATA +765 -0
  74. weatherdb-1.1.0.dist-info/RECORD +77 -0
  75. weatherdb-1.1.0.dist-info/WHEEL +5 -0
  76. weatherdb-1.1.0.dist-info/entry_points.txt +2 -0
  77. 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
@@ -0,0 +1,3 @@
1
+ from .connections import db_engine
2
+ from . import models
3
+ from . import views
@@ -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
+ ]