streamlit-octostar-utils 0.5.0.dev5__tar.gz → 0.5.0.dev7__tar.gz

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 (45) hide show
  1. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/PKG-INFO +1 -1
  2. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/pyproject.toml +1 -1
  3. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/api_crafter/nifi.py +105 -46
  4. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/LICENSE +0 -0
  5. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/README.md +0 -0
  6. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/__init__.py +0 -0
  7. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/api_crafter/__init__.py +0 -0
  8. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/api_crafter/celery.py +0 -0
  9. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/api_crafter/contents.py +0 -0
  10. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/api_crafter/fastapi.py +0 -0
  11. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/api_crafter/parallelism.py +0 -0
  12. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/api_crafter/parser/__init__.py +0 -0
  13. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/api_crafter/parser/combine_fields.py +0 -0
  14. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/api_crafter/parser/entities_parser.py +0 -0
  15. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/api_crafter/parser/generics.py +0 -0
  16. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/api_crafter/parser/info.py +0 -0
  17. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/api_crafter/parser/linkchart_functions.py +0 -0
  18. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/api_crafter/parser/matches.py +0 -0
  19. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/api_crafter/parser/parameters.py +0 -0
  20. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/api_crafter/parser/rules.py +0 -0
  21. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/api_crafter/parser/signals.py +0 -0
  22. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/core/__init__.py +0 -0
  23. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/core/dict.py +0 -0
  24. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/core/filetypes.py +0 -0
  25. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/core/threading/__init__.py +0 -0
  26. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/core/threading/key_queue.py +0 -0
  27. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/core/timestamp.py +0 -0
  28. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/nlp/__init__.py +0 -0
  29. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/nlp/custom_recognizers.py +0 -0
  30. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/nlp/language.py +0 -0
  31. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/nlp/ner.py +0 -0
  32. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/octostar/__init__.py +0 -0
  33. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/octostar/client.py +0 -0
  34. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/octostar/context.py +0 -0
  35. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/octostar/permissions.py +0 -0
  36. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/ontology/__init__.py +0 -0
  37. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/ontology/inheritance.py +0 -0
  38. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/ontology/relationships.py +0 -0
  39. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/ontology/validation.py +0 -0
  40. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/style/__init__.py +0 -0
  41. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/style/common.py +0 -0
  42. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/threading/__init__.py +0 -0
  43. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/threading/async_task_manager.py +0 -0
  44. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/threading/session_callback_manager.py +0 -0
  45. {streamlit_octostar_utils-0.5.0.dev5 → streamlit_octostar_utils-0.5.0.dev7}/streamlit_octostar_utils/threading/session_state_hot_swapper.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: streamlit-octostar-utils
3
- Version: 0.5.0.dev5
3
+ Version: 0.5.0.dev7
4
4
  Summary:
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -5,7 +5,7 @@ include = '\.pyi?$'
5
5
 
6
6
  [tool.poetry]
7
7
  name = "streamlit-octostar-utils"
8
- version = "0.5.0-dev.5"
8
+ version = "0.5.0-dev.7"
9
9
  description = ""
10
10
  license = "MIT"
11
11
  authors = ["Octostar"]
@@ -45,6 +45,7 @@ TAG_RELATIONSHIP = RelationshipName(name="has_tag", type="mtm")
45
45
  FRAGMENT_RELATIONSHIP = RelationshipName(name="is_fragment_of", type="otm")
46
46
  PREVIOUS_FRAGMENT_RELATIONSHIP = RelationshipName(name="has_previous_fragment", type="otm")
47
47
  NEXT_FRAGMENT_RELATIONSHIP = RelationshipName(name="has_next_fragment", type="otm")
48
+ SOURCE_FRAGMENT_ENTITY_RELATIONSHIP = RelationshipName(name="is_child_fragment_of", type="otm")
48
49
  OS_RESERVED_FIELDS = [
49
50
  "os_entity_uid", "entity_id", "entity_type", "os_concept",
50
51
  "entity_label", "os_created_at", "os_created_by",
@@ -212,6 +213,7 @@ class NifiFragmenter(object):
212
213
  raise ValueError("Cannot have more than 100k entities for fragmentation")
213
214
  identifier = str(uuid.uuid4())
214
215
  root_uid = fragments[0].record["os_entity_uid"]
216
+ root_type = fragments[0].record["os_concept"]
215
217
  for i, entity in enumerate(fragments):
216
218
  travel_dict(entity.request["nifi_attributes"], fragmenter_keylist.split("."), "w")(
217
219
  {"identifier": identifier, "count": count, "index": i}
@@ -225,7 +227,7 @@ class NifiFragmenter(object):
225
227
  "fragments_stack"
226
228
  ]
227
229
  travel_dict(entity.request["config"]["fragment"], fragmenter_keylist.split("."), "w")(
228
- {"identifier": identifier, "count": count, "index": i, "root_uid": root_uid}
230
+ {"identifier": identifier, "count": count, "index": i, "root_uid": root_uid, "root_type": root_type}
229
231
  )
230
232
 
231
233
  def push_defragment_strategy(fragment, defragmenter_config):
@@ -424,30 +426,34 @@ class NifiFragmenter(object):
424
426
  )
425
427
 
426
428
  @staticmethod
427
- def resolve_source_entity_uid(entity, fragment_root_source=None) -> str:
428
- """Resolve the source_entity_uid to use for child fragments.
429
+ def resolve_source_entity(entity, fragment_root_source=None) -> tuple:
430
+ """Resolve the source entity (recursive originator) for child fragments.
429
431
 
430
432
  When fragment_root_source is set (a fragment name or stack index),
431
- the UID is looked up via entity.get_fragment_root_uid() -- this is
432
- necessary when the current entity is a clone that may not be persisted.
433
- Otherwise falls back to the entity's own UID.
433
+ the root is looked up via entity.get_fragment_root(). When it is
434
+ None and the entity is already fragmented, the oldest fragmentation
435
+ level's root is used (the original non-fragment ancestor). Otherwise
436
+ falls back to the entity itself.
434
437
 
435
438
  Args:
436
439
  entity: A NifiEntity or NifiEntityProxy.
437
- fragment_root_source: None to use the entity's own UID, an int
440
+ fragment_root_source: None for automatic resolution, an int
438
441
  to index into the fragments stack, or a string fragmenter
439
442
  keylist name.
440
443
 
441
444
  Returns:
442
- The resolved source entity UID string.
445
+ (source_entity_uid, source_entity_type) tuple.
443
446
  """
444
- if fragment_root_source is None:
445
- return entity.record["os_entity_uid"]
446
- try:
447
- idx = int(fragment_root_source)
448
- return entity.get_fragment_root_uid(idx)
449
- except (ValueError, TypeError):
450
- return entity.get_fragment_root_uid(fragment_root_source)
447
+ if fragment_root_source is not None:
448
+ try:
449
+ idx = int(fragment_root_source)
450
+ return entity.get_fragment_root(idx)
451
+ except (ValueError, TypeError):
452
+ return entity.get_fragment_root(fragment_root_source)
453
+ stack = entity.request.get("config", {}).get("fragment", {}).get("fragments_stack", [])
454
+ if stack:
455
+ return entity.get_fragment_root(-1)
456
+ return (entity.record["os_entity_uid"], entity.record["os_concept"])
451
457
 
452
458
 
453
459
  class NifiEntityBatch(object):
@@ -519,7 +525,7 @@ class NifiContextManager(object):
519
525
  )
520
526
  entities = sorted(
521
527
  entities,
522
- key=lambda x: string_to_datetime(x.record.get("os_last_updated_at")),
528
+ key=lambda x: string_to_datetime(x.request.get("entity_timestamp")),
523
529
  )
524
530
  entities = list({e.record["entity_id"]: e for e in entities}.values())
525
531
  entities = [
@@ -592,7 +598,7 @@ class NifiContextManager(object):
592
598
  all_entities.extend(_process_entity(entity, processor_name))
593
599
  all_entities = sorted(
594
600
  all_entities,
595
- key=lambda x: string_to_datetime(x.record.get("os_last_updated_at")),
601
+ key=lambda x: string_to_datetime(x.request.get("entity_timestamp")),
596
602
  )
597
603
  self.out_entities = list({e.record["entity_id"]: e for e in all_entities}.values())
598
604
  self.sync_entities()
@@ -616,34 +622,68 @@ class NifiContextManager(object):
616
622
  @contextmanager
617
623
  def reindex_lock(self, entities: List[Union[dict, "NifiEntity"]], timeout: int = 7200):
618
624
  if not entities:
619
- yield
625
+ yield True
620
626
  return
621
- entities = [e if isinstance(e, dict) else e.record for e in entities]
627
+
628
+ import logging
629
+ _lock_logger = logging.getLogger(__name__)
630
+
631
+ records = []
632
+ for e in entities:
633
+ if isinstance(e, dict):
634
+ records.append(
635
+ (e, e.get("entity_timestamp"))
636
+ )
637
+ else:
638
+ records.append(
639
+ (e.record, e.request.get("entity_timestamp") if e.request else None)
640
+ )
641
+
622
642
  long_expiry = (datetime.now(timezone.utc) + timedelta(seconds=timeout)).strftime("%Y-%m-%dT%H:%M:%SZ")
623
643
  statuses = [
624
644
  {
625
- "entity_id": e["os_entity_uid"],
626
- "entity_type": e["os_concept"],
645
+ "entity_id": rec["os_entity_uid"],
646
+ "entity_type": rec["os_concept"],
647
+ "do_not_update_before": ts,
627
648
  "do_not_reindex_before": long_expiry,
628
- "do_not_update_before": long_expiry,
629
649
  }
630
- for e in entities
650
+ for rec, ts in records
651
+ if ts is not None
631
652
  ]
632
- update_processing_status.sync(statuses, client=self.client)
653
+ if len(statuses) < len(records):
654
+ missing = [rec["os_entity_uid"] for rec, ts in records if ts is None]
655
+ _lock_logger.warning(
656
+ f"reindex_lock: {len(missing)} entities have no entity_timestamp, cannot set lock: {missing}"
657
+ )
658
+ if not statuses:
659
+ yield True
660
+ return
661
+
662
+ result = update_processing_status.sync(statuses, client=self.client)
663
+ skipped_ids = result.get("skipped_entity_ids", []) if isinstance(result, dict) else []
664
+ if skipped_ids:
665
+ _lock_logger.warning(
666
+ f"reindex_lock: {len(skipped_ids)} entities have a newer pipeline run, aborting sync: {skipped_ids}"
667
+ )
668
+ yield False
669
+ return
670
+
633
671
  try:
634
- yield
672
+ yield True
635
673
  finally:
636
674
  short_expiry = (datetime.now(timezone.utc) + timedelta(seconds=1)).strftime("%Y-%m-%dT%H:%M:%SZ")
637
675
  statuses = [
638
676
  {
639
- "entity_id": e["os_entity_uid"],
640
- "entity_type": e["os_concept"],
677
+ "entity_id": rec["os_entity_uid"],
678
+ "entity_type": rec["os_concept"],
679
+ "do_not_update_before": ts,
641
680
  "do_not_reindex_before": short_expiry,
642
- "do_not_update_before": short_expiry,
643
681
  }
644
- for e in entities
682
+ for rec, ts in records
683
+ if ts is not None
645
684
  ]
646
- update_processing_status.sync(statuses, client=self.client)
685
+ if statuses:
686
+ update_processing_status.sync(statuses, client=self.client)
647
687
 
648
688
  def sync_entities(self):
649
689
  if not self.lazy_sync:
@@ -658,14 +698,17 @@ class NifiContextManager(object):
658
698
  fetch_rel_entities, fetch_concept_rels = self._find_relationships_to_fetch(entities)
659
699
 
660
700
  all_entities_to_modify = attachables_to_write + [e[0] for e in entities_to_upsert]
661
- with self.reindex_lock(all_entities_to_modify):
701
+ with self.reindex_lock(all_entities_to_modify) as locked:
702
+ if not locked:
703
+ return
662
704
  self._sync_write_attachables(attachables_to_write)
663
705
  self._sync_upsert_entities(entities_to_upsert)
664
706
  self._sync_fetch_relationships(entities, fetch_rel_entities, fetch_concept_rels)
665
707
 
708
+ now_ts = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
666
709
  for entity in all_entities_to_modify:
667
710
  entity.request["is_temporary"] = False
668
- entity.request["entity_timestamp"] = entity.record["os_last_updated_at"]
711
+ entity.request["entity_timestamp"] = now_ts
669
712
 
670
713
  for entity in entities:
671
714
  entity.sync_params = {}
@@ -712,7 +755,7 @@ class NifiContextManager(object):
712
755
  file.record["entity_id"] = file.record["os_entity_uid"]
713
756
  file.record["entity_type"] = file.record["os_concept"]
714
757
  file.record["entity_label"] = file.label
715
- file.request["entity_timestamp"] = file.record["os_last_updated_at"]
758
+ file.request["entity_timestamp"] = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
716
759
  file.request["is_temporary"] = False
717
760
 
718
761
  def _sync_upsert_entities(self, entities_to_upsert):
@@ -736,7 +779,7 @@ class NifiContextManager(object):
736
779
  entity.record["entity_id"] = entity.record["os_entity_uid"]
737
780
  entity.record["entity_type"] = entity.record["os_concept"]
738
781
  entity.record["entity_label"] = entity.label
739
- entity.request["entity_timestamp"] = entity.record["os_last_updated_at"]
782
+ entity.request["entity_timestamp"] = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
740
783
  entity.request["is_temporary"] = False
741
784
 
742
785
  def _find_relationships_to_fetch(self, entities) -> dict:
@@ -1052,7 +1095,17 @@ class NifiEntity(object):
1052
1095
  )
1053
1096
  return NifiFragmenter.get_fragment_info(self, key).get("index", 0) == 0
1054
1097
 
1055
- def get_fragment_root_uid(self, fragment_name_or_idx) -> str:
1098
+ def get_fragment_root(self, fragment_name_or_idx) -> tuple:
1099
+ """Return (root_uid, root_type) for a given fragmentation level.
1100
+
1101
+ Args:
1102
+ fragment_name_or_idx: An int to index into fragments_stack
1103
+ (e.g. -1 for the oldest fragmentation), or a string
1104
+ fragmenter keylist name.
1105
+
1106
+ Returns:
1107
+ (root_uid, root_type) tuple for the root entity at that level.
1108
+ """
1056
1109
  fragment_config = self.request.get("config", {}).get("fragment", {})
1057
1110
  fragments_stack = fragment_config.get("fragments_stack", [])
1058
1111
  if isinstance(fragment_name_or_idx, int):
@@ -1077,7 +1130,10 @@ class NifiEntity(object):
1077
1130
  raise KeyError(
1078
1131
  f"No root_uid found in fragment config for '{fragment_key}'"
1079
1132
  )
1080
- return fragment_data["root_uid"]
1133
+ return (
1134
+ fragment_data["root_uid"],
1135
+ fragment_data.get("root_type", "os_fragment"),
1136
+ )
1081
1137
 
1082
1138
  def to_json(self):
1083
1139
  if self.drop_on_output:
@@ -1111,7 +1167,7 @@ class NifiEntity(object):
1111
1167
  proxy_otm_children = [c.record for c in proxy_otm_children]
1112
1168
  full_entity_children = sorted(
1113
1169
  full_entity_children,
1114
- key=lambda x: string_to_datetime(x.record.get("os_last_updated_at")),
1170
+ key=lambda x: string_to_datetime(x.request.get("entity_timestamp")),
1115
1171
  )
1116
1172
  full_entity_children = list({c.uid: c.to_json() for c in full_entity_children}.values())
1117
1173
  children = full_entity_children + proxy_entity_children + proxy_otm_children
@@ -1144,7 +1200,7 @@ class NifiEntity(object):
1144
1200
  "relationships": ont_relationships,
1145
1201
  "label_keys": ont_label_keys,
1146
1202
  },
1147
- "entity_timestamp": None,
1203
+ "entity_timestamp": self.request.get("entity_timestamp"),
1148
1204
  "sync_params": {
1149
1205
  NifiContextManager.SyncFlag.UPSERT_ENTITY_ALL.name: True,
1150
1206
  },
@@ -1371,6 +1427,7 @@ class NifiEntity(object):
1371
1427
  self,
1372
1428
  os_workspace,
1373
1429
  source_entity_uid,
1430
+ source_entity_type,
1374
1431
  filename,
1375
1432
  filetype,
1376
1433
  file: Union[Contents, bytes],
@@ -1385,9 +1442,12 @@ class NifiEntity(object):
1385
1442
  next_fragment_uid=None,
1386
1443
  next_fragment_relationship_uid=None,
1387
1444
  next_fragment_relationship=NEXT_FRAGMENT_RELATIONSHIP,
1445
+ source_entity_relationship_uid=None,
1446
+ source_entity_relationship=SOURCE_FRAGMENT_ENTITY_RELATIONSHIP,
1388
1447
  ):
1389
1448
  fields = {
1390
1449
  **fields,
1450
+ "os_parent_uid": self.record["os_entity_uid"],
1391
1451
  "source_entity_uid": source_entity_uid,
1392
1452
  "previous_entity_uid": previous_fragment_uid,
1393
1453
  "next_entity_uid": next_fragment_uid,
@@ -1403,9 +1463,14 @@ class NifiEntity(object):
1403
1463
  os_relationship_uid,
1404
1464
  os_entity_type,
1405
1465
  )
1406
- prev_rel, next_rel = None, None
1407
1466
  fragment_uid = child_entity.record["os_entity_uid"]
1408
1467
  fragment_type = child_entity.record["os_concept"]
1468
+ source_rel = self._add_relationship(
1469
+ os_workspace, fragment_uid, fragment_type,
1470
+ source_entity_uid, source_entity_type,
1471
+ source_entity_relationship, {}, source_entity_relationship_uid,
1472
+ )
1473
+ prev_rel, next_rel = None, None
1409
1474
  if previous_fragment_uid:
1410
1475
  prev_rel = self._add_relationship(
1411
1476
  os_workspace, fragment_uid, fragment_type,
@@ -1418,7 +1483,7 @@ class NifiEntity(object):
1418
1483
  next_fragment_uid, os_entity_type,
1419
1484
  next_fragment_relationship, {}, next_fragment_relationship_uid,
1420
1485
  )
1421
- return child_entity, child_rel, prev_rel, next_rel
1486
+ return child_entity, child_rel, source_rel, prev_rel, next_rel
1422
1487
 
1423
1488
  def add_tag(self, os_workspace, name, group, order, color, fields={}):
1424
1489
  return self.add_child_entity(
@@ -1445,12 +1510,6 @@ class NifiEntity(object):
1445
1510
  to_entity.annotations = recursive_update_dict(to_entity.annotations, annotations_to_propagate, merge_method)
1446
1511
 
1447
1512
 
1448
- def more_recent_than(record_a, record_b):
1449
- return string_to_datetime(record_a.get("os_last_updated_at")) >= string_to_datetime(
1450
- record_b.get("os_last_updated_at")
1451
- )
1452
-
1453
-
1454
1513
  class NifiRoute(Route):
1455
1514
  def __init__(self, app, tasks_routes, processor_name, celery_executor, router=None):
1456
1515
  self.app = app