
    YiY1                         d dl mZmZ d dlmZ d dlmZ 	 d dlmZm	Z	 erd dlmZmZ d dl mZmZmZmZmZmZ  G d	 d
      Zy# e
$ r d dlZej                  Zd Z	Y ?w xY w)    )TYPE_CHECKINGcast)contexts)Client)iscoroutinefunctionmarkcoroutinefunctionNc                     | S )N )funcs    e/mnt/e/genesis-system/.venvs/browser-army/lib/python3.12/site-packages/posthog/integrations/django.pyr   r      s        )HttpRequestHttpResponse)CallableDictAnyOptionalUnion	Awaitablec                   T    e Zd ZdZdZdZd Zd Zd Zd Z	d Z
d Zd	 Zd
 Zd Zd Zy)PosthogContextMiddlewarea  Middleware to automatically track Django requests.

    This middleware wraps all calls with a posthog context. It attempts to extract the following from the request headers:
    - Session ID, (extracted from `X-POSTHOG-SESSION-ID`)
    - Distinct ID, (extracted from `X-POSTHOG-DISTINCT-ID`)
    - Request URL as $current_url
    - Request Method as $request_method

    The context will also auto-capture exceptions and send them to PostHog, unless you disable it by setting
    `POSTHOG_MW_CAPTURE_EXCEPTIONS` to `False` in your Django settings. The exceptions are captured using the
    global client, unless the setting `POSTHOG_MW_CLIENT` is set to a custom client instance

    The middleware behaviour is customisable through 3 additional functions:
    - `POSTHOG_MW_EXTRA_TAGS`, which is a Callable[[HttpRequest], Dict[str, Any]] expected to return a dictionary of additional tags to be added to the context.
    - `POSTHOG_MW_REQUEST_FILTER`, which is a Callable[[HttpRequest], bool] expected to return `False` if the request should not be tracked.
    - `POSTHOG_MW_TAG_MAP`, which is a Callable[[Dict[str, Any]], Dict[str, Any]], which you can use to modify the tags before they're added to the context.

    You can use the `POSTHOG_MW_TAG_MAP` function to remove any default tags you don't want to capture, or override them with your own values.

    Context tags are automatically included as properties on all events captured within a context, including exceptions.
    See the context documentation for more information. The extracted distinct ID and session ID, if found, are used to
    associate all events captured in the middleware context with the same distinct ID and session as currently active on the
    frontend. See the documentation for `set_context_session` and `identify_context` for more details.

    This middleware is hybrid-capable: it supports both WSGI (sync) and ASGI (async) Django applications. The middleware
    detects at initialization whether the next middleware in the chain is async or sync, and adapts its behavior accordingly.
    This ensures compatibility with both pure sync and pure async middleware chains, as well as mixed chains in ASGI mode.
    Tc                    || _         t        |      | _        | j                  rt        |        ddlm} t        |d      r1t        |j                        rt        d|j                        | _
        nd | _
        t        |d      r1t        |j                        rt        d|j                        | _        nd | _        t        |d      r1t        |j                        rt        d|j                        | _        nd | _        t        |d	      r,t        |j                   t"              r|j                   | _        nd
| _        t        |d      r6t        |j&                  t(              rt        d|j&                        | _        y d | _        y )Nr   )settingsPOSTHOG_MW_EXTRA_TAGSz1Optional[Callable[[HttpRequest], Dict[str, Any]]]POSTHOG_MW_REQUEST_FILTERz'Optional[Callable[[HttpRequest], bool]]POSTHOG_MW_TAG_MAPz4Optional[Callable[[Dict[str, Any]], Dict[str, Any]]]POSTHOG_MW_CAPTURE_EXCEPTIONSTPOSTHOG_MW_CLIENTzOptional[Client])get_responser   _is_coroutiner   django.confr   hasattrcallabler   r   
extra_tagsr   request_filterr   tag_map
isinstancer   boolcapture_exceptionsr   r   client)selfr   r   s      r   __init__z!PosthogContextMiddleware.__init__9   sZ   (0> !$'(845(**;
 #C..DO
 #DO889h..?
 #'922#D
 #'D812x''8
  F++DL
  DL8<=*22DC
 '/&L&LD#&*D#801j&&7
 183M3MNDKDKr   c                 P    | j                  |      \  }}| j                  |||      S )z*Extract tags from request in sync context.)extract_request_user_build_tagsr+   requestuser_id
user_emails       r   extract_tagsz%PosthogContextMiddleware.extract_tagsq   s.     #77@*==r   c                    i }|j                   j                  d      }|rt        j                  |       |j                   j                  d      xs |}|rt        j                  |       |r||d<   |j                         }|r||d<   |j                  r|j                  |d<   |j                  r|j                  |d<   |j                   j                  d      }|r||d<   |j                   j                  d	      }	|	r|	|d
<   | j                  r$| j                  |      }
|
r|j                  |
       | j                  r| j                  |      }|S )z
        Build tags dict from request and user info.

        Centralized tag extraction logic used by both sync and async paths.
        zX-POSTHOG-SESSION-IDzX-POSTHOG-DISTINCT-IDemailz$current_urlz$request_methodz$request_pathzX-Forwarded-Forz$ip_addressz
User-Agentz$user_agent)headersgetr   set_context_sessionidentify_contextbuild_absolute_urimethodpathr$   updater&   )r+   r1   r2   r3   tags
session_iddistinct_idabsolute_url
ip_address
user_agentextras              r   r/   z$PosthogContextMiddleware._build_tagsw   sA     __(()?@
((4 oo))*ABMg%%k2 &DM 113#/D  >>&-nnD"# <<$+LLD! __(():;
",D __((6
",D ??OOG,EE" <<<<%Dr   c                 >    t        |dd      }| j                  |      S )z7Extract user ID and email from request in sync context.userN)getattr_resolve_user_details)r+   r1   rG   s      r   r.   z-PosthogContextMiddleware.extract_request_user   s#     w-))$//r   c                 l   K   | j                  |       d{   \  }}| j                  |||      S 7 w)a  
        Async version of extract_tags for use in async request handling.

        Uses await request.auser() instead of request.user to avoid
        SynchronousOnlyOperation in async context.

        Follows Django's naming convention for async methods (auser, asave, etc.).
        N)aextract_request_userr/   r0   s       r   aextract_tagsz&PosthogContextMiddleware.aextract_tags   s;      %)$>$>w$GG*== Hs   424c                    K   t        |dd      }t        |      r!	  |        d{   }| j                  |      S y7 # t        $ r Y yw xY ww)a&  
        Async version of extract_request_user for use in async request handling.

        Uses await request.auser() instead of request.user to avoid
        SynchronousOnlyOperation in async context.

        Follows Django's naming convention for async methods (auser, asave, etc.).
        auserN)NN)rH   r#   rI   	Exception)r+   r1   rN   rG   s       r   rK   z.PosthogContextMiddleware.aextract_request_user   sY      $/E?""W}11$77  % " ""s1   A
? =? A? 	AA
AAc                     d}d}|||fS t        |dd      }t        |      r |       }|s||fS t        |dd      }|t        |      }t        |dd      }|rt        |      }||fS )z
        Extract user ID and email from a user object.

        Handles both authenticated and unauthenticated users, as well as
        legacy Django where is_authenticated was a method.
        Nis_authenticatedFpkr6   )rH   r#   str)r+   rG   r2   r6   rQ   user_pkr3   s          r   rI   z.PosthogContextMiddleware._resolve_user_details   s     <E>! #4);UC$%/1E>! $d+'lG T7D1

OE~r   c                    | j                   r| j                  |      S | j                  r"| j                  |      s| j                  |      S t	        j
                  | j                  | j                        5  | j                  |      j                         D ]  \  }}t	        j                  ||        | j                  |      cddd       S # 1 sw Y   yxY w)z
        Unified entry point for both sync and async request handling.

        When sync_capable and async_capable are both True, Django passes requests
        without conversion. This method detects the mode and routes accordingly.
        r*   N)r    	__acall__r%   r   r   new_contextr)   r*   r4   itemstagr+   r1   kvs       r   __call__z!PosthogContextMiddleware.__call__   s     >>'** ""4+>+>w+G((11%%d&=&=dkkR 2 --g6<<> 'DAqLLA&' ((1	2 2 2s   7ACCc                   K   | j                   r*| j                  |      s| j                  |       d{   S t        j                  | j                  | j
                        5  | j                  |       d{   j                         D ]  \  }}t        j                  ||        | j                  |       d{   cddd       S 7 7 T7 # 1 sw Y   yxY ww)a  
        Asynchronous entry point for async request handling.

        This method is called when the middleware chain is async.
        Uses aextract_tags() which calls request.auser() to avoid
        SynchronousOnlyOperation when accessing user in async context.
        NrV   )	r%   r   r   rX   r)   r*   rL   rY   rZ   r[   s       r   rW   z"PosthogContextMiddleware.__acall__  s      t':':7'C**7333!!$"9"9$++N 	4#11'::AAC #1Q"# **733		4 	4 4 ; 4		4 	4sL   2CC/C$C9C:AC>C?CCCCCCc                     | j                   r| j                  |      sy| j                  sy| j                  r| j                  j                  |       yddlm}  ||       y)a!  
        Process exceptions from views and downstream middleware.

        Django calls this WHILE still inside the context created by __call__,
        so request tags have already been extracted and set. This method just
        needs to capture the exception directly.

        Django converts view exceptions into responses before they propagate through
        the middleware stack, so the context manager in __call__/__acall__ never sees them.

        Note: Django's process_exception is always synchronous, even for async views.
        Nr   )capture_exception)r%   r)   r*   ra   posthog)r+   r1   	exceptionra   s       r   process_exceptionz*PosthogContextMiddleware.process_exception$  sO     t':':7'C&& ;;KK)))41i(r   N)__name__
__module____qualname____doc__sync_capableasync_capabler,   r4   r/   r.   rL   rK   rI   r^   rW   rd   r
   r   r   r   r      sH    : LM6p>8t0>. D2*4$)r   r   )typingr   r   rb   r   posthog.clientr   asgiref.syncr   r   ImportErrorasynciodjango.httpr   r   r   r   r   r   r   r   r   r
   r   r   <module>rq      sU    &  !G 5FFg) g)#  	!55	s   A AA