1# -*- coding: utf-8 -*- #
2# Copyright 2018 Google LLC. All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#    http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15"""This module manages the survey prompting."""
16
17from __future__ import absolute_import
18from __future__ import division
19from __future__ import unicode_literals
20
21import time
22
23from googlecloudsdk.core import config
24from googlecloudsdk.core import log
25from googlecloudsdk.core import yaml
26from googlecloudsdk.core.util import files as file_utils
27from googlecloudsdk.core.util import prompt_helper
28
29SURVEY_PROMPT_INTERVAL = 86400 * 14  # 14 days
30SURVEY_PROMPT_INTERVAL_AFTER_ANSWERED = 86400 * 30 * 3  # 90 days
31
32
33class PromptRecord(prompt_helper.PromptRecordBase):
34  """The survey prompt record.
35
36  Attributes:
37    _cache_file_path: cache file path.
38    last_answer_survey_time: the time user most recently answered the survey
39      (epoch time).
40    last_prompt_time: the time when user is most recently prompted (epoch time).
41    dirty: bool, True if record in the cache file should be updated. Otherwise,
42      False.
43  """
44
45  def __init__(self):
46    super(PromptRecord, self).__init__(
47        cache_file_path=config.Paths().survey_prompting_cache_path)
48    self._last_prompt_time, self._last_answer_survey_time = (
49        self.ReadPromptRecordFromFile())
50
51  def ReadPromptRecordFromFile(self):
52    """Loads the prompt record from the cache file.
53
54    Returns:
55       Two-value tuple (last_prompt_time, last_answer_survey_time)
56    """
57    if not self.CacheFileExists():
58      return None, None
59
60    try:
61      with file_utils.FileReader(self._cache_file_path) as f:
62        data = yaml.load(f)
63      return (data.get('last_prompt_time', None),
64              data.get('last_answer_survey_time', None))
65    except Exception:  # pylint:disable=broad-except
66      log.debug('Failed to parse survey prompt cache. '
67                'Using empty cache instead.')
68      return None, None
69
70  def _ToDictionary(self):
71    res = {}
72    if self._last_prompt_time is not None:
73      res['last_prompt_time'] = self._last_prompt_time
74    if self._last_answer_survey_time is not None:
75      res['last_answer_survey_time'] = self._last_answer_survey_time
76    return res
77
78  @property
79  def last_answer_survey_time(self):
80    return self._last_answer_survey_time
81
82  @last_answer_survey_time.setter
83  def last_answer_survey_time(self, value):
84    self._last_answer_survey_time = value
85    self._dirty = True
86
87
88class SurveyPrompter(prompt_helper.BasePrompter):
89  """Manages prompting user for survey.
90
91  Attributes:
92     _prompt_record: PromptRecord, the record of the survey prompt history.
93     _prompt_message: str, the prompting message.
94  """
95  _DEFAULT_SURVEY_PROMPT_MSG = ('To take a quick anonymous survey, run:\n'
96                                '  $ gcloud survey')
97
98  def __init__(self, msg=_DEFAULT_SURVEY_PROMPT_MSG):
99    self._prompt_record = PromptRecord()
100    self._prompt_message = msg
101
102  def PrintPromptMsg(self):
103    log.status.write('\n\n' + self._prompt_message + '\n\n')
104
105  def ShouldPrompt(self):
106    """Check if the user should be prompted."""
107    if not (log.out.isatty() and log.err.isatty()):
108      return False
109
110    last_prompt_time = self._prompt_record.last_prompt_time
111    last_answer_survey_time = self._prompt_record.last_answer_survey_time
112    now = time.time()
113    if last_prompt_time and (now - last_prompt_time) < SURVEY_PROMPT_INTERVAL:
114      return False
115    if last_answer_survey_time and (now - last_answer_survey_time <
116                                    SURVEY_PROMPT_INTERVAL_AFTER_ANSWERED):
117      return False
118    return True
119
120  def Prompt(self):
121    """Prompts user for survey if user should be prompted."""
122    # Don't prompt users right after users install gcloud. Wait for 14 days.
123    if not self._prompt_record.CacheFileExists():
124      with self._prompt_record as pr:
125        pr.last_prompt_time = time.time()
126      return
127
128    if self.ShouldPrompt():
129      self.PrintPromptMsg()
130      with self._prompt_record as pr:
131        pr.last_prompt_time = time.time()
132