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.
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
+ ]