def test_time_bucket(self): now = datetime.now(pytz.timezone(app_config.PROJECT_TIMEZONE)) five_hours_ago = now - timedelta(hours=5) ten_hours_ago = now - timedelta(hours=10) bucket = TimeTools.time_bucket(five_hours_ago) self.assertEqual(bucket, '4 hours') bucket = TimeTools.time_bucket(ten_hours_ago) self.assertEqual(bucket, '8 hours')
def test_humanist_time_bucket(self): bucket = TimeTools.humanist_time_bucket({'minutes': 5, 'seconds': 5}) self.assertEqual(bucket, '5 minutes 5 seconds') bucket = TimeTools.humanist_time_bucket({'minutes': 0, 'seconds': 1}) self.assertEqual(bucket, '1 second') bucket = TimeTools.humanist_time_bucket({'minutes': 5, 'seconds': 0}) self.assertEqual(bucket, '5 minutes') bucket = TimeTools.humanist_time_bucket({'minutes': 1, 'seconds': 1}) self.assertEqual(bucket, '1 minute 1 second')
def handle_slug_inquiry(self, message): """ Respond to an inquiry about the slug with stats and charts """ match = re.search(self.SLUG_SEARCH_REGEX, message.body["text"]) slug = match.group(1) if slug: # Try to match the story to a slug to accurately get a team # The Google Analytics property ID comes from the team config # We use the default team if none is found stories = Story.select().where(Story.slug.contains(slug)) team = self.config.get_team_for_stories(stories) linger_rows = self.get_linger_data(team=team, slug=slug) if not linger_rows: return {"text": "Sorry, I wasn't able to find linger rate stats for %s" % slug} median = NPRLingerRate.get_median(linger_rows) print "Got median" print median people = "{:,}".format(median["total_people"]) time_text = TimeTools.humanist_time_bucket(median) reply = u"*%s* people spent a median *%s* on `%s`." % (people, time_text, slug) reply += "\n\nThis graphic appears in %s %s I am tracking:" % ( inflector.number_to_words(len(stories)), inflector.plural("story", len(stories)), ) for story in stories: reply += "\n" + "*<%s|%s>*" % (story.url, story.name.strip()) # Get linger rate data for charting. all_graphics_rows = self.get_linger_data(team=team) all_graphics_median = NPRLingerRate.get_median(all_graphics_rows) attachments = [ { "fallback": slug + " update", "color": "#eeeeee", "title": slug, "image_url": self.get_histogram_url(linger_rows, median), }, { "fallback": slug + " update", "color": "#eeeeee", "title": "How all graphics performed", "image_url": self.get_histogram_url(all_graphics_rows, all_graphics_median), }, ] return {"text": reply, "attachments": attachments}
def get_update_message(self, story): """ Handle periodic checks on the metric. For each slug in the story, get the analytics we need, and craft an relevant update message. """ story_slugs = story.slug_list() team = self.config.get_team_for_story(story) hours_since = TimeTools.hours_since(story.date) messages = [] if len(story_slugs) > 1: # Some stories have many slugs. # We break out the stats into a nice little grid so they're # easier to read. message = ( "%s hours in and here's what I know about the graphics on _%s_:" ) % (hours_since, story.name) fields = [] for slug in story_slugs: stats = self.get_stats_for_slug(team=team, slug=slug) if stats: fields.append({ "title": slug, "value": stats['time_text'], "short": True }) attachments = [{ "fallback": story.name + " update", "color": "#eeeeee", "title": story.name, "title_link": story.url, "fields": fields }] return {'text': message, 'attachments': attachments} else: # For stories with only one slug, we have a slightly different # message: stats = self.get_stats_for_slug(team=team, slug=story_slugs[0]) if stats: message = "%s hours in, *%s users* have spent a median *%s* on _%s_ (`%s`)" % ( hours_since, stats['people'], stats['time_text'], story.name, story_slugs[0]) return {'text': message}
def get_update_message(self, story): """ Handle periodic checks on the metric. For each slug in the story, get the analytics we need, and craft an relevant update message. """ story_slugs = story.slug_list() team = self.config.get_team_for_story(story) hours_since = TimeTools.hours_since(story.date) messages = [] if len(story_slugs) > 1: # Some stories have many slugs. # We break out the stats into a nice little grid so they're # easier to read. message = ("%s hours in and here's what I know about the graphics on _%s_:") % (hours_since, story.name) fields = [] for slug in story_slugs: stats = self.get_stats_for_slug(team=team, slug=slug) if stats: fields.append({"title": slug, "value": stats["time_text"], "short": True}) attachments = [ { "fallback": story.name + " update", "color": "#eeeeee", "title": story.name, "title_link": story.url, "fields": fields, } ] return {"text": message, "attachments": attachments} else: # For stories with only one slug, we have a slightly different # message: stats = self.get_stats_for_slug(team=team, slug=story_slugs[0]) if stats: message = "%s hours in, *%s users* have spent a median *%s* on _%s_ (`%s`)" % ( hours_since, stats["people"], stats["time_text"], story.name, story_slugs[0], ) return {"text": message}
def get_stats_for_slug(self, team, slug): """ Return clean stats about how long people looked at a slug """ linger_rows = self.get_linger_data(team=team, slug=slug) if not linger_rows: return False median = NPRLingerRate.get_median(linger_rows) return { 'median': median, 'people': "{:,}".format(median['total_people']), 'time_text': TimeTools.humanist_time_bucket(median) }
def test_humanist_time_bucket(self): bucket = TimeTools.humanist_time_bucket({ 'minutes': 5, 'seconds': 5 }) self.assertEqual(bucket, '5 minutes 5 seconds') bucket = TimeTools.humanist_time_bucket({ 'minutes': 0, 'seconds': 1 }) self.assertEqual(bucket, '1 second') bucket = TimeTools.humanist_time_bucket({ 'minutes': 5, 'seconds': 0 }) self.assertEqual(bucket, '5 minutes') bucket = TimeTools.humanist_time_bucket({ 'minutes': 1, 'seconds': 1 }) self.assertEqual(bucket, '1 minute 1 second')
def get_stats_for_slug(self, team, slug): """ Return clean stats about how long people looked at a slug """ linger_rows = self.get_linger_data(team=team, slug=slug) if not linger_rows: return False median = NPRLingerRate.get_median(linger_rows) return { "median": median, "people": "{:,}".format(median["total_people"]), "time_text": TimeTools.humanist_time_bucket(median), }
def handle_linger_update(message): if 'doing' not in message.body['text']: return m = GRUBER_URLINTEXT_PAT.findall(message.body['text']) if not m[0]: return url = str(m[0][0]) url = url.replace('&', '&') logger.info("Looking for url %s" % url) try: story = Story.select().where(Story.url == url).get() except: message.reply("Sorry, I don't have stats for %s" % url) return story_time_bucket = story.time_bucket() stats_per_slug = analytics.get_linger_data_for_story(story) if len(stats_per_slug) is not 0: reply = ("Here's what I know about the graphics on _%s_:") % ( story.name.strip()) fields = [] for stat in stats_per_slug: time = TimeTools.humanist_time_bucket(stat['stats']) fields.append({ "title": stat['slug'], "value": time, "short": True }) attachments = [{ "fallback": story.name + " update", "color": "#eeeeee", "title": story.name, "title_link": story.url, "fields": fields }] # Use send_message instead of message.reply, otherwise we lose # the bot icon. slackTools.send_message(message.body['channel'], reply, attachments)
def handle_slug_inquiry(self, message): """ Respond to an inquiry about the slug with stats and charts """ match = re.search(self.SLUG_SEARCH_REGEX, message.body['text']) slug = match.group(1) if slug: # Try to match the story to a slug to accurately get a team # The Google Analytics property ID comes from the team config # We use the default team if none is found stories = Story.select().where(Story.slug.contains(slug)) team = self.config.get_team_for_stories(stories) linger_rows = self.get_linger_data(team=team, slug=slug) if not linger_rows: return { 'text': "Sorry, I wasn't able to find linger rate stats for %s" % slug } median = NPRLingerRate.get_median(linger_rows) print "Got median" print median people = "{:,}".format(median['total_people']) time_text = TimeTools.humanist_time_bucket(median) reply = u"*%s* people spent a median *%s* on `%s`." % ( people, time_text, slug) reply += '\n\nThis graphic appears in %s %s I am tracking:' % ( inflector.number_to_words( len(stories)), inflector.plural('story', len(stories))) for story in stories: reply += '\n' + '*<%s|%s>*' % (story.url, story.name.strip()) # Get linger rate data for charting. all_graphics_rows = self.get_linger_data(team=team) all_graphics_median = NPRLingerRate.get_median(all_graphics_rows) attachments = [{ "fallback": slug + " update", "color": "#eeeeee", "title": slug, "image_url": self.get_histogram_url(linger_rows, median) }, { "fallback": slug + " update", "color": "#eeeeee", "title": "How all graphics performed", "image_url": self.get_histogram_url(all_graphics_rows, all_graphics_median) }] return {'text': reply, 'attachments': attachments}
def time_bucket(self): return TimeTools.time_bucket(self.date)
def handle_slug_question(message): m = re.search(LINGER_RATE_REGEX, message.body['text']) if not m: return slug = m.group(1) if slug: median = analytics.get_linger_rate(slug) stories = Story.select().where(Story.slug.contains(slug)) message.reply("Ok! I'm looking up %s. This may take a second." % slug) if median: people = "{:,}".format(median['total_people']) time_text = TimeTools.humanist_time_bucket(median) reply = u"*%s* people spent a median *%s* on `%s`." % ( people, time_text, slug) # List the stories this slug appears on reply += '\n\nThis graphic appears in %s %s:' % ( inflector.number_to_words( len(stories)), inflector.plural('story', len(stories))) for story in stories: reply += '\n' + '*<%s|%s>*' % (story.url, story.name.strip()) # Get linger rate data linger_rows = analytics.get_linger_rows(slug) linger_histogram_url = ChartTools.linger_histogram_link( linger_rows, median) all_graphics_rows = analytics.get_linger_rows() all_graphics_median = analytics.get_linger_rate() all_histogram = ChartTools.linger_histogram_link( all_graphics_rows, all_graphics_median) attachments = [{ "fallback": slug + " update", "color": "#eeeeee", "title": slug, "image_url": linger_histogram_url }, { "fallback": slug + " update", "color": "#eeeeee", "title": "How all graphics performed", "image_url": all_histogram }] # Get scroll data, if any. scroll_depth_rows = analytics.get_depth_rate(slug) if scroll_depth_rows: scroll_histogram_url = ChartTools.scroll_histogram_link( scroll_depth_rows) if stories[0].screenshot: scroll_histogram_url = ChartTools.add_screenshot_to_chart( stories[0].screenshot, scroll_histogram_url) attachments.append({ "fallback": slug + " update", "color": "#eeeeee", "title": "How far down did people scroll?", "image_url": scroll_histogram_url }) slackTools.send_message(message.body['channel'], reply, attachments, unfurl_links=False) else: message.reply("I wasn't able to figure out the linger rate of %s" % slug)
def time_bucket(self): return TimeTools.time_bucket(self.article_posted)