
    	]j@              	          d Z ddlZddlZddlmZmZ ddlmZmZ ddlZddl	m
Z
 ddlmZ ddlmZ  ej                  e      Zdej$                  fd	Zd
ej$                  defdZd dedee   deej$                  ej$                  f   fdZdedej$                  fdZdej$                  defdZ G d d      Z G d d      Zd!dZd defdZd dee   fdZ	 d"dedee   fdZdededee   fdZ dededee   fdZ!y)#aP  
FSM utility functions.

This module provides:
1. UUID7 utilities for time-series optimization (uses uuid-utils library)
2. FSM-specific helper functions for organization resolution and state management

UUID7 provides natural time ordering and global uniqueness, making it ideal
for INSERT-only architectures with millions of records.
    N)datetimetimezone)OptionalTuple)CurrentContext)	load_func)settingsreturnc                  f    t        j                         } t        j                  t	        |             S )ai  
    Generate a UUID7 with embedded timestamp for natural time ordering.

    UUID7 embeds the timestamp in the first 48 bits, providing:
    - Natural chronological ordering without additional indexes
    - Global uniqueness across distributed systems
    - Time-based partitioning capabilities

    Returns:
        UUID7 instance with embedded timestamp
    )
uuid_utilsuuid7uuidUUIDstr)	uuid7_objs    @/root/env/lib/python3.12/site-packages/label_studio/fsm/utils.pygenerate_uuid7r      s%       "I99S^$$    uuid7_idc                 v    | j                   dz	  dz  }t        j                  |dz  t        j                        S )aI  
    Extract timestamp from UUID7 ID.

    Args:
        uuid7_id: UUID7 instance to extract timestamp from

    Returns:
        datetime: Timestamp embedded in the UUID7

    Example:
        uuid7_id = generate_uuid7()
        timestamp = timestamp_from_uuid7(uuid7_id)
        # timestamp is when the UUID7 was generated
    P   l      )tz)intr   fromtimestampr   utc)r   timestamp_mss     r   timestamp_from_uuid7r   0   s3      LLB&=9L!!,"5(,,GGr   
start_timeend_timec                 R   |#t        j                  t        j                        }t	        | j                         dz        dz
  }t	        |j                         dz        dz   }t        j                  |dz  dz  dz        }t        j                  |dz  dz  dz  dz        }||fS )a  
    Generate UUID7 range for time-based queries.

    Creates UUID7 boundaries for efficient time-range filtering without
    requiring timestamp indexes.

    Args:
        start_time: Start of time range
        end_time: End of time range (defaults to now)

    Returns:
        Tuple of (start_uuid, end_uuid) for range queries

    Example:
        start_uuid, end_uuid = uuid7_time_range(
            datetime(2024, 1, 1),
            datetime(2024, 1, 2)
        )
        # Query: WHERE id >= start_uuid AND id <= end_uuid
    r      r                             r   l    )r   nowr   r   r   	timestampr   r   )r   r    start_timestamp_msend_timestamp_ms
start_uuidend_uuids         r   uuid7_time_ranger,   E   s    * <<- Z113d:;a?8--/$67!;  2b 8YG:VWJyy."4CzRVcdeHxr   r'   c                 x    t        | j                         dz        }t        j                  |dz  dz  dz        S )a  
    Generate UUID7 from specific timestamp for range queries.

    Args:
        timestamp: Timestamp to embed in UUID7

    Returns:
        UUID7 with embedded timestamp

    Example:
        # Get all states from the last hour
        start_time = timezone.now() - timedelta(hours=1)
        start_uuid = uuid7_from_timestamp(start_time)
        states = StateModel.objects.filter(id__gte=start_uuid)
    r   r   r#   r$   r%   )r   r'   r   r   )r'   r   s     r   uuid7_from_timestampr.   i   s<    " y**,t34L
 99,",;zJKKr   
uuid_valuec                      | j                   dk(  S )z
    Validate that a UUID is a valid UUID7.

    Args:
        uuid_value: UUID to validate

    Returns:
        True if valid UUID7, False otherwise
       )version)r/   s    r   validate_uuid7r3      s     ""r   c                   X    e Zd ZdZed        Zed	dedee   fd       Zedefd       Z	y)

UUID7Fieldz
    Custom field utilities for UUID7 handling in Django models.

    Provides helper methods for UUID7-specific operations that can be
    used by models inheriting from BaseState.
    c                 @    | j                  d      j                         S )z.Get latest record using UUID7 natural orderingz-id)order_byfirst)querysets    r   get_latest_by_uuid7zUUID7Field.get_latest_by_uuid7   s       '--//r   Nr   r    c                 F    t        ||      \  }}| j                  ||      S )z=Filter queryset by time range using UUID7 embedded timestamps)id__gteid__lte)r,   filter)r9   r   r    r*   r+   s        r   filter_by_time_rangezUUID7Field.filter_by_time_range   s(      0
HE
Hz8DDr   sincec                 <    t        |      }| j                  |      S )z1Filter queryset for records since a specific time)r<   )r.   r>   )r9   r@   r*   s      r   filter_since_timezUUID7Field.filter_since_time   s     *%0
z22r   N)
__name__
__module____qualname____doc__staticmethodr:   r   r   r?   rB    r   r   r5   r5      sb     0 0 E8 ExPXGY E E
 38 3 3r   r5   c                   J    e Zd ZdZddee   fdZd	dedej                  fdZ
y)
UUID7Generatorz
    UUID7 generator with optional custom timestamp.

    Useful for testing or when you need to generate UUIDs with specific timestamps.
    Nbase_timestampc                 j    |xs# t        j                  t        j                        | _        d| _        y)z
        Initialize generator with optional base timestamp.

        Args:
            base_timestamp: Base timestamp to use (defaults to current time)
        r   N)r   r&   r   r   rL   _counter)selfrL   s     r   __init__zUUID7Generator.__init__   s%     -JX\\0Jr   	offset_msr
   c                     t        | j                  j                         dz        |z   }| xj                  dz  c_        |dz  dz  | j                  dz  dz  z  dz  }t	        j
                  |      S )	z
        Generate UUID7 with timestamp offset.

        Args:
            offset_ms: Millisecond offset from base timestamp

        Returns:
            UUID7 with adjusted timestamp
        r   r"   r   r#   i  @   r$   r%   )r   rL   r'   rN   r   r   )rO   rQ   r   uuid_ints       r   generatezUUID7Generator.generate   so     4..88:TABYN !B&95$--%:OTV9VW[efyyX&&r   rC   )r   )rD   rE   rF   rG   r   r   rP   r   r   r   rU   rI   r   r   rK   rK      s/    x'9 '# 'dii 'r   rK   c                    t        j                         }|r|S | yt        | dd      }|st        | d      r$| j                  rt        | j                  dd      }net        | d      rY| j
                  rMt        | j
                  d      r7| j
                  j                  r!t        | j
                  j                  dd      }|s0|r.t        |d      r"|j                  r|j                  j                  }|t        j                  |       |S )a  
    Resolve organization_id using consistent logic without additional queries.

    This provides organization_id resolution for logging and state tracking
    without duplicating database queries.

    Args:
        entity: The entity to resolve organization_id for
        user: Optional user for fallback organization resolution

    Returns:
        organization_id or None
    Norganization_idprojecttaskactive_organization)	r   get_organization_idgetattrhasattrrX   rY   rZ   idset_organization_id)entityuserrW   s      r   resolve_organization_idrb      s     %88:O ~ f&7>O 69%&..%fnn6GNOVV$i9X]c]h]h]p]p%fkk&9&9;LdSO t6K(LQUQiQi2255 "**?;r   c                 *    t        j                         S )a  
    Check if FSM is enabled via feature flags and thread-local override.

    PERFORMANCE: This function now checks the cached FSM state that was set
    when the user was first initialized in CurrentContext. This avoids repeated
    feature flag lookups throughout the request.

    The check order is:
    1. Check thread-local override (for test cleanup, bulk operations)
    2. Check cached feature flag state (set once per request)
    3. Fallback to direct feature flag check (for edge cases without context)

    Args:
        user: User for feature flag evaluation (optional, used as fallback only)

    Returns:
        True if FSM should be active
    )r   is_fsm_enabled)ra   s    r   rd   rd     s    * ((**r   c                    t        |      sy	 ddlm}  |       }|j                  |       S # t        $ r}t
        j                  d| j                  j                   d| j                   dt        |       d| j                  j                  | j                  t        | |      t        |      d	       Y d}~yd}~ww xY w)
z
    Safely get current state with error handling.
    Args:
        entity: The entity to get state for
        user: The user making the request (for feature flag checking)
    Returns:
        Current state string or None if failed
    Nr   get_state_managerz Failed to get current state for  : zfsm.get_state_error)evententity_type	entity_idrW   errorextra)rd   fsm.state_managerrg   get_current_state_value	Exceptionloggerwarning_metalabel_lowerpkr   rb   )r`   ra   rg   StateManageres        r   get_current_state_saferz     s     $7(*33F;; .v||/G/G.H&))TVWZ[\W]V^_.%||77#YY#:64#HQ 	 		
 s   , 	B=A>B88B=inferred_statec                    t        |      syt        j                         r|S 	 ddlm}  |       }|j                  |       }|	||k(  s|s|S |_t        j                  d| j                  j                   d| j                   dd| j                  j                  | j                  d	       y| j                  j                  j                         }	t        |	|      }
|
rSt        j                  d
|	 d| j                   d|	| j                  ||
d	       |j                  | |
|||xs i        |S t        j                  d|	 d| d|	| j                  |d	       y# t        $ r~}t        j!                  d| j                  j                   d| j                   dt#        |       d| j                  j                  | j                  t#        |      dd       Y d}~yd}~ww xY w)a^  
    Get current state, or initialize it if it doesn't exist.

    This function handles "cold start" scenarios where pre-existing entities
    don't have FSM state records. It will:
    1. If the state already exists, use that
    2. If the state doesn't exist, infer the state from the entity and initialize it with an appropriate transition
    2. Return the state value (never returns None if initialization succeeds)

    Args:
        entity: The entity to get or initialize state for
        user: User for FSM context
        inferred_state: Pre-computed inferred state
        reason: Custom reason for the state initialization (optional, overrides default reason)
        context_data: Additional context data to store with state record (optional)
        overwrite_state: Overwrite the state if it already exists (optional)
    Returns:
        Current or newly initialized state value, or None if FSM disabled or failed

    Examples:
        >>> task = Task.objects.get(id=123)  # Pre-existing task without state
        >>> from fsm.state_inference import get_or_infer_state
        >>> inferred_state = get_or_infer_state(task)
        >>> state = get_or_initialize_state(task, user=request.user, inferred_state=inferred_state)
        >>> # state is now 'COMPLETED' or 'CREATED' based on task.is_labeled
        >>> # and a state record has been created
    Nr   rf   zCannot initialize state for rh   z - inference failedzfsm.initialize_state_failed)rj   rk   rl   rn   z(Initializing FSM state for pre-existing zfsm.cold_start_initialization)rj   rk   rl   r{   transition_name)r`   r}   ra   reasoncontext_dataz'No initialization transition found for z -> z fsm.no_initialization_transition)rj   rk   rl   r{   z&Failed to get or initialize state for ri   zfsm.get_or_initialize_error)rj   rk   rl   rm   T)ro   exc_info)rd   r   is_fsm_disabledrp   rg   rq   rs   rt   ru   
model_namerw   lower"get_initialization_transition_nameinfoexecute_transitionrr   rm   r   )r`   ra   r{   r~   r   overwrite_staterg   rx   current_staterk   r}   ry   s               r   get_or_initialize_stater   8  s   < $ %%'F7(* %<<VD$->*IQ`  !NN.v||/F/F.GqSfg:#)<<#:#:!'    ll--335<[.YKK:;-qT<#.!'&4'6  	 ++ /)/R ,  "!NN9+d>JZ[?#.!'&4	    4V\\5L5L4MQvyykY[\_`a\b[cd6%||66#YYQ	  	 		
 s,   (E  A E  .BE  3,E   	G')A4G""G'rk   target_statec                    ddl m}m}m} | dk(  r1||j                  k(  ry||j
                  k(  ry||j                  k(  ryy| dk(  r1||j                  k(  ry||j                  k(  ry	||j
                  k(  ry
y| dk(  r||j                  k(  ryy)a7  
    Get the appropriate transition name for initializing an entity to a target state.

    Args:
        entity_type: Type of entity ('task', 'project', 'annotation')
        target_state: The target state to initialize to

    Returns:
        Transition name, or None if no appropriate transition exists
    r   )AnnotationStateChoicesProjectStateChoicesTaskStateChoicesrY   task_createdtask_completedtask_in_progressrX   project_createdproject_in_progressproject_completed
annotationannotation_createdN)fsm.state_choicesr   r   r   CREATED	COMPLETEDIN_PROGRESS)rk   r   r   r   r   s        r   #_get_initialization_transition_namer     s     `_f+333!-777#-999%  
		!.666$0<<<(0:::&
 	 
	$1999'r   c                 B     t        t        j                        | |      S rC   )r   r	   "FSM_INITIALIZATION_TRANSITION_NAME)rk   r   s     r   r   r     s    A9X@@A+|\\r   rC   )NN)NNF)"rG   loggingr   r   r   typingr   r   r   core.current_requestr   core.utils.commonr   django.confr	   	getLoggerrD   rs   r   r   r   r,   r.   boolr3   r5   rK   rb   rd   r   rz   r   r   r   rI   r   r   <module>r      sk  	   ' "  / '  			8	$%		 %$H499 H H*!  ! Xh5G ! SXY]YbYbdhdmdmYmSn ! HLH L L2
#tyy 
#T 
#3 34"' "'T+\+ +0# @ X]k"%kc]k\S  PXY\P] D]C ]s ]xX[} ]r   