breadcrumb_utils.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. from __future__ import annotations
  2. from sqlalchemy import select
  3. from flexmeasures import Sensor, Asset, Account
  4. from flexmeasures.utils.flexmeasures_inflection import human_sorted
  5. from flask import url_for, current_app
  6. def get_breadcrumb_info(
  7. entity: Sensor | Asset | Account | None, current_page: str = None
  8. ) -> dict:
  9. breadcrumb = {
  10. "ancestors": get_ancestry(entity),
  11. "siblings": get_siblings(entity),
  12. }
  13. if entity is not None and isinstance(entity, Asset):
  14. # Add additional Views CTAs for Assets
  15. try:
  16. breadcrumb["current_asset_view"] = current_page
  17. breadcrumb["views"] = [
  18. {
  19. "url": url_for("AssetCrudUI:context", id=entity.id),
  20. "name": "Context",
  21. "type": "Asset",
  22. },
  23. {
  24. "url": url_for("AssetCrudUI:graphs", id=entity.id),
  25. "name": "Graphs",
  26. "type": "Asset",
  27. },
  28. {
  29. "url": url_for("AssetCrudUI:properties", id=entity.id),
  30. "name": "Properties",
  31. "type": "Asset",
  32. },
  33. {
  34. "url": url_for("AssetCrudUI:auditlog", id=entity.id),
  35. "name": "Audit Log",
  36. "type": "Asset",
  37. },
  38. {
  39. "url": url_for("AssetCrudUI:status", id=entity.id),
  40. "name": "Status",
  41. "type": "Asset",
  42. },
  43. ]
  44. except Exception as e:
  45. print("Error in updating breadcrumb variables:", e)
  46. return breadcrumb
  47. def get_ancestry(entity: Sensor | Asset | Account | None) -> list[dict]:
  48. """
  49. Return a list of ancestors meta data, with URLs for their pages, their name and type.
  50. This function calls itself recursively to go up the ancestral tree, up to the account.
  51. This function also allows customization for assets and sensors (for this, set "breadcrumb_ancestry" attribute).
  52. """
  53. custom_ancestry = None
  54. if entity is not None and not isinstance(entity, Account):
  55. custom_ancestry = entity.get_attribute("breadcrumb_ancestry")
  56. if custom_ancestry is not None and isinstance(custom_ancestry, list):
  57. # Append current Asset to the custom ancestry breadcrumb
  58. if entity is not None and isinstance(entity, Asset):
  59. current_entity_info = {
  60. "url": url_for("AssetCrudUI:get", id=entity.id),
  61. "name": entity.name,
  62. "type": "Asset",
  63. }
  64. custom_ancestry.append(current_entity_info)
  65. return custom_ancestry
  66. # Public account
  67. if entity is None:
  68. return [{"url": None, "name": "PUBLIC", "type": "Account"}]
  69. # account
  70. if isinstance(entity, Account):
  71. return [
  72. {
  73. "url": url_for("AccountCrudUI:get", account_id=entity.id),
  74. "name": entity.name,
  75. "type": "Account",
  76. }
  77. ]
  78. # sensor
  79. if isinstance(entity, Sensor):
  80. current_entity_info = [
  81. {
  82. "url": url_for("SensorUI:get", id=entity.id),
  83. "name": entity.name,
  84. "type": "Sensor",
  85. }
  86. ]
  87. return get_ancestry(entity.generic_asset) + current_entity_info
  88. # asset
  89. if isinstance(entity, Asset):
  90. current_entity_info = [
  91. {
  92. "url": url_for("AssetCrudUI:get", id=entity.id),
  93. "name": entity.name,
  94. "type": "Asset",
  95. }
  96. ]
  97. # asset without parent
  98. if entity.parent_asset is None:
  99. return get_ancestry(entity.owner) + current_entity_info
  100. else: # asset with parent
  101. return get_ancestry(entity.parent_asset) + current_entity_info
  102. return []
  103. def get_siblings(entity: Sensor | Asset | Account | None) -> list[dict]:
  104. """
  105. Return a list of siblings meta data, with URLs for their pages, name and type.
  106. This function also allows customization (for this, set "breadcrumb_siblings" attribute).
  107. """
  108. custom_siblings = None
  109. if entity is not None and not isinstance(entity, Account):
  110. custom_siblings = entity.get_attribute("breadcrumb_siblings")
  111. if custom_siblings is not None and isinstance(custom_siblings, list):
  112. return custom_siblings
  113. siblings = []
  114. if isinstance(entity, Sensor):
  115. siblings = [
  116. {
  117. "url": url_for("SensorUI:get", id=sensor.id),
  118. "name": sensor.name,
  119. "type": "Sensor",
  120. }
  121. for sensor in entity.generic_asset.sensors
  122. ]
  123. if isinstance(entity, Asset):
  124. if entity.parent_asset is not None:
  125. sibling_assets = entity.parent_asset.child_assets
  126. elif entity.owner is not None:
  127. session = current_app.db.session
  128. sibling_assets = session.scalars(
  129. select(Asset).filter(
  130. Asset.parent_asset_id.is_(None), Asset.owner == entity.owner
  131. )
  132. ).all()
  133. else:
  134. session = current_app.db.session
  135. sibling_assets = session.scalars(
  136. select(Asset).filter(
  137. Asset.account_id.is_(None), Asset.parent_asset_id.is_(None)
  138. )
  139. ).all()
  140. siblings = [
  141. {
  142. "url": url_for("AssetCrudUI:get", id=asset.id),
  143. "name": asset.name,
  144. "type": "Asset",
  145. }
  146. for asset in sibling_assets
  147. ]
  148. siblings = human_sorted(siblings, attr="name")
  149. return siblings