
    gSC              	          d dl Z d dlZd dlZd dlZd dlZd dlZd dlmZ d dl	m
Z
 d dlmZmZ d dlZd dlZej                   j#                  ej                   j%                  ej                   j'                  ej                   j)                  e      d                    ej,                  e      Zej3                  ej4                          G d de      Z G d d	e      Z G d
 de      Zy)    N)params_dictEvent)	BaseStore	DictStorez..c                   v    e Zd ZdZddefdZd Zd ZddZd Z	d	 Z
dd
Zd Zd Zd Zd Zd Zd Zd Zd Zy)	BaseGa4mpa6  
    Parent class that provides an interface for sending data to Google Analytics, supporting the GA4 Measurement Protocol.

    Parameters
    ----------
    api_secret : string
        Generated through the Google Analytics UI. To create a new secret, navigate in the Google Analytics UI to: Admin > Data Streams >
        [choose your stream] > Measurement Protocol API Secrets > Create

    See Also
    --------

    * Measurement Protocol (Google Analytics 4): https://developers.google.com/analytics/devguides/collection/protocol/ga4

    Examples
    --------
    # Initialize tracking object for gtag usage
    >>> ga = gtagMP(api_secret = "API_SECRET", measurement_id = "MEASUREMENT_ID", client_id="CLIENT_ID")

    # Initialize tracking object for Firebase usage
    >>> ga = firebaseMP(api_secret = "API_SECRET", firebase_app_id = "FIREBASE_APP_ID", app_instance_id="APP_INSTANCE_ID")

    # Build an event
    >>> event_type = 'new_custom_event'
    >>> event_parameters = {'parameter_key_1': 'parameter_1', 'parameter_key_2': 'parameter_2'}
    >>> event = {'name': event_type, 'params': event_parameters }
    >>> events = [event]

    # Send a custom event to GA4 immediately
    >>> ga.send(events)

    # Postponed send of a custom event to GA4
    >>> ga.send(events, postpone=True)
    >>> ga.postponed_send()
    Nstorec                     t        j                          | _        || _        g | _        |t	        |t
              sJ d       |xs
 t               | _        | j                          d| _	        d| _
        y )Nz3if supplied, store must be an instance of BaseStorez+https://www.google-analytics.com/mp/collectz1https://www.google-analytics.com/debug/mp/collect)time_initialization_time
api_secret_event_list
isinstancer   r   r
   _check_store_requirements_base_domain_validation_domain)selfr   r
   s       /opt/Tautulli/lib/ga4mp/ga4mp.py__init__zBaseGa4mp.__init__A   sc    $(IIK!$}
5) <s>ss<)ik
&&(I"U    c                     | j                   j                  d      0| j                   j                  dt        | j                               | j                   j                  dt        | j                  dz               y )N
session_idnamevaluelast_interaction_time_msec  )r
   get_session_parameterset_session_parameterintr   r   s    r   r   z#BaseGa4mp._check_store_requirementsK   sd    ::++L9AJJ,,,c$JcJcFd,e

((.JRUVZVoVorvVvRw(xr   c                     t        |      S )N)r   r   )r   r   s     r   create_new_eventzBaseGa4mp.create_new_eventR   s    $r   c                    | j                  |       | j                  |       | j                  |       |du rI|D ]C  }| j                  t	        j                               |d<   | j
                  j                  |       E yt        dt        |      d      D cg c]
  }|||dz     }}| j                  |||       yc c}w )a  
        Method to send an http post request to google analytics with the specified events.

        Parameters
        ----------
        events : List[Dict]
            A list of dictionaries of the events to be sent to Google Analytics. The list of dictionaries should adhere
            to the following format:

            [{'name': 'level_end',
            'params' : {'level_name': 'First',
                        'success': 'True'}
            },
            {'name': 'level_up',
            'params': {'character': 'John Madden',
                        'level': 'First'}
            }]

        validation_hit : bool, optional
            Boolean to depict if events should be tested against the Measurement Protocol Validation Server, by default False
        postpone : bool, optional
            Boolean to depict if provided event list should be postponed, by default False
        date : datetime
            Python datetime object for sending a historical event at the given date. Date cannot be in the future.
        T_timestamp_microsr      )validation_hitdateN)
_check_params_check_date_not_in_future#_add_session_id_and_engagement_time_get_timestampr   r   appendrangelen
_http_post)r   eventsr(   postponer)   eventbatched_event_lists          r   sendzBaseGa4mp.sendU   s    8 	6"&&t,008t /-1-@-@-M)*  ''./ 9>aVb8Q"/4uurz*" " OO">  	"s   B?c                 \    | j                   D ]  }| j                  |gd        g | _         y)zX
        Method to send the events provided to Ga4mp.send(events,postpone=True)
        T)r3   N)r   r1   )r   r4   s     r   postponed_sendzBaseGa4mp.postponed_send   s5    
 %% 	4EOOUGdO3	4 r   c                 .    t        j                  |       y)a  
        Method to append event name and parameters key-value pairing(s) to parameters dictionary.

        Parameters
        ----------
        new_name_and_parameters : Dict
            A dictionary with one key-value pair representing a new type of event to be sent to Google Analytics.
            The dictionary should adhere to the following format:

            {'new_name': ['new_param_1', 'new_param_2', 'new_param_3']}
        N)r   update)r   new_name_and_parameterss     r   append_event_to_params_dictz%BaseGa4mp.append_event_to_params_dict   s     	23r   c                    | j                  |       d}| j                  }|du r| j                  }t        j	                  d|        d}|D ]  }| j                  |      }	| j                  |      }
| j                  |
       |r|d   |d   d	n||
d
<   |nt        j	                  d|        |du sJ d       | j                  |      }| j                  |      }t        |      |
d<   t        j	                  d|
d           |r|d   |
d<   t        j                  j                  |	      }|j                  dd       t        j                   |
      }|j#                  d      }|j                  dt%        |             t        j                  j'                  ||      }|j(                  }t        j	                  d|        t        j	                  d|        |dz  } |S )aR  
        Method to send http POST request to google-analytics.

        Parameters
        ----------
        batched_event_list : List[List[Dict]]
            List of List of events. Places initial event payload into a list to send http POST in batches.
        validation_hit : bool, optional
            Boolean to depict if events should be tested against the Measurement Protocol Validation Server, by default False
        postpone : bool, optional
            Boolean to depict if provided event list should be postponed, by default False
        date : datetime
            Python datetime object for sending a historical event at the given date. Date cannot be in the future.
            Timestamp micros supports up to 48 hours of backdating.
            If date is specified, postpone must be False or an assertion will be thrown.
        NTzSending POST to:    )domain)batchr   params)r   rA   r2   zSetting event timestamp to: Fz;Cannot send postponed historical hit, ensure postpone=Falsetimestamp_microszTimestamp of request is: r&   zContent-Typezapplication/json; charset=utf-8zutf-8zContent-LengthzBatch Number: zStatus code: )r+   r   r   loggerinfo
_build_url_build_request_add_user_props_to_hit_datetime_to_timestampr-   r!   urllibrequestRequest
add_headerjsondumpsencoder0   urlopenstatus)r   r5   r(   r3   r)   status_coder?   batch_numberr@   urlrJ   tsts_microreqjsondatajson_data_as_bytesresults                    r   r1   zBaseGa4mp._http_post   s   " 	&&t, ""T!,,F'x01 ' &	E///0C)))6G''0
  v%/B H :4&AB%QPQ% 006..r2.1(m*+7@R8S7TUV.34G.H*+..((-CNN>+LMzz'*H!)!9NN+S1C-DE^^++C1CDF --KKK.78KK-}56ALM&	P r   c                 |   t        |      t        k(  sJ d       |D ]/  }t        |t              sJ d       d|v sJ d       d|v r*J d        |D ]j  }|d   }|d   }|t	        j
                         v s$t        |   D ];  }||j                         vst        j                  d| dt        |    d	| d
       = l y)aj  
        Method to check whether the provided event payload parameters align with supported parameters.

        Parameters
        ----------
        events : List[Dict]
            A list of dictionaries of the events to be sent to Google Analytics. The list of dictionaries should adhere
            to the following format:

            [{'name': 'level_end',
            'params' : {'level_name': 'First',
                        'success': 'True'}
            },
            {'name': 'level_up',
            'params': {'character': 'John Madden',
                        'level': 'First'}
            }]
        zevents should be a listz0each event should be an instance of a dictionaryr   z#each event should have a "name" keyrA   z%each event should have a "params" keyz7WARNING: Event parameters do not match event type.
For z* event type, the correct parameter(s) are z.
The parameter 'z' triggered this warning.
For a breakdown of currently supported event types and their parameters go here: https://support.google.com/analytics/answer/9267735
N)typelistr   dictr   keysrC   warning)r   r2   r4   e
event_nameevent_params	parameters          r   r*   zBaseGa4mp._check_params   s   . F|t#>%>># 	NEeT*^,^^*U?I$II?u$M&MM$	N  	A6JX;L[--//!,Z!8 I (9(9(;;VWaVb  cM  NY  Zd  Ne  Mf  fx  yB  xC  Cd  e		r   c                 p   |D ]  }t        t        j                         dz        }|d   }d|j                         vr| j                  j	                  d      |d<   d|j                         vsk| j                  j	                  d      }||kD  r||z
  nd|d<   | j                  j                  d|        y)	z`
        Method to add the session_id and engagement_time_msec parameter to all events.
        r   rA   r   engagement_time_msecr   r   r   N)r!   r   r_   r
   r   r    )r   r2   r4   current_time_in_millisecondsrc   last_interaction_times         r   r,   z-BaseGa4mp._add_session_id_and_engagement_time  s      		xE+.tyy{T/A+B( ?L<#4#4#66-1ZZ-M-Ml-[\*%\->->-@@(,

(H(HIe(f% pL  Od  pd7SVk7k  jk34

006RZv0w		xr   c                    | j                   j                         D ]  }	 |dv r-|j                  || j                   j                  |      i       nVd|j	                         vr|j                  di i       |d   j                  |d| j                   j                  |      ii        y#  t
        j                  d|        Y xY w)z
        Method is a helper function to add user properties to outgoing hits.

        Parameters
        ----------
        hit : dict
        )user_idnon_personalized_adsuser_propertiesr   z-Failed to add user property to outgoing hit: N)r
   get_all_user_propertiesr:   get_user_propertyr_   rC   rD   )r   hitkeys      r   rG   z BaseGa4mp._add_user_props_to_hit!  s     ::557 	SC
S==JJTZZ%A%A#%FGH(
:

$5r#:;)*11w

(D(DS(IJK	SSKC5QRs   BB))Cc                     t        |dz        S )z
        Method returns UNIX timestamp in microseconds for postponed hits.

        Parameters
        ----------
        None
        g    .A)r!   )r   	timestamps     r   r-   zBaseGa4mp._get_timestamp8  s     9s?##r   c                 H    t        j                  |j                               S )a  
        Private method to convert a datetime object into a timestamp

        Parameters
        ----------
        dt : datetime
            A datetime object in any format

        Returns
        -------
        timestamp
            A UNIX timestamp in milliseconds
        )r   mktime	timetuple)r   dts     r   rH   z BaseGa4mp._datetime_to_timestampB  s     {{2<<>**r   c                 Z    |y|t         j                   j                         k  sJ d       y)z
        Method to check that provided date is not in the future.

        Parameters
        ----------
        date : datetime
            Python datetime object
        Nz%Provided date cannot be in the future)datetimenow)r   r)   s     r   r+   z#BaseGa4mp._check_date_not_in_futureR  s5     < ))--//767/r   c                     t        d      NzYSubclass should be using this function, but it was called through the base class instead.NotImplementedErrorr   r?   s     r   rE   zBaseGa4mp._build_urlb      !"}~~r   c                     t        d      r{   r|   r   r@   s     r   rF   zBaseGa4mp._build_requeste  r   r   N)FFN)__name__
__module____qualname____doc__r   r   r   r$   r6   r8   r<   r1   r*   r,   rG   r-   rH   r+   rE   rF    r   r   r	   r	      se    "HV) Vy -^	4 DL+ZxS.$+ 7 r   r	   c                   4     e Zd ZdZ fdZd Zd Zd Z xZS )GtagMPa  
    Subclass for users of gtag. See `Ga4mp` parent class for examples.

    Parameters
    ----------
    measurement_id : string
        The identifier for a Data Stream. Found in the Google Analytics UI under: Admin > Data Streams > [choose your stream] > Measurement ID (top-right)
    client_id : string
        A unique identifier for a client, representing a specific browser/device.
    c                 @    t         |   |       || _        || _        y r   )superr   measurement_id	client_id)r   r   r   r   	__class__s       r   r   zGtagMP.__init__t  s    $,"r   c                 >    | d| j                    d| j                   S )Nz?measurement_id=&api_secret=)r   r   r~   s     r   rE   zGtagMP._build_urly  s&    )$*=*=)>l4??J[\\r   c                      | j                   |dS )N)r   r2   )r   r   s     r   rF   zGtagMP._build_request|  s    !^^u==r   c                     dt        j                  dd      z  dz   t        t        t	        j                                     z   S )z
        Utility function for generating a new client ID matching the typical format of 10 random digits and the UNIX timestamp in seconds, joined by a period.
        z%0.10dr   l   c(	 .)randomrandintstrr!   r   r"   s    r   random_client_idzGtagMP.random_client_id  s4     &..:66<s3tyy{CS?TTTr   )	r   r   r   r   r   rE   rF   r   __classcell__r   s   @r   r   r   h  s    	#
]>Ur   r   c                   .     e Zd ZdZ fdZd Zd Z xZS )
FirebaseMPa.  
    Subclass for users of Firebase. See `Ga4mp` parent class for examples.

    Parameters
    ----------
    firebase_app_id : string
        The identifier for a Firebase app. Found in the Firebase console under: Project Settings > General > Your Apps > App ID.
    app_instance_id : string
        A unique identifier for a Firebase app instance.
            * Android - getAppInstanceId() - https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics#public-taskstring-getappinstanceid
            * Kotlin - getAppInstanceId() - https://firebase.google.com/docs/reference/kotlin/com/google/firebase/analytics/FirebaseAnalytics#getappinstanceid
            * Swift - appInstanceID() - https://firebase.google.com/docs/reference/swift/firebaseanalytics/api/reference/Classes/Analytics#appinstanceid
            * Objective-C - appInstanceID - https://firebase.google.com/docs/reference/ios/firebaseanalytics/api/reference/Classes/FIRAnalytics#+appinstanceid
            * C++ - GetAnalyticsInstanceId() - https://firebase.google.com/docs/reference/cpp/namespace/firebase/analytics#getanalyticsinstanceid
            * Unity - GetAnalyticsInstanceIdAsync() - https://firebase.google.com/docs/reference/unity/class/firebase/analytics/firebase-analytics#getanalyticsinstanceidasync
    c                 @    t         |   |       || _        || _        y r   )r   r   firebase_app_idapp_instance_id)r   r   r   r   r   s       r   r   zFirebaseMP.__init__  s     $..r   c                 >    | d| j                    d| j                   S )Nz?firebase_app_id=r   )r   r   r~   s     r   rE   zFirebaseMP._build_url  s&    *4+?+?*@T__L]^^r   c                      | j                   |dS )N)r   r2   )r   r   s     r   rF   zFirebaseMP._build_request  s    #'#7#75IIr   )r   r   r   r   r   rE   rF   r   r   s   @r   r   r     s    "/
_Jr   r   )rM   loggingurllib.requestrI   r   rx   r   ga4mp.utilsr   ga4mp.eventr   ga4mp.storer   r   ossyspathr.   normpathjoindirname__file__	getLoggerr   rC   setLevelINFOobjectr	   r   r   r   r   r   <module>r      s          #  ,  GGRWW\\"''//(";TBC 
		8	$  J JX
UY U:J Jr   