pandahub 0.3.12__zip → 0.3.14__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.12 → pandahub-0.3.14}/CHANGELOG.md +5 -0
  2. {pandahub-0.3.12 → pandahub-0.3.14}/PKG-INFO +1 -1
  3. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/api/routers/variants.py +1 -1
  4. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/lib/PandaHub.py +116 -47
  5. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/lib/database_toolbox.py +44 -4
  6. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub.egg-info/PKG-INFO +1 -1
  7. {pandahub-0.3.12 → pandahub-0.3.14}/pyproject.toml +1 -1
  8. {pandahub-0.3.12 → pandahub-0.3.14}/CONTRIBUTING.rst +0 -0
  9. {pandahub-0.3.12 → pandahub-0.3.14}/LICENSE +0 -0
  10. {pandahub-0.3.12 → pandahub-0.3.14}/MANIFEST.in +0 -0
  11. {pandahub-0.3.12 → pandahub-0.3.14}/README.md +0 -0
  12. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/__init__.py +0 -0
  13. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/api/__init__.py +0 -0
  14. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/api/dependencies.py +0 -0
  15. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/api/internal/__init__.py +0 -0
  16. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/api/internal/db.py +0 -0
  17. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/api/internal/schemas.py +0 -0
  18. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/api/internal/settings.py +0 -0
  19. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/api/internal/toolbox.py +0 -0
  20. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/api/internal/users.py +0 -0
  21. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/api/main.py +0 -0
  22. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/api/routers/__init__.py +0 -0
  23. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/api/routers/auth.py +0 -0
  24. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/api/routers/net.py +0 -0
  25. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/api/routers/projects.py +0 -0
  26. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/api/routers/timeseries.py +0 -0
  27. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/api/routers/users.py +0 -0
  28. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/lib/__init__.py +0 -0
  29. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/lib/datatypes.py +0 -0
  30. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/lib/mongodb_indexes.py +0 -0
  31. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/lib/timeseries/__init__.py +0 -0
  32. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/lib/timeseries/data_sources/__init__.py +0 -0
  33. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/lib/timeseries/data_sources/mongo_data.py +0 -0
  34. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub/lib/timeseries/output_writer_mongodb.py +0 -0
  35. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub.egg-info/SOURCES.txt +0 -0
  36. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub.egg-info/dependency_links.txt +0 -0
  37. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub.egg-info/requires.txt +0 -0
  38. {pandahub-0.3.12 → pandahub-0.3.14}/pandahub.egg-info/top_level.txt +0 -0
  39. {pandahub-0.3.12 → pandahub-0.3.14}/requirements.txt +0 -0
  40. {pandahub-0.3.12 → pandahub-0.3.14}/setup.cfg +0 -0
  41. {pandahub-0.3.12 → pandahub-0.3.14}/setup.py +0 -0
  42. {pandahub-0.3.12 → pandahub-0.3.14}/test/__init__.py +0 -0
  43. {pandahub-0.3.12 → pandahub-0.3.14}/test/conftest.py +0 -0
  44. {pandahub-0.3.12 → pandahub-0.3.14}/test/performance_test.py +0 -0
  45. {pandahub-0.3.12 → pandahub-0.3.14}/test/test_client.py +0 -0
  46. {pandahub-0.3.12 → pandahub-0.3.14}/test/test_networks.py +0 -0
  47. {pandahub-0.3.12 → pandahub-0.3.14}/test/test_projects.py +0 -0
  48. {pandahub-0.3.12 → pandahub-0.3.14}/test/test_timeseries.py +0 -0
  49. {pandahub-0.3.12 → pandahub-0.3.14}/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.12
3
+ Version: 0.3.14
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
@@ -335,14 +337,23 @@ class PandaHub:
335
337
  self.set_active_project(project_name, realm)
336
338
 
337
339
  def lock_project(self):
338
- db = self.mongo_client["user_management"]["projects"]
339
- result = db.update_one(
340
+ coll = self.mongo_client["user_management"]["projects"]
341
+ result = coll.find_one_and_update(
340
342
  {
341
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
+ ],
342
353
  },
343
- {"$set": {"locked": True, "locked_by": self.user_id}},
354
+ {"$set": {"locked": True, "locked_by": str(self.user_id)}},
344
355
  )
345
- return result.acknowledged and result.modified_count > 0
356
+ return result is not None
346
357
 
347
358
 
348
359
  # def lock_project(self):
@@ -409,26 +420,42 @@ class PandaHub:
409
420
  else:
410
421
  return project_doc
411
422
 
412
- def _get_project_database(self) -> MongoClient:
423
+ def _get_project_database(self) -> Database:
413
424
  return self.get_project_database()
414
425
 
415
- 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:
416
445
  """
417
446
  Get a MongoClient instance connected to the database for the current active project, optionally set to the given collection.
418
447
 
419
448
  Parameters
420
449
  ----------
421
- collection
450
+ collection_name
422
451
  Name of document collection
423
452
 
424
453
  Returns
425
454
  -------
426
- MongoClient
455
+ pymongo.collection.Collection
427
456
  """
428
- project_db = self.mongo_client[str(self.active_project["_id"])]
429
- if collection is None:
430
- return project_db
431
- return project_db[collection]
457
+ return self.get_project_database()[collection_name]
458
+
432
459
 
433
460
  def _get_global_database(self) -> MongoClient:
434
461
  if (
@@ -461,7 +488,7 @@ class PandaHub:
461
488
  """
462
489
  if not self.active_project:
463
490
  raise PandaHubError("No project activated!")
464
- return self.get_project_database("_networks").find({}, {"_id:": 1}).distinct("_id")
491
+ return self.get_project_collection("_networks").find({}, {"_id:": 1}).distinct("_id")
465
492
 
466
493
  def get_project_version(self):
467
494
  return self.active_project.get("version", "0.2.2")
@@ -1647,21 +1674,22 @@ class PandaHub:
1647
1674
  var_data = {"var_type": "base", "not_in_var": [], "variant": None}
1648
1675
  else:
1649
1676
  var_data = {"var_type": "addition", "not_in_var": [], "variant": variant}
1650
-
1677
+ net_doc = db["_networks"].find_one({"_id": net_id})
1651
1678
  data = []
1652
1679
  for elm_data in elements_data:
1653
- self._add_missing_defaults(db, net_id, element_type, elm_data)
1680
+ self._add_missing_defaults(element_type, elm_data, net_doc)
1654
1681
  self._ensure_dtypes(element_type, elm_data)
1655
1682
  data.append({**elm_data, **var_data, "net_id": net_id})
1656
1683
  collection = self._collection_name_of_element(element_type)
1657
1684
  db[collection].insert_many(data, ordered=False)
1658
1685
  return data
1659
1686
 
1660
- def _add_missing_defaults(self, db, net_id, element_type, element_data):
1687
+ def _add_missing_defaults(self, element_type, element_data, net_doc):
1661
1688
  func_str = f"create_{element_type}"
1662
- if not hasattr(pp, func_str):
1689
+ package = pp if net_doc['sector'] == 'power' else pps
1690
+ if not hasattr(package, func_str):
1663
1691
  return
1664
- create_func = getattr(pp, func_str)
1692
+ create_func = getattr(package, func_str)
1665
1693
  sig = signature(create_func)
1666
1694
  params = sig.parameters
1667
1695
 
@@ -1677,7 +1705,6 @@ class PandaHub:
1677
1705
  if element_type in ["line", "trafo", "trafo3w"]:
1678
1706
  # add standard type values
1679
1707
  std_type = element_data["std_type"]
1680
- net_doc = db["_networks"].find_one({"_id": net_id})
1681
1708
  if net_doc is not None:
1682
1709
  # std_types = json.loads(net_doc["data"]["std_types"], cls=io_pp.PPJSONDecoder)[element_type]
1683
1710
  std_types = net_doc["data"]["std_types"]
@@ -1688,6 +1715,9 @@ class PandaHub:
1688
1715
  if element_type == "line":
1689
1716
  if "g_us_per_km" not in element_data:
1690
1717
  element_data["g_us_per_km"] = 0
1718
+ if element_type in ['sink', 'source']:
1719
+ if not 'mdot_kg_per_s' in element_data:
1720
+ element_data["mdot_kg_per_s"] = None
1691
1721
 
1692
1722
  def _ensure_dtypes(self, element_type, data):
1693
1723
  dtypes = self._datatypes.get(element_type)
@@ -1751,14 +1781,14 @@ class PandaHub:
1751
1781
  The value of the created index.
1752
1782
  """
1753
1783
  query_filter = query_filter if query_filter is not None else {}
1754
- coll = self.get_project_database(collection)
1784
+ coll = self.get_project_collection(collection)
1755
1785
  for retry in range(retries):
1756
1786
  try:
1757
- max_index = next(coll.find(query_filter, projection={index_field: 1})
1787
+ max_index_doc = next(coll.find(query_filter, projection={index_field: 1})
1758
1788
  .sort(index_field, -1)
1759
1789
  .limit(1),
1760
1790
  None)
1761
- index = 0 if max_index is None else int(max_index[0][index_field]) + 1
1791
+ index = 0 if max_index_doc is None else int(max_index_doc[index_field]) + 1
1762
1792
  coll.insert_one({index_field: index})
1763
1793
  break
1764
1794
  except DuplicateKeyError:
@@ -2026,6 +2056,17 @@ class PandaHub:
2026
2056
  metadata = kwargs
2027
2057
  if data_type is not None:
2028
2058
  metadata["data_type"] = data_type
2059
+ net_id = metadata.get('net_id', None)
2060
+ if net_id is None:
2061
+ net_ids = db["_networks"].distinct("_id")
2062
+ if len(net_ids) == 1:
2063
+ net_id = net_ids[0]
2064
+ else:
2065
+ raise ValueError(
2066
+ "No net_id was provided and multiple networks exist in the database. "
2067
+ "Please provide a net_id."
2068
+ )
2069
+ metadata["_id"] = f"{net_id}_{metadata.get('element_type', None)}_{metadata.get('element_index', None)}_{data_type}"
2029
2070
  if isinstance(timeseries, pd.Series):
2030
2071
  documents = [
2031
2072
  {"metadata": metadata, "timestamp": idx, "value": value}
@@ -2036,7 +2077,10 @@ class PandaHub:
2036
2077
  {"metadata": metadata, "timestamp": idx, **row.to_dict()}
2037
2078
  for idx, row in timeseries.iterrows()
2038
2079
  ]
2039
- return db[collection_name].insert_many(documents)
2080
+ db[collection_name].insert_many(documents)
2081
+ if kwargs.get("return_id"):
2082
+ return metadata["_id"]
2083
+ return None
2040
2084
  document = create_timeseries_document(
2041
2085
  timeseries=timeseries,
2042
2086
  data_type=data_type,
@@ -2308,12 +2352,17 @@ class PandaHub:
2308
2352
  pipeline.append({"$match": meta_filter})
2309
2353
  pipeline.append({"$project": {"_id": 0, "metadata": 0}})
2310
2354
  timeseries = db[collection_name].aggregate_pandas_all(pipeline)
2355
+ if len(timeseries) == 0:
2356
+ raise PandaHubError("no documents matching the provided filter found", 404)
2311
2357
  timeseries.set_index("timestamp", inplace=True)
2312
2358
  if include_metadata:
2313
2359
  raise NotImplementedError(
2314
2360
  "Not implemented yet for timeseries collections"
2315
2361
  )
2316
- return timeseries
2362
+ if len(timeseries.columns) == 1:
2363
+ return timeseries[timeseries.columns[0]]
2364
+ else:
2365
+ return timeseries
2317
2366
  filter_document = {**filter_document, **kwargs}
2318
2367
  pipeline = [{"$match": filter_document}]
2319
2368
  if not compressed_ts_data:
@@ -2400,6 +2449,7 @@ class PandaHub:
2400
2449
  collection_name="timeseries",
2401
2450
  global_database=False,
2402
2451
  project_id=None,
2452
+ timestamp_range=None,
2403
2453
  ):
2404
2454
  """
2405
2455
  Returns a DataFrame, containing all metadata matching the provided filter.
@@ -2440,10 +2490,17 @@ class PandaHub:
2440
2490
  pipeline.append({"$match": document_filter})
2441
2491
  else:
2442
2492
  document_filter = {}
2493
+ if timestamp_range is not None:
2494
+ document_filter["timestamp"] = {
2495
+ "$gte": timestamp_range[0],
2496
+ "$lt": timestamp_range[1],
2497
+ }
2443
2498
  document = db[collection_name].find_one(
2444
2499
  document_filter, projection={"timestamp": 0, "_id": 0}
2445
2500
  )
2446
- value_fields = ["$%s" % field for field in document.keys()]
2501
+ if document is None:
2502
+ return pd.DataFrame()
2503
+ value_fields = ["$%s" % field for field in document.keys() if field != "metadata"]
2447
2504
  group_dict = {
2448
2505
  "_id": "$metadata._id",
2449
2506
  "max_value": {"$max": {"$max": value_fields}},
@@ -2568,27 +2625,30 @@ class PandaHub:
2568
2625
  document_filter,
2569
2626
  projection={"timestamp": 0, "metadata": 0, "_id": 0},
2570
2627
  )
2571
- meta_pipeline = []
2572
- meta_pipeline.append({"$match": document_filter})
2573
- value_fields = ["$%s" % field for field in document.keys()]
2574
- group_dict = {
2575
- "_id": "$metadata._id",
2576
- "max_value": {"$max": {"$max": value_fields}},
2577
- "min_value": {"$min": {"$min": value_fields}},
2578
- "first_timestamp": {"$min": "$timestamp"},
2579
- "last_timestamp": {"$max": "$timestamp"},
2580
- }
2581
- document = db[collection_name].find_one(document_filter)
2582
- metadata_fields = {
2583
- metadata_field: {"$first": "$metadata.%s" % metadata_field}
2584
- for metadata_field in document["metadata"].keys()
2585
- if metadata_field != "_id"
2586
- }
2587
- group_dict.update(metadata_fields)
2588
- meta_pipeline.append({"$group": group_dict})
2589
- meta_data = {
2590
- d["_id"]: d for d in db[collection_name].aggregate(meta_pipeline)
2591
- }
2628
+ if document is None:
2629
+ meta_data = {}
2630
+ else:
2631
+ meta_pipeline = []
2632
+ meta_pipeline.append({"$match": document_filter})
2633
+ value_fields = ["$%s" % field for field in document.keys()]
2634
+ group_dict = {
2635
+ "_id": "$metadata._id",
2636
+ "max_value": {"$max": {"$max": value_fields}},
2637
+ "min_value": {"$min": {"$min": value_fields}},
2638
+ "first_timestamp": {"$min": "$timestamp"},
2639
+ "last_timestamp": {"$max": "$timestamp"},
2640
+ }
2641
+ document = db[collection_name].find_one(document_filter)
2642
+ metadata_fields = {
2643
+ metadata_field: {"$first": "$metadata.%s" % metadata_field}
2644
+ for metadata_field in document["metadata"].keys()
2645
+ if metadata_field != "_id"
2646
+ }
2647
+ group_dict.update(metadata_fields)
2648
+ meta_pipeline.append({"$group": group_dict})
2649
+ meta_data = {
2650
+ d["_id"]: d for d in db[collection_name].aggregate(meta_pipeline)
2651
+ }
2592
2652
  timeseries = []
2593
2653
  ts_all = db[collection_name].aggregate_pandas_all(pipeline)
2594
2654
  if len(ts_all) == 0:
@@ -2934,7 +2994,16 @@ class PandaHub:
2934
2994
  """
2935
2995
  self.check_permission("write")
2936
2996
  db = self._get_project_database()
2937
-
2997
+ if self.collection_is_timeseries(collection_name):
2998
+ meta_filter = {
2999
+ "metadata.element_type": element_type,
3000
+ "metadata.data_type": data_type,
3001
+ }
3002
+ if netname is not None:
3003
+ meta_filter["metadata.netname"] = netname
3004
+ if element_index is not None:
3005
+ meta_filter["metadata.element_index"] = element_index
3006
+ return db[collection_name].delete_many(meta_filter)
2938
3007
  filter_document = {"element_type": element_type, "data_type": data_type}
2939
3008
  if netname is not None:
2940
3009
  filter_document["netname"] = netname
@@ -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.12
3
+ Version: 0.3.14
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.12" # File format version '__format_version__' is tracked in __init__.py
7
+ version = "0.3.14" # 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