
    	]j6                        d Z ddlZddlmZ ddlmZmZmZ ddlmZm	Z	m
Z
 ddlmZ ddlmZ ddlmZ dd	lmZ dd
lmZ  ej,                  e      Z eedd      Ze dZe dZ eedd      Z eedd      Z eedd      Zde de!fdZ"d*de dee   de#fdZ$de dee   fdZ%de fdZ&de fdZ'de#fdZ(dee    fdZ)d*deee       de#fd Z*de#fd!Z+dee    dee,   fd"Z-dee    de#fd#Z.d*d$e de,fd%Z/d&ee,   de,fd'Z0d+d(e#de#fd)Z1y),z
Redis-based user activity tracking system and background tasks for synchronization.

This module provides functionality to cache user last_activity timestamps in Redis
with batch synchronization to the database to reduce database load.
    N)datetime)ListOptionalSet)_redisredis_connectedstart_job_async_or_sync)settings)get_user_model)transaction)timezone)get_connectionUSER_ACTIVITY_REDIS_KEY_PREFIXuser_activity_counter_batchUSER_ACTIVITY_BATCH_SIZEd   USER_ACTIVITY_SYNC_THRESHOLD2   USER_ACTIVITY_REDIS_TTLiQ user_idreturnc                     t          d|  S )z Get Redis key for user activity.:)USER_ACTIVITY_KEY_PREFIX)r   s    T/root/env/lib/python3.12/site-packages/label_studio/users/functions/last_activity.py_get_user_activity_keyr   !   s    &'q	22    	timestampc                    t               sy|t        j                         }	 |j                         }t	        |       }t               }|j                  |t        |       |j                  t        |        |j                  t        t               t               }t        j                  d| |       y# t        $ r!}t        j                  d| |       Y d}~yd}~ww xY w)z
    Set user last activity timestamp in Redis.

    Args:
        user_id: User ID
        timestamp: Activity timestamp (defaults to current time)

    Returns:
        True if successfully set, False otherwise
    FNz+Updated activity for user %s, counter at %sTz+Failed to set user activity for user %s: %s)r   django_timezonenow	isoformatr   r   setex	REDIS_TTLsaddUSER_ACTIVITY_BATCH_KEYexpireincrement_activity_counterloggerdebug	Exceptionerror)r   r    timestamp_str	redis_keyredis_clientcurrent_countes          r   set_user_last_activityr4   &   s     #'')	!++-*73	 &' 	9i? 	17;3Y? 34BG][ BGQOs   BB1 1	C:CCc                 *   t         y	 t        |       }t               }|j                  |      }|r6t	        |t
              r|j                  d      }t        j                  |      S y# t        $ r!}t        j                  d| |       Y d}~yd}~ww xY w)z
    Get user last activity timestamp from Redis with database fallback.

    Args:
        user_id: User ID

    Returns:
        Last activity timestamp or None if not found
    Nzutf-8z+Failed to get user activity for user %s: %s)r   r   r   get
isinstancebytesdecoder   fromisoformatr-   r+   r.   )r   r0   r1   r/   r3   s        r   get_user_last_activityr;   R   s     ~P*73	%'$((3-/ - 4 4W =))-88   PBGQOOPs   AA( (	B1BBc                     t               sy	 t               } | j                  t              }| j	                  t        t
               t        j                  d|       |S # t        $ r }t        j                  d|       Y d}~yd}~ww xY w)zj
    Increment activity counter and return current count.

    Returns:
        Current counter value
    r   z"Activity counter incremented to %sz(Failed to increment activity counter: %sN)
r   r   incrUSER_ACTIVITY_COUNTER_KEYr)   r&   r+   r,   r-   r.   )r1   r2   r3   s      r   r*   r*   o   ss     %'$))*CD5yA9=I ?Cs   AA 	B'BBc                      t               sy	 t               } | j                  t              }|t	        |      S dS # t
        $ r }t        j                  d|       Y d}~yd}~ww xY w)zY
    Get current activity counter value.

    Returns:
        Current counter value
    r   Nz"Failed to get activity counter: %s)r   r   r6   r>   intr-   r+   r.   )r1   countr3   s      r   get_activity_counterrB      sa     %'  !:;".s5z5A5 91=s   +; ; 	A$AA$c                     t               sy	 t               } | j                  t        d       | j	                  t        t
               t        j                  d       y# t        $ r }t        j                  d|       Y d}~yd}~ww xY w)zh
    Reset activity counter to 0.

    Returns:
        True if successfully reset, False otherwise
    Fr   zActivity counter reset to 0Tz$Failed to reset activity counter: %sN)
r   r   setr>   r)   r&   r+   r,   r-   r.   )r1   r3   s     r   reset_activity_counterrE      sn     	%'2A65yA23 ;Q?s   AA 	B&BBc                  4   t               s
t               S 	 t               } | j                  t              }|D ch c]  }|st        |j                                 c}S c c}w # t        $ r*}t        j                  d|       t               cY d}~S d}~ww xY w)zc
    Get all user IDs from batch set.

    Returns:
        Set of user IDs to be synchronized
    z Failed to get batch user IDs: %sN)
r   rD   r   smembersr(   r@   r9   r-   r+   r.   )r1   user_idsuidr3   s       r   get_batch_user_idsrJ      sv     u%'(()@A-5=cCJJL!=== 7;us4   #A$ AAA$ A$ $	B-BBBrH   c                    t               sy	 t               }| r| r- |j                  t        g|   n|j	                  t               t
        j                  d| xs d       y# t        $ r }t
        j                  d|       Y d}~yd}~ww xY w)z
    Clear user IDs from batch set.

    Args:
        user_ids: Specific user IDs to remove (if None, clears all)

    Returns:
        True if successfully cleared, False otherwise
    FzCleared batch user IDs: %sallTz"Failed to clear batch user IDs: %sN)	r   r   sremr(   deleter+   r,   r-   r.   )rH   r1   r3   s      r   clear_batch_user_idsrO      s     %'!!!"9EHE  78183DuE 91=s   AA# #	B,BBc                  f    t               } | t        k\  }|rt        j                  d| t               |S )z
    Check if activities should be synchronized to database.

    Returns:
        True if sync threshold is reached, False otherwise
    z Sync threshold reached: %s >= %s)rB   SYNC_THRESHOLDr+   info)r2   should_syncs     r   should_sync_activitiesrT      s0     )*M>1K6~Vr   c                     t               sg S g }| D ]$  }	 t        |      }|r|j                  ||d       & |S # t        $ r!}t        j                  d||       Y d}~Nd}~ww xY w)z
    Get user activities from Redis for database synchronization.

    Args:
        user_ids: Set of user IDs to get activities for

    Returns:
        List of dictionaries with user_id and last_activity
    )r   last_activityz2Failed to get activity for user %s during sync: %sN)r   r;   appendr-   r+   r.   )rH   
activitiesr   r    r3   s        r   get_user_activities_for_syncrY      sx     	J 	.w7I!!g	"RS	 	  	LLMwXYZ	s   !:	A$AA$c                 8   t               sy	 t               }| D cg c]  }t        |       }}|r |j                  |  t	        |        t
        j                  dt        |              yc c}w # t        $ r }t
        j                  d|       Y d}~yd}~ww xY w)z
    Clean up Redis activity data for given user IDs.

    Args:
        user_ids: Set of user IDs to clean up

    Returns:
        True if successfully cleaned, False otherwise
    Fz"Cleaned up Redis data for %s usersTz)Failed to cleanup Redis activity data: %sN)
r   r   r   rN   rO   r+   r,   lenr-   r.   )rH   r1   r   keys_to_deleter3   s        r   cleanup_redis_activity_datar]     s     %' JRRg09RRL0 	X&93x=I S  @!Ds'   A0 A+=A0 +A0 0	B9BB	max_usersc                 F   | t         } t        j                  d       	 t               }|st        j                  d       dddddS t	        |      | kD  r-t        t        |      d|        }t        j                  d|        t        j                  d	t	        |             t        |      }|s:t        j                  d
t	        |             t        |       ddt	        |      ddS t        |      }|d   rRt        |       t               }|rt	        |      t        k  r)t                t        j                  dt	        |             t        j                  d|       |S # t        $ r5}t        j                  d|d       ddddt        |       dcY d}~S d}~ww xY w)z
    Synchronize user activities from Redis to database.

    Args:
        max_users: Maximum number of users to process (defaults to BATCH_SIZE)

    Returns:
        Dictionary with sync results
    Nz'Starting user activity sync to databasezNo user activities to syncTr   zNo activities to syncsuccess	processederrorsmessagezLimited batch to %s userszSyncing activities for %s usersz#No activity data found for %s userszNo activity data foundra   zBReset activity counter after successful sync (remaining users: %s)zActivity sync completed: %sz"Failed to sync user activities: %sexc_infoF   zSync failed: )
BATCH_SIZEr+   rR   rJ   r[   rD   listrY   warningr]   _bulk_update_user_activitiesrQ   rE   r-   r.   str)r^   rH   rX   sync_resultremaining_usersr3   s         r   sync_user_activities_to_dbro   0  sv    	
KK9:+d%'KK45#!qMdee x=9$4>*956HKK3Y?5s8}E 2(;
NN@#h-P'1#!s8}Yqrr 3:>y!'1 12O"c/&:^&K&(`befubvw1;? d91tL qAMZ]^_Z`YaJbccds+   'E" B E" (A9E" "	F +*FF F rX   c                    | sddddS d}d}	 t        j                         5  t               }| D cg c]  }|d   	 }}|j                  j	                  d      j                  |      j                  dd      }|D ci c]  }|j                  | }}g }	| D ]  }|d   }
|d   }|j                  |
      }|ra|j                  ||j                  kD  r||_	        |	j                  |       |d
z  }Yt        j                  d|
||j                         |d
z  }t        j                  d|
       |d
z  } |	r>|j                  j                  |	dgd       t        j                  dt!        |	             d||t!        |	      dcd	d	d	       S c c}w c c}w # 1 sw Y   y	xY w# t"        $ r8}t        j%                  d|d       d||d
z   dt'        |       dcY d	}~S d	}~ww xY w)z
    Bulk update user activities in database.

    Args:
        activities: List of activity dictionaries

    Returns:
        Dictionary with update results
    Tr   )ra   rb   rc   r   )skip_locked)id__inidrV   Nrg   z0Skipping outdated activity for user %s: %s <= %szUser %s not found in databaser   )
batch_sizezBulk updated %s users)ra   rb   rc   updatedz)Failed to bulk update user activities: %sre   FzBulk update failed: r`   )r   atomicr   objectsselect_for_updatefilteronlyrs   r6   rV   rW   r+   r,   rj   bulk_updaterR   r[   r-   r.   rl   )rX   rb   rc   UseractivityrH   existing_usersuser	user_dictusers_to_updater   new_activityr3   s                r   rk   rk   m  s    a1==IF2
! (	p!#D<FG+GHG..4.@GGxGX]]^bdst  4BB4$BIB !O&  "9-'8 }}W-))1\DDVDV5V-9*'..t4!Q	N#( ..	 "Q	NN#BGLaKF+ 0 ((?:KX[(\3S5IJ#)vZ]^mZnoQ(	p (	p H C(	p (	pT  
@!dS"qj-c!fX6	
 	

sX   F& FFAF?FC3F	F& 
FF#F& #F& &	G'/-G"G'"G'forcec                    | s t               st        j                  d       y	 t        t        dd       t                t        j                  d       y# t        $ r }t        j                  d|       Y d}~yd}~ww xY w)	z
    Schedule user activity synchronization if needed.

    Args:
        force: Force sync even if threshold not reached

    Returns:
        True if sync was scheduled, False otherwise
    z$Sync threshold not reached, skippingFlowT)
queue_nameredisz Scheduled user activity sync jobz$Failed to schedule activity sync: %sN)	rT   r+   r,   r	   ro   rE   rR   r-   r.   )r   r3   s     r   schedule_activity_syncr     sh     /1;<
 :uTXY 67 ;Q?s   1A 	A?A::A?)N)F)2__doc__loggingr   typingr   r   r   
core.redisr   r   r	   django.confr
   django.contrib.authr   	django.dbr   django.utilsr   r"   	django_rqr   	getLogger__name__r+   getattrr   r>   r(   rh   rQ   r&   r@   rl   r   boolr4   r;   r*   rB   rE   rJ   rO   rT   dictrY   r]   ro   rk   r    r   r   <module>r      s     & & G G   . ! 4 $			8	$ #8-M_ 78A 56f=  X93?
#A2FH7?	3C 3C 3
)C )HX4F )RV )XPC PHX,> P:C 0c ( ,CH (8CH#5  @  3s8 T
 6#c( t @:d# :d :dzB
T$Z B
D B
J$ 4 r   