pandahub 0.3.11__zip → 0.3.13__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 (49) hide show
  1. {pandahub-0.3.11 → pandahub-0.3.13}/CHANGELOG.md +5 -0
  2. {pandahub-0.3.11 → pandahub-0.3.13}/PKG-INFO +1 -1
  3. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/api/routers/variants.py +1 -1
  4. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/lib/PandaHub.py +50 -19
  5. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/lib/database_toolbox.py +44 -4
  6. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub.egg-info/PKG-INFO +1 -1
  7. {pandahub-0.3.11 → pandahub-0.3.13}/pyproject.toml +1 -1
  8. {pandahub-0.3.11 → pandahub-0.3.13}/CONTRIBUTING.rst +0 -0
  9. {pandahub-0.3.11 → pandahub-0.3.13}/LICENSE +0 -0
  10. {pandahub-0.3.11 → pandahub-0.3.13}/MANIFEST.in +0 -0
  11. {pandahub-0.3.11 → pandahub-0.3.13}/README.md +0 -0
  12. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/__init__.py +0 -0
  13. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/api/__init__.py +0 -0
  14. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/api/dependencies.py +0 -0
  15. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/api/internal/__init__.py +0 -0
  16. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/api/internal/db.py +0 -0
  17. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/api/internal/schemas.py +0 -0
  18. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/api/internal/settings.py +0 -0
  19. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/api/internal/toolbox.py +0 -0
  20. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/api/internal/users.py +0 -0
  21. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/api/main.py +0 -0
  22. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/api/routers/__init__.py +0 -0
  23. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/api/routers/auth.py +0 -0
  24. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/api/routers/net.py +0 -0
  25. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/api/routers/projects.py +0 -0
  26. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/api/routers/timeseries.py +0 -0
  27. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/api/routers/users.py +0 -0
  28. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/lib/__init__.py +0 -0
  29. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/lib/datatypes.py +0 -0
  30. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/lib/mongodb_indexes.py +0 -0
  31. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/lib/timeseries/__init__.py +0 -0
  32. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/lib/timeseries/data_sources/__init__.py +0 -0
  33. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/lib/timeseries/data_sources/mongo_data.py +0 -0
  34. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub/lib/timeseries/output_writer_mongodb.py +0 -0
  35. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub.egg-info/SOURCES.txt +0 -0
  36. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub.egg-info/dependency_links.txt +0 -0
  37. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub.egg-info/requires.txt +0 -0
  38. {pandahub-0.3.11 → pandahub-0.3.13}/pandahub.egg-info/top_level.txt +0 -0
  39. {pandahub-0.3.11 → pandahub-0.3.13}/requirements.txt +0 -0
  40. {pandahub-0.3.11 → pandahub-0.3.13}/setup.cfg +0 -0
  41. {pandahub-0.3.11 → pandahub-0.3.13}/setup.py +0 -0
  42. {pandahub-0.3.11 → pandahub-0.3.13}/test/__init__.py +0 -0
  43. {pandahub-0.3.11 → pandahub-0.3.13}/test/conftest.py +0 -0
  44. {pandahub-0.3.11 → pandahub-0.3.13}/test/performance_test.py +0 -0
  45. {pandahub-0.3.11 → pandahub-0.3.13}/test/test_client.py +0 -0
  46. {pandahub-0.3.11 → pandahub-0.3.13}/test/test_networks.py +0 -0
  47. {pandahub-0.3.11 → pandahub-0.3.13}/test/test_projects.py +0 -0
  48. {pandahub-0.3.11 → pandahub-0.3.13}/test/test_timeseries.py +0 -0
  49. {pandahub-0.3.11 → pandahub-0.3.13}/tutorials/config.py +0 -0
@@ -1,5 +1,10 @@
1
1
  # Change Log
2
2
 
3
+ ## [0.3.13]
4
+ - FIXED: fetching integer index on empty collection
5
+ - IMPROVED: context manager for mongo client /database / collection
6
+ - IMPROVED: project locking returns True if user already holds a lock on the project
7
+
3
8
  ## [0.3.11]
4
9
 
5
10
  - IMPROVED: add lib.database_toolbox.get_mongo_client function
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pandahub
3
- Version: 0.3.11
3
+ Version: 0.3.13
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
@@ -22,7 +22,7 @@ class GetVariantsModel(BaseModel):
22
22
  @router.post("/get_variants")
23
23
  def get_variants(data: GetVariantsModel, ph=Depends(pandahub)):
24
24
  ph.set_active_project_by_id(data.project_id)
25
- variants_collection = ph.get_project_database("variant")
25
+ variants_collection = ph.get_project_collection("variant")
26
26
 
27
27
  variants = variants_collection.find({"net_id": data.net_id}, projection={"_id": 0})
28
28
  return {variant.pop("index"): variant for variant in variants}
@@ -17,6 +17,8 @@ from functools import reduce
17
17
  from operator import getitem
18
18
  from uuid import UUID
19
19
  from pymongo import MongoClient, ReplaceOne
20
+ from pymongo.database import Database
21
+ from pymongo.collection import Collection
20
22
  from pymongo.errors import ServerSelectionTimeoutError, DuplicateKeyError
21
23
 
22
24
  import pandapipes as pps
@@ -79,6 +81,7 @@ class PandaHub:
79
81
  "read": ["owner", "developer", "guest"],
80
82
  "write": ["owner", "developer"],
81
83
  "user_management": ["owner"],
84
+ "delete_project": ["owner"],
82
85
  }
83
86
 
84
87
  # -------------------------
@@ -248,7 +251,7 @@ class PandaHub:
248
251
  if project_id:
249
252
  self.set_active_project_by_id(project_id)
250
253
  project_id = self.active_project["_id"]
251
- self.check_permission("write")
254
+ self.check_permission("delete_project")
252
255
  if not i_know_this_action_is_final:
253
256
  raise PandaHubError(
254
257
  "Calling this function will delete the whole project and all the nets stored within. It can not be reversed. Add 'i_know_this_action_is_final=True' to confirm."
@@ -334,14 +337,23 @@ class PandaHub:
334
337
  self.set_active_project(project_name, realm)
335
338
 
336
339
  def lock_project(self):
337
- db = self.mongo_client["user_management"]["projects"]
338
- result = db.update_one(
340
+ coll = self.mongo_client["user_management"]["projects"]
341
+ result = coll.find_one_and_update(
339
342
  {
340
343
  "_id": self.active_project["_id"],
344
+ "$or": [
345
+ {"locked": {"$eq": False}},
346
+ {
347
+ "$and": [
348
+ {"locked": {"$eq": True}},
349
+ {"locked_by": str(self.user_id)},
350
+ ]
351
+ },
352
+ ],
341
353
  },
342
- {"$set": {"locked": True, "locked_by": self.user_id}},
354
+ {"$set": {"locked": True, "locked_by": str(self.user_id)}},
343
355
  )
344
- return result.acknowledged and result.modified_count > 0
356
+ return result is not None
345
357
 
346
358
 
347
359
  # def lock_project(self):
@@ -408,26 +420,42 @@ class PandaHub:
408
420
  else:
409
421
  return project_doc
410
422
 
411
- def _get_project_database(self) -> MongoClient:
423
+ def _get_project_database(self) -> Database:
412
424
  return self.get_project_database()
413
425
 
414
- def get_project_database(self, collection: Optional[str] = None) -> MongoClient:
426
+ def get_project_database(self, collection: Optional[str] = None) -> Database:
427
+ """
428
+ Get the database for the current active project.
429
+
430
+ Parameters
431
+ ----------
432
+ collection or None
433
+ Name of document collection (DEPRECATED)
434
+
435
+ Returns
436
+ -------
437
+ pymongo.database.Database
438
+ """
439
+ if collection is None:
440
+ return self.mongo_client[str(self.active_project["_id"])]
441
+ logger.warning(f"Passing a collection to get_project_database is deprecated. Use get_project_collection instead!")
442
+ return self.get_project_collection(collection)
443
+
444
+ def get_project_collection(self, collection_name: str) -> Collection:
415
445
  """
416
446
  Get a MongoClient instance connected to the database for the current active project, optionally set to the given collection.
417
447
 
418
448
  Parameters
419
449
  ----------
420
- collection
450
+ collection_name
421
451
  Name of document collection
422
452
 
423
453
  Returns
424
454
  -------
425
- MongoClient
455
+ pymongo.collection.Collection
426
456
  """
427
- project_db = self.mongo_client[str(self.active_project["_id"])]
428
- if collection is None:
429
- return project_db
430
- return project_db[collection]
457
+ return self.get_project_database()[collection_name]
458
+
431
459
 
432
460
  def _get_global_database(self) -> MongoClient:
433
461
  if (
@@ -460,7 +488,7 @@ class PandaHub:
460
488
  """
461
489
  if not self.active_project:
462
490
  raise PandaHubError("No project activated!")
463
- return self.get_project_database("_networks").find({}, {"_id:": 1}).distinct("_id")
491
+ return self.get_project_collection("_networks").find({}, {"_id:": 1}).distinct("_id")
464
492
 
465
493
  def get_project_version(self):
466
494
  return self.active_project.get("version", "0.2.2")
@@ -1750,14 +1778,14 @@ class PandaHub:
1750
1778
  The value of the created index.
1751
1779
  """
1752
1780
  query_filter = query_filter if query_filter is not None else {}
1753
- coll = self.get_project_database(collection)
1781
+ coll = self.get_project_collection(collection)
1754
1782
  for retry in range(retries):
1755
1783
  try:
1756
- max_index = next(coll.find(query_filter, projection={index_field: 1})
1784
+ max_index_doc = next(coll.find(query_filter, projection={index_field: 1})
1757
1785
  .sort(index_field, -1)
1758
1786
  .limit(1),
1759
1787
  None)
1760
- index = 0 if max_index is None else int(max_index[0][index_field]) + 1
1788
+ index = 0 if max_index_doc is None else int(max_index_doc[index_field]) + 1
1761
1789
  coll.insert_one({index_field: index})
1762
1790
  break
1763
1791
  except DuplicateKeyError:
@@ -2674,7 +2702,10 @@ class PandaHub:
2674
2702
  }
2675
2703
  pipeline.append({"$project": projection})
2676
2704
  if not include_metadata:
2677
- pipeline.append({"$project": {"timeseries_data": 1}})
2705
+ if compressed_ts_data:
2706
+ pipeline.append({"$project": {"timeseries_data": 1, "num_timestamps": 1}})
2707
+ else:
2708
+ pipeline.append({"$project": {"timeseries_data": 1}})
2678
2709
 
2679
2710
  timeseries = []
2680
2711
  for ts in db[collection_name].aggregate(pipeline):
@@ -2682,7 +2713,7 @@ class PandaHub:
2682
2713
  continue
2683
2714
  data = ts["timeseries_data"]
2684
2715
  if compressed_ts_data:
2685
- timeseries_data = decompress_timeseries_data(data, ts_format)
2716
+ timeseries_data = decompress_timeseries_data(data, ts_format, ts["num_timestamps"])
2686
2717
  ts["timeseries_data"] = timeseries_data
2687
2718
  else:
2688
2719
  if ts_format == "timestamp_value":
@@ -1,9 +1,12 @@
1
1
  # -*- coding: utf-8 -*-
2
- from typing import Optional
2
+ from contextlib import contextmanager
3
+ from collections.abc import Iterator
3
4
 
4
5
  import numpy as np
5
6
  import pandas as pd
6
7
  from pymongo import MongoClient
8
+ from pymongo.database import Database
9
+ from pymongo.collection import Collection
7
10
 
8
11
  from pandahub.api.internal.settings import MONGODB_URL, MONGODB_USER, MONGODB_PASSWORD
9
12
  from pandahub.lib.datatypes import DATATYPES
@@ -188,7 +191,7 @@ def create_timeseries_document(timeseries,
188
191
  document = add_timestamp_info_to_document(document, timeseries, ts_format)
189
192
  document = {**document, **kwargs}
190
193
 
191
- if not "_id" in document: # IDs set by users will not be overwritten
194
+ if "_id" not in document: # IDs set by users will not be overwritten
192
195
  document["_id"] = get_document_hash(document)
193
196
 
194
197
  if compress_ts_data:
@@ -425,8 +428,8 @@ def migrate_userdb_to_beanie(ph):
425
428
  {'$out': 'users'}]
426
429
  userdb.aggregate(migration)
427
430
 
428
- def get_mongo_client(connection_url=MONGODB_URL, connection_user=MONGODB_USER,
429
- connection_password=MONGODB_PASSWORD) -> MongoClient:
431
+ def get_mongo_client(connection_url: str = MONGODB_URL, connection_user: str = MONGODB_USER,
432
+ connection_password: str = MONGODB_PASSWORD) -> MongoClient:
430
433
  mongo_client_args = {
431
434
  "host": connection_url,
432
435
  "uuidRepresentation": "standard",
@@ -438,3 +441,40 @@ def get_mongo_client(connection_url=MONGODB_URL, connection_user=MONGODB_USER,
438
441
  "password": connection_password,
439
442
  }
440
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
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pandahub
3
- Version: 0.3.11
3
+ Version: 0.3.13
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pandahub"
7
- version = "0.3.11" # File format version '__format_version__' is tracked in __init__.py
7
+ version = "0.3.13" # 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" },
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