
    i9                    t   d Z ddlmZ ddlZddlZddlmZ ddlmZmZm	Z	 ddl
mZ ddlmZ ddlZddlmZ d	d
ddddZe G d d                      ZdKdZdLdZdMdZdNdZ	 	 	 dOdPd!Z	 dQdRd%ZdSd'Z	 dTdUd,ZdVd.Z	 dWdXd0ZdYd1Z	 	 	 	 	 dZd[d9ZdVd:Zd\d>Z d]d?Z!d^dAZ"d_dBZ#	 dWd`dDZ$dadFZ%	 dWdbdJZ&dS )caC  
CalendarObjectResource operations - Sans-I/O business logic.

This module contains pure functions for working with calendar objects
(events, todos, journals) without performing any network I/O.
Both sync and async clients use these same functions.

These functions work on icalendar component objects or raw data strings.
    )annotationsN)	dataclass)datetime	timedeltatimezone)Any)quote)rrulestrCHILDPARENTSIBLINGFINISHTOSTART	DEPENDENT)r   r   r   z
DEPENDS-ONr   c                  <    e Zd ZU dZded<   ded<   ded<   ded<   dS )CalendarObjectDataz&Data extracted from a calendar object.
str | NoneuidurletagdataN)__name__
__module____qualname____doc____annotations__     _/root/projects/butler/venv/lib/python3.11/site-packages/caldav/operations/calendarobject_ops.pyr   r   !   sB         00OOOOOOr   r   returnstrc                 B    t          t          j                              S )z)Generate a new UID for a calendar object.)r    uuiduuid4r   r   r   _generate_uidr$   +   s    tz||r   r   c                H    t          |                     dd                    S )z
    URL-quote a UID for use in a CalDAV object URL.

    Slashes are double-quoted (replaced with %2F before percent-encoding)
    per https://github.com/python-caldav/caldav/issues/143.
    /z%2F)r	   replace)r   s    r   
_quote_uidr(   0   s      S%(()))r   
parent_urlc                b    t          |          }|                     d          s| dz  } |  | dS )a)  
    Generate a URL for a calendar object based on its UID.

    Handles special characters in UID by proper quoting.

    Args:
        parent_url: URL of the parent calendar (must end with /)
        uid: The UID of the calendar object

    Returns:
        Full URL for the calendar object
    r&   .ics)r(   endswith)r)   r   
quoted_uids      r   _generate_urlr.   :   sC     CJs## c
******r   pathr   c                    |                      d          sdS t          j        d|           }|r|                    d          S dS )z
    Extract UID from a .ics file path.

    Args:
        path: Path like "/calendars/user/calendar/event-uid.ics"

    Returns:
        The UID portion, or None if not found
    r+   Nz(/|^)([^/]*).ics$   )r,   researchgroup)r/   matchs     r   _extract_uid_from_pathr6   M   sL     ==   tI*D11E {{1~~4r   	componentr   given_id
given_pathexisting_idtuple[str, str]c                ^   |p|}|s&|                      d          }|rt          |          }|s&|r$|                    d          rt          |          }|st	                      }d| v r|                     d           |                     d|           |r|}nt          |          dz   }||fS )a-  
    Determine the UID and path for a calendar object.

    This is Sans-I/O logic extracted from CalendarObjectResource._find_id_path().

    Priority:
    1. given_id parameter
    2. existing_id (from object)
    3. UID from component
    4. UID extracted from path
    5. Generate new UID

    Args:
        component: icalendar component (VEVENT, VTODO, etc.)
        given_id: Explicitly provided ID
        given_path: Explicitly provided path
        existing_id: ID already set on the object

    Returns:
        Tuple of (uid, relative_path)
    UIDr+   )getr    r,   r6   r$   popaddr(   )r7   r8   r9   r:   r   uid_propr/   s          r   _find_id_and_pathrB   _   s    6 
!kC  =='' 	 h--C 1: 1*"5"5f"="= 1$Z00 oo 	eMM%  (#'9r   DTEND	end_paramr   c                (   d| v r| d         j         S d| v r|| v r| |         j         }| d         j         }t          |t                    t          |t                    k    rjt          |t                    s t          |j        |j        |j                  }t          |t                    s t          |j        |j        |j                  }||z
  S d| v r2| d         j         }t          |t                    st          d          S t          d          S )a  
    Get duration from a calendar component.

    According to the RFC, either DURATION or DTEND/DUE should be set,
    but never both. This function calculates duration from whichever is present.

    Args:
        component: icalendar component (VEVENT, VTODO, etc.)
        end_param: The end parameter name ("DTEND" for events, "DUE" for todos)

    Returns:
        Duration as timedelta
    DURATIONDTSTART   )daysr   )dt
isinstancer   yearmonthdayr   )r7   rD   endstartdtstarts        r   _get_durationrR      s   " Y$''I)y"8"8	"%)$' c8$$
5((C(CCCeX.. E U[%)DDc8,, =sxCG<<U{ II&)'8,, 	%!$$$$Q<<r   datetime | Nonec                    d| v r| d         j         S d| v r| d         j         S d| v rd| v r| d         j         | d         j         z   S dS )z
    Get due date from a VTODO component.

    Handles DUE, DTEND, or DURATION+DTSTART.

    Args:
        component: icalendar VTODO component

    Returns:
        Due date/datetime, or None if not set
    DUErC   rF   rG   N)rJ   r7   s    r   _get_duerW      sk     	""	I		!$$	y	 	 Y)%;%;#&:)>)AAA4r   rG   durationmovable_attrNonec                D   d| v pd| v }d| v }|r|r|                      |d           |dk    r|                      dd           |dk    r&|                     d| d         j        |z
             dS |dk    r&|                     d| d         j        |z              dS dS d| v r&|                     d| d         j        |z
             dS d| v r&|                     d| d         j        |z              dS d| v r|                      d           |                     d|           dS )a)  
    Set duration on a component, adjusting other properties as needed.

    If both DTSTART and DUE/DTEND are set, one must be moved.

    Args:
        component: icalendar component to modify
        duration: New duration
        movable_attr: Which attribute to move ("DTSTART" or "DUE")
    rU   rF   rG   N)r?   r@   rJ   )r7   rX   rY   has_due	has_starts        r   _set_durationr^      sk    y ;J)$;GY&I ,9 ,lD)))5  MM*d+++9$$MM)Yu%5%88%CDDDDDU""MM%9!5!88!CDDDDD #"	)		i5!1!4x!?@@@@@	i		eYy14x?@@@@@""MM*%%%j(+++++r   boolc                x    |                      d          dS |                      dd          }|dv rdS |dv rdS dS )	z
    Check if a VTODO component is pending (not completed).

    Args:
        component: icalendar VTODO component

    Returns:
        True if task is pending, False if completed/cancelled
    	COMPLETEDNFSTATUSNEEDS-ACTION)rc   z
IN-PROCESST)	CANCELLEDra   )r>   )r7   statuss     r   _is_task_pendingrf      sW     }}[!!-u]]8^44F///t+++u 4r   completion_timestampc                    |t          j        t          j                  }|                     dd           |                     dd           |                     d|           dS )z
    Mark a VTODO component as completed.

    Modifies the component in place.

    Args:
        component: icalendar VTODO component
        completion_timestamp: When the task was completed (defaults to now)
    Nrb   ra   )r   nowr   utcr?   r@   )r7   rg   s     r   _mark_task_completedrk     s_     #'|HL99MM(D!!!MM(K(((MM+344444r   c                    |                      dd           |                      dd           |                     dd           |                      dd           |                      dd           dS )zj
    Mark a VTODO component as not completed.

    Args:
        component: icalendar VTODO component
    re   Nrb   rc   	completedra   )r?   r@   rV   s    r   _mark_task_uncompletedrn   (  sn     MM(D!!!MM(D!!!MM(N+++MM+t$$$MM+t$$$$$r   Trrule
Any | NonerQ   use_fixed_deadlinesbool | Noneignore_countc                   ||                      d          }|dS |t          d |D                       }|h|r3d| v r| d         j        }nT|pt          j        t
          j                  }n3t          | d          }|pt          j        t
          j                  |z
  }t          |d          r|	                    t
          j                  }|p|}|r-d|v r)|
                                }|                    d           t          |                                                    d          |	          }|                    |          S )
a  
    Calculate the next DTSTART for a recurring task after completion.

    This implements the logic from Todo._next().

    Args:
        component: icalendar VTODO component with RRULE
        completion_timestamp: When the task was completed
        rrule: Override RRULE (default: from component)
        dtstart: Override DTSTART (default: calculated based on use_fixed_deadlines)
        use_fixed_deadlines: If True, preserve DTSTART from component.
                            If False, use completion time minus duration.
                            If None, auto-detect from BY* parameters in rrule.
        ignore_count: If True, ignore COUNT in RRULE

    Returns:
        Next DTSTART datetime, or None if no more recurrences
    NRRULEc              3  D   K   | ]}|                     d           |V  dS )BYN)
startswith.0xs     r   	<genexpr>z-_calculate_next_recurrence.<locals>.<genexpr>W  s3      !I!Iall46H6H!I!!I!I!I!I!I!Ir   rG   rU   
astimezoneCOUNTzutf-8)rQ   )r>   anyrJ   r   ri   r   rj   rR   hasattrr}   copyr?   r
   to_icaldecodeafter)	r7   rg   ro   rQ   rq   rs   rX   ts	rrule_objs	            r   _calculate_next_recurrencer   6  sW   4 }g&&=4 "!!I!IU!I!I!III  	VI%%#I.1.L(,x|2L2L$Y66H+Ix|HL/I/IXUG w%% 3$$X\22		(B  5((

		' //88'JJJI??2r   c                    d| vrdS | d         }|                     dd          }|Mt          |t                    r|d         n|}|dk    rdS t          |t                    r	|dz
  |d<   n|dz
  |d<   dS )z
    Reduce the COUNT in an RRULE by 1.

    Args:
        component: icalendar component with RRULE

    Returns:
        False if COUNT was 1 (task should end), True otherwise
    ru   Tr~   Nr   rH   F)r>   rK   list)r7   ro   count	count_vals       r   _reduce_rrule_countr   t  s     itgEIIgt$$E *5$ 7 7BE!HHU	>>5eT"" 	+ 1}E!HH&]E'N4r   r   vobject_instanceicalendar_instancec                Z    t          | r|                     d          dk    p|p|          S )z
    Check if calendar object data is loaded.

    Args:
        data: Raw iCalendar data string
        vobject_instance: vobject instance (if any)
        icalendar_instance: icalendar instance (if any)

    Returns:
        True if data is loaded
    zBEGIN:rH   )r_   r   )r   r   r   s      r   _is_calendar_data_loadedr     s4      2$**X..2]7G]K]^^^r   c                    | sdS |                      d          |                      d          z   |                      d          z   dk    S )z
    Check if data contains VEVENT, VTODO, or VJOURNAL.

    Args:
        data: Raw iCalendar data string

    Returns:
        True if a calendar component is present
    FzBEGIN:VEVENTzBEGIN:VTODOzBEGIN:VJOURNALr   )r   )r   s    r   _has_calendar_componentr     sR      u 	

>""TZZ%>%>>L\A]A]]	
 
r   	list[Any]c                $    d | j         D             S )z
    Get all subcomponents except VTIMEZONE.

    Args:
        icalendar_instance: icalendar.Calendar instance

    Returns:
        List of non-timezone subcomponents
    c                F    g | ]}t          |t          j                  |S r   )rK   	icalendarTimezonery   s     r   
<listcomp>z3_get_non_timezone_subcomponents.<locals>.<listcomp>  s*    aaa!z!YM_?`?`aAaaar   )subcomponents)r   s    r   _get_non_timezone_subcomponentsr     s     ba)7aaaar   c                    t          |           }|sdS |D ]B}t          |t          j        t          j        t          j        t          j        f          r|c S CdS )aN  
    Get the primary (non-timezone) component from a calendar.

    For events/todos/journals, there should be exactly one.
    For recurrence sets, returns the master component.

    Args:
        icalendar_instance: icalendar.Calendar instance

    Returns:
        The primary component (VEVENT, VTODO, VJOURNAL, or VFREEBUSY)
    N)r   rK   r   EventTodoJournalFreeBusy)r   
componentscomps      r   _get_primary_componentr     sp     11CDDJ t  _ini.?AST
 
 	 KKK		 4r   new_uidc                    |                                  }|                    dd           |                    d|pt                                 |S )z
    Create a copy of a component with a new UID.

    Args:
        component: icalendar component to copy
        new_uid: New UID (generated if not provided)

    Returns:
        Copy of the component with new UID
    r=   N)r   r?   r@   r$   )r7   r   new_comps      r   _copy_component_with_new_uidr     sI     ~~HLLLL2=??333Or   reltypec                Z    t                               |                                           S )z
    Get the reverse relation type for a given relation type.

    Args:
        reltype: Relation type (e.g., "PARENT", "CHILD")

    Returns:
        Reverse relation type, or None if not defined
    )RELTYPE_REVERSE_MAPr>   upper)r   s    r   _get_reverse_reltyper     s      ""7==??333r   reltypes
set | Nonedict[str, set]c                F   ddl m}  |t                    }|                     dg           }t	          |t
                    s|g}|D ]L}|j                            dd          }|r||vr$||                             t          |                     Mt          |          S )z
    Extract RELATED-TO relations from a component.

    Args:
        component: icalendar component
        reltypes: Optional set of relation types to filter

    Returns:
        Dict mapping reltype -> set of UIDs
    r   )defaultdictz
RELATED-TORELTYPEr   )
collectionsr   setr>   rK   r   paramsr@   r    dict)r7   r   r   result	relationsrelr   s          r   _extract_relationsr      s     ('''''[FlB//Ii&&  K	 & &*..H55 	x//wCHH%%%%<<r   )r   r    )r   r    r   r    )r)   r    r   r    r   r    )r/   r    r   r   )NNN)
r7   r   r8   r   r9   r   r:   r   r   r;   )rC   )r7   r   rD   r    r   r   )r7   r   r   rS   )rG   )r7   r   rX   r   rY   r    r   rZ   )r7   r   r   r_   )N)r7   r   rg   rS   r   rZ   )r7   r   r   rZ   )NNNNT)r7   r   rg   rS   ro   rp   rQ   rS   rq   rr   rs   r_   r   rS   )r   r   r   r   r   r   r   r_   )r   r   r   r_   )r   r   r   r   )r   r   r   rp   )r7   r   r   r   r   r   )r   r    r   r   )r7   r   r   r   r   r   )'r   
__future__r   r2   r"   dataclassesr   r   r   r   typingr   urllib.parser	   r   dateutil.rruler
   r   r   r$   r(   r.   r6   rB   rR   rW   r^   rf   rk   rn   r   r   r   r   r   r   r   r   r   r   r   r   <module>r      s    # " " " " " 				  ! ! ! ! ! ! 2 2 2 2 2 2 2 2 2 2                 # # # # # # !              
* * * *+ + + +&   (  !"	6 6 6 6 6v ( ( ( ( (V   0 "!, !, !, !, !,H   2 -15 5 5 5 5*% % % %  -1#'+; ; ; ; ;|   8_ _ _ _&
 
 
 
$b b b b   :     (
4 
4 
4 
4        r   