pandahub 0.3.2__zip → 0.3.3__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.
Files changed (53) hide show
  1. {pandahub-0.3.2 → pandahub-0.3.3}/PKG-INFO +1 -1
  2. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/__init__.py +1 -1
  3. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/api/internal/db.py +2 -0
  4. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/api/internal/schemas.py +2 -0
  5. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/api/internal/settings.py +0 -1
  6. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/api/routers/variants.py +3 -4
  7. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/lib/PandaHub.py +165 -98
  8. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/lib/database_toolbox.py +5 -7
  9. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/lib/datatypes.py +1 -1
  10. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/lib/mongodb_indexes.py +35 -2
  11. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub.egg-info/PKG-INFO +1 -1
  12. {pandahub-0.3.2 → pandahub-0.3.3}/setup.py +1 -1
  13. {pandahub-0.3.2 → pandahub-0.3.3}/AUTHORS +0 -0
  14. {pandahub-0.3.2 → pandahub-0.3.3}/CHANGELOG.md +0 -0
  15. {pandahub-0.3.2 → pandahub-0.3.3}/CONTRIBUTING.rst +0 -0
  16. {pandahub-0.3.2 → pandahub-0.3.3}/LICENSE +0 -0
  17. {pandahub-0.3.2 → pandahub-0.3.3}/MANIFEST.in +0 -0
  18. {pandahub-0.3.2 → pandahub-0.3.3}/README.md +0 -0
  19. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/api/__init__.py +0 -0
  20. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/api/dependencies.py +0 -0
  21. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/api/internal/__init__.py +0 -0
  22. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/api/internal/toolbox.py +0 -0
  23. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/api/internal/users.py +0 -0
  24. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/api/main.py +0 -0
  25. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/api/routers/__init__.py +0 -0
  26. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/api/routers/auth.py +0 -0
  27. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/api/routers/net.py +0 -0
  28. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/api/routers/projects.py +0 -0
  29. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/api/routers/timeseries.py +0 -0
  30. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/api/routers/users.py +0 -0
  31. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/client/PandaHubClient.py +0 -0
  32. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/client/__init__.py +0 -0
  33. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/client/user_management.py +0 -0
  34. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/lib/__init__.py +0 -0
  35. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/lib/timeseries/__init__.py +0 -0
  36. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/lib/timeseries/data_sources/__init__.py +0 -0
  37. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/lib/timeseries/data_sources/mongo_data.py +0 -0
  38. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/lib/timeseries/output_writer_mongodb.py +0 -0
  39. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/test/__init__.py +0 -0
  40. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/test/conftest.py +0 -0
  41. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/test/performance_test.py +0 -0
  42. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/test/test_client.py +0 -0
  43. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/test/test_networks.py +0 -0
  44. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/test/test_projects.py +0 -0
  45. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub/test/test_timeseries.py +0 -0
  46. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub.egg-info/SOURCES.txt +0 -0
  47. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub.egg-info/dependency_links.txt +0 -0
  48. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub.egg-info/entry_points.txt +0 -0
  49. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub.egg-info/not-zip-safe +0 -0
  50. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub.egg-info/requires.txt +0 -0
  51. {pandahub-0.3.2 → pandahub-0.3.3}/pandahub.egg-info/top_level.txt +0 -0
  52. {pandahub-0.3.2 → pandahub-0.3.3}/requirements.txt +0 -0
  53. {pandahub-0.3.2 → pandahub-0.3.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pandahub
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Data hub for pandapower and pandapipes networks based on MongoDB
5
5
  Home-page: https://github.com/e2nIEE/pandahub
6
6
  Author: Jan Ulffers, Leon Thurner, Jannis Kupka, Mike Vogt, Joschka Thurner, Alexander Scheidler
@@ -1,4 +1,4 @@
1
- __version__ = "0.3.2"
1
+ __version__ = "0.3.3"
2
2
 
3
3
  from pandahub.lib.PandaHub import PandaHub, PandaHubError
4
4
  from pandahub.client.PandaHubClient import PandaHubClient
@@ -9,6 +9,7 @@ from fastapi_users_db_beanie.access_token import (
9
9
  BeanieBaseAccessToken,
10
10
  )
11
11
 
12
+ from pandahub.api.internal.settings import REGISTRATION_ADMIN_APPROVAL
12
13
  from pandahub.api.internal import settings
13
14
  from pydantic import Field
14
15
 
@@ -22,6 +23,7 @@ db = client["user_management"]
22
23
 
23
24
  class User(BeanieBaseUser, Document):
24
25
  id: uuid.UUID = Field(default_factory=uuid.uuid4)
26
+ is_active: bool = not REGISTRATION_ADMIN_APPROVAL
25
27
  class Settings(BeanieBaseUser.Settings):
26
28
  name = "users"
27
29
 
@@ -7,8 +7,10 @@ from pandahub.api.internal.settings import REGISTRATION_ADMIN_APPROVAL
7
7
  class UserRead(schemas.BaseUser[uuid.UUID]):
8
8
  pass
9
9
 
10
+
10
11
  class UserCreate(schemas.BaseUserCreate):
11
12
  is_active: bool = not REGISTRATION_ADMIN_APPROVAL
12
13
 
14
+
13
15
  class UserUpdate(schemas.BaseUserUpdate):
14
16
  pass
@@ -47,5 +47,4 @@ SECRET = get_secret("SECRET") or None
47
47
  REGISTRATION_ENABLED = settings_bool("REGISTRATION_ENABLED", default=True)
48
48
  REGISTRATION_ADMIN_APPROVAL = settings_bool("REGISTRATION_ADMIN_APPROVAL", default=False)
49
49
 
50
- DATATYPES_MODULE = os.getenv("DATATYPES_MODULE") or "pandahub.lib.datatypes"
51
50
  CREATE_INDEXES_WITH_PROJECT = settings_bool("CREATE_INDEXES_WITH_PROJECT", default=True)
@@ -21,11 +21,10 @@ class GetVariantsModel(BaseModel):
21
21
 
22
22
  @router.post("/get_variants")
23
23
  def get_variants(data: GetVariantsModel, ph=Depends(pandahub)):
24
- project_id = data.project_id
25
- ph.set_active_project_by_id(project_id)
26
- db = ph._get_project_database()
24
+ ph.set_active_project_by_id(data.project_id)
25
+ variants_collection = ph.get_project_database("variant")
27
26
 
28
- variants = db["variant"].find({"net_id": data.net_id}, projection={"_id": 0})
27
+ variants = variants_collection.find({"net_id": data.net_id}, projection={"_id": 0})
29
28
  response = {}
30
29
  for var in variants:
31
30
  response[var.pop("index")] = var
@@ -1,13 +1,11 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import builtins
3
- import importlib
4
3
  import json
5
4
  import logging
6
- import traceback
7
5
  import warnings
8
6
  from inspect import signature, _empty
9
7
  from collections.abc import Callable
10
- from typing import Optional, Union
8
+ from typing import Optional, Union, TypeVar
11
9
 
12
10
  import numpy as np
13
11
  import pandas as pd
@@ -20,10 +18,11 @@ from pymongo import MongoClient, ReplaceOne
20
18
  from pymongo.errors import ServerSelectionTimeoutError
21
19
 
22
20
  import pandapipes as pps
23
- from pandapipes import from_json_string as from_json_pps
21
+ from pandapipes import from_json_string as from_json_pps, FromSerializableRegistryPpipe
24
22
  import pandapower as pp
25
23
  import pandapower.io_utils as io_pp
26
- import pandahub.api.internal.settings as SETTINGS
24
+ from pandahub.api.internal.settings import MONGODB_URL, MONGODB_USER, MONGODB_PASSWORD, MONGODB_GLOBAL_DATABASE_URL, \
25
+ MONGODB_GLOBAL_DATABASE_USER, MONGODB_GLOBAL_DATABASE_PASSWORD, CREATE_INDEXES_WITH_PROJECT
27
26
  from pandahub.lib.database_toolbox import (
28
27
  create_timeseries_document,
29
28
  convert_timeseries_to_subdocuments,
@@ -34,10 +33,11 @@ from pandahub.lib.database_toolbox import (
34
33
  decompress_timeseries_data,
35
34
  convert_geojsons,
36
35
  )
37
- from pandahub.lib.mongodb_indexes import mongodb_indexes
36
+ from pandahub.lib.mongodb_indexes import MONGODB_INDEXES
38
37
 
39
38
  logger = logging.getLogger(__name__)
40
39
  from pandahub import __version__
40
+ from pandahub.lib.datatypes import DATATYPES
41
41
  from packaging import version
42
42
 
43
43
 
@@ -60,6 +60,8 @@ class PandaHubError(Exception):
60
60
  # PandaHub
61
61
  # -------------------------
62
62
 
63
+ ProjectID = TypeVar("ProjectID", str, int, ObjectId)
64
+ SettingsValue = TypeVar("SettingsValue", str, int, float, list, dict)
63
65
 
64
66
  class PandaHub:
65
67
  permissions = {
@@ -68,21 +70,19 @@ class PandaHub:
68
70
  "user_management": ["owner"],
69
71
  }
70
72
 
71
- _datatypes = getattr(
72
- importlib.import_module(SETTINGS.DATATYPES_MODULE), "datatypes"
73
- )
74
-
75
73
  # -------------------------
76
74
  # Initialization
77
75
  # -------------------------
78
76
 
79
77
  def __init__(
80
78
  self,
81
- connection_url=SETTINGS.MONGODB_URL,
82
- connection_user=SETTINGS.MONGODB_USER,
83
- connection_password=SETTINGS.MONGODB_PASSWORD,
79
+ connection_url=MONGODB_URL,
80
+ connection_user=MONGODB_USER,
81
+ connection_password=MONGODB_PASSWORD,
84
82
  check_server_available=False,
85
83
  user_id=None,
84
+ datatypes=DATATYPES,
85
+ mongodb_indexes=MONGODB_INDEXES,
86
86
  ):
87
87
  mongo_client_args = {
88
88
  "host": connection_url,
@@ -94,19 +94,19 @@ class PandaHub:
94
94
  "username": connection_user,
95
95
  "password": connection_password,
96
96
  }
97
+ self._datatypes = datatypes
98
+ self.mongodb_indexes = mongodb_indexes
97
99
  self.mongo_client = MongoClient(**mongo_client_args)
98
100
  self.mongo_client_global_db = None
99
101
  self.active_project = None
100
102
  self.user_id = user_id
101
103
  self.base_variant_filter = {
102
104
  "$or": [
103
- {"var_type": {"$exists": False}},
104
105
  {"var_type": "base"},
105
106
  {"var_type": None},
106
107
  {"var_type": np.nan},
107
108
  ]
108
109
  }
109
- self.mongodb_indexes = mongodb_indexes
110
110
  if check_server_available:
111
111
  self.server_is_available()
112
112
 
@@ -212,6 +212,7 @@ class PandaHub:
212
212
  metadata=None,
213
213
  project_id=None,
214
214
  activate=True,
215
+ additional_project_data=None,
215
216
  ):
216
217
  if self.project_exists(name, realm):
217
218
  raise PandaHubError("Project already exists")
@@ -219,19 +220,22 @@ class PandaHub:
219
220
  settings = {}
220
221
  if metadata is None:
221
222
  metadata = {}
223
+ if additional_project_data is None:
224
+ additional_project_data = {}
222
225
  project_data = {
223
226
  "name": name,
224
227
  "realm": realm,
225
228
  "settings": settings,
226
229
  "metadata": metadata,
227
230
  "version": __version__,
231
+ **additional_project_data,
228
232
  }
229
233
  if project_id:
230
234
  project_data["_id"] = project_id
231
235
  if self.user_id is not None:
232
236
  project_data["users"] = {self.user_id: "owner"}
233
237
  self.mongo_client["user_management"]["projects"].insert_one(project_data)
234
- if SETTINGS.CREATE_INDEXES_WITH_PROJECT:
238
+ if CREATE_INDEXES_WITH_PROJECT:
235
239
  self._create_mongodb_indexes(project_data["_id"])
236
240
  if activate:
237
241
  self.set_active_project_by_id(project_data["_id"])
@@ -269,6 +273,7 @@ class PandaHub:
269
273
  "settings": p["settings"],
270
274
  "locked": p.get("locked"),
271
275
  "locked_by": p.get("locked_by"),
276
+ "locked_reason": p.get("locked_reason"),
272
277
  "permissions": self.get_permissions_by_role(
273
278
  p.get("users").get(self.user_id)
274
279
  )
@@ -278,7 +283,7 @@ class PandaHub:
278
283
  for p in projects
279
284
  ]
280
285
 
281
- def set_active_project(self, project_name, realm=None):
286
+ def set_active_project(self, project_name:str, realm=None):
282
287
  projects = self.get_projects()
283
288
  active_projects = [
284
289
  project for project in projects if project["name"] == project_name
@@ -291,7 +296,7 @@ class PandaHub:
291
296
  project_id = active_projects[0]["id"]
292
297
  self.set_active_project_by_id(project_id)
293
298
 
294
- def set_active_project_by_id(self, project_id):
299
+ def set_active_project_by_id(self, project_id:ProjectID):
295
300
  try:
296
301
  project_id = ObjectId(project_id)
297
302
  except InvalidId:
@@ -300,7 +305,7 @@ class PandaHub:
300
305
  if self.active_project is None:
301
306
  raise PandaHubError("Project not found!", 404)
302
307
 
303
- def rename_project(self, project_name):
308
+ def rename_project(self, project_name:str):
304
309
  self.has_permission("write")
305
310
  project_collection = self.mongo_client["user_management"].projects
306
311
  realm = self.active_project["realm"]
@@ -334,6 +339,20 @@ class PandaHub:
334
339
  )
335
340
  return result.acknowledged and result.modified_count > 0
336
341
 
342
+
343
+ # def lock_project(self):
344
+ # db = self.mongo_client["user_management"]["projects"]
345
+ # result = db.update_one(
346
+ #
347
+ # result = db.find_one_and_update(
348
+ # {
349
+ # "_id": self.active_project["_id"],
350
+ # "_id": self.active_project["_id"], "locked": False
351
+ # },
352
+ # {"$set": {"locked": True, "locked_by": self.user_id}},
353
+ # )
354
+ # return result.acknowledged and result.modified_count > 0
355
+
337
356
  def unlock_project(self):
338
357
  db = self.mongo_client["user_management"]["projects"]
339
358
  return db.update_one(
@@ -359,7 +378,7 @@ class PandaHub:
359
378
  else:
360
379
  raise PandaHubError("You don't have rights to access this project", 403)
361
380
 
362
- def project_exists(self, project_name=None, realm=None):
381
+ def project_exists(self, project_name:Optional[str]=None, realm=None):
363
382
  project_collection = self.mongo_client["user_management"].projects
364
383
  project = project_collection.find_one({"name": project_name, "realm": realm})
365
384
  return project is not None
@@ -385,22 +404,40 @@ class PandaHub:
385
404
  else:
386
405
  return project_doc
387
406
 
388
- def _get_project_database(self):
389
- return self.mongo_client[str(self.active_project["_id"])]
407
+ def _get_project_database(self) -> MongoClient:
408
+ return self.get_project_database()
409
+
410
+ def get_project_database(self, collection: Optional[str] = None) -> MongoClient:
411
+ """
412
+ Get a MongoClient instance connected to the database for the current active project, optionally set to the given collection.
413
+
414
+ Parameters
415
+ ----------
416
+ collection
417
+ Name of document collection
418
+
419
+ Returns
420
+ -------
421
+ MongoClient
422
+ """
423
+ project_db = self.mongo_client[str(self.active_project["_id"])]
424
+ if collection is None:
425
+ return project_db
426
+ return project_db[collection]
390
427
 
391
- def _get_global_database(self):
428
+ def _get_global_database(self) -> MongoClient:
392
429
  if (
393
430
  self.mongo_client_global_db is None
394
- and SETTINGS.MONGODB_GLOBAL_DATABASE_URL is not None
431
+ and MONGODB_GLOBAL_DATABASE_URL is not None
395
432
  ):
396
433
  mongo_client_args = {
397
- "host": SETTINGS.MONGODB_GLOBAL_DATABASE_URL,
434
+ "host": MONGODB_GLOBAL_DATABASE_URL,
398
435
  "uuidRepresentation": "standard",
399
436
  }
400
- if SETTINGS.MONGODB_GLOBAL_DATABASE_USER:
437
+ if MONGODB_GLOBAL_DATABASE_USER:
401
438
  mongo_client_args |= {
402
- "username": SETTINGS.MONGODB_GLOBAL_DATABASE_USER,
403
- "password": SETTINGS.MONGODB_GLOBAL_DATABASE_PASSWORD,
439
+ "username": MONGODB_GLOBAL_DATABASE_USER,
440
+ "password": MONGODB_GLOBAL_DATABASE_PASSWORD,
404
441
  }
405
442
  self.mongo_client_global_db = MongoClient(**mongo_client_args)
406
443
  if self.mongo_client_global_db is None:
@@ -408,6 +445,19 @@ class PandaHub:
408
445
  else:
409
446
  return self.mongo_client_global_db["global_data"]
410
447
 
448
+ def get_network_ids(self) -> list[int]:
449
+ """
450
+ Retrieve the id's of all networks in the active project.
451
+
452
+ Returns
453
+ -------
454
+ list
455
+ network ids
456
+ """
457
+ if not self.active_project:
458
+ raise PandaHubError("No project activated!")
459
+ return self.get_project_database("_network").find({}, {"_id:": 1}).distinct("_id")
460
+
411
461
  def get_project_version(self):
412
462
  return self.active_project.get("version", "0.2.2")
413
463
 
@@ -655,7 +705,7 @@ class PandaHub:
655
705
  only_tables=None,
656
706
  project_id=None,
657
707
  geo_mode="string",
658
- variants=[],
708
+ variants=None,
659
709
  ):
660
710
  if project_id:
661
711
  self.set_active_project_by_id(project_id)
@@ -675,7 +725,7 @@ class PandaHub:
675
725
  only_tables=None,
676
726
  convert=True,
677
727
  geo_mode="string",
678
- variants=[],
728
+ variants=None,
679
729
  ):
680
730
  self.check_permission("read")
681
731
  return self._get_net_from_db_by_id(
@@ -694,7 +744,7 @@ class PandaHub:
694
744
  only_tables=None,
695
745
  convert=True,
696
746
  geo_mode="string",
697
- variants=[],
747
+ variants=None,
698
748
  ):
699
749
  db = self._get_project_database()
700
750
  meta = self._get_network_metadata(db, id_)
@@ -725,20 +775,16 @@ class PandaHub:
725
775
  return net
726
776
 
727
777
  def deserialize_and_update_data(self, net, meta):
778
+ registry = io_pp.FromSerializableRegistry if meta.get("sector", "power") == "power" \
779
+ else FromSerializableRegistryPpipe
728
780
  if version.parse(self.get_project_version()) <= version.parse("0.2.3"):
729
- if meta.get("sector", "power") == "power":
730
- data = dict(
731
- (k, json.loads(v, cls=io_pp.PPJSONDecoder))
732
- for k, v in meta["data"].items()
733
- )
734
- net.update(data)
735
- else:
736
- data = dict((k, from_json_pps(v)) for k, v in meta["data"].items())
737
- net.update(data)
781
+ data = dict((k, json.loads(v, cls=io_pp.PPJSONDecoder, registry_class=registry))
782
+ for k, v in meta["data"].items())
783
+ net.update(data)
738
784
  else:
739
785
  for key, value in meta["data"].items():
740
786
  if type(value) == str and value.startswith("serialized_"):
741
- value = json.loads(value[11:], cls=io_pp.PPJSONDecoder)
787
+ value = json.loads(value[11:], cls=io_pp.PPJSONDecoder, registry_class=registry)
742
788
  net[key] = value
743
789
 
744
790
  def get_subnet_from_db(
@@ -748,7 +794,7 @@ class PandaHub:
748
794
  include_results=True,
749
795
  add_edge_branches=True,
750
796
  geo_mode="string",
751
- variants=[],
797
+ variants=None,
752
798
  additional_filters: dict[
753
799
  str, Callable[[pp.auxiliary.pandapowerNet], dict]
754
800
  ] = {},
@@ -775,7 +821,7 @@ class PandaHub:
775
821
  include_results=True,
776
822
  add_edge_branches=True,
777
823
  geo_mode="string",
778
- variants=[],
824
+ variants=None,
779
825
  ignore_elements=[],
780
826
  additional_filters: dict[
781
827
  str, Callable[[pp.auxiliary.pandapowerNet], dict]
@@ -804,7 +850,14 @@ class PandaHub:
804
850
  )
805
851
  buses = net.bus.index.tolist()
806
852
 
807
- branch_operator = "$or" if add_edge_branches else "$and"
853
+ if isinstance(add_edge_branches, bool):
854
+ if add_edge_branches:
855
+ add_edge_branches = ["line", "trafo", "switch"]
856
+ else:
857
+ add_edge_branches = []
858
+ elif not isinstance(add_edge_branches, list):
859
+ raise ValueError("add_edge_branches must be a list or a boolean")
860
+ line_operator = "$or" if "line" in add_edge_branches else "$and"
808
861
  # Add branch elements connected to at least one bus
809
862
  self._add_element_from_collection(
810
863
  net,
@@ -812,7 +865,7 @@ class PandaHub:
812
865
  "line",
813
866
  net_id,
814
867
  {
815
- branch_operator: [
868
+ line_operator: [
816
869
  {"from_bus": {"$in": buses}},
817
870
  {"to_bus": {"$in": buses}},
818
871
  ]
@@ -821,12 +874,13 @@ class PandaHub:
821
874
  variants=variants,
822
875
  dtypes=dtypes,
823
876
  )
877
+ trafo_operator = "$or" if "trafo" in add_edge_branches else "$and"
824
878
  self._add_element_from_collection(
825
879
  net,
826
880
  db,
827
881
  "trafo",
828
882
  net_id,
829
- {branch_operator: [{"hv_bus": {"$in": buses}}, {"lv_bus": {"$in": buses}}]},
883
+ {trafo_operator: [{"hv_bus": {"$in": buses}}, {"lv_bus": {"$in": buses}}]},
830
884
  geo_mode=geo_mode,
831
885
  variants=variants,
832
886
  dtypes=dtypes,
@@ -837,7 +891,7 @@ class PandaHub:
837
891
  "trafo3w",
838
892
  net_id,
839
893
  {
840
- branch_operator: [
894
+ trafo_operator: [
841
895
  {"hv_bus": {"$in": buses}},
842
896
  {"mv_bus": {"$in": buses}},
843
897
  {"lv_bus": {"$in": buses}},
@@ -847,7 +901,7 @@ class PandaHub:
847
901
  variants=variants,
848
902
  dtypes=dtypes,
849
903
  )
850
-
904
+ switch_operator = "$or" if "switch" in add_edge_branches else "$and"
851
905
  self._add_element_from_collection(
852
906
  net,
853
907
  db,
@@ -857,7 +911,7 @@ class PandaHub:
857
911
  "$and": [
858
912
  {"et": "b"},
859
913
  {
860
- branch_operator: [
914
+ switch_operator: [
861
915
  {"bus": {"$in": buses}},
862
916
  {"element": {"$in": buses}},
863
917
  ]
@@ -937,11 +991,9 @@ class PandaHub:
937
991
  )
938
992
  all_elements = list(set(all_elements) - set(ignore_elements))
939
993
 
940
- # Add elements for which the user has provided a filter function
941
- for element, filter_func in additional_filters.items():
942
- if element in ignore_elements:
943
- continue
944
- element_filter = filter_func(net)
994
+ # add all node elements that are connected to buses within the network
995
+ for element in node_elements:
996
+ element_filter = {"bus": {"$in": buses}}
945
997
  self._add_element_from_collection(
946
998
  net,
947
999
  db,
@@ -954,9 +1006,11 @@ class PandaHub:
954
1006
  dtypes=dtypes,
955
1007
  )
956
1008
 
957
- # add all node elements that are connected to buses within the network
958
- for element in node_elements:
959
- element_filter = {"bus": {"$in": buses}}
1009
+ # Add elements for which the user has provided a filter function
1010
+ for element, filter_func in additional_filters.items():
1011
+ if element in ignore_elements:
1012
+ continue
1013
+ element_filter = filter_func(net)
960
1014
  self._add_element_from_collection(
961
1015
  net,
962
1016
  db,
@@ -1015,6 +1069,7 @@ class PandaHub:
1015
1069
  project_id=None,
1016
1070
  metadata=None,
1017
1071
  skip_results=False,
1072
+ net_id=None,
1018
1073
  ):
1019
1074
  if project_id:
1020
1075
  self.set_active_project_by_id(project_id)
@@ -1029,8 +1084,11 @@ class PandaHub:
1029
1084
  self.delete_net_from_db(name)
1030
1085
  else:
1031
1086
  raise PandaHubError("Network name already exists")
1032
- max_id_network = db["_networks"].find_one(sort=[("_id", -1)])
1033
- _id = 0 if max_id_network is None else max_id_network["_id"] + 1
1087
+
1088
+ if net_id is None:
1089
+ max_id_network = db["_networks"].find_one(sort=[("_id", -1)])
1090
+ net_id = 0 if max_id_network is None else max_id_network["_id"] + 1
1091
+ db["_networks"].insert_one({"_id": net_id})
1034
1092
 
1035
1093
  data = {}
1036
1094
  dtypes = {}
@@ -1045,10 +1103,12 @@ class PandaHub:
1045
1103
  dtypes[element] = get_dtypes(element_data, self._datatypes.get(element))
1046
1104
  if element_data.empty:
1047
1105
  continue
1048
- # convert pandapower dataframe object to dict and save to db
1049
- element_data = convert_element_to_dict(
1050
- element_data.copy(deep=True), _id, self._datatypes.get(element)
1051
- )
1106
+ element_data = element_data.copy(deep=True)
1107
+ if "var_type" in element_data:
1108
+ element_data["var_type"] = element_data["var_type"].fillna("base")
1109
+ else:
1110
+ element_data["var_type"] = "base"
1111
+ element_data = convert_element_to_dict(element_data, net_id, self._datatypes.get(element))
1052
1112
  self._write_element_to_db(db, element, element_data)
1053
1113
 
1054
1114
  else:
@@ -1057,17 +1117,16 @@ class PandaHub:
1057
1117
  data[element] = element_data
1058
1118
 
1059
1119
  # write network metadata
1060
- net_dict = {
1061
- "_id": _id,
1120
+ network_data = {
1062
1121
  "name": name,
1063
1122
  "sector": sector,
1064
1123
  "dtypes": dtypes,
1065
1124
  "data": data,
1066
1125
  }
1067
-
1068
1126
  if metadata is not None:
1069
- net_dict.update(metadata)
1070
- db["_networks"].insert_one(net_dict)
1127
+ network_data.update(metadata)
1128
+ db["_networks"].update_one({"_id": net_id}, {"$set": network_data})
1129
+ return network_data | {"_id": net_id}
1071
1130
 
1072
1131
  def _write_net_collections_to_db(self, db, collections):
1073
1132
  for element, element_data in collections.items():
@@ -1140,7 +1199,7 @@ class PandaHub:
1140
1199
  include_results=True,
1141
1200
  only_tables=None,
1142
1201
  geo_mode="string",
1143
- variants=[],
1202
+ variants=None,
1144
1203
  dtypes=None,
1145
1204
  ):
1146
1205
  if only_tables is not None and not element_type in only_tables:
@@ -1350,9 +1409,6 @@ class PandaHub:
1350
1409
  project_id=None,
1351
1410
  **kwargs,
1352
1411
  ):
1353
- logger.info(
1354
- f"Setting {parameter} = {value} in {element_type} with index {element_index} and variant {variant}"
1355
- )
1356
1412
  if variant is not None:
1357
1413
  variant = int(variant)
1358
1414
  if project_id:
@@ -1650,32 +1706,30 @@ class PandaHub:
1650
1706
  # Variants
1651
1707
  # -------------------------
1652
1708
 
1653
- def create_variant(self, data):
1709
+ def create_variant(self, data, index: Optional[int] = None):
1654
1710
  db = self._get_project_database()
1655
1711
  net_id = int(data["net_id"])
1656
- max_index = list(
1657
- db["variant"]
1658
- .find({"net_id": net_id}, projection={"_id": 0, "index": 1})
1659
- .sort("index", -1)
1660
- .limit(1)
1661
- )
1662
- if not max_index:
1663
- index = 1
1664
- for coll in self._get_net_collections(db):
1665
- update = {"$set": {"var_type": "base", "not_in_var": []}}
1666
- db[coll].update_many({}, update)
1667
-
1668
- else:
1669
- index = int(max_index[0]["index"]) + 1
1712
+ if index is None:
1713
+ max_index = list(
1714
+ db["variant"]
1715
+ .find({"net_id": net_id}, projection={"_id": 0, "index": 1})
1716
+ .sort("index", -1)
1717
+ .limit(1)
1718
+ )
1719
+ index = int(max_index[0]["index"]) + 1 if max_index else 1
1670
1720
 
1671
1721
  data["index"] = index
1672
-
1673
1722
  if data.get("default_name") is not None and data.get("name") is None:
1674
1723
  data["name"] = data.pop("default_name") + " " + str(index)
1675
-
1676
1724
  db["variant"].insert_one(data)
1677
1725
  del data["_id"]
1678
1726
 
1727
+ if index == 1:
1728
+ for coll in self._get_net_collections(db):
1729
+ db[coll].update_many(
1730
+ {"$or": [{"var_type": None}, {"var_type": np.nan}]},
1731
+ {"$set": {"var_type": "base", "not_in_var": []}}
1732
+ )
1679
1733
  return data
1680
1734
 
1681
1735
  def delete_variant(self, net_id, index):
@@ -1702,7 +1756,7 @@ class PandaHub:
1702
1756
  db = self._get_project_database()
1703
1757
  db["variant"].update_one({"net_id": net_id, "index": index}, {"$set": data})
1704
1758
 
1705
- def get_variant_filter(self, variants):
1759
+ def get_variant_filter(self, variants: Optional[int]) -> dict:
1706
1760
  """
1707
1761
  Creates a mongodb query filter to retrieve pandapower elements for the given variant(s).
1708
1762
 
@@ -1716,8 +1770,22 @@ class PandaHub:
1716
1770
  dict
1717
1771
  mongodb query filter for the given variant(s)
1718
1772
  """
1719
- if type(variants) is list and variants:
1720
- if len(variants) > 1:
1773
+ if isinstance(variants, list):
1774
+ warnings.warn(
1775
+ f"Passing variants as list is deprecated, use None or int instead (variants: {variants})",
1776
+ DeprecationWarning,
1777
+ stacklevel=2,
1778
+ )
1779
+ if len(variants) == 0:
1780
+ variants = None
1781
+ elif len(variants) == 1:
1782
+ variants = variants[0]
1783
+ elif len(variants) > 1:
1784
+ warnings.warn(
1785
+ f"Passing multiple variants is deprecated, use None or int instead (variants: {variants})",
1786
+ DeprecationWarning,
1787
+ stacklevel=2,
1788
+ )
1721
1789
  variants = [
1722
1790
  int(var) for var in variants
1723
1791
  ] # make sure variants are of type int
@@ -1730,8 +1798,6 @@ class PandaHub:
1730
1798
  },
1731
1799
  ]
1732
1800
  }
1733
- else:
1734
- variants = variants[0]
1735
1801
  if variants:
1736
1802
  variants = int(variants)
1737
1803
  return {
@@ -2261,7 +2327,8 @@ class PandaHub:
2261
2327
  pipeline.append({"$project": {"timeseries_data": 1}})
2262
2328
  else:
2263
2329
  if not include_metadata:
2264
- pipeline.append({"$project": {"timeseries_data": 1}})
2330
+ pipeline.append({"$project": {"timeseries_data": 1,
2331
+ "num_timestamps": 1}})
2265
2332
  data = list(db[collection_name].aggregate(pipeline))
2266
2333
  if len(data) == 0:
2267
2334
  raise PandaHubError("no documents matching the provided filter found", 404)
@@ -2270,9 +2337,9 @@ class PandaHub:
2270
2337
  else:
2271
2338
  data = data[0]
2272
2339
  if compressed_ts_data:
2273
- timeseries_data = decompress_timeseries_data(
2274
- data["timeseries_data"], ts_format
2275
- )
2340
+ timeseries_data = decompress_timeseries_data(data["timeseries_data"],
2341
+ ts_format,
2342
+ num_timestamps=data["num_timestamps"])
2276
2343
  else:
2277
2344
  if ts_format == "timestamp_value":
2278
2345
  timeseries_data = pd.Series(
@@ -3,7 +3,7 @@ from typing import Optional
3
3
 
4
4
  import numpy as np
5
5
  import pandas as pd
6
- from pandahub.api.internal import settings
6
+ from pandahub.lib.datatypes import DATATYPES
7
7
  import base64
8
8
  import hashlib
9
9
  import logging
@@ -114,7 +114,7 @@ def convert_timeseries_to_subdocuments(timeseries):
114
114
  def compress_timeseries_data(timeseries_data, ts_format):
115
115
  import blosc
116
116
  if ts_format == "timestamp_value":
117
- timeseries_data = np.array([timeseries_data.index.astype(int),
117
+ timeseries_data = np.array([timeseries_data.index.astype("int64"),
118
118
  timeseries_data.values])
119
119
  return blosc.compress(timeseries_data.tobytes(),
120
120
  shuffle=blosc.SHUFFLE,
@@ -125,11 +125,11 @@ def compress_timeseries_data(timeseries_data, ts_format):
125
125
  cname="zlib")
126
126
 
127
127
 
128
- def decompress_timeseries_data(timeseries_data, ts_format):
128
+ def decompress_timeseries_data(timeseries_data, ts_format, num_timestamps):
129
129
  import blosc
130
130
  if ts_format == "timestamp_value":
131
131
  data = np.frombuffer(blosc.decompress(timeseries_data),
132
- dtype=np.float64).reshape((35040,2),
132
+ dtype=np.float64).reshape((num_timestamps, 2),
133
133
  order="F")
134
134
  return pd.Series(data[:,1], index=pd.to_datetime(data[:,0]))
135
135
  elif ts_format == "array":
@@ -233,10 +233,8 @@ def convert_element_to_dict(element_data, net_id, default_dtypes=None):
233
233
  load_geojsons(element_data)
234
234
  return element_data.to_dict(orient="records")
235
235
 
236
- def convert_dataframes_to_dicts(net, net_id, version_, datatypes=None):
237
- if datatypes is None:
238
- datatypes = getattr(importlib.import_module(settings.DATATYPES_MODULE), "datatypes")
239
236
 
237
+ def convert_dataframes_to_dicts(net, net_id, version_, datatypes=DATATYPES):
240
238
  dataframes = {}
241
239
  other_parameters = {}
242
240
  types = {}
@@ -1,5 +1,5 @@
1
1
 
2
- datatypes = {
2
+ DATATYPES = {
3
3
  "bus": {
4
4
  "name": str,
5
5
  "vn_kv": float,
@@ -1,44 +1,58 @@
1
1
  from pymongo import DESCENDING, GEOSPHERE, IndexModel
2
2
 
3
- mongodb_indexes = {
3
+ VARIANT_INDEXES = [
4
+ IndexModel([("variant", DESCENDING)]),
5
+ IndexModel([("var_type", DESCENDING)]),
6
+ IndexModel([("not_in_var", DESCENDING)]),
7
+ ]
8
+ MONGODB_INDEXES = {
4
9
  # pandapower
5
10
  "net_bus": [
6
11
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
7
12
  IndexModel([("geo", GEOSPHERE)]),
13
+ IndexModel([("substation", DESCENDING)]),
14
+ *VARIANT_INDEXES,
8
15
  ],
9
16
  "net_line": [
10
17
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
11
18
  IndexModel([("from_bus", DESCENDING)]),
12
19
  IndexModel([("to_bus", DESCENDING)]),
13
20
  IndexModel([("geo", GEOSPHERE)]),
21
+ *VARIANT_INDEXES,
14
22
  ],
15
23
  "net_trafo":[
16
24
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
17
25
  IndexModel([("hv_bus", DESCENDING)]),
18
26
  IndexModel([("lv_bus", DESCENDING)]),
27
+ *VARIANT_INDEXES,
19
28
  ],
20
29
  "net_switch": [
21
30
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
22
31
  IndexModel([("bus", DESCENDING)]),
23
32
  IndexModel([("element", DESCENDING)]),
24
33
  IndexModel([("et", DESCENDING)]),
34
+ *VARIANT_INDEXES,
25
35
  ],
26
36
  "net_load": [
27
37
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
28
38
  IndexModel([("bus", DESCENDING)]),
39
+ *VARIANT_INDEXES,
29
40
  ],
30
41
  "net_sgen": [
31
42
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
32
43
  IndexModel([("bus", DESCENDING)]),
44
+ *VARIANT_INDEXES,
33
45
  ],
34
46
  "net_gen": [
35
47
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
36
48
  IndexModel([("bus", DESCENDING)]),
49
+ *VARIANT_INDEXES,
37
50
  ],
38
51
  "net_ext_grid": [
39
52
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
40
53
  IndexModel([("bus", DESCENDING)]),
41
54
  IndexModel([("junction", DESCENDING)]),
55
+ *VARIANT_INDEXES,
42
56
  ],
43
57
  "net_shunt": [
44
58
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
@@ -47,87 +61,105 @@ mongodb_indexes = {
47
61
  "net_xward": [
48
62
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
49
63
  IndexModel([("bus", DESCENDING)]),
64
+ *VARIANT_INDEXES,
50
65
  ],
51
66
  "net_ward": [
52
67
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
53
68
  IndexModel([("bus", DESCENDING)]),
69
+ *VARIANT_INDEXES,
54
70
  ],
55
71
  "net_motor": [
56
72
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
57
73
  IndexModel([("bus", DESCENDING)]),
74
+ *VARIANT_INDEXES,
58
75
  ],
59
76
  "net_storage": [
60
77
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
61
78
  IndexModel([("bus", DESCENDING)]),
79
+ *VARIANT_INDEXES,
62
80
  ],
63
81
 
64
82
  # pandapipes
65
83
  "net_junction": [
66
84
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
67
85
  IndexModel([("geo", GEOSPHERE)]),
86
+ *VARIANT_INDEXES,
68
87
  ],
69
88
  "net_pipe": [
70
89
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
71
90
  IndexModel([("from_junction", DESCENDING)]),
72
91
  IndexModel([("to_junction", DESCENDING)]),
73
92
  IndexModel([("geo", GEOSPHERE)]),
93
+ *VARIANT_INDEXES,
74
94
  ],
75
95
  "net_valve": [
76
96
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
77
97
  IndexModel([("from_junction", DESCENDING)]),
78
98
  IndexModel([("to_junction", DESCENDING)]),
99
+ *VARIANT_INDEXES,
79
100
  ],
80
101
  "net_sink": [
81
102
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
82
103
  IndexModel([("junction", DESCENDING)]),
104
+ *VARIANT_INDEXES,
83
105
  ],
84
106
  "net_source": [
85
107
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
86
108
  IndexModel([("junction", DESCENDING)]),
109
+ *VARIANT_INDEXES,
87
110
  ],
88
111
  "net_water_tank": [
89
112
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
90
113
  IndexModel([("junction", DESCENDING)]),
114
+ *VARIANT_INDEXES,
91
115
  ],
92
116
  "net_flow_control": [
93
117
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
94
118
  IndexModel([("from_junction", DESCENDING)]),
95
119
  IndexModel([("to_junction", DESCENDING)]),
120
+ *VARIANT_INDEXES,
96
121
  ],
97
122
  "net_press_control": [
98
123
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
99
124
  IndexModel([("from_junction", DESCENDING)]),
100
125
  IndexModel([("to_junction", DESCENDING)]),
126
+ *VARIANT_INDEXES,
101
127
  ],
102
128
  "net_compressor": [
103
129
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
104
130
  IndexModel([("from_junction", DESCENDING)]),
105
131
  IndexModel([("to_junction", DESCENDING)]),
132
+ *VARIANT_INDEXES,
106
133
  ],
107
134
  "net_pump": [
108
135
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
109
136
  IndexModel([("from_junction", DESCENDING)]),
110
137
  IndexModel([("to_junction", DESCENDING)]),
138
+ *VARIANT_INDEXES,
111
139
  ],
112
140
  "net_circ_pump_mass": [
113
141
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
114
142
  IndexModel([("return_junction", DESCENDING)]),
115
143
  IndexModel([("flow_junction", DESCENDING)]),
144
+ *VARIANT_INDEXES,
116
145
  ],
117
146
  "net_circ_pump_pressure": [
118
147
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
119
148
  IndexModel([("return_junction", DESCENDING)]),
120
149
  IndexModel([("flow_junction", DESCENDING)]),
150
+ *VARIANT_INDEXES,
121
151
  ],
122
152
  "net_heat_exchanger": [
123
153
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
124
154
  IndexModel([("from_junction", DESCENDING)]),
125
155
  IndexModel([("to_junction", DESCENDING)]),
156
+ *VARIANT_INDEXES,
126
157
  ],
127
158
  "net_heat_consumer": [
128
159
  IndexModel([("net_id", DESCENDING), ("index", DESCENDING), ("variant", DESCENDING)], unique=True),
129
160
  IndexModel([("from_junction", DESCENDING)]),
130
161
  IndexModel([("to_junction", DESCENDING)]),
162
+ *VARIANT_INDEXES,
131
163
  ],
132
164
 
133
165
  # others
@@ -147,7 +179,7 @@ mongodb_indexes = {
147
179
  IndexModel([("substation_buses", DESCENDING)]),
148
180
  IndexModel([("level", DESCENDING)]),
149
181
  IndexModel([("geo", GEOSPHERE)]),
150
- IndexModel([("variant", DESCENDING)]),
182
+ *VARIANT_INDEXES,
151
183
  ],
152
184
  "net_substation": [
153
185
  IndexModel(
@@ -158,5 +190,6 @@ mongodb_indexes = {
158
190
  IndexModel([("type", DESCENDING)]),
159
191
  IndexModel([("level", DESCENDING)]),
160
192
  IndexModel([("geo", GEOSPHERE)]),
193
+ *VARIANT_INDEXES,
161
194
  ],
162
195
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pandahub
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Data hub for pandapower and pandapipes networks based on MongoDB
5
5
  Home-page: https://github.com/e2nIEE/pandahub
6
6
  Author: Jan Ulffers, Leon Thurner, Jannis Kupka, Mike Vogt, Joschka Thurner, Alexander Scheidler
@@ -36,7 +36,7 @@ setup(
36
36
  name='pandahub',
37
37
  packages=find_packages(),
38
38
  url='https://github.com/e2nIEE/pandahub',
39
- version='0.3.2',
39
+ version='0.3.3',
40
40
  include_package_data=True,
41
41
  long_description_content_type='text/markdown',
42
42
  zip_safe=False,
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