pandahub 0.3.16__zip → 0.4.0__zip
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.
- {pandahub-0.3.16 → pandahub-0.4.0}/CHANGELOG.md +3 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/PKG-INFO +44 -2
- pandahub-0.4.0/README.md +58 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/api/internal/settings.py +1 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/lib/PandaHub.py +54 -31
- pandahub-0.4.0/pandahub/lib/__init__.py +1 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/lib/database_toolbox.py +0 -55
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/lib/datatypes.py +4 -10
- pandahub-0.4.0/pandahub/lib/mongo_client.py +78 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub.egg-info/PKG-INFO +44 -2
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub.egg-info/SOURCES.txt +1 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub.egg-info/requires.txt +2 -2
- {pandahub-0.3.16 → pandahub-0.4.0}/pyproject.toml +1 -1
- {pandahub-0.3.16 → pandahub-0.4.0}/requirements.txt +2 -2
- pandahub-0.3.16/README.md +0 -16
- pandahub-0.3.16/pandahub/lib/timeseries/data_sources/__init__.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/CONTRIBUTING.rst +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/LICENSE +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/MANIFEST.in +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/__init__.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/api/__init__.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/api/dependencies.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/api/internal/__init__.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/api/internal/db.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/api/internal/schemas.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/api/internal/toolbox.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/api/internal/users.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/api/main.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/api/routers/__init__.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/api/routers/auth.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/api/routers/net.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/api/routers/projects.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/api/routers/timeseries.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/api/routers/users.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/api/routers/variants.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/lib/mongodb_indexes.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/lib/timeseries/__init__.py +0 -0
- {pandahub-0.3.16/pandahub/lib → pandahub-0.4.0/pandahub/lib/timeseries/data_sources}/__init__.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/lib/timeseries/data_sources/mongo_data.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub/lib/timeseries/output_writer_mongodb.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub.egg-info/dependency_links.txt +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/pandahub.egg-info/top_level.txt +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/setup.cfg +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/setup.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/test/__init__.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/test/conftest.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/test/performance_test.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/test/test_client.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/test/test_networks.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/test/test_projects.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/test/test_timeseries.py +0 -0
- {pandahub-0.3.16 → pandahub-0.4.0}/tutorials/config.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pandahub
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Data hub for pandapower and pandapipes networks based on MongoDB
|
|
5
5
|
Author-email: Jan Ulffers <jan.ulffers@iee.fraunhofer.de>, Leon Thurner <leon.thurner@retoflow.de>, Jannis Kupka <jannis.kupka@retoflow.de>, Mike Vogt <mike.vogt@iee.fraunhofer.de>, Joschka Thurner <joschka.thurner@retoflow.de>, Alexander Scheidler <alexander.scheidler@iee.fraunhofer.de>
|
|
6
6
|
License: Copyright (c) 2022 by University of Kassel, Fraunhofer Institute for Energy Economics
|
|
@@ -59,7 +59,7 @@ Provides-Extra: test
|
|
|
59
59
|
Provides-Extra: all
|
|
60
60
|
License-File: LICENSE
|
|
61
61
|
|
|
62
|
-
[](https://www.pandapower.org) [](https://www.pandapipes.org)
|
|
62
|
+
from lib import get_mongo_client[](https://www.pandapower.org) [](https://www.pandapipes.org)
|
|
63
63
|
|
|
64
64
|
[](https://pypi.org/project/pandahub/) [](https://pypi.org/project/pandahub/) [](https://github.com/e2nIEE/pandahub/blob/master/LICENSE)
|
|
65
65
|
|
|
@@ -75,3 +75,45 @@ Swagger UI is available at http://localhost:8002/docs.
|
|
|
75
75
|
|
|
76
76
|
If you develop on the library and do not need the fastapi app, `docker compose up db -d` starts only the mongodb
|
|
77
77
|
container.
|
|
78
|
+
|
|
79
|
+
## MongoClient handling
|
|
80
|
+
|
|
81
|
+
You can supply a mongodb connection string (or the host as connection string and user/password separately) either as arguments to PandaHub
|
|
82
|
+
or by setting the following environment variables:
|
|
83
|
+
|
|
84
|
+
* MONGODB_URL
|
|
85
|
+
* MONGODB_USER (optional)
|
|
86
|
+
* MONGODB_PASSWORD (optional)
|
|
87
|
+
|
|
88
|
+
These values are then used as default arguments when creating a PandaHub object.
|
|
89
|
+
|
|
90
|
+
By default, each instantiation of a PandaHub instance will create a new MongoClient instance. This is fine for interactive use or small scripts. To clean up the mongo client, call `ph.close()` on the instance once you are done with it.
|
|
91
|
+
|
|
92
|
+
When running the pandahub API as a web application, provide database credentials through the environment variables and additionally set `PANDAHUB_GLOBAL_DB_CLIENT` to `true`.
|
|
93
|
+
A single, global mongodb client instance per process will then be used for all PandaHub instantiations, as recommended by [pymongo](https://pymongo.readthedocs.io/en/stable/faq.html#how-does-connection-pooling-work-in-pymongo).
|
|
94
|
+
|
|
95
|
+
You can use the following properties to access database resources from a PandaHub object:
|
|
96
|
+
|
|
97
|
+
* *ph.project_db*: database of the activated project (e.g. `ph.project_db.net_bus.find().to_list()`)
|
|
98
|
+
* *ph.mgmt_db*: the user_management database
|
|
99
|
+
* *ph.users_collection*: the users collection from the management database
|
|
100
|
+
* *ph.projects_collection*: the projects collection from the management database
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
In case you want to access the database but do not need a PandaHub object, there are two convenience functions available:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
from pandahub.lib import get_mongo_client
|
|
107
|
+
|
|
108
|
+
my_collection = get_mongo_client("db", "collection")
|
|
109
|
+
docs = collection.find().to_list()
|
|
110
|
+
```
|
|
111
|
+
`PANDAHUB_GLOBAL_DB_CLIENT` is respected, you either get the global client or a fresh instance, in which case you are responsible for cleaning up the connection.
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from pandahub.lib import mongo_client
|
|
115
|
+
|
|
116
|
+
with mongo_client("db", "my_collection") as collection:
|
|
117
|
+
docs = collection.find().to_list()
|
|
118
|
+
```
|
|
119
|
+
With the context manager, a new MongoClient is created (even if `PANDAHUB_GLOBAL_DB_CLIENT` is `true`) and closed on completion of the with block.
|
pandahub-0.4.0/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from lib import get_mongo_client[](https://www.pandapower.org) [](https://www.pandapipes.org)
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/pandahub/) [](https://pypi.org/project/pandahub/) [](https://github.com/e2nIEE/pandahub/blob/master/LICENSE)
|
|
4
|
+
|
|
5
|
+
pandahub is a data hub for pandapower and pandapipes networks based on MongoDB. It allows you to store pandapower and
|
|
6
|
+
pandapipes networks as well as timeseries in a MongoDB. pandahub allows you to access the database directly through the PandaHub class,
|
|
7
|
+
but also provides a REST-API based on FastAPI. Access through the API is managed with a user management implementation based on FastAPI Users.
|
|
8
|
+
|
|
9
|
+
## Development
|
|
10
|
+
`docker compose up -d` runs a mongodb container alongside a pandahub api instance with live reload available
|
|
11
|
+
at http://localhost:8002. To connect to an existing database instead, set `MONGODB_URL` to the connection string through an environment variable / in you `.env` file.
|
|
12
|
+
|
|
13
|
+
Swagger UI is available at http://localhost:8002/docs.
|
|
14
|
+
|
|
15
|
+
If you develop on the library and do not need the fastapi app, `docker compose up db -d` starts only the mongodb
|
|
16
|
+
container.
|
|
17
|
+
|
|
18
|
+
## MongoClient handling
|
|
19
|
+
|
|
20
|
+
You can supply a mongodb connection string (or the host as connection string and user/password separately) either as arguments to PandaHub
|
|
21
|
+
or by setting the following environment variables:
|
|
22
|
+
|
|
23
|
+
* MONGODB_URL
|
|
24
|
+
* MONGODB_USER (optional)
|
|
25
|
+
* MONGODB_PASSWORD (optional)
|
|
26
|
+
|
|
27
|
+
These values are then used as default arguments when creating a PandaHub object.
|
|
28
|
+
|
|
29
|
+
By default, each instantiation of a PandaHub instance will create a new MongoClient instance. This is fine for interactive use or small scripts. To clean up the mongo client, call `ph.close()` on the instance once you are done with it.
|
|
30
|
+
|
|
31
|
+
When running the pandahub API as a web application, provide database credentials through the environment variables and additionally set `PANDAHUB_GLOBAL_DB_CLIENT` to `true`.
|
|
32
|
+
A single, global mongodb client instance per process will then be used for all PandaHub instantiations, as recommended by [pymongo](https://pymongo.readthedocs.io/en/stable/faq.html#how-does-connection-pooling-work-in-pymongo).
|
|
33
|
+
|
|
34
|
+
You can use the following properties to access database resources from a PandaHub object:
|
|
35
|
+
|
|
36
|
+
* *ph.project_db*: database of the activated project (e.g. `ph.project_db.net_bus.find().to_list()`)
|
|
37
|
+
* *ph.mgmt_db*: the user_management database
|
|
38
|
+
* *ph.users_collection*: the users collection from the management database
|
|
39
|
+
* *ph.projects_collection*: the projects collection from the management database
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
In case you want to access the database but do not need a PandaHub object, there are two convenience functions available:
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from pandahub.lib import get_mongo_client
|
|
46
|
+
|
|
47
|
+
my_collection = get_mongo_client("db", "collection")
|
|
48
|
+
docs = collection.find().to_list()
|
|
49
|
+
```
|
|
50
|
+
`PANDAHUB_GLOBAL_DB_CLIENT` is respected, you either get the global client or a fresh instance, in which case you are responsible for cleaning up the connection.
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from pandahub.lib import mongo_client
|
|
54
|
+
|
|
55
|
+
with mongo_client("db", "my_collection") as collection:
|
|
56
|
+
docs = collection.find().to_list()
|
|
57
|
+
```
|
|
58
|
+
With the context manager, a new MongoClient is created (even if `PANDAHUB_GLOBAL_DB_CLIENT` is `true`) and closed on completion of the with block.
|
|
@@ -58,3 +58,4 @@ DEBUG = settings_bool("DEBUG", default=False)
|
|
|
58
58
|
PANDAHUB_SERVER_URL = get_os_env("PANDAHUB_SERVER_URL", "0.0.0.0")
|
|
59
59
|
PANDAHUB_SERVER_PORT = int(get_os_env('PANDAHUB_SERVER_PORT', 8002))
|
|
60
60
|
WORKERS = int(get_os_env('WORKER', 2))
|
|
61
|
+
PANDAHUB_GLOBAL_DB_CLIENT = settings_bool("PANDAHUB_GLOBAL_DB_CLIENT", default=False)
|
|
@@ -27,19 +27,19 @@ import pandapower as pp
|
|
|
27
27
|
import pandapower.io_utils as io_pp
|
|
28
28
|
|
|
29
29
|
from pandahub.api.internal.settings import MONGODB_URL, MONGODB_USER, MONGODB_PASSWORD, MONGODB_GLOBAL_DATABASE_URL, \
|
|
30
|
-
MONGODB_GLOBAL_DATABASE_USER, MONGODB_GLOBAL_DATABASE_PASSWORD, CREATE_INDEXES_WITH_PROJECT
|
|
30
|
+
MONGODB_GLOBAL_DATABASE_USER, MONGODB_GLOBAL_DATABASE_PASSWORD, CREATE_INDEXES_WITH_PROJECT, PANDAHUB_GLOBAL_DB_CLIENT
|
|
31
31
|
from pandahub.lib.database_toolbox import (
|
|
32
32
|
create_timeseries_document,
|
|
33
33
|
convert_timeseries_to_subdocuments,
|
|
34
34
|
convert_element_to_dict,
|
|
35
|
-
get_mongo_client,
|
|
36
35
|
json_to_object,
|
|
37
36
|
serialize_object_data,
|
|
38
37
|
get_dtypes,
|
|
39
38
|
decompress_timeseries_data,
|
|
40
39
|
convert_geojsons,
|
|
41
|
-
get_metadata_for_timeseries_collections
|
|
40
|
+
get_metadata_for_timeseries_collections,
|
|
42
41
|
)
|
|
42
|
+
from pandahub.lib import get_mongo_client
|
|
43
43
|
from pandahub.lib.mongodb_indexes import MONGODB_INDEXES
|
|
44
44
|
|
|
45
45
|
logger = logging.getLogger(__name__)
|
|
@@ -102,7 +102,11 @@ class PandaHub:
|
|
|
102
102
|
):
|
|
103
103
|
self._datatypes = datatypes
|
|
104
104
|
self.mongodb_indexes = mongodb_indexes
|
|
105
|
-
self.mongo_client = get_mongo_client(
|
|
105
|
+
self.mongo_client = get_mongo_client(
|
|
106
|
+
connection_url=connection_url,
|
|
107
|
+
connection_user=connection_user,
|
|
108
|
+
connection_password=connection_password,
|
|
109
|
+
)
|
|
106
110
|
self.mongo_client_global_db = None
|
|
107
111
|
self.active_project = None
|
|
108
112
|
self.user_id = user_id
|
|
@@ -117,6 +121,27 @@ class PandaHub:
|
|
|
117
121
|
if check_server_available:
|
|
118
122
|
self.server_is_available()
|
|
119
123
|
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def project_db(self) -> Database:
|
|
127
|
+
"""Get the database of the current active project"""
|
|
128
|
+
return self.get_project_database()
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def mgmt_db(self) -> Database:
|
|
132
|
+
"""Get the user_management database"""
|
|
133
|
+
return self.mongo_client["user_management"]
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def users_collection(self) -> Collection:
|
|
137
|
+
"""Get the users collection"""
|
|
138
|
+
return self.mongo_client["user_management"]["users"]
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def projects_collection(self) -> Collection:
|
|
142
|
+
"""Get the projects collection"""
|
|
143
|
+
return self.mongo_client["user_management"]["projects"]
|
|
144
|
+
|
|
120
145
|
# -------------------------
|
|
121
146
|
# Database connection checks
|
|
122
147
|
# -------------------------
|
|
@@ -150,6 +175,12 @@ class PandaHub:
|
|
|
150
175
|
except ServerSelectionTimeoutError as e:
|
|
151
176
|
return "connection timeout"
|
|
152
177
|
|
|
178
|
+
def close(self, force=False):
|
|
179
|
+
"""Close the database connection."""
|
|
180
|
+
if PANDAHUB_GLOBAL_DB_CLIENT and not force:
|
|
181
|
+
msg = "Closing the global MongoClient instance will break all subsequent database connections. If this is intentional, use force=True"
|
|
182
|
+
raise PandaHubError(msg)
|
|
183
|
+
self.mongo_client.close()
|
|
153
184
|
# -------------------------
|
|
154
185
|
# Permission check
|
|
155
186
|
# -------------------------
|
|
@@ -272,7 +303,7 @@ class PandaHub:
|
|
|
272
303
|
if realm is not None:
|
|
273
304
|
filter_dict["realm"] = realm
|
|
274
305
|
db = self.mongo_client["user_management"]
|
|
275
|
-
projects =
|
|
306
|
+
projects = db["projects"].find(filter_dict).to_list()
|
|
276
307
|
return [
|
|
277
308
|
{
|
|
278
309
|
"id": str(p["_id"]),
|
|
@@ -401,8 +432,7 @@ class PandaHub:
|
|
|
401
432
|
return project is not None
|
|
402
433
|
|
|
403
434
|
def _get_project_document(self, filter_dict: dict) -> Optional[dict]:
|
|
404
|
-
|
|
405
|
-
projects = list(project_collection.find(filter_dict))
|
|
435
|
+
projects = self.projects_collection.find(filter_dict).to_list()
|
|
406
436
|
if len(projects) == 0: # project doesn't exist
|
|
407
437
|
return None
|
|
408
438
|
if len(projects) > 1:
|
|
@@ -437,8 +467,12 @@ class PandaHub:
|
|
|
437
467
|
-------
|
|
438
468
|
pymongo.database.Database
|
|
439
469
|
"""
|
|
470
|
+
if self.active_project is None:
|
|
471
|
+
msg = "Can not access project database - no project active!"
|
|
472
|
+
raise PandaHubError(msg)
|
|
473
|
+
project_id = str(self.active_project["_id"])
|
|
440
474
|
if collection is None:
|
|
441
|
-
return self.mongo_client[
|
|
475
|
+
return self.mongo_client[project_id]
|
|
442
476
|
logger.warning(f"Passing a collection to get_project_database is deprecated. Use get_project_collection instead!")
|
|
443
477
|
return self.get_project_collection(collection)
|
|
444
478
|
|
|
@@ -487,8 +521,6 @@ class PandaHub:
|
|
|
487
521
|
list
|
|
488
522
|
network ids
|
|
489
523
|
"""
|
|
490
|
-
if not self.active_project:
|
|
491
|
-
raise PandaHubError("No project activated!")
|
|
492
524
|
return self.get_project_collection("_networks").find({}, {"_id:": 1}).distinct("_id")
|
|
493
525
|
|
|
494
526
|
def get_project_version(self):
|
|
@@ -503,7 +535,7 @@ class PandaHub:
|
|
|
503
535
|
return
|
|
504
536
|
|
|
505
537
|
if version.parse(self.get_project_version()) < version.parse("0.2.3"):
|
|
506
|
-
db = self.
|
|
538
|
+
db = self.project_db
|
|
507
539
|
all_collection_names = db.list_collection_names()
|
|
508
540
|
old_net_collections = [
|
|
509
541
|
name
|
|
@@ -516,9 +548,7 @@ class PandaHub:
|
|
|
516
548
|
for element in old_net_collections:
|
|
517
549
|
db[element].rename(self._collection_name_of_element(element))
|
|
518
550
|
# for all networks
|
|
519
|
-
for d in
|
|
520
|
-
db["_networks"].find({}, projection={"sector": 1, "data": 1})
|
|
521
|
-
):
|
|
551
|
+
for d in db["_networks"].find({}, projection={"sector": 1, "data": 1}).to_list():
|
|
522
552
|
# load old format
|
|
523
553
|
if d.get("sector", "power") == "power":
|
|
524
554
|
data = dict(
|
|
@@ -729,7 +759,7 @@ class PandaHub:
|
|
|
729
759
|
self.set_active_project_by_id(project_id)
|
|
730
760
|
self.check_permission("read")
|
|
731
761
|
db = self._get_project_database()
|
|
732
|
-
return
|
|
762
|
+
return db["_networks"].find().to_list()
|
|
733
763
|
|
|
734
764
|
|
|
735
765
|
def get_network_by_name(
|
|
@@ -1241,11 +1271,11 @@ class PandaHub:
|
|
|
1241
1271
|
proj = {"net": 0}
|
|
1242
1272
|
if not load_area:
|
|
1243
1273
|
proj["area_geojson"] = 0
|
|
1244
|
-
nets = pd.DataFrame(
|
|
1274
|
+
nets = pd.DataFrame(db.find(fi, projection=proj)).to_list()
|
|
1245
1275
|
return nets
|
|
1246
1276
|
|
|
1247
1277
|
def _get_net_id_from_name(self, name, db):
|
|
1248
|
-
metadata =
|
|
1278
|
+
metadata = db["_networks"].find({"name": name}).to_list()
|
|
1249
1279
|
if len(metadata) > 1:
|
|
1250
1280
|
msg = (f"Multiple networks with the name {metadata[0]['name']} found in the database. "
|
|
1251
1281
|
f"Use the corresponding function which selects the network by net_id instead.")
|
|
@@ -1302,9 +1332,7 @@ class PandaHub:
|
|
|
1302
1332
|
else:
|
|
1303
1333
|
filter_dict = {**filter_dict, **filter}
|
|
1304
1334
|
|
|
1305
|
-
data =
|
|
1306
|
-
db[self._collection_name_of_element(element_type)].find(filter_dict)
|
|
1307
|
-
)
|
|
1335
|
+
data = db[self._collection_name_of_element(element_type)].find(filter_dict).to_list()
|
|
1308
1336
|
if len(data) == 0:
|
|
1309
1337
|
return
|
|
1310
1338
|
if dtypes is None:
|
|
@@ -1344,17 +1372,12 @@ class PandaHub:
|
|
|
1344
1372
|
if project_id:
|
|
1345
1373
|
self.set_active_project_by_id(project_id)
|
|
1346
1374
|
self.check_permission("write")
|
|
1347
|
-
db = self._get_project_database()
|
|
1348
1375
|
|
|
1349
1376
|
collection = self._collection_name_of_element(element_type)
|
|
1350
1377
|
dtypes = self._datatypes.get(element_type)
|
|
1351
1378
|
|
|
1352
1379
|
variant_filter = self.get_variant_filter(variant)
|
|
1353
|
-
documents =
|
|
1354
|
-
db[collection].find(
|
|
1355
|
-
{"index": element_index, "net_id": net_id, **variant_filter}
|
|
1356
|
-
)
|
|
1357
|
-
)
|
|
1380
|
+
documents = self.projects_collection[collection].find({"index": element_index, "net_id": net_id, **variant_filter}).to_list()
|
|
1358
1381
|
if len(documents) == 1:
|
|
1359
1382
|
document = documents[0]
|
|
1360
1383
|
else:
|
|
@@ -1446,7 +1469,7 @@ class PandaHub:
|
|
|
1446
1469
|
**self.get_variant_filter(variant),
|
|
1447
1470
|
}
|
|
1448
1471
|
|
|
1449
|
-
deletion_targets =
|
|
1472
|
+
deletion_targets = db[collection].find(element_filter).to_list()
|
|
1450
1473
|
if not deletion_targets:
|
|
1451
1474
|
return []
|
|
1452
1475
|
|
|
@@ -1554,7 +1577,7 @@ class PandaHub:
|
|
|
1554
1577
|
value = dtypes[parameter](value)
|
|
1555
1578
|
collection = self._collection_name_of_element(element_type)
|
|
1556
1579
|
|
|
1557
|
-
js =
|
|
1580
|
+
js = db[collection].find({"index": element_index, "net_id": net_id}).to_list(1)
|
|
1558
1581
|
obj = json_to_object(js["object"])
|
|
1559
1582
|
setattr(obj, parameter, value)
|
|
1560
1583
|
db[collection].update_one(
|
|
@@ -2419,7 +2442,7 @@ class PandaHub:
|
|
|
2419
2442
|
if not include_metadata:
|
|
2420
2443
|
pipeline.append({"$project": {"timeseries_data": 1,
|
|
2421
2444
|
"num_timestamps": 1}})
|
|
2422
|
-
data =
|
|
2445
|
+
data = db[collection_name].aggregate(pipeline).to_list()
|
|
2423
2446
|
if len(data) == 0:
|
|
2424
2447
|
raise PandaHubError("no documents matching the provided filter found", 404)
|
|
2425
2448
|
elif len(data) > 1:
|
|
@@ -2532,7 +2555,7 @@ class PandaHub:
|
|
|
2532
2555
|
pipeline.append({"$match": {"$and": match_filter}})
|
|
2533
2556
|
projection = {"$project": {"timeseries_data": 0}}
|
|
2534
2557
|
pipeline.append(projection)
|
|
2535
|
-
metadata =
|
|
2558
|
+
metadata = db[collection_name].aggregate(pipeline).to_list()
|
|
2536
2559
|
df_metadata = pd.DataFrame(metadata)
|
|
2537
2560
|
if len(df_metadata):
|
|
2538
2561
|
df_metadata.set_index("_id", inplace=True)
|
|
@@ -3086,7 +3109,7 @@ class PandaHub:
|
|
|
3086
3109
|
global_database=False,
|
|
3087
3110
|
):
|
|
3088
3111
|
db = self._get_project_or_global_db(project_id, global_database)
|
|
3089
|
-
collections =
|
|
3112
|
+
collections = db.list_collections(filter={"name": collection_name}).to_list()
|
|
3090
3113
|
return len(collections) == 1 and collections[0]["type"] == "timeseries"
|
|
3091
3114
|
|
|
3092
3115
|
def _get_project_or_global_db(self, project_id=None, global_database=False):
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from pandahub.lib.mongo_client import mongo_client, get_mongo_client
|
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
from contextlib import contextmanager
|
|
3
|
-
from collections.abc import Iterator
|
|
4
2
|
|
|
5
3
|
import numpy as np
|
|
6
4
|
import pandas as pd
|
|
7
|
-
from pymongo import MongoClient
|
|
8
|
-
from pymongo.database import Database
|
|
9
|
-
from pymongo.collection import Collection
|
|
10
5
|
|
|
11
|
-
from pandahub.api.internal.settings import MONGODB_URL, MONGODB_USER, MONGODB_PASSWORD
|
|
12
6
|
from pandahub.lib.datatypes import DATATYPES
|
|
13
7
|
import base64
|
|
14
8
|
import hashlib
|
|
@@ -428,55 +422,6 @@ def migrate_userdb_to_beanie(ph):
|
|
|
428
422
|
{'$out': 'users'}]
|
|
429
423
|
userdb.aggregate(migration)
|
|
430
424
|
|
|
431
|
-
def get_mongo_client(connection_url: str = MONGODB_URL, connection_user: str = MONGODB_USER,
|
|
432
|
-
connection_password: str = MONGODB_PASSWORD) -> MongoClient:
|
|
433
|
-
mongo_client_args = {
|
|
434
|
-
"host": connection_url,
|
|
435
|
-
"uuidRepresentation": "standard",
|
|
436
|
-
"connect": False,
|
|
437
|
-
}
|
|
438
|
-
if connection_user:
|
|
439
|
-
mongo_client_args |= {
|
|
440
|
-
"username": connection_user,
|
|
441
|
-
"password": connection_password,
|
|
442
|
-
}
|
|
443
|
-
return MongoClient(**mongo_client_args)
|
|
444
|
-
|
|
445
|
-
@contextmanager
|
|
446
|
-
def mongo_client(database: str | None = None, collection: str | None = None, connection_url: str = MONGODB_URL,
|
|
447
|
-
connection_user: str = MONGODB_USER, connection_password: str = MONGODB_PASSWORD) -> Iterator[MongoClient | Database | Collection]:
|
|
448
|
-
"""Contextmanager for pymongo MongoClient / Database / Collection with close after use.
|
|
449
|
-
|
|
450
|
-
Parameters
|
|
451
|
-
----------
|
|
452
|
-
database: str or None
|
|
453
|
-
The database to connect to
|
|
454
|
-
collection: str or None
|
|
455
|
-
The collection to connect to
|
|
456
|
-
connection_url: str or None
|
|
457
|
-
Defaults to MONGODB_URL env var
|
|
458
|
-
connection_user: str or None
|
|
459
|
-
Defaults to MONGODB_USER env var
|
|
460
|
-
connection_password: str or None
|
|
461
|
-
Defaults to MONGODB_PASSWORD env var
|
|
462
|
-
|
|
463
|
-
Returns
|
|
464
|
-
-------
|
|
465
|
-
Contextmanager yielding MongoClient / Database / Collection
|
|
466
|
-
"""
|
|
467
|
-
if collection is not None and database is None:
|
|
468
|
-
raise ValueError("Must specify database to access a collection!")
|
|
469
|
-
client = get_mongo_client(connection_url, connection_user, connection_password)
|
|
470
|
-
try:
|
|
471
|
-
if database is not None and collection is not None:
|
|
472
|
-
yield client[database][collection]
|
|
473
|
-
elif database is not None:
|
|
474
|
-
yield client[database]
|
|
475
|
-
else:
|
|
476
|
-
yield client
|
|
477
|
-
finally:
|
|
478
|
-
client.close()
|
|
479
|
-
|
|
480
425
|
|
|
481
426
|
def get_metadata_for_timeseries_collections(db, data_type=None, net_id=None, element_type=None, element_index=None, **kwargs):
|
|
482
427
|
if element_type is None:
|
|
@@ -7,7 +7,8 @@ DATATYPES = {
|
|
|
7
7
|
"zone": str,
|
|
8
8
|
"in_service": bool,
|
|
9
9
|
"max_vm_pu": float,
|
|
10
|
-
"min_vm_pu": float
|
|
10
|
+
"min_vm_pu": float,
|
|
11
|
+
"geo": object
|
|
11
12
|
},
|
|
12
13
|
"load": {
|
|
13
14
|
"name": str,
|
|
@@ -95,7 +96,8 @@ DATATYPES = {
|
|
|
95
96
|
"type": str,
|
|
96
97
|
"in_service": bool,
|
|
97
98
|
"max_loading_percent": float,
|
|
98
|
-
"std_type": str
|
|
99
|
+
"std_type": str,
|
|
100
|
+
"geo": object
|
|
99
101
|
},
|
|
100
102
|
"trafo": {
|
|
101
103
|
"name": str,
|
|
@@ -306,12 +308,4 @@ DATATYPES = {
|
|
|
306
308
|
'initial_run': bool,
|
|
307
309
|
"recycle": str
|
|
308
310
|
},
|
|
309
|
-
"line_geodata": {
|
|
310
|
-
"coords": object
|
|
311
|
-
},
|
|
312
|
-
"bus_geodata": {
|
|
313
|
-
"x": float,
|
|
314
|
-
"y": float,
|
|
315
|
-
"coords": object
|
|
316
|
-
},
|
|
317
311
|
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from contextlib import contextmanager
|
|
2
|
+
from typing import Iterator
|
|
3
|
+
|
|
4
|
+
from pymongo import MongoClient
|
|
5
|
+
from pymongo.synchronous.collection import Collection
|
|
6
|
+
from pymongo.synchronous.database import Database
|
|
7
|
+
|
|
8
|
+
from pandahub.api.internal.settings import PANDAHUB_GLOBAL_DB_CLIENT, MONGODB_URL, MONGODB_USER, MONGODB_PASSWORD
|
|
9
|
+
|
|
10
|
+
def _get_mongo_client(connection_url: str = MONGODB_URL, connection_user: str = MONGODB_USER,
|
|
11
|
+
connection_password: str = MONGODB_PASSWORD) -> MongoClient:
|
|
12
|
+
mongo_client_args = {
|
|
13
|
+
"host": connection_url,
|
|
14
|
+
"uuidRepresentation": "standard",
|
|
15
|
+
"connect": False,
|
|
16
|
+
}
|
|
17
|
+
if connection_user:
|
|
18
|
+
mongo_client_args |= {
|
|
19
|
+
"username": connection_user,
|
|
20
|
+
"password": connection_password,
|
|
21
|
+
}
|
|
22
|
+
return MongoClient(**mongo_client_args)
|
|
23
|
+
|
|
24
|
+
if PANDAHUB_GLOBAL_DB_CLIENT:
|
|
25
|
+
_global_mongo_client = _get_mongo_client(connection_url=MONGODB_URL, connection_user = MONGODB_USER, connection_password = MONGODB_PASSWORD)
|
|
26
|
+
else:
|
|
27
|
+
_global_mongo_client = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_mongo_client(database: str | None = None, collection: str | None = None, connection_url: str = MONGODB_URL, connection_user: str = MONGODB_USER,
|
|
31
|
+
connection_password: str = MONGODB_PASSWORD) -> Iterator[MongoClient | Database | Collection]:
|
|
32
|
+
if collection is not None and database is None:
|
|
33
|
+
raise ValueError("Must specify database to access a collection!")
|
|
34
|
+
if _global_mongo_client is None:
|
|
35
|
+
client = _get_mongo_client(connection_url, connection_user, connection_password)
|
|
36
|
+
else:
|
|
37
|
+
client = _global_mongo_client
|
|
38
|
+
return _get_db_or_coll(client, database, collection)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@contextmanager
|
|
42
|
+
def mongo_client(database: str | None = None, collection: str | None = None, connection_url: str = MONGODB_URL,
|
|
43
|
+
connection_user: str = MONGODB_USER, connection_password: str = MONGODB_PASSWORD) -> Iterator[MongoClient | Database | Collection]:
|
|
44
|
+
"""Contextmanager for pymongo MongoClient / Database / Collection with close after use.
|
|
45
|
+
|
|
46
|
+
Parameters
|
|
47
|
+
----------
|
|
48
|
+
database: str or None
|
|
49
|
+
The database to connect to
|
|
50
|
+
collection: str or None
|
|
51
|
+
The collection to connect to
|
|
52
|
+
connection_url: str or None
|
|
53
|
+
Defaults to MONGODB_URL env var
|
|
54
|
+
connection_user: str or None
|
|
55
|
+
Defaults to MONGODB_USER env var
|
|
56
|
+
connection_password: str or None
|
|
57
|
+
Defaults to MONGODB_PASSWORD env var
|
|
58
|
+
|
|
59
|
+
Returns
|
|
60
|
+
-------
|
|
61
|
+
Contextmanager yielding MongoClient / Database / Collection
|
|
62
|
+
"""
|
|
63
|
+
if collection is not None and database is None:
|
|
64
|
+
raise ValueError("Must specify database to access a collection!")
|
|
65
|
+
client = _get_mongo_client(connection_url, connection_user, connection_password)
|
|
66
|
+
try:
|
|
67
|
+
yield _get_db_or_coll(client, database, collection)
|
|
68
|
+
finally:
|
|
69
|
+
client.close()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _get_db_or_coll(client: MongoClient, database: str | None = None, collection: str | None = None) -> Iterator[MongoClient | Database | Collection]:
|
|
73
|
+
if database is not None and collection is not None:
|
|
74
|
+
return client[database][collection]
|
|
75
|
+
elif database is not None:
|
|
76
|
+
return client[database]
|
|
77
|
+
else:
|
|
78
|
+
return client
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pandahub
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Data hub for pandapower and pandapipes networks based on MongoDB
|
|
5
5
|
Author-email: Jan Ulffers <jan.ulffers@iee.fraunhofer.de>, Leon Thurner <leon.thurner@retoflow.de>, Jannis Kupka <jannis.kupka@retoflow.de>, Mike Vogt <mike.vogt@iee.fraunhofer.de>, Joschka Thurner <joschka.thurner@retoflow.de>, Alexander Scheidler <alexander.scheidler@iee.fraunhofer.de>
|
|
6
6
|
License: Copyright (c) 2022 by University of Kassel, Fraunhofer Institute for Energy Economics
|
|
@@ -59,7 +59,7 @@ Provides-Extra: test
|
|
|
59
59
|
Provides-Extra: all
|
|
60
60
|
License-File: LICENSE
|
|
61
61
|
|
|
62
|
-
[](https://www.pandapower.org) [](https://www.pandapipes.org)
|
|
62
|
+
from lib import get_mongo_client[](https://www.pandapower.org) [](https://www.pandapipes.org)
|
|
63
63
|
|
|
64
64
|
[](https://pypi.org/project/pandahub/) [](https://pypi.org/project/pandahub/) [](https://github.com/e2nIEE/pandahub/blob/master/LICENSE)
|
|
65
65
|
|
|
@@ -75,3 +75,45 @@ Swagger UI is available at http://localhost:8002/docs.
|
|
|
75
75
|
|
|
76
76
|
If you develop on the library and do not need the fastapi app, `docker compose up db -d` starts only the mongodb
|
|
77
77
|
container.
|
|
78
|
+
|
|
79
|
+
## MongoClient handling
|
|
80
|
+
|
|
81
|
+
You can supply a mongodb connection string (or the host as connection string and user/password separately) either as arguments to PandaHub
|
|
82
|
+
or by setting the following environment variables:
|
|
83
|
+
|
|
84
|
+
* MONGODB_URL
|
|
85
|
+
* MONGODB_USER (optional)
|
|
86
|
+
* MONGODB_PASSWORD (optional)
|
|
87
|
+
|
|
88
|
+
These values are then used as default arguments when creating a PandaHub object.
|
|
89
|
+
|
|
90
|
+
By default, each instantiation of a PandaHub instance will create a new MongoClient instance. This is fine for interactive use or small scripts. To clean up the mongo client, call `ph.close()` on the instance once you are done with it.
|
|
91
|
+
|
|
92
|
+
When running the pandahub API as a web application, provide database credentials through the environment variables and additionally set `PANDAHUB_GLOBAL_DB_CLIENT` to `true`.
|
|
93
|
+
A single, global mongodb client instance per process will then be used for all PandaHub instantiations, as recommended by [pymongo](https://pymongo.readthedocs.io/en/stable/faq.html#how-does-connection-pooling-work-in-pymongo).
|
|
94
|
+
|
|
95
|
+
You can use the following properties to access database resources from a PandaHub object:
|
|
96
|
+
|
|
97
|
+
* *ph.project_db*: database of the activated project (e.g. `ph.project_db.net_bus.find().to_list()`)
|
|
98
|
+
* *ph.mgmt_db*: the user_management database
|
|
99
|
+
* *ph.users_collection*: the users collection from the management database
|
|
100
|
+
* *ph.projects_collection*: the projects collection from the management database
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
In case you want to access the database but do not need a PandaHub object, there are two convenience functions available:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
from pandahub.lib import get_mongo_client
|
|
107
|
+
|
|
108
|
+
my_collection = get_mongo_client("db", "collection")
|
|
109
|
+
docs = collection.find().to_list()
|
|
110
|
+
```
|
|
111
|
+
`PANDAHUB_GLOBAL_DB_CLIENT` is respected, you either get the global client or a fresh instance, in which case you are responsible for cleaning up the connection.
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from pandahub.lib import mongo_client
|
|
115
|
+
|
|
116
|
+
with mongo_client("db", "my_collection") as collection:
|
|
117
|
+
docs = collection.find().to_list()
|
|
118
|
+
```
|
|
119
|
+
With the context manager, a new MongoClient is created (even if `PANDAHUB_GLOBAL_DB_CLIENT` is `true`) and closed on completion of the with block.
|
|
@@ -32,6 +32,7 @@ pandahub/lib/PandaHub.py
|
|
|
32
32
|
pandahub/lib/__init__.py
|
|
33
33
|
pandahub/lib/database_toolbox.py
|
|
34
34
|
pandahub/lib/datatypes.py
|
|
35
|
+
pandahub/lib/mongo_client.py
|
|
35
36
|
pandahub/lib/mongodb_indexes.py
|
|
36
37
|
pandahub/lib/timeseries/__init__.py
|
|
37
38
|
pandahub/lib/timeseries/output_writer_mongodb.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pandahub"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0" # File format version '__format_version__' is tracked in __init__.py
|
|
8
8
|
authors=[
|
|
9
9
|
{ name = "Jan Ulffers", email = "jan.ulffers@iee.fraunhofer.de" },
|
|
10
10
|
{ name = "Leon Thurner", email = "leon.thurner@retoflow.de" },
|
pandahub-0.3.16/README.md
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
[](https://www.pandapower.org) [](https://www.pandapipes.org)
|
|
2
|
-
|
|
3
|
-
[](https://pypi.org/project/pandahub/) [](https://pypi.org/project/pandahub/) [](https://github.com/e2nIEE/pandahub/blob/master/LICENSE)
|
|
4
|
-
|
|
5
|
-
pandahub is a data hub for pandapower and pandapipes networks based on MongoDB. It allows you to store pandapower and
|
|
6
|
-
pandapipes networks as well as timeseries in a MongoDB. pandahub allows you to access the database directly through the PandaHub class,
|
|
7
|
-
but also provides a REST-API based on FastAPI. Access through the API is managed with a user management implementation based on FastAPI Users.
|
|
8
|
-
|
|
9
|
-
## Development
|
|
10
|
-
`docker compose up -d` runs a mongodb container alongside a pandahub api instance with live reload available
|
|
11
|
-
at http://localhost:8002. To connect to an existing database instead, set `MONGODB_URL` to the connection string through an environment variable / in you `.env` file.
|
|
12
|
-
|
|
13
|
-
Swagger UI is available at http://localhost:8002/docs.
|
|
14
|
-
|
|
15
|
-
If you develop on the library and do not need the fastapi app, `docker compose up db -d` starts only the mongodb
|
|
16
|
-
container.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pandahub-0.3.16/pandahub/lib → pandahub-0.4.0/pandahub/lib/timeseries/data_sources}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|