o
    g$                     @   s   d Z ddlmZ ddlmZ ddlm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 dd	lmZ ddlZG d
d deZdS )a  This module implements update checking and notification to the user.

It provides a context manager around the cache file that stores information
about the last update check.  The general process is as follows:

1) This stores the last time an update check occurred, so the check will only
   be done if the update check frequency has expired.
2) When an update check is done, all notifications in the latest snapshot are
   queried to see if their condition matches the current state of the SDK.  Any
   notifications that match are "activated" and cached.
3) Every time a command is run, Notify() is called to notify the user of
   available updates.  It loops over the activated notifications and determines
   if any of the triggers match the current command invocation.  If there is a
   match, the notification is printed and the last nag time is recorded for that
   particular notification.  At most one notification is printed per command.
   The priority is determined by the order the notifications are registered
   in the component snapshot.
    )absolute_import)division)unicode_literalsN)config)log)schemas)filesc                   @   s   e Zd ZdZdZdd Zdd Zdd Zd	d
 Zdd Z	dd Z
dd Zdd Zdd Zdd Zd!ddZdd Zdd Zdd Zd S )"UpdateCheckDatazBA class to hold update checking data and to perform notifications.iQ c                 C   s    t  j| _d| _|  | _d S )NF)r   Pathsupdate_check_cache_path_last_update_check_file_dirty	_LoadData_dataself r   E/tmp/google-cloud-sdk/lib/googlecloudsdk/core/updater/update_check.py__init__8   s   zUpdateCheckData.__init__c                 C   sh   t j| jstji S t| j}zt	
|}tj|W S  ty3   td tji  Y S w )z%Deserializes data from the json file.zDFailed to parse update check cache file.  Using empty cache instead.)ospathisfiler   r   LastUpdateCheckFromDictionaryr   ReadFileContentsjsonloads
ValueErrorr   debug)r   raw_datadatar   r   r   r   =   s   

zUpdateCheckData._LoadDatac                 C   s.   | j sdS t| jt| j  d| _ dS )z!Serializes data to the json file.NF)r   r   WriteFileContentsr   r   dumpsr   ToDictionaryr   r   r   r   	_SaveDataK   s   
zUpdateCheckData._SaveDatac                 C   s   | S Nr   r   r   r   r   	__enter__S   s   zUpdateCheckData.__enter__c                 G   s   |    d S r%   )r$   )r   argsr   r   r   __exit__V   s   zUpdateCheckData.__exit__c                 C      | j jS )a  Gets the revision of the snapshot from the last update check.

    Returns:
      long, The revision of the last checked snapshot.  This is a long int but
        formatted as an actual date in seconds (i.e 20151009132504). It is *NOT*
        seconds since the epoch.
    )r   last_update_check_revisionr   r   r   r   LastUpdateCheckRevisionY   s   z'UpdateCheckData.LastUpdateCheckRevisionc                 C   r)   )zGets the time of the last update check as seconds since the epoch.

    Returns:
      int, The time of the last update check in seconds since the epoch.
    )r   last_update_check_timer   r   r   r   LastUpdateCheckTimec   s   z#UpdateCheckData.LastUpdateCheckTimec                 C   s   t   | jj S )zzGets the number of seconds since we last did an update check.

    Returns:
      int, The amount of time in seconds.
    )timer   r,   r   r   r   r   SecondsSinceLastUpdateCheckk   s   z+UpdateCheckData.SecondsSinceLastUpdateCheckc                 C   s   |   tjkS )zChecks if it is time to do an update check.

    Returns:
      True, if enough time has elapsed and we should perform another update
      check.  False otherwise.
    )r/   r	   !UPDATE_CHECK_FREQUENCY_IN_SECONDSr   r   r   r   ShouldDoUpdateChecks   s   z#UpdateCheckData.ShouldDoUpdateCheckc                 C   s   t dd | jjD S )zReturns whether we already know about updates that are available.

    Returns:
      bool, True if we know about updates, False otherwise.
    c                 S   s   g | ]}|j jr|qS r   )	conditioncheck_components).0notificationr   r   r   
<listcomp>   s    z4UpdateCheckData.UpdatesAvailable.<locals>.<listcomp>)boolr   notificationsr   r   r   r   UpdatesAvailable}   s   z UpdateCheckData.UpdatesAvailableFc           	      C   s   |s	|   |jkr=td tjj}tjj}g }|jj}|D ]}|j	
|||r4td|j || q|| j_|   t | j_|j| j_d| _dS )a  Sets that we just did an update check and found the given snapshot.

    If the given snapshot is different than the last one we saw, refresh the set
    of activated notifications for available updates for any notifications with
    matching conditions.

    You must call Save() to persist these changes or use this as a context
    manager.

    Args:
      snapshot: snapshots.ComponentSnapshot, The latest snapshot available.
      component_updates_available: bool, True if there are updates to components
        we have installed.  False otherwise.
      force: bool, True to force a recalculation of whether there are available
        updates, even if the snapshot revision has not changed.
    zUpdating notification cache...zActivating notification: [%s]TN)r+   revisionr   r   r   INSTALLATION_CONFIGversionsdk_definitionr8   r2   Matchesidappendr   _CleanUpLastNagTimesr.   r,   r*   r   )	r   snapshotcomponent_updates_availableforcecurrent_versioncurrent_revision	activatedpossible_notificationsr5   r   r   r   SetFromSnapshot   s$   



zUpdateCheckData.SetFromSnapshotc              	   C   sn   t d tjdtdddddtjdddtdddd}|g| j_| 	  t

 | j_d| j_d	| _dS )
a  Sets that we just did an update check and found a new schema version.

    An incompatible schema version means there are definitely updates available
    but we can't read the notifications to correctly notify the user.  This will
    install a default notification for the incompatible schema.

    You must call Save() to persist these changes or use this as a context
    manager.
    z<Incompatible schema found.  Activating default notification.incompatibleNFi:	 )	frequencycommand_regex)r?   r2   triggerr5   r   T)r   r   r   NotificationSpec	ConditionTriggerNotificationr   r8   rA   r.   r,   r*   r   )r   notification_specr   r   r   SetFromIncompatibleSchema   s   



z)UpdateCheckData.SetFromIncompatibleSchemac                    s:   dd | j jD  t fddt| j jD | j _dS )zClean the map holding the last nag times for each notification.

    If a notification is no longer activate, it is removed from the map.  Any
    notifications that are still activated have their last nag times preserved.
    c                 S   s   g | ]}|j qS r   )r?   )r4   nr   r   r   r6      s    z8UpdateCheckData._CleanUpLastNagTimes.<locals>.<listcomp>c                 3   s$    | ]\}}| v r||fV  qd S r%   r   )r4   namevalueactivated_idsr   r   	<genexpr>   s    z7UpdateCheckData._CleanUpLastNagTimes.<locals>.<genexpr>N)r   r8   dictsix	iteritemslast_nag_timesr   r   rW   r   rA      s
   
z$UpdateCheckData._CleanUpLastNagTimesc                 C   s|   t j r
t j sdS | jjD ]+}|j}| jj|d}|j	
||r;t j|j  t | jj|< d| _ dS qdS )a  Notify the user of any available updates.

    This should be called for every command that is run.  It does not actually
    do an update check, and does not necessarily notify the user each time.  The
    user will only be notified if there are activated notifications and if the
    trigger for one of the activated notifications matches.  At most one
    notification will be printed per command.  Order or priority is determined
    by the order in which the notifications are registered in the component
    snapshot file.

    Args:
      command_path: str, The '.' separated path of the command that is currently
        being run (i.e. gcloud.foo.bar).
    Nr   T)r   outisattystatusr   r8   r?   r]   getrM   r>   writer5   NotificationMessager.   r   )r   command_pathr5   rU   last_nag_timer   r   r   Notify   s   zUpdateCheckData.NotifyN)F)__name__
__module____qualname____doc__r0   r   r   r$   r&   r(   r+   r-   r/   r1   r9   rI   rS   rA   rf   r   r   r   r   r	   3   s"    


'r	   )rj   
__future__r   r   r   r   r   r.   googlecloudsdk.corer   r   googlecloudsdk.core.updaterr   googlecloudsdk.core.utilr   r[   objectr	   r   r   r   r   <module>   s   