from taipy.config import Config  from taipy.config._config import _Config  from taipy.config.checker._checkers._config_checker import _ConfigChecker  from taipy.config.checker.issue_collector import IssueCollector  from taipy.config.common.frequency import Frequency    from ..data_node_config import DataNodeConfig  from ..scenario_config import ScenarioConfig  from ..task_config import TaskConfig      class _ScenarioConfigChecker(_ConfigChecker):      def __init__(self, config: _Config, collector: IssueCollector):          super().__init__(config, collector)        def _check(self) -> IssueCollector:          scenario_configs = self._config._sections[ScenarioConfig.name]          for scenario_config_id, scenario_config in scenario_configs.items():              if scenario_config_id != _Config.DEFAULT_KEY:                  self._check_if_entity_property_key_used_is_predefined(scenario_config)                  self._check_existing_config_id(scenario_config)                  self._check_frequency(scenario_config_id, scenario_config)                  self._check_task_configs(scenario_config_id, scenario_config)                  self._check_addition_data_node_configs(scenario_config_id, scenario_config)                  self._check_additional_dns_not_overlapping_tasks_dns(scenario_config_id, scenario_config)                  self._check_tasks_in_sequences_exist_in_scenario_tasks(scenario_config_id, scenario_config)                  self._check_comparators(scenario_config_id, scenario_config)            return self._collector        def _check_task_configs(self, scenario_config_id: str, scenario_config: ScenarioConfig):          self._check_children(              ScenarioConfig,              scenario_config_id,              scenario_config._TASKS_KEY,              scenario_config.tasks,              TaskConfig,          )        def _check_addition_data_node_configs(self, scenario_config_id: str, scenario_config: ScenarioConfig):          self._check_children(              ScenarioConfig,              scenario_config_id,              scenario_config._ADDITIONAL_DATA_NODES_KEY,              scenario_config.additional_data_nodes,              DataNodeConfig,              can_be_empty=True,          )        def _check_frequency(self, scenario_config_id: str, scenario_config: ScenarioConfig):          if scenario_config.frequency and not isinstance(scenario_config.frequency, Frequency):              self._error(                  scenario_config._FREQUENCY_KEY,                  scenario_config.frequency,                  f"{scenario_config._FREQUENCY_KEY} field of ScenarioConfig `{scenario_config_id}` must be"                  f" populated with a Frequency value.",              )        def _check_comparators(self, scenario_config_id: str, scenario_config: ScenarioConfig):          if scenario_config.comparators is not None and not isinstance(scenario_config.comparators, dict):              self._error(                  ScenarioConfig._COMPARATOR_KEY,                  scenario_config.comparators,                  f"{ScenarioConfig._COMPARATOR_KEY} field of ScenarioConfig"                  f" `{scenario_config_id}` must be populated with a dictionary value.",              )          else:              for data_node_id, comparator in scenario_config.comparators.items():                  if data_node_id not in Config.data_nodes:                      self._error(                          ScenarioConfig._COMPARATOR_KEY,                          scenario_config.comparators,                          f"The key `{data_node_id}` in {ScenarioConfig._COMPARATOR_KEY} field of ScenarioConfig"                          f" `{scenario_config_id}` must be populated with a valid data node configuration id.",                      )                  if not callable(comparator):                      if not isinstance(comparator, list) or not all(callable(comp) for comp in comparator):                          self._error(                              ScenarioConfig._COMPARATOR_KEY,                              scenario_config.comparators,                              f"The value of `{data_node_id}` in {ScenarioConfig._COMPARATOR_KEY} field of ScenarioConfig"                              f" `{scenario_config_id}` must be populated with a list of Callable values.",                          )        def _check_additional_dns_not_overlapping_tasks_dns(self, scenario_config_id: str, scenario_config: ScenarioConfig):          data_node_configs = set()          for task_config in scenario_config.task_configs:              if isinstance(task_config, TaskConfig):                  input_dn_configs = task_config.input_configs if task_config.input_configs else []                  output_dn_configs = task_config.output_configs if task_config.output_configs else []                  data_node_configs.update({*input_dn_configs, *output_dn_configs})            for additional_data_node_config in scenario_config.additional_data_node_configs:              if additional_data_node_config in data_node_configs:                  self._warning(                      ScenarioConfig._ADDITIONAL_DATA_NODES_KEY,                      scenario_config.additional_data_node_configs,                      f"The additional data node `{additional_data_node_config.id}` in"                      f" {ScenarioConfig._ADDITIONAL_DATA_NODES_KEY} field of ScenarioConfig"                      f" `{scenario_config_id}` has already existed as an input or output data node of"                      f" ScenarioConfig `{scenario_config_id}` tasks.",                  )        def _check_tasks_in_sequences_exist_in_scenario_tasks(          self, scenario_config_id: str, scenario_config: ScenarioConfig      ):          scenario_task_ids = set()          for task_config in scenario_config.tasks:              if isinstance(task_config, TaskConfig):                  scenario_task_ids.add(task_config.id)          for sequence_tasks in scenario_config.sequences.values():              self._check_children(                  ScenarioConfig,                  scenario_config_id,                  scenario_config._SEQUENCES_KEY,                  sequence_tasks,                  TaskConfig,                  can_be_empty=True,              )              for task in sequence_tasks:                  if isinstance(task, TaskConfig) and task.id not in scenario_task_ids:                      self._error(                          ScenarioConfig._SEQUENCES_KEY,                          scenario_config.sequences,                          f"The task `{task.id}` in {ScenarioConfig._SEQUENCES_KEY} field of ScenarioConfig"                          f" `{scenario_config_id}` must exist in {ScenarioConfig._TASKS_KEY} field of ScenarioConfig"                          f" `{scenario_config_id}`.",                      )


from datetime import timedelta  from typing import Dict    from taipy.config._config import _Config  from taipy.config.checker._checker import _ConfigChecker  from taipy.config.checker.issue_collector import IssueCollector  from taipy.config.common.scope import Scope    from ..data_node_config import DataNodeConfig      class _DataNodeConfigChecker(_ConfigChecker):      def __init__(self, config: _Config, collector: IssueCollector):          super().__init__(config, collector)        def _check(self) -> IssueCollector:          data_node_configs: Dict[str, DataNodeConfig] = self._config._sections[DataNodeConfig.name]          for data_node_config_id, data_node_config in data_node_configs.items():              self._check_existing_config_id(data_node_config)              self._check_if_entity_property_key_used_is_predefined(data_node_config)              self._check_storage_type(data_node_config_id, data_node_config)              self._check_scope(data_node_config_id, data_node_config)              self._check_validity_period(data_node_config_id, data_node_config)              self._check_required_properties(data_node_config_id, data_node_config)              self._check_callable(data_node_config_id, data_node_config)              self._check_generic_read_write_fct_and_args(data_node_config_id, data_node_config)              self._check_exposed_type(data_node_config_id, data_node_config)          return self._collector        def _check_storage_type(self, data_node_config_id: str, data_node_config: DataNodeConfig):          if data_node_config.storage_type not in DataNodeConfig._ALL_STORAGE_TYPES:              self._error(                  data_node_config._STORAGE_TYPE_KEY,                  data_node_config.storage_type,                  f"`{data_node_config._STORAGE_TYPE_KEY}` field of DataNodeConfig `{data_node_config_id}` must be"                  f" either csv, sql_table, sql, mongo_collection, pickle, excel, generic, json, parquet, or in_memory.",              )        def _check_scope(self, data_node_config_id: str, data_node_config: DataNodeConfig):          if not isinstance(data_node_config.scope, Scope):              self._error(                  data_node_config._SCOPE_KEY,                  data_node_config.scope,                  f"`{data_node_config._SCOPE_KEY}` field of DataNodeConfig `{data_node_config_id}` must be"                  f" populated with a Scope value.",              )        def _check_validity_period(self, data_node_config_id: str, data_node_config: DataNodeConfig):          if data_node_config.validity_period and not isinstance(data_node_config.validity_period, timedelta):              self._error(                  data_node_config._VALIDITY_PERIOD_KEY,                  data_node_config.validity_period,                  f"`{data_node_config._VALIDITY_PERIOD_KEY}` field of DataNodeConfig `{data_node_config_id}` must be"                  f" None or populated with a timedelta value.",              )        def _check_required_properties(self, data_node_config_id: str, data_node_config: DataNodeConfig):          if storage_type := data_node_config.storage_type:              if storage_type in DataNodeConfig._REQUIRED_PROPERTIES:                  required_properties = DataNodeConfig._REQUIRED_PROPERTIES[storage_type]                  if storage_type == DataNodeConfig._STORAGE_TYPE_VALUE_SQL:                      if data_node_config.properties:                          if engine := data_node_config.properties.get(DataNodeConfig._REQUIRED_DB_ENGINE_SQL_PROPERTY):                              if engine == DataNodeConfig._DB_ENGINE_SQLITE:                                  required_properties = [                                      DataNodeConfig._REQUIRED_DB_NAME_SQL_PROPERTY,                                      DataNodeConfig._REQUIRED_DB_ENGINE_SQL_PROPERTY,                                      DataNodeConfig._REQUIRED_READ_QUERY_SQL_PROPERTY,                                      DataNodeConfig._REQUIRED_WRITE_QUERY_BUILDER_SQL_PROPERTY,                                  ]                              else:                                  required_properties = [                                      DataNodeConfig._OPTIONAL_DB_USERNAME_SQL_PROPERTY,                                      DataNodeConfig._OPTIONAL_DB_PASSWORD_SQL_PROPERTY,                                      DataNodeConfig._REQUIRED_DB_NAME_SQL_PROPERTY,                                      DataNodeConfig._REQUIRED_DB_ENGINE_SQL_PROPERTY,                                      DataNodeConfig._REQUIRED_READ_QUERY_SQL_PROPERTY,                                      DataNodeConfig._REQUIRED_WRITE_QUERY_BUILDER_SQL_PROPERTY,                                  ]                  if storage_type == DataNodeConfig._STORAGE_TYPE_VALUE_SQL_TABLE:                      if data_node_config.properties:                          if engine := data_node_config.properties.get(DataNodeConfig._REQUIRED_DB_ENGINE_SQL_PROPERTY):                              if engine == DataNodeConfig._DB_ENGINE_SQLITE:                                  required_properties = [                                      DataNodeConfig._REQUIRED_DB_NAME_SQL_PROPERTY,                                      DataNodeConfig._REQUIRED_DB_ENGINE_SQL_PROPERTY,                                      DataNodeConfig._REQUIRED_TABLE_NAME_SQL_TABLE_PROPERTY,                                  ]                              else:                                  required_properties = [                                      DataNodeConfig._OPTIONAL_DB_USERNAME_SQL_PROPERTY,                                      DataNodeConfig._OPTIONAL_DB_PASSWORD_SQL_PROPERTY,                                      DataNodeConfig._REQUIRED_DB_NAME_SQL_PROPERTY,                                      DataNodeConfig._REQUIRED_DB_ENGINE_SQL_PROPERTY,                                      DataNodeConfig._REQUIRED_TABLE_NAME_SQL_TABLE_PROPERTY,                                  ]                  for required_property in required_properties:                      if not data_node_config.properties or required_property not in data_node_config.properties:                          if data_node_config_id == DataNodeConfig._DEFAULT_KEY:                              self._warning(                                  required_property,                                  None,                                  f"DataNodeConfig `{data_node_config_id}` is missing the required "                                  f"property `{required_property}` for type `{storage_type}`.",                              )                          else:                              self._error(                                  required_property,                                  None,                                  f"DataNodeConfig `{data_node_config_id}` is missing the required "                                  f"property `{required_property}` for type `{storage_type}`.",                              )        def _check_generic_read_write_fct_and_args(self, data_node_config_id: str, data_node_config: DataNodeConfig):          if data_node_config.storage_type == DataNodeConfig._STORAGE_TYPE_VALUE_GENERIC:              properties_to_check = [                  DataNodeConfig._OPTIONAL_READ_FUNCTION_ARGS_GENERIC_PROPERTY,                  DataNodeConfig._OPTIONAL_WRITE_FUNCTION_ARGS_GENERIC_PROPERTY,              ]              for prop_key in properties_to_check:                  if data_node_config.properties and prop_key in data_node_config.properties:                      prop_value = data_node_config.properties[prop_key]                      if not isinstance(prop_value, list):                          self._error(                              prop_key,                              prop_value,                              f"`{prop_key}` field of DataNodeConfig"                              f" `{data_node_config_id}` must be populated with a List value.",                          )              if data_node_config_id != DataNodeConfig._DEFAULT_KEY:                  properties_to_check_at_least_one = [                      DataNodeConfig._OPTIONAL_READ_FUNCTION_GENERIC_PROPERTY,                      DataNodeConfig._OPTIONAL_WRITE_FUNCTION_GENERIC_PROPERTY,                  ]                  has_at_least_one = False                  for prop_key in properties_to_check_at_least_one:                      if data_node_config.properties and prop_key in data_node_config.properties:                          has_at_least_one = True                  if not has_at_least_one:                      self._error(                          ", ".join(properties_to_check_at_least_one),                          None,                          f"Either `{DataNodeConfig._OPTIONAL_READ_FUNCTION_GENERIC_PROPERTY}` field or "                          f"`{DataNodeConfig._OPTIONAL_WRITE_FUNCTION_GENERIC_PROPERTY}` field of "                          f"DataNodeConfig `{data_node_config_id}` must be populated with a Callable function.",                      )        def _check_callable(self, data_node_config_id: str, data_node_config: DataNodeConfig):          properties_to_check = {              DataNodeConfig._STORAGE_TYPE_VALUE_GENERIC: [                  DataNodeConfig._OPTIONAL_READ_FUNCTION_GENERIC_PROPERTY,                  DataNodeConfig._OPTIONAL_WRITE_FUNCTION_GENERIC_PROPERTY,              ],              DataNodeConfig._STORAGE_TYPE_VALUE_SQL: [                  DataNodeConfig._REQUIRED_WRITE_QUERY_BUILDER_SQL_PROPERTY,                  DataNodeConfig._OPTIONAL_APPEND_QUERY_BUILDER_SQL_PROPERTY,              ],          }            if data_node_config.storage_type in properties_to_check.keys():              for prop_key in properties_to_check[data_node_config.storage_type]:                  prop_value = data_node_config.properties.get(prop_key) if data_node_config.properties else None                  if prop_value and not callable(prop_value):                      self._error(                          prop_key,                          prop_value,                          f"`{prop_key}` of DataNodeConfig `{data_node_config_id}` must be"                          f" populated with a Callable function.",                      )        def _check_exposed_type(self, data_node_config_id: str, data_node_config: DataNodeConfig):          if not isinstance(data_node_config.exposed_type, str):              return          if data_node_config.exposed_type not in DataNodeConfig._ALL_EXPOSED_TYPES:              self._error(                  data_node_config._EXPOSED_TYPE_KEY,                  data_node_config.exposed_type,                  f"The `{data_node_config._EXPOSED_TYPE_KEY}` of DataNodeConfig `{data_node_config_id}` "                  f'must be either "pandas", "modin", "numpy", or a custom type.',              )


from taipy.config._config import _Config  from taipy.config.checker._checkers._config_checker import _ConfigChecker  from taipy.config.checker.issue_collector import IssueCollector    from ..data_node_config import DataNodeConfig  from ..task_config import TaskConfig      class _TaskConfigChecker(_ConfigChecker):      def __init__(self, config: _Config, collector: IssueCollector):          super().__init__(config, collector)        def _check(self) -> IssueCollector:          task_configs = self._config._sections[TaskConfig.name]          for task_config_id, task_config in task_configs.items():              if task_config_id != _Config.DEFAULT_KEY:                  self._check_existing_config_id(task_config)                  self._check_if_entity_property_key_used_is_predefined(task_config)                  self._check_existing_function(task_config_id, task_config)                  self._check_inputs(task_config_id, task_config)                  self._check_outputs(task_config_id, task_config)          return self._collector        def _check_inputs(self, task_config_id: str, task_config: TaskConfig):          self._check_children(              TaskConfig, task_config_id, task_config._INPUT_KEY, task_config.input_configs, DataNodeConfig          )        def _check_outputs(self, task_config_id: str, task_config: TaskConfig):          self._check_children(              TaskConfig, task_config_id, task_config._OUTPUT_KEY, task_config.output_configs, DataNodeConfig          )        def _check_existing_function(self, task_config_id: str, task_config: TaskConfig):          if not task_config.function:              self._error(                  task_config._FUNCTION,                  task_config.function,                  f"{task_config._FUNCTION} field of TaskConfig `{task_config_id}` is empty.",              )          else:              if not callable(task_config.function):                  self._error(                      task_config._FUNCTION,                      task_config.function,                      f"{task_config._FUNCTION} field of TaskConfig `{task_config_id}` must be"                      f" populated with Callable value.",                  )


from dataclasses import dataclass, field  from datetime import datetime  from functools import singledispatch  from typing import Any, Optional    from ..common._repr_enum import _ReprEnum  from ..exceptions.exceptions import InvalidEventAttributeName, InvalidEventOperation      class EventOperation(_ReprEnum):      """Enum representing a type of operation performed on a Core entity.        `EventOperation` is used as an attribute of the `Event^` object to describe the      operation performed on an entity.
      The possible operations are `CREATION`, `UPDATE`, `DELETION`, or `SUBMISSION`.      """        CREATION = 1      UPDATE = 2      DELETION = 3      SUBMISSION = 4      class EventEntityType(_ReprEnum):      """Enum representing an entity type.        `EventEntityType` is used as an attribute of the `Event^` object to describe      an entity that was changed.
      The possible operations are `CYCLE`, `SCENARIO`, `SEQUENCE`, `TASK`, `DATA_NODE`, `JOB` or `SUBMISSION`.      """        CYCLE = 1      SCENARIO = 2      SEQUENCE = 3      TASK = 4      DATA_NODE = 5      JOB = 6      SUBMISSION = 7      _NO_ATTRIBUTE_NAME_OPERATIONS = set([EventOperation.CREATION, EventOperation.DELETION, EventOperation.SUBMISSION])  _UNSUBMITTABLE_ENTITY_TYPES = (EventEntityType.CYCLE, EventEntityType.DATA_NODE, EventEntityType.JOB)  _ENTITY_TO_EVENT_ENTITY_TYPE = {      "scenario": EventEntityType.SCENARIO,      "sequence": EventEntityType.SEQUENCE,      "task": EventEntityType.TASK,      "data": EventEntityType.DATA_NODE,      "job": EventEntityType.JOB,      "cycle": EventEntityType.CYCLE,      "submission": EventEntityType.SUBMISSION,  }      @dataclass(frozen=True)  class Event:      """Event object used to notify any change in the Core service.        An event holds the necessary attributes to identify the change.        Attributes:          entity_type (EventEntityType^): Type of the entity that was changed (`DataNode^`,              `Scenario^`, `Cycle^`, etc. ).          entity_id (Optional[str]): Unique identifier of the entity that was changed.          operation (EventOperation^): Enum describing the operation (among `CREATION`, `UPDATE`, `DELETION`,              and `SUBMISSION`) that was performed on the entity.          attribute_name (Optional[str]): Name of the entity's attribute changed. Only relevant for `UPDATE`              operations          attribute_value (Optional[str]): Name of the entity's attribute changed. Only relevant for `UPDATE`              operations          metadata (dict): A dict of additional medata about the source of this event          creation_date (datetime): Date and time of the event creation.      """        entity_type: EventEntityType      operation: EventOperation      entity_id: Optional[str] = None      attribute_name: Optional[str] = None      attribute_value: Optional[Any] = None        metadata: dict = field(default_factory=dict)      creation_date: datetime = field(init=False)        def __post_init__(self):          # Creation date          super().__setattr__("creation_date", datetime.now())            # Check operation:          if self.entity_type in _UNSUBMITTABLE_ENTITY_TYPES and self.operation == EventOperation.SUBMISSION:              raise InvalidEventOperation            # Check attribute name:          if self.operation in _NO_ATTRIBUTE_NAME_OPERATIONS and self.attribute_name is not None:              raise InvalidEventAttributeName      @singledispatch  def _make_event(      entity: Any,      operation: EventOperation,      /,      attribute_name: Optional[str] = None,      attribute_value: Optional[Any] = None,      **kwargs,  ) -> Event:      """Helper function to make an event for this entity with the given `EventOperation^` type.      In case of `EventOperation.UPDATE^` events, an attribute name and value must be given.        Parameters:          entity (Any): The entity object to generate an event for.          operation (EventOperation^): The operation of the event. The possible values are:

CREATION

UPDATE

DELETION

SUBMISSION

          attribute_name (Optional[str]): The name of the updated attribute for a `EventOperation.UPDATE`.              This argument is always given in case of an UPDATE.          attribute_value (Optional[Any]): The value of the udated attribute for a `EventOperation.UPDATE`.              This argument is always given in case of an UPDATE.          **kwargs (dict[str, any]): Any extra information that would be passed to the metadata event.              Note: you should pass only simple types: str, float, double as values."""      raise Exception(f"Unexpected entity type: {type(entity)}")


from queue import SimpleQueue  from typing import Any, Dict, Optional, Set, Tuple    from ._registration import _Registration  from ._topic import _Topic  from .event import Event, EventEntityType, EventOperation      def _publish_event(      entity_type: EventEntityType,      operation: EventOperation,      /,      entity_id: Optional[str] = None,      attribute_name: Optional[str] = None,      attribute_value: Optional[Any] = None,      **kwargs,  ):      """Internal helper function to send events.        It basically creates an event corresponding to the given arguments      and send it using `Notifier.publish(event)`        Parameters:          entity_type (EventEntityType^)          operation (EventOperation^)          entity_id (Optional[str])          attribute_name (Optional[str])          attribute_value (Optional[Any])          **kwargs      """      event = Event(          entity_id=entity_id,          entity_type=entity_type,          operation=operation,          attribute_name=attribute_name,          attribute_value=attribute_value,          metadata=kwargs,      )      Notifier.publish(event)      class Notifier:      """A class for managing event registrations and publishing `Core^` service events."""        _topics_registrations_list: Dict[_Topic, Set[_Registration]] = {}        @classmethod      def register(          cls,          entity_type: Optional[EventEntityType] = None,          entity_id: Optional[str] = None,          operation: Optional[EventOperation] = None,          attribute_name: Optional[str] = None,      ) -> Tuple[str, SimpleQueue]:          """Register a listener for a specific event topic.            The topic is defined by the combination of the entity type, the entity id,          the operation and the attribute name.            Parameters:              entity_type (Optional[EventEntityType^]): If provided, the listener will                  be notified for all events related to this entity type. Otherwise,                  the listener will be notified for events related to all entity types.
                  The possible entity type values are defined in the `EventEntityType^`                  enum. The possible values are:

CYCLE

SCENARIO

SEQUENCE

TASK

DATA_NODE

JOB

              entity_id (Optional[str]): If provided, the listener will be notified                  for all events related to this entity. Otherwise, the listener                  will be notified for events related to all entities.              operation (Optional[EventOperation^]): If provided, the listener will                  be notified for all events related to this operation. Otherwise,                  the listener will be notified for events related to all operations.
                  The possible operation values are defined in the `EventOperation^`                  enum. The possible values are:

CREATION

UPDATE

DELETION

SUBMISSION

              attribute_name (Optional[str]): If provided, the listener will be notified                  for all events related to this entity's attribute. Otherwise, the listener                  will be notified for events related to all attributes.            Returns:              A tuple containing the registration id and the event queue.          """          registration = _Registration(entity_type, entity_id, operation, attribute_name)            if registrations := cls._topics_registrations_list.get(registration.topic, None):              registrations.add(registration)          else:              cls._topics_registrations_list[registration.topic] = {registration}            return registration.registration_id, registration.queue        @classmethod      def unregister(cls, registration_id: str):          """Unregister a listener.            Parameters:              registration_id (RegistrationId^): The registration id returned by the `register` method.          """          to_remove_registration: Optional[_Registration] = None            for _, registrations in cls._topics_registrations_list.items():              for registration in registrations:                  if registration.registration_id == registration_id:                      to_remove_registration = registration                      break            if to_remove_registration:              registrations = cls._topics_registrations_list[to_remove_registration.topic]              registrations.remove(to_remove_registration)              if len(registrations) == 0:                  del cls._topics_registrations_list[to_remove_registration.topic]        @classmethod      def publish(cls, event):          """Publish a `Core^` service event to all registered listeners whose topic matches the event.            Parameters:              event (Event^): The event to publish.          """          for topic, registrations in cls._topics_registrations_list.items():              if Notifier._is_matching(event, topic):                  for registration in registrations:                      registration.queue.put(event)        @staticmethod      def _is_matching(event: Event, topic: _Topic) -> bool:          """Check if an event matches a topic."""          if topic.entity_type is not None and event.entity_type != topic.entity_type:              return False          if topic.entity_id is not None and event.entity_id != topic.entity_id:              return False          if topic.operation is not None and event.operation != topic.operation:              return False          if topic.attribute_name is not None and event.attribute_name and event.attribute_name != topic.attribute_name:              return False          return True


"""  Package for notifications about changes on `Core^` service entities.      The Core service generates `Event^` objects to track changes on entities.  These events are then relayed to a `Notifier^`, which handles the dispatch  to consumers interested in specific event topics.    To subscribe, a consumer needs to invoke the `Notifier.register()^` method.  This call will yield a `RegistrationId^` and a dedicated event queue for  receiving notifications.    To handle notifications, an event consumer (e.g., the `CoreEventConsumerBase^`  object) must be instantiated with an associated event queue.  """    from ._registration import _Registration  from ._topic import _Topic  from .core_event_consumer import CoreEventConsumerBase  from .event import _ENTITY_TO_EVENT_ENTITY_TYPE, Event, EventEntityType, EventOperation, _make_event  from .notifier import Notifier, _publish_event  from .registration_id import RegistrationId


from typing import NewType    RegistrationId = NewType("RegistrationId", str)  RegistrationId.__doc__ = """Registration identifier. It can be used to instantiate a `CoreEventConsumerBase^`."""


from queue import SimpleQueue  from typing import Optional  from uuid import uuid4    from ._topic import _Topic  from .event import EventEntityType, EventOperation  from .registration_id import RegistrationId      class _Registration:        _ID_PREFIX = "REGISTRATION"      __SEPARATOR = "_"        def __init__(          self,          entity_type: Optional[EventEntityType] = None,          entity_id: Optional[str] = None,          operation: Optional[EventOperation] = None,          attribute_name: Optional[str] = None,      ):            self.registration_id: str = self._new_id()          self.topic: _Topic = _Topic(entity_type, entity_id, operation, attribute_name)          self.queue: SimpleQueue = SimpleQueue()        @staticmethod      def _new_id() -> RegistrationId:          """Generate a unique registration identifier."""          return RegistrationId(_Registration.__SEPARATOR.join([_Registration._ID_PREFIX, str(uuid4())]))        def __hash__(self) -> int:          return hash(self.registration_id)


from typing import Optional    from ..exceptions.exceptions import InvalidEventOperation  from .event import _UNSUBMITTABLE_ENTITY_TYPES, EventEntityType, EventOperation      class _Topic:      def __init__(          self,          entity_type: Optional[EventEntityType] = None,          entity_id: Optional[str] = None,          operation: Optional[EventOperation] = None,          attribute_name: Optional[str] = None,      ):            self.entity_type = entity_type          self.entity_id = entity_id          self.operation = self.__preprocess_operation(operation, self.entity_type)          self.attribute_name = self.__preprocess_attribute_name(attribute_name, self.operation)        @classmethod      def __preprocess_attribute_name(          cls, attribute_name: Optional[str] = None, operation: Optional[EventOperation] = None      ) -> Optional[str]:            # if operation in _NO_ATTRIBUTE_NAME_OPERATIONS and attribute_name is not None:          #     raise InvalidEventAttributeName          return attribute_name        @classmethod      def __preprocess_operation(          cls, operation: Optional[EventOperation] = None, entity_type: Optional[EventEntityType] = None      ) -> Optional[EventOperation]:          if (              entity_type              and operation              and entity_type in _UNSUBMITTABLE_ENTITY_TYPES              and operation == EventOperation.SUBMISSION          ):              raise InvalidEventOperation          return operation        def __hash__(self):          return hash((self.entity_type, self.entity_id, self.operation, self.attribute_name))        def __eq__(self, __value) -> bool:          if (              self.entity_type == __value.entity_type              and self.entity_id == __value.entity_id              and self.operation == __value.operation              and self.attribute_name == __value.attribute_name          ):              return True          return False


import abc  import threading  from queue import Empty, SimpleQueue    from .event import Event      class CoreEventConsumerBase(threading.Thread):      """Abstract base class for implementing a Core event consumer.        This class provides a framework for consuming events from a queue in a separate thread.      It should be subclassed, and the `process_event` method should be implemented to define      the custom logic for handling incoming events.        Example usage:        ```python      class MyEventConsumer(CoreEventConsumerBase):          def process_event(self, event: Event):              # Custom event processing logic here              print(f"Received event created at : {event.creation_date}")              pass        consumer = MyEventConsumer("consumer_1", event_queue)      consumer.start()      # ...      consumer.stop()      ```        Subclasses should implement the `process_event` method to define their specific event handling behavior.        Attributes:          queue (SimpleQueue): The queue from which events will be consumed.          """        def __init__(self, registration_id: str, queue: SimpleQueue):          """Initialize a CoreEventConsumerBase instance.            Parameters:              registration_id (str): A unique identifier of the registration. You can get a                  registration id invoking `Notifier.register()^` method.              queue (SimpleQueue): The queue from which events will be consumed. You can get a                  queue invoking `Notifier.register()^` method.          """          threading.Thread.__init__(self, name=f"Thread-Taipy-Core-Consumer-{registration_id}")          self.daemon = True          self.queue = queue          self.__STOP_FLAG = False          self._TIMEOUT = 0.1        def start(self):          """Start the event consumer thread."""          self.__STOP_FLAG = False          threading.Thread.start(self)        def stop(self):          """Stop the event consumer thread."""          self.__STOP_FLAG = True        def run(self):          while not self.__STOP_FLAG:              try:                  event: Event = self.queue.get(block=True, timeout=self._TIMEOUT)                  self.process_event(event)              except Empty:                  pass        @abc.abstractmethod      def process_event(self, event: Event):          """This method should be overridden in subclasses to define how events are processed."""          raise NotImplementedError


import re  import uuid  from datetime import datetime  from typing import Any, Dict, Optional    from taipy.config.common.frequency import Frequency    from .._entity._entity import _Entity  from .._entity._labeled import _Labeled  from .._entity._properties import _Properties  from .._entity._reload import _Reloader, _self_reload, _self_setter  from ..exceptions.exceptions import _SuspiciousFileOperation  from ..notification.event import Event, EventEntityType, EventOperation, _make_event  from .cycle_id import CycleId      class Cycle(_Entity, _Labeled):      """An iteration of a recurrent work pattern.        Attributes:          id (str): The unique identifier of the cycle.          frequency (Frequency^): The frequency of this cycle.          creation_date (datetime): The date and time of the creation of this cycle.          start_date (datetime): The date and time of the start of this cycle.          end_date (datetime): The date and time of the end of this cycle.          name (str): The name of this cycle.          properties (dict[str, Any]): A dictionary of additional properties.      """        _ID_PREFIX = "CYCLE"      __SEPARATOR = "_"      _MANAGER_NAME = "cycle"        def __init__(          self,          frequency: Frequency,          properties: Dict[str, Any],          creation_date: datetime,          start_date: datetime,          end_date: datetime,          name: Optional[str] = None,          id: Optional[CycleId] = None,      ):          self._frequency = frequency          self._creation_date = creation_date          self._start_date = start_date          self._end_date = end_date          self._name = self._new_name(name)          self.id = id or self._new_id(self._name)          self._properties = _Properties(self, **properties)        def _new_name(self, name: Optional[str] = None) -> str:          if name:              return name          if self._frequency == Frequency.DAILY:              # Example "Monday, 2. January 2023"              return self._start_date.strftime("%A, %d. %B %Y")          if self._frequency == Frequency.WEEKLY:              # Example "Week 01 2023, from 2. January"              return self._start_date.strftime("Week %W %Y, from %d. %B")          if self._frequency == Frequency.MONTHLY:              # Example "January 2023"              return self._start_date.strftime("%B %Y")          if self._frequency == Frequency.QUARTERLY:              # Example "2023 Q1"              return f"{self._start_date.strftime('%Y')} Q{(self._start_date.month-1)//3+1}"          if self._frequency == Frequency.YEARLY:              # Example "2023"              return self._start_date.strftime("%Y")          return Cycle.__SEPARATOR.join([str(self._frequency.value), self._start_date.ctime()])        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def frequency(self):          return self._frequency        @frequency.setter  # type: ignore      @_self_setter(_MANAGER_NAME)      def frequency(self, val):          self._frequency = val        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def creation_date(self):          return self._creation_date        @creation_date.setter  # type: ignore      @_self_setter(_MANAGER_NAME)      def creation_date(self, val):          self._creation_date = val        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def start_date(self):          return self._start_date        @start_date.setter  # type: ignore      @_self_setter(_MANAGER_NAME)      def start_date(self, val):          self._start_date = val        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def end_date(self):          return self._end_date        @end_date.setter  # type: ignore      @_self_setter(_MANAGER_NAME)      def end_date(self, val):          self._end_date = val        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def name(self):          return self._name        @name.setter  # type: ignore      @_self_setter(_MANAGER_NAME)      def name(self, val):          self._name = val        @property      def properties(self):          self._properties = _Reloader()._reload(self._MANAGER_NAME, self)._properties          return self._properties        @staticmethod      def _new_id(name: str) -> CycleId:          def _get_valid_filename(name: str) -> str:              """              Source: https://github.com/django/django/blob/main/django/utils/text.py              """              s = str(name).strip().replace(" ", "_")              s = re.sub(r"(?u)[^-\w.]", "", s)              if s in {"", ".", ".."}:                  raise _SuspiciousFileOperation("Could not derive file name from '%s'" % name)              s = str(s).strip().replace(" ", "_")              return re.sub(r"(?u)[^-\w.]", "", s)            return CycleId(_get_valid_filename(Cycle.__SEPARATOR.join([Cycle._ID_PREFIX, name, str(uuid.uuid4())])))        def __getattr__(self, attribute_name):          protected_attribute_name = attribute_name          if protected_attribute_name in self._properties:              return self._properties[protected_attribute_name]          raise AttributeError(f"{attribute_name} is not an attribute of cycle {self.id}")        def __eq__(self, other):          return self.id == other.id        def __hash__(self):          return hash(self.id)        def get_label(self) -> str:          """Returns the cycle label.            Returns:              The label of the cycle as a string.          """          return self._get_label()        def get_simple_label(self) -> str:          """Returns the cycle simple label.            Returns:              The simple label of the cycle as a string.          """          return self._get_simple_label()      @_make_event.register(Cycle)  def _make_event_for_cycle(      cycle: Cycle,      operation: EventOperation,      /,      attribute_name: Optional[str] = None,      attribute_value: Optional[Any] = None,      **kwargs,  ) -> Event:      metadata = {**kwargs}      return Event(          entity_type=EventEntityType.CYCLE,          entity_id=cycle.id,          operation=operation,          attribute_name=attribute_name,          attribute_value=attribute_value,          metadata=metadata,      )


import calendar  from datetime import datetime, time, timedelta  from typing import Callable, Dict, List, Optional    from taipy.config.common.frequency import Frequency    from .._entity._entity_ids import _EntityIds  from .._manager._manager import _Manager  from .._repository._abstract_repository import _AbstractRepository  from ..job._job_manager_factory import _JobManagerFactory  from ..notification import EventEntityType, EventOperation, _publish_event  from ..submission._submission_manager_factory import _SubmissionManagerFactory  from .cycle import Cycle  from .cycle_id import CycleId      class _CycleManager(_Manager[Cycle]):      _ENTITY_NAME = Cycle.__name__      _repository: _AbstractRepository      _EVENT_ENTITY_TYPE = EventEntityType.CYCLE        @classmethod      def _create(          cls, frequency: Frequency, name: Optional[str] = None, creation_date: Optional[datetime] = None, **properties      ):          creation_date = creation_date if creation_date else datetime.now()          start_date = _CycleManager._get_start_date_of_cycle(frequency, creation_date)          end_date = _CycleManager._get_end_date_of_cycle(frequency, start_date)          cycle = Cycle(              frequency, properties, creation_date=creation_date, start_date=start_date, end_date=end_date, name=name          )          cls._set(cycle)          _publish_event(              cls._EVENT_ENTITY_TYPE,              EventOperation.CREATION,              entity_id=cycle.id,          )          return cycle        @classmethod      def _get_or_create(          cls, frequency: Frequency, creation_date: Optional[datetime] = None, name: Optional[str] = None      ) -> Cycle:          creation_date = creation_date if creation_date else datetime.now()          start_date = _CycleManager._get_start_date_of_cycle(frequency, creation_date)          cycles = cls._get_cycles_by_frequency_and_start_date(              frequency=frequency, start_date=start_date, cycles=cls._get_all()          )          if len(cycles) > 0:              return cycles[0]          else:              return cls._create(frequency=frequency, creation_date=creation_date, name=name)        @staticmethod      def _get_start_date_of_cycle(frequency: Frequency, creation_date: datetime):          start_date = creation_date.date()          start_time = time()          if frequency == Frequency.DAILY:              start_date = start_date          if frequency == Frequency.WEEKLY:              start_date = start_date - timedelta(days=start_date.weekday())          if frequency == Frequency.MONTHLY:              start_date = start_date.replace(day=1)          if frequency == Frequency.YEARLY:              start_date = start_date.replace(day=1, month=1)          return datetime.combine(start_date, start_time)        @staticmethod      def _get_end_date_of_cycle(frequency: Frequency, start_date: datetime):          end_date = start_date          if frequency == Frequency.DAILY:              end_date = end_date + timedelta(days=1)          if frequency == Frequency.WEEKLY:              end_date = end_date + timedelta(7 - end_date.weekday())          if frequency == Frequency.MONTHLY:              last_day_of_month = calendar.monthrange(start_date.year, start_date.month)[1]              end_date = end_date.replace(day=last_day_of_month) + timedelta(days=1)          if frequency == Frequency.YEARLY:              end_date = end_date.replace(month=12, day=31) + timedelta(days=1)          return end_date - timedelta(microseconds=1)        @classmethod      def _hard_delete(cls, cycle_id: CycleId):          cycle = cls._get(cycle_id)          entity_ids_to_delete = cls._get_children_entity_ids(cycle)          entity_ids_to_delete.cycle_ids.add(cycle.id)          cls._delete_entities_of_multiple_types(entity_ids_to_delete)        @classmethod      def _get_children_entity_ids(cls, cycle: Cycle) -> _EntityIds:          from ..scenario._scenario_manager_factory import _ScenarioManagerFactory            entity_ids = _EntityIds()            scenarios = _ScenarioManagerFactory._build_manager()._get_all_by_cycle(cycle)            for scenario in scenarios:              entity_ids.scenario_ids.add(scenario.id)              owner_ids = {scenario.id, cycle.id}              for sequence in scenario.sequences.values():                  if sequence.owner_id in owner_ids:                      entity_ids.sequence_ids.add(sequence.id)              for task in scenario.tasks.values():                  if task.owner_id in owner_ids:                      entity_ids.task_ids.add(task.id)              for data_node in scenario.data_nodes.values():                  if data_node.owner_id in owner_ids:                      entity_ids.data_node_ids.add(data_node.id)            jobs = _JobManagerFactory._build_manager()._get_all()          for job in jobs:              if job.task.id in entity_ids.task_ids:                  entity_ids.job_ids.add(job.id)            submissions = _SubmissionManagerFactory._build_manager()._get_all()          submitted_entity_ids = list(entity_ids.scenario_ids.union(entity_ids.sequence_ids, entity_ids.task_ids))          for submission in submissions:              if submission.entity_id in submitted_entity_ids:                  entity_ids.submission_ids.add(submission.id)            return entity_ids        @classmethod      def _get_cycles_by_frequency_and_start_date(          cls, frequency: Frequency, start_date: datetime, cycles: List[Cycle]      ) -> List[Cycle]:          return cls._get_cycles_cdt(              lambda cycle: cycle.frequency == frequency and cycle.start_date == start_date, cycles          )        @classmethod      def _get_cycles_by_frequency_and_overlapping_date(          cls, frequency: Frequency, date: datetime, cycles: List[Cycle]      ) -> List[Cycle]:          return cls._get_cycles_cdt(              lambda cycle: cycle.frequency == frequency and cycle.start_date <= date <= cycle.end_date, cycles          )        @classmethod      def _get_cycles_cdt(cls, cdt: Callable[[Cycle], bool], cycles: List[Cycle]) -> List[Cycle]:          return [cycle for cycle in cycles if cdt(cycle)]


#  # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with  # the License. You may obtain a copy of the License at  #  #        http://www.apache.org/licenses/LICENSE-2.0  #  # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on  # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the  # specific language governing permissions and limitations under the License.


#  # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with  # the License. You may obtain a copy of the License at  #  #        http://www.apache.org/licenses/LICENSE-2.0  #  # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on  # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the  # specific language governing permissions and limitations under the License.  from .._repository._filesystem_repository import _FileSystemRepository  from ._cycle_converter import _CycleConverter  from ._cycle_model import _CycleModel      class _CycleFSRepository(_FileSystemRepository):      def __init__(self):          super().__init__(model_type=_CycleModel, converter=_CycleConverter, dir_name="cycles")


#  # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with  # the License. You may obtain a copy of the License at  #  #        http://www.apache.org/licenses/LICENSE-2.0  #  # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on  # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the  # specific language governing permissions and limitations under the License.  from .._repository._sql_repository import _SQLRepository  from ._cycle_converter import _CycleConverter  from ._cycle_model import _CycleModel      class _CycleSQLRepository(_SQLRepository):      def __init__(self):          super().__init__(model_type=_CycleModel, converter=_CycleConverter)


from typing import NewType    CycleId = NewType("CycleId", str)  CycleId.__doc__ = """Type that holds a `Cycle^` identifier."""


from dataclasses import dataclass  from typing import Any, Dict    from sqlalchemy import JSON, Column, Enum, String, Table    from taipy.config.common.frequency import Frequency    from .._repository._base_taipy_model import _BaseModel  from .._repository.db._sql_base_model import mapper_registry  from .cycle_id import CycleId      @mapper_registry.mapped  @dataclass  class _CycleModel(_BaseModel):      __table__ = Table(          "cycle",          mapper_registry.metadata,          Column("id", String, primary_key=True),          Column("name", String),          Column("frequency", Enum(Frequency)),          Column("properties", JSON),          Column("creation_date", String),          Column("start_date", String),          Column("end_date", String),      )      id: CycleId      name: str      frequency: Frequency      properties: Dict[str, Any]      creation_date: str      start_date: str      end_date: str        @staticmethod      def from_dict(data: Dict[str, Any]):          return _CycleModel(              id=data["id"],              name=data["name"],              frequency=Frequency._from_repr(data["frequency"]),              properties=_BaseModel._deserialize_attribute(data["properties"]),              creation_date=data["creation_date"],              start_date=data["start_date"],              end_date=data["end_date"],          )        def to_list(self):          return [              self.id,              self.name,              repr(self.frequency),              _BaseModel._serialize_attribute(self.properties),              self.creation_date,              self.start_date,              self.end_date,          ]


from datetime import datetime    from .._repository._abstract_converter import _AbstractConverter  from ..cycle._cycle_model import _CycleModel  from ..cycle.cycle import Cycle      class _CycleConverter(_AbstractConverter):      @classmethod      def _entity_to_model(cls, cycle: Cycle) -> _CycleModel:          return _CycleModel(              id=cycle.id,              name=cycle._name,              frequency=cycle._frequency,              creation_date=cycle._creation_date.isoformat(),              start_date=cycle._start_date.isoformat(),              end_date=cycle._end_date.isoformat(),              properties=cycle._properties.data,          )        @classmethod      def _model_to_entity(cls, model: _CycleModel) -> Cycle:          return Cycle(              id=model.id,              name=model.name,              frequency=model.frequency,              properties=model.properties,              creation_date=datetime.fromisoformat(model.creation_date),              start_date=datetime.fromisoformat(model.start_date),              end_date=datetime.fromisoformat(model.end_date),          )


from typing import Type    from .._manager._manager_factory import _ManagerFactory  from ..common._utils import _load_fct  from ..cycle._cycle_manager import _CycleManager  from ._cycle_fs_repository import _CycleFSRepository  from ._cycle_sql_repository import _CycleSQLRepository      class _CycleManagerFactory(_ManagerFactory):        __REPOSITORY_MAP = {"default": _CycleFSRepository, "sql": _CycleSQLRepository}        @classmethod      def _build_manager(cls) -> Type[_CycleManager]:  # type: ignore          if cls._using_enterprise():              cycle_manager = _load_fct(                  cls._TAIPY_ENTERPRISE_CORE_MODULE + ".cycle._cycle_manager", "_CycleManager"              )  # type: ignore              build_repository = _load_fct(                  cls._TAIPY_ENTERPRISE_CORE_MODULE + ".cycle._cycle_manager_factory", "_CycleManagerFactory"              )._build_repository  # type: ignore          else:              cycle_manager = _CycleManager              build_repository = cls._build_repository          cycle_manager._repository = build_repository()  # type: ignore          return cycle_manager  # type: ignore        @classmethod      def _build_repository(cls):          return cls._get_repository_with_repo_map(cls.__REPOSITORY_MAP)()


from abc import abstractmethod  from importlib import util  from typing import Type    from taipy.config import Config    from ._manager import _Manager      class _ManagerFactory:      _TAIPY_ENTERPRISE_MODULE = "taipy.enterprise"      _TAIPY_ENTERPRISE_CORE_MODULE = _TAIPY_ENTERPRISE_MODULE + ".core"        @classmethod      @abstractmethod      def _build_manager(cls) -> Type[_Manager]:  # type: ignore          raise NotImplementedError        @classmethod      def _build_repository(cls):          raise NotImplementedError        @classmethod      def _using_enterprise(cls) -> bool:          return util.find_spec(cls._TAIPY_ENTERPRISE_MODULE) is not None        @staticmethod      def _get_repository_with_repo_map(repository_map: dict):          return repository_map.get(Config.core.repository_type, repository_map.get("default"))


import pathlib  from importlib import metadata  from typing import Dict, Generic, Iterable, List, Optional, TypeVar, Union    from taipy.logger._taipy_logger import _TaipyLogger    from .._entity._entity_ids import _EntityIds  from .._repository._abstract_repository import _AbstractRepository  from ..exceptions.exceptions import ModelNotFound  from ..notification import Event, EventOperation, Notifier    EntityType = TypeVar("EntityType")      class _Manager(Generic[EntityType]):      _repository: _AbstractRepository      _logger = _TaipyLogger._get_logger()      _ENTITY_NAME: str = "Entity"        @classmethod      def _delete_all(cls):          """          Deletes all entities.          """          cls._repository._delete_all()          if hasattr(cls, "_EVENT_ENTITY_TYPE"):              Notifier.publish(                  Event(                      cls._EVENT_ENTITY_TYPE,                      EventOperation.DELETION,                      metadata={"delete_all": True},                  )              )        @classmethod      def _delete_many(cls, ids: Iterable):          """          Deletes entities by a list of ids.          """          cls._repository._delete_many(ids)          if hasattr(cls, "_EVENT_ENTITY_TYPE"):              for entity_id in ids:                  Notifier.publish(                      Event(                          cls._EVENT_ENTITY_TYPE,  # type: ignore                          EventOperation.DELETION,                          entity_id=entity_id,                          metadata={"delete_all": True},                      )                  )        @classmethod      def _delete_by_version(cls, version_number: str):          """          Deletes entities by version number.          """          cls._repository._delete_by(attribute="version", value=version_number)          if hasattr(cls, "_EVENT_ENTITY_TYPE"):              Notifier.publish(                  Event(                      cls._EVENT_ENTITY_TYPE,  # type: ignore                      EventOperation.DELETION,                      metadata={"delete_by_version": version_number},                  )              )        @classmethod      def _delete(cls, id):          """          Deletes an entity by id.          """          cls._repository._delete(id)          if hasattr(cls, "_EVENT_ENTITY_TYPE"):              Notifier.publish(                  Event(                      cls._EVENT_ENTITY_TYPE,                      EventOperation.DELETION,                      entity_id=id,                  )              )        @classmethod      def _set(cls, entity: EntityType):          """          Save or update an entity.          """          cls._repository._save(entity)        @classmethod      def _get_all(cls, version_number: Optional[str] = "all") -> List[EntityType]:          """          Returns all entities.          """          filters: List[Dict] = []          return cls._repository._load_all(filters)        @classmethod      def _get_all_by(cls, filters: Optional[List[Dict]] = None) -> List[EntityType]:          """          Returns all entities based on a criteria.          """          if not filters:              filters = []          return cls._repository._load_all(filters)        @classmethod      def _get(cls, entity: Union[str, EntityType], default=None) -> EntityType:          """          Returns an entity by id or reference.          """          entity_id = entity if isinstance(entity, str) else entity.id  # type: ignore          try:              return cls._repository._load(entity_id)          except ModelNotFound:              cls._logger.error(f"{cls._ENTITY_NAME} not found: {entity_id}")              return default        @classmethod      def _exists(cls, entity_id: str) -> bool:          """          Returns True if the entity id exists.          """          return cls._repository._exists(entity_id)        @classmethod      def _delete_entities_of_multiple_types(cls, _entity_ids: _EntityIds):          """          Deletes entities of multiple types.          """          from ..cycle._cycle_manager_factory import _CycleManagerFactory          from ..data._data_manager_factory import _DataManagerFactory          from ..job._job_manager_factory import _JobManagerFactory          from ..scenario._scenario_manager_factory import _ScenarioManagerFactory          from ..sequence._sequence_manager_factory import _SequenceManagerFactory          from ..submission._submission_manager_factory import _SubmissionManagerFactory          from ..task._task_manager_factory import _TaskManagerFactory            _CycleManagerFactory._build_manager()._delete_many(_entity_ids.cycle_ids)          _SequenceManagerFactory._build_manager()._delete_many(_entity_ids.sequence_ids)          _ScenarioManagerFactory._build_manager()._delete_many(_entity_ids.scenario_ids)          _TaskManagerFactory._build_manager()._delete_many(_entity_ids.task_ids)          _JobManagerFactory._build_manager()._delete_many(_entity_ids.job_ids)          _DataManagerFactory._build_manager()._delete_many(_entity_ids.data_node_ids)          _SubmissionManagerFactory._build_manager()._delete_many(_entity_ids.submission_ids)        @classmethod      def _export(cls, id: str, folder_path: Union[str, pathlib.Path]):          """          Export an entity.          """          return cls._repository._export(id, folder_path)        @classmethod      def _is_editable(cls, entity: Union[EntityType, _EntityIds]) -> bool:          return True        @classmethod      def _is_readable(cls, entity: Union[EntityType, _EntityIds]) -> bool:          return True


import uuid  from typing import List, Optional, Union    from taipy.config import Config  from taipy.config._config_comparator._comparator_result import _ComparatorResult  from taipy.config.checker.issue_collector import IssueCollector  from taipy.config.exceptions.exceptions import InconsistentEnvVariableError  from taipy.logger._taipy_logger import _TaipyLogger    from .._manager._manager import _Manager  from ..exceptions.exceptions import ConflictedConfigurationError, ModelNotFound, NonExistingVersion  from ._version import _Version  from ._version_fs_repository import _VersionFSRepository      class _VersionManager(_Manager[_Version]):      _ENTITY_NAME = _Version.__name__        __logger = _TaipyLogger._get_logger()        __DEVELOPMENT_VERSION = ["development", "dev"]      __LATEST_VERSION = "latest"      __PRODUCTION_VERSION = "production"      __ALL_VERSION = ["all", ""]        _DEFAULT_VERSION = __LATEST_VERSION        _repository: _VersionFSRepository        @classmethod      def _get(cls, entity: Union[str, _Version], default=None) -> _Version:          """          Returns the version entity by id or reference.          """          entity_id = entity if isinstance(entity, str) else entity.id          try:              return cls._repository._load(entity_id)          except ModelNotFound:              return default        @classmethod      def _get_or_create(cls, id: str, force: bool) -> _Version:          if version := cls._get(id):              comparator_result = Config._comparator._find_conflict_config(version.config, Config._applied_config, id)              if comparator_result.get(_ComparatorResult.CONFLICTED_SECTION_KEY):                  if force:                      cls.__logger.warning(                          f"Option --force is detected, overriding the configuration of version {id} ..."                      )                      version.config = Config._applied_config                  else:                      raise ConflictedConfigurationError()            else:              version = _Version(id=id, config=Config._applied_config)            cls._set(version)          return version        @classmethod      def _get_all(cls, version_number: Optional[Union[str, List]] = "all") -> List[_Version]:          """          Returns all entities.          """          version_number = cls._replace_version_number(version_number)  # type: ignore          if not isinstance(version_number, List):              version_number = [version_number] if version_number else []          filters = [{"version": version} for version in version_number]          return cls._repository._load_all(filters)        @classmethod      def _set_development_version(cls, version_number: str) -> str:          cls._get_or_create(version_number, force=True)          cls._repository._set_development_version(version_number)          return version_number        @classmethod      def _get_development_version(cls) -> str:          try:              return cls._repository._get_development_version()          except (FileNotFoundError, ModelNotFound):              return cls._set_development_version(str(uuid.uuid4()))        @classmethod      def _set_experiment_version(cls, version_number: str, force: bool = False) -> str:          if version_number == cls._get_development_version():              raise SystemExit(                  f"Version number {version_number} is the development version. Please choose a different name"                  f" for this experiment."              )            if version_number in cls._get_production_versions():              raise SystemExit(                  f"Version number {version_number} is already a production version. Please choose a different name"                  f" for this experiment."              )            try:              cls._get_or_create(version_number, force)          except ConflictedConfigurationError:              raise SystemExit(                  f"Please add a new experiment version or run your application with --force option to"                  f" override the Config of experiment {version_number}."              )          cls._repository._set_latest_version(version_number)          return version_number        @classmethod      def _get_latest_version(cls) -> str:          try:              return cls._repository._get_latest_version()          except (FileNotFoundError, ModelNotFound):              # If there is no version in the system yet, create a new version as development version              # This set the default versioning behavior on Jupyter notebook to Development mode              return cls._set_development_version(str(uuid.uuid4()))        @classmethod      def _set_production_version(cls, version_number: str, force: bool = False) -> str:          if version_number == cls._get_development_version():              cls._set_development_version(str(uuid.uuid4()))            try:              cls._get_or_create(version_number, force)          except ConflictedConfigurationError:              raise SystemExit(                  f"Please add a new production version with migration functions.\n"                  f"If old entities remain compatible with the new configuration, you can also run your application with"                  f" --force option to override the production configuration of version {version_number}."              )          cls._repository._set_production_version(version_number)          return version_number        @classmethod      def _get_production_versions(cls) -> List[str]:          try:              return cls._repository._get_production_versions()          except (FileNotFoundError, ModelNotFound):              return []        @classmethod      def _delete_production_version(cls, version_number) -> str:          return cls._repository._delete_production_version(version_number)        @classmethod      def _replace_version_number(cls, version_number: Optional[str] = None):          if version_number is None:              version_number = cls._replace_version_number(cls._DEFAULT_VERSION)                production_versions = cls._get_production_versions()              if version_number in production_versions:                  return production_versions                return version_number            if version_number == cls.__LATEST_VERSION:              return cls._get_latest_version()          if version_number in cls.__DEVELOPMENT_VERSION:              return cls._get_development_version()          if version_number == cls.__PRODUCTION_VERSION:              return cls._get_production_versions()          if version_number in cls.__ALL_VERSION:              return ""            try:              if version := cls._get(version_number):                  return version.id          except InconsistentEnvVariableError:  # The version exist but the Config is alternated              return version_number            raise NonExistingVersion(version_number)        @classmethod      def _manage_version(cls):          from ..taipy import clean_all_entities_by_version            if Config.core.mode == "development":              current_version_number = cls._get_development_version()              cls.__logger.info(f"Development mode: Clean all entities of version {current_version_number}")              clean_all_entities_by_version(current_version_number)              cls._set_development_version(current_version_number)            elif Config.core.mode in ["experiment", "production"]:              default_version_number = {                  "experiment": str(uuid.uuid4()),                  "production": cls._get_latest_version(),              }              version_setter = {                  "experiment": cls._set_experiment_version,                  "production": cls._set_production_version,              }                if Config.core.version_number:                  current_version_number = Config.core.version_number              else:                  current_version_number = default_version_number[Config.core.mode]                version_setter[Config.core.mode](current_version_number, Config.core.force)              if Config.core.mode == "production":                  cls.__check_production_migration_config()          else:              raise SystemExit(f"Undefined execution mode: {Config.core.mode}.")        @classmethod      def __check_production_migration_config(self):          from ..config.checkers._migration_config_checker import _MigrationConfigChecker            collector = _MigrationConfigChecker(Config._applied_config, IssueCollector())._check()          for issue in collector._warnings:              self.__logger.warning(str(issue))          for issue in collector._infos:              self.__logger.info(str(issue))          for issue in collector._errors:              self.__logger.error(str(issue))          if len(collector._errors) != 0:              raise SystemExit("Configuration errors found. Please check the error log for more information.")        @classmethod      def _delete_entities_of_multiple_types(cls, _entity_ids):          raise NotImplementedError


from datetime import datetime  from typing import Any    from taipy.config import Config  from taipy.config._config import _Config    from .._entity._entity import _Entity      class _Version(_Entity):      def __init__(self, id: str, config: Any) -> None:          self.id: str = id          self.config: _Config = config          self.creation_date: datetime = datetime.now()        def __eq__(self, other):          return self.id == other.id and self.__is_config_eq(other)        def __is_config_eq(self, other):          return Config._serializer._str(self.config) == Config._serializer._str(other.config)


from typing import Dict, List    from .._version._version_manager_factory import _VersionManagerFactory      class _VersionMixin:        _version_manager = _VersionManagerFactory._build_manager()        @classmethod      def __fetch_version_number(cls, version_number):          version_number = _VersionManagerFactory._build_manager()._replace_version_number(version_number)            if not isinstance(version_number, List):              version_number = [version_number] if version_number else []          return version_number        @classmethod      def _build_filters_with_version(cls, version_number) -> List[Dict]:          filters = []          if versions := cls.__fetch_version_number(version_number):              filters = [{"version": version} for version in versions]          return filters        @classmethod      def _get_latest_version(cls):          return cls._version_manager._get_latest_version()


from .._manager._manager_factory import _ManagerFactory  from ..common import _utils  from ._version_fs_repository import _VersionFSRepository  from ._version_manager import _VersionManager  from ._version_sql_repository import _VersionSQLRepository      class _VersionManagerFactory(_ManagerFactory):        __REPOSITORY_MAP = {"default": _VersionFSRepository, "sql": _VersionSQLRepository}        @classmethod      def _build_manager(cls) -> _VersionManager:  # type: ignore          if cls._using_enterprise():              version_manager = _utils._load_fct(                  cls._TAIPY_ENTERPRISE_CORE_MODULE + "._version._version_manager", "_VersionManager"              )  # type: ignore              build_repository = _utils._load_fct(                  cls._TAIPY_ENTERPRISE_CORE_MODULE + "._version._version_manager_factory", "_VersionManagerFactory"              )._build_repository  # type: ignore          else:              version_manager = _VersionManager              build_repository = cls._build_repository          version_manager._repository = build_repository()  # type: ignore          return version_manager  # type: ignore        @classmethod      def _build_repository(cls):          return cls._get_repository_with_repo_map(cls.__REPOSITORY_MAP)()


from abc import ABC, abstractmethod      class _VersionRepositoryInterface(ABC):      _LATEST_VERSION_KEY = "latest_version"      _DEVELOPMENT_VERSION_KEY = "development_version"      _PRODUCTION_VERSION_KEY = "production_version"        @abstractmethod      def _set_latest_version(self, version_number):          raise NotImplementedError        @abstractmethod      def _get_latest_version(self):          raise NotImplementedError        @abstractmethod      def _set_development_version(self, version_number):          raise NotImplementedError        @abstractmethod      def _get_development_version(self):          raise NotImplementedError        @abstractmethod      def _set_production_version(self, version_number):          raise NotImplementedError        @abstractmethod      def _get_production_versions(self):          raise NotImplementedError        @abstractmethod      def _delete_production_version(self, version_number):          raise NotImplementedError


import json  from typing import List    from taipy.logger._taipy_logger import _TaipyLogger    from .._repository._filesystem_repository import _FileSystemRepository  from ..exceptions.exceptions import VersionIsNotProductionVersion  from ._version_converter import _VersionConverter  from ._version_model import _VersionModel  from ._version_repository_interface import _VersionRepositoryInterface      class _VersionFSRepository(_FileSystemRepository, _VersionRepositoryInterface):      def __init__(self):          super().__init__(model_type=_VersionModel, converter=_VersionConverter, dir_name="version")        @property      def _version_file_path(self):          return super()._storage_folder / "version.json"        def _delete_all(self):          super()._delete_all()            if self._version_file_path.exists():              self._version_file_path.unlink()        def _set_latest_version(self, version_number):          if self._version_file_path.exists():              with open(self._version_file_path, "r") as f:                  file_content = json.load(f)              file_content[self._LATEST_VERSION_KEY] = version_number          else:              self.dir_path.mkdir(parents=True, exist_ok=True)              file_content = {                  self._LATEST_VERSION_KEY: version_number,                  self._DEVELOPMENT_VERSION_KEY: "",                  self._PRODUCTION_VERSION_KEY: [],              }          self._version_file_path.write_text(              json.dumps(                  file_content,                  ensure_ascii=False,                  indent=0,              )          )        def _get_latest_version(self) -> str:          with open(self._version_file_path, "r") as f:              file_content = json.load(f)          return file_content[self._LATEST_VERSION_KEY]        def _set_development_version(self, version_number):          if self._version_file_path.exists():              with open(self._version_file_path, "r") as f:                  file_content = json.load(f)                file_content[self._DEVELOPMENT_VERSION_KEY] = version_number              file_content[self._LATEST_VERSION_KEY] = version_number          else:              self.dir_path.mkdir(parents=True, exist_ok=True)              file_content = {                  self._LATEST_VERSION_KEY: version_number,                  self._DEVELOPMENT_VERSION_KEY: version_number,                  self._PRODUCTION_VERSION_KEY: [],              }          self._version_file_path.write_text(              json.dumps(                  file_content,                  ensure_ascii=False,                  indent=0,              )          )        def _get_development_version(self) -> str:          with open(self._version_file_path, "r") as f:              file_content = json.load(f)          return file_content[self._DEVELOPMENT_VERSION_KEY]        def _set_production_version(self, version_number):          if self._version_file_path.exists():              with open(self._version_file_path, "r") as f:                  file_content = json.load(f)              file_content[self._LATEST_VERSION_KEY] = version_number              if version_number not in file_content[self._PRODUCTION_VERSION_KEY]:                  file_content[self._PRODUCTION_VERSION_KEY].append(version_number)              else:                  _TaipyLogger._get_logger().info(f"Version {version_number} is already a production version.")          else:              self.dir_path.mkdir(parents=True, exist_ok=True)              file_content = {                  self._LATEST_VERSION_KEY: version_number,                  self._DEVELOPMENT_VERSION_KEY: "",                  self._PRODUCTION_VERSION_KEY: [version_number],              }          self._version_file_path.write_text(              json.dumps(                  file_content,                  ensure_ascii=False,                  indent=0,              )          )        def _get_production_versions(self) -> List[str]:          with open(self._version_file_path, "r") as f:              file_content = json.load(f)          return file_content[self._PRODUCTION_VERSION_KEY]        def _delete_production_version(self, version_number):          try:              with open(self._version_file_path, "r") as f:                  file_content = json.load(f)              if version_number not in file_content[self._PRODUCTION_VERSION_KEY]:                  raise VersionIsNotProductionVersion(f"Version '{version_number}' is not a production version.")              file_content[self._PRODUCTION_VERSION_KEY].remove(version_number)              self._version_file_path.write_text(                  json.dumps(                      file_content,                      ensure_ascii=False,                      indent=0,                  )              )          except FileNotFoundError:              raise VersionIsNotProductionVersion(f"Version '{version_number}' is not a production version.")


from sqlalchemy.dialects import sqlite    from .._repository._sql_repository import _SQLRepository  from ..exceptions.exceptions import ModelNotFound, VersionIsNotProductionVersion  from ._version_converter import _VersionConverter  from ._version_model import _VersionModel  from ._version_repository_interface import _VersionRepositoryInterface      class _VersionSQLRepository(_SQLRepository, _VersionRepositoryInterface):      def __init__(self):          super().__init__(model_type=_VersionModel, converter=_VersionConverter)        def _set_latest_version(self, version_number):          if old_latest := self.db.execute(str(self.table.select().filter_by(is_latest=True))).fetchone():              old_latest = self.model_type.from_dict(old_latest)              old_latest.is_latest = False              self._update_entry(old_latest)            version = self.__get_by_id(version_number)          version.is_latest = True          self._update_entry(version)        def _get_latest_version(self):          if latest := self.db.execute(              str(self.table.select().filter_by(is_latest=True).compile(dialect=sqlite.dialect()))          ).fetchone():              return latest["id"]          raise ModelNotFound(self.model_type, "")        def _set_development_version(self, version_number):          if old_development := self.db.execute(str(self.table.select().filter_by(is_development=True))).fetchone():              old_development = self.model_type.from_dict(old_development)              old_development.is_development = False              self._update_entry(old_development)            version = self.__get_by_id(version_number)          version.is_development = True          self._update_entry(version)            self._set_latest_version(version_number)        def _get_development_version(self):          if development := self.db.execute(str(self.table.select().filter_by(is_development=True))).fetchone():              return development["id"]          raise ModelNotFound(self.model_type, "")        def _set_production_version(self, version_number):          version = self.__get_by_id(version_number)          version.is_production = True          self._update_entry(version)            self._set_latest_version(version_number)        def _get_production_versions(self):          if productions := self.db.execute(              str(self.table.select().filter_by(is_production=True).compile(dialect=sqlite.dialect())),          ).fetchall():              return [p["id"] for p in productions]          return []        def _delete_production_version(self, version_number):          version = self.__get_by_id(version_number)            if not version or not version.is_production:              raise VersionIsNotProductionVersion(f"Version '{version_number}' is not a production version.")          version.is_production = False          self._update_entry(version)        def __get_by_id(self, version_id):          query = str(self.table.select().filter_by(id=version_id).compile(dialect=sqlite.dialect()))          entry = self.db.execute(query, [version_id]).fetchone()          return self.model_type.from_dict(entry) if entry else None


from datetime import datetime    from taipy.config import Config    from .._repository._abstract_converter import _AbstractConverter  from .._version._version import _Version  from .._version._version_model import _VersionModel      class _VersionConverter(_AbstractConverter):      @classmethod      def _entity_to_model(cls, version: _Version) -> _VersionModel:          return _VersionModel(              id=version.id, config=Config._to_json(version.config), creation_date=version.creation_date.isoformat()          )        @classmethod      def _model_to_entity(cls, model: _VersionModel) -> _Version:          version = _Version(id=model.id, config=Config._from_json(model.config))          version.creation_date = datetime.fromisoformat(model.creation_date)          return version


from dataclasses import dataclass  from typing import Any, Dict    from sqlalchemy import Boolean, Column, String, Table    from .._repository._base_taipy_model import _BaseModel  from .._repository.db._sql_base_model import mapper_registry      @mapper_registry.mapped  @dataclass  class _VersionModel(_BaseModel):      __table__ = Table(          "version",          mapper_registry.metadata,          Column("id", String, primary_key=True),          Column("config", String),  # config is store as a json string          Column("creation_date", String),          Column("is_production", Boolean),          Column("is_development", Boolean),          Column("is_latest", Boolean),      )      id: str      config: Dict[str, Any]      creation_date: str        @staticmethod      def from_dict(data: Dict[str, Any]):          model = _VersionModel(              id=data["id"],              config=data["config"],              creation_date=data["creation_date"],          )          model.is_production = data.get("is_production")  # type: ignore          model.is_development = data.get("is_development")  # type: ignore          model.is_latest = data.get("is_latest")  # type: ignore          return model        def to_list(self):          return [              self.id,              self.config,              self.creation_date,              self.is_production,              self.is_development,              self.is_latest,          ]


from typing import Callable, List    from taipy.config.config import Config    from .._entity._reload import _Reloader  from ..config import MigrationConfig  from ._version_manager_factory import _VersionManagerFactory      def _migrate_entity(entity):      if (          latest_version := _VersionManagerFactory._build_manager()._get_latest_version()      ) in _VersionManagerFactory._build_manager()._get_production_versions():          if migration_fcts := __get_migration_fcts_to_latest(entity._version, entity.config_id):              with _Reloader():                  for fct in migration_fcts:                      entity = fct(entity)                  entity._version = latest_version        return entity      def __get_migration_fcts_to_latest(source_version: str, config_id: str) -> List[Callable]:      migration_fcts_to_latest: List[Callable] = []        production_versions = _VersionManagerFactory._build_manager()._get_production_versions()      try:          start_index = production_versions.index(source_version) + 1      except ValueError:          return migration_fcts_to_latest        versions_to_migrate = production_versions[start_index:]        for version in versions_to_migrate:          migration_fct = Config.unique_sections[MigrationConfig.name].migration_fcts.get(version, {}).get(config_id)          if migration_fct:              migration_fcts_to_latest.append(migration_fct)        return migration_fcts_to_latest


import sys    from taipy._cli._base_cli import _CLI  from taipy.config import Config  from taipy.config.exceptions.exceptions import InconsistentEnvVariableError  from taipy.logger._taipy_logger import _TaipyLogger    from ...data._data_manager_factory import _DataManagerFactory  from ...exceptions.exceptions import VersionIsNotProductionVersion  from ...job._job_manager_factory import _JobManagerFactory  from ...scenario._scenario_manager_factory import _ScenarioManagerFactory  from ...sequence._sequence_manager_factory import _SequenceManagerFactory  from ...taipy import clean_all_entities_by_version  from ...task._task_manager_factory import _TaskManagerFactory  from .._version_manager_factory import _VersionManagerFactory  from ._bcolor import _Bcolors      class _VersionCLI:      """Command-line interface of the versioning system."""        __logger = _TaipyLogger._get_logger()        @classmethod      def create_parser(cls):          version_parser = _CLI._add_subparser("manage-versions", help="Taipy version control system.")            version_parser.add_argument(              "-l", "--list", action="store_true", help="List all existing versions of the Taipy application."          )            version_parser.add_argument(              "--rename", nargs=2, metavar=("OLD_VERSION", "NEW_VERSION"), help="Rename a Taipy version."          )            version_parser.add_argument(              "--compare-config",              nargs=2,              metavar=("VERSION_1", "VERSION_2"),              help="Compare the Configuration of 2 Taipy versions.",          )            version_parser.add_argument(              "-d", "--delete", metavar="VERSION", help="Delete a Taipy version by version number."          )            version_parser.add_argument(              "-dp",              "--delete-production",              metavar="VERSION",              help="Delete a Taipy version from production by version number. The version is still kept as an experiment "              "version.",          )        @classmethod      def parse_arguments(cls):          args = _CLI._parse()            if getattr(args, "which", None) != "manage-versions":              return            if args.list:              print(cls.__list_versions())              sys.exit(0)            if args.rename:              try:                  cls.__rename_version(args.rename[0], args.rename[1])              except InconsistentEnvVariableError as error:                  cls.__logger.error(                      f"Fail to rename version {args.rename[0]} to {args.rename[1]} due to outdated Configuration."                      f"Detail: {str(error)}"                  )                  sys.exit(1)                cls.__logger.info(f"Successfully renamed version '{args.rename[0]}' to '{args.rename[1]}'.")              sys.exit(0)            if args.compare_config:              cls.__compare_version_config(args.compare_config[0], args.compare_config[1])              sys.exit(0)            if args.delete_production:              try:                  _VersionManagerFactory._build_manager()._delete_production_version(args.delete_production)                  cls.__logger.info(                      f"Successfully delete version {args.delete_production} from the production version list."                  )                  sys.exit(0)              except VersionIsNotProductionVersion as e:                  raise SystemExit(e)            if args.delete:              if clean_all_entities_by_version(args.delete):                  cls.__logger.info(f"Successfully delete version {args.delete}.")              else:                  sys.exit(1)                sys.exit(0)        @classmethod      def __list_versions(cls):          list_version_message = f"\n{'Version number':<36}   {'Mode':<20}   {'Creation date':<20}\n"            latest_version_number = _VersionManagerFactory._build_manager()._get_latest_version()          development_version_number = _VersionManagerFactory._build_manager()._get_development_version()          production_version_numbers = _VersionManagerFactory._build_manager()._get_production_versions()            versions = _VersionManagerFactory._build_manager()._get_all()          versions.sort(key=lambda x: x.creation_date, reverse=True)            bold = False          for version in versions:              if version.id == development_version_number:                  list_version_message += _Bcolors.GREEN                  mode = "Development"              elif version.id in production_version_numbers:                  list_version_message += _Bcolors.PURPLE                  mode = "Production"              else:                  list_version_message += _Bcolors.BLUE                  mode = "Experiment"                if version.id == latest_version_number:                  list_version_message += _Bcolors.BOLD                  bold = True                  mode += " (latest)"                list_version_message += (                  f"{(version.id):<36}   {mode:<20}   {version.creation_date.strftime('%Y-%m-%d %H:%M:%S'):<20}"              )              list_version_message += _Bcolors.END                if bold:                  list_version_message += _Bcolors.END              list_version_message += "\n"            return list_version_message        @classmethod      def __rename_version(cls, old_version: str, new_version: str):          _version_manager = _VersionManagerFactory._build_manager()            # Check if the new version already exists, return an error          if _version_manager._get(new_version):              cls.__logger.error(f"Version name '{new_version}' is already used.")              sys.exit(1)            # Make sure that all entities of the old version are exists and loadable          version_entity = _version_manager._get(old_version)          if version_entity is None:              cls.__logger.error(f"Version '{old_version}' does not exist.")              sys.exit(1)            jobs = _JobManagerFactory._build_manager()._get_all(version_number=old_version)          scenarios = _ScenarioManagerFactory._build_manager()._get_all(version_number=old_version)          sequences = _SequenceManagerFactory._build_manager()._get_all(version_number=old_version)          tasks = _TaskManagerFactory._build_manager()._get_all(version_number=old_version)          datanodes = _DataManagerFactory._build_manager()._get_all(version_number=old_version)            # Update the version of all entities          for job in jobs:              job._version = new_version              _JobManagerFactory._build_manager()._set(job)          for scenario in scenarios:              scenario._version = new_version              _ScenarioManagerFactory._build_manager()._set(scenario)          for sequence in sequences:              sequence._version = new_version              _SequenceManagerFactory._build_manager()._set(sequence)          for task in tasks:              task._version = new_version              _TaskManagerFactory._build_manager()._set(task)          for datanode in datanodes:              datanode._version = new_version              _DataManagerFactory._build_manager()._set(datanode)            # Update the version entity          if old_version in _version_manager._get_production_versions():              _version_manager._set_production_version(new_version)          if old_version == _version_manager._get_latest_version():              _version_manager._set_experiment_version(new_version)          if old_version == _version_manager._get_development_version():              _version_manager._set_development_version(new_version)          _version_manager._delete(old_version)            try:              _version_manager._delete_production_version(old_version)          except VersionIsNotProductionVersion:              pass            if not _version_manager._get(new_version):              version_entity.id = new_version              _version_manager._set(version_entity)        @classmethod      def __compare_version_config(cls, version_1: str, version_2: str):          version_entity_1 = _VersionManagerFactory._build_manager()._get(version_1)          if version_entity_1 is None:              cls.__logger.error(f"Version '{version_1}' does not exist.")              sys.exit(1)            version_entity_2 = _VersionManagerFactory._build_manager()._get(version_2)          if version_entity_2 is None:              cls.__logger.error(f"Version '{version_2}' does not exist.")              sys.exit(1)            Config._comparator._compare(              version_entity_1.config,              version_entity_2.config,              version_1,              version_2,          )


#  # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with  # the License. You may obtain a copy of the License at  #  #        http://www.apache.org/licenses/LICENSE-2.0  #  # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on  # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the  # specific language governing permissions and limitations under the License.


import os  import sys      def _vt_codes_enabled_in_windows_registry():      """      Check the Windows Registry to see if VT code handling has been enabled      by default, see https://superuser.com/a/1300251/447564.      """      try:          # winreg is only available on Windows.          import winreg      except ImportError:          return False      else:          try:              reg_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Console")              reg_key_value, _ = winreg.QueryValueEx(reg_key, "VirtualTerminalLevel")          except FileNotFoundError:              return False          else:              return reg_key_value == 1      def _is_color_supported():      """      Return True if the running system's terminal supports color,      and False otherwise.      """        is_a_tty = hasattr(sys.stdout, "isatty") and sys.stdout.isatty()        return is_a_tty and (          sys.platform != "win32"          or "ANSICON" in os.environ          or "WT_SESSION" in os.environ  # Windows Terminal supports VT codes.          or os.environ.get("TERM_PROGRAM") == "vscode"  # VSCode's built-in terminal supports colors.          or _vt_codes_enabled_in_windows_registry()      )      class _Bcolors:      PURPLE = "\033[95m" if _is_color_supported() else ""      BLUE = "\033[94m" if _is_color_supported() else ""      CYAN = "\033[96m" if _is_color_supported() else ""      GREEN = "\033[92m" if _is_color_supported() else ""      BOLD = "\033[1m" if _is_color_supported() else ""      UNDERLINE = "\033[4m" if _is_color_supported() else ""      END = "\033[0m" if _is_color_supported() else ""


from .exceptions import *