Skip to content
Browse files

[bug 727086] Add unique visitors chart to KPI dashboard.

* Created a webtrends API helper.
* Refactored existing API calls in dashboards app to use it for getting
  wiki reports.
* Cron job to call webtrends API and save the data to the metrics model.
* API call to get this data.
* UI to display the chart.
  • Loading branch information...
1 parent 791d26f commit 4a9aacc0247a8ba676e2122b0dc1d6fc48417024 @rlr rlr committed Feb 23, 2012
View
34 apps/dashboards/models.py
@@ -1,6 +1,5 @@
import json
import logging
-from urllib2 import HTTPBasicAuthHandler, build_opener
from django.conf import settings
from django.contrib.auth.models import Group
@@ -11,22 +10,13 @@
from dashboards import THIS_WEEK, ALL_TIME, PERIODS
from dashboards.personal import GROUP_DASHBOARDS
from sumo.models import ModelBase
+from sumo.webtrends import Webtrends, StatsException
from wiki.models import Document
log = logging.getLogger('k.dashboards')
-class StatsException(Exception):
- """An error in the stats returned by the third-party analytics package"""
- def __init__(self, msg):
- self.msg = msg
-
-
-class StatsIOError(IOError):
- """An error communicating with WebTrends"""
-
-
def period_dates():
"""Return when each period begins and ends, relative to now.
@@ -119,27 +109,9 @@ def _visit_counts(cls, json_data):
@classmethod
def json_for(cls, period):
- """Return the JSON-formatted WebTrends stats for the given period.
-
- Make one attempt to fetch and reload the data. If something fails, it's
- the caller's responsibility to retry.
-
- """
- auth_handler = HTTPBasicAuthHandler()
- auth_handler.add_password(realm=settings.WEBTRENDS_REALM,
- uri=settings.WEBTRENDS_WIKI_REPORT_URL,
- user=settings.WEBTRENDS_USER,
- passwd=settings.WEBTRENDS_PASSWORD)
- opener = build_opener(auth_handler)
+ """Return the JSON-formatted WebTrends stats for the given period."""
start, end = period_dates()[period]
- url = (settings.WEBTRENDS_WIKI_REPORT_URL +
- '&start_period=%s&end_period=%s' % (start, end))
- try:
- # TODO: A wrong username or password results in a recursion depth
- # error.
- return opener.open(url).read()
- except IOError, e:
- raise StatsIOError(*e.args)
+ return Webtrends.wiki_report(start, end)
class GroupDashboard(ModelBase):
View
4 apps/dashboards/tests/test_models.py
@@ -4,9 +4,9 @@
from mock import patch
from nose.tools import raises, eq_
-from dashboards.models import (WikiDocumentVisits, StatsException, THIS_WEEK,
- StatsIOError)
+from dashboards.models import WikiDocumentVisits, THIS_WEEK
from sumo.tests import TestCase
+from sumo.webtrends import StatsException, StatsIOError
from wiki.tests import document, revision
View
22 apps/kpi/api.py
@@ -11,7 +11,7 @@
from tastypie.resources import Resource
from customercare.models import Reply
-from kpi.models import Metric, MetricKind
+from kpi.models import Metric, MetricKind, VISITORS_METRIC_CODE
from questions.models import Question, Answer, AnswerVote
from wiki.models import HelpfulVote, Revision
@@ -167,7 +167,7 @@ class Meta(SearchClickthroughMeta):
class SolutionResource(CachedResource):
- """Returns the number of questions maked as the solution."""
+ """Returns the number of questions marked as solved."""
date = fields.DateField('date')
solved = fields.IntegerField('solved', default=0)
questions = fields.IntegerField('questions', default=0)
@@ -364,6 +364,24 @@ class Meta:
allowed_methods = ['get']
+class VisitorsResource(CachedResource):
+ """Returns the number of unique visitors per day."""
+ date = fields.DateField('date')
+ visitors = fields.IntegerField('visitors', default=0)
+
+ def get_object_list(self, request):
+ # Set up the query for the data we need
+ kind = MetricKind.objects.get(code=VISITORS_METRIC_CODE)
+ qs = Metric.objects.filter(kind=kind).order_by('-start')
+
+ return [Struct(date=m.start, visitors=m.value) for m in qs]
+
+ class Meta(object):
+ cache = SimpleCache()
+ resource_name = 'kpi_visitors'
+ allowed_methods = ['get']
+
+
def _monthly_qs_for(model_cls):
"""Return a queryset with the extra select for month and year."""
return model_cls.objects.filter(created__gte=_start_date()).extra(
View
36 apps/kpi/cron.py
@@ -0,0 +1,36 @@
+from datetime import datetime, date, timedelta
+
+import cronjobs
+
+from kpi.models import Metric, MetricKind, VISITORS_METRIC_CODE
+from sumo.webtrends import Webtrends
+
+
+@cronjobs.register
+def update_visitors_metric():
+ """Get new visitor data from webtrends and save."""
+ try:
+ # Get the latest metric value.
+ last_metric = Metric.objects.filter(
+ kind__code=VISITORS_METRIC_CODE).order_by('-start')[0]
+ # Start updating the day after the last updated.
+ start = last_metric.start + timedelta(days=1)
+ except IndexError:
+ # There are no metrics yet, start from 2011-01-01
+ start = date(2011, 01, 01)
+
+ # Collect up until yesterday
+ end = date.today() - timedelta(days=1)
+
+ # Get the visitor data from webtrends.
+ visitors = Webtrends.visits(start, end)
+
+ # Create the metrics.
+ metric_kind = MetricKind.objects.get(code=VISITORS_METRIC_CODE)
+ for date_str, visits in visitors.items():
+ day = datetime.strptime(date_str,"%Y-%m-%d").date()
+ Metric.objects.create(
+ kind=metric_kind,
+ start=day,
+ end=day + timedelta(days=1),
+ value=visits)
View
3 apps/kpi/models.py
@@ -4,6 +4,9 @@
from sumo.models import ModelBase
+VISITORS_METRIC_CODE = 'general keymetrics:visitors'
+
+
class MetricKind(ModelBase):
"""A programmer-readable identifier of a metric, like 'clicks: search'"""
code = CharField(max_length=255, unique=True)
View
1 apps/kpi/templates/kpi/dashboard.html
@@ -15,6 +15,7 @@
data-aoa-contributors-url="{{ url('api_dispatch_list', resource_name='kpi_active_aoa_contributors', api_name ='v1') }}"
data-sphinx-ctr-url="{{ url('api_dispatch_list', resource_name='sphinx-clickthrough-rate', api_name ='v1') }}"
data-elastic-ctr-url="{{ url('api_dispatch_list', resource_name='elastic-clickthrough-rate', api_name ='v1') }}"
+ data-visitors-url="{{ url('api_dispatch_list', resource_name='kpi_visitors', api_name ='v1') }}"
data-vote-url="{{ url('api_dispatch_list', resource_name='kpi_vote', api_name ='v1') }}">
</div>
View
19 apps/kpi/tests/test_api.py
@@ -5,7 +5,7 @@
from nose.tools import eq_
from customercare.tests import reply
-from kpi.models import Metric
+from kpi.models import Metric, VISITORS_METRIC_CODE
from kpi.tests import metric, metric_kind
from sumo.tests import TestCase, LocalizingClient
from sumo.urlresolvers import reverse
@@ -209,3 +209,20 @@ def test_sphinx_clickthrough_post(self):
# Correspnding ElasticSearch APIs are likely correct by dint
# of factoring.
+
+ def test_visitors(self):
+ """Test unique visitors API call."""
+ # Create a reply
+ kind = metric_kind(code=VISITORS_METRIC_CODE, save=True)
+ metric(kind=kind, start=date.today(), end=date.today(), value=42,
+ save=True)
+
+ # There should be only one active contributor.
+ url = reverse('api_dispatch_list',
+ kwargs={'resource_name': 'kpi_visitors',
+ 'api_name': 'v1'})
+
+ response = self.client.get(url + '?format=json')
+ eq_(200, response.status_code)
+ r = json.loads(response.content)
+ eq_(r['objects'][0]['visitors'], 42)
View
27 apps/kpi/tests/test_cron.py
@@ -0,0 +1,27 @@
+from datetime import date
+
+from mock import patch
+from nose.tools import eq_
+
+from kpi.cron import update_visitors_metric, Webtrends
+from kpi.models import Metric, VISITORS_METRIC_CODE
+from kpi.tests import metric_kind
+from sumo.tests import TestCase
+
+
+class UpdateVisitorsTests(TestCase):
+ @patch.object(Webtrends, 'visits')
+ def test_update_visitors_cron(self, visits):
+ """Verify the cron job inserts the right rows."""
+ visitor_kind = metric_kind(code=VISITORS_METRIC_CODE, save=True)
+ visits.return_value = {'2012-01-13': 42,
+ '2012-01-14': 193,
+ '2012-01-15': 33}
+
+ update_visitors_metric()
+
+ metrics = Metric.objects.filter(kind=visitor_kind)
+ eq_(3, len(metrics))
+ eq_(42, metrics[0].value)
+ eq_(193, metrics[1].value)
+ eq_(date(2012, 01, 15), metrics[2].start)
View
3 apps/kpi/urls.py
@@ -4,7 +4,7 @@
from kpi.api import (SolutionResource, VoteResource, FastResponseResource,
ActiveKbContributorsResource, ActiveAnswerersResource,
SphinxClickthroughResource, ElasticClickthroughResource,
- ArmyOfAwesomeContributorResource)
+ ArmyOfAwesomeContributorResource, VisitorsResource)
v1_api = Api(api_name='v1')
v1_api.register(SolutionResource())
@@ -15,6 +15,7 @@
v1_api.register(SphinxClickthroughResource())
v1_api.register(ElasticClickthroughResource())
v1_api.register(ArmyOfAwesomeContributorResource())
+v1_api.register(VisitorsResource())
urlpatterns = patterns('kpi.views',
View
25 apps/sumo/tests/test_webtrends.py
@@ -0,0 +1,25 @@
+from datetime import date
+
+from mock import patch
+from nose.tools import eq_
+
+from sumo.tests import TestCase
+from sumo.webtrends import Webtrends
+
+
+KEY_METRICS_JSON_RESPONSE = '{ "definition" : { "accountID" : 123 , "profileID" : "ABC123" , "ID" : "ProfileMetrics" , "name" : "Profile Metrics" , "description" : "" , "language" : null , "timezone" : "UTC -1" , "dimensions" : [ { "ID" : "Profile ID" , "name" : "Profile ID" } ] , "measures" : [ { "name" : "PageViews" , "ID" : "PageViews" , "columnID" : 0 , "measureFormatType" : null },{ "name" : "Visits" , "ID" : "Visits" , "columnID" : 1 , "measureFormatType" : null },{ "name" : "Visitors" , "ID" : "Visitors" , "columnID" : 2 , "measureFormatType" : null },{ "name" : "NewVisitors" , "ID" : "NewVisitors" , "columnID" : 6 , "measureFormatType" : null },{ "name" : "BounceRate" , "ID" : "BounceRate" , "columnID" : 9 , "measureFormatType" : "percent" },{ "name" : "AvgTimeonSite" , "ID" : "AvgTimeOnSite" , "columnID" : 10 , "measureFormatType" : "time_seconds" },{ "name" : "AvgVisitorsperDay" , "ID" : "AvgVisitorsPerDay" , "columnID" : 11 , "measureFormatType" : null },{ "name" : "PageViewsperVisit" , "ID" : "PageViewsPerVisit" , "columnID" : 12 , "measureFormatType" : null },{ "name" : "AvgTimeonSiteperVisitor" , "ID" : "AvgSiteTimePerVisitor" , "columnID" : 13 , "measureFormatType" : "time_seconds" } ] } ,"data" : [ { "ABC123" : { "attributes" : { } , "measures" : { "PageViews" : 10523913 , "Visits" : 4274456 , "Visitors" : 4044284 , "NewVisitors" : 2078406 , "BounceRate" : 65.4970831375969 , "AvgTimeonSite" : 274.68279239068 , "AvgVisitorsperDay" : 577754.857142857 , "PageViewsperVisit" : 2.46204733421048 , "AvgTimeonSiteperVisitor" : 96.5800146577243 } , "SubRows" : [ { "period" : "Day" , "start_date" : "2012-01-01" , "end_date" : "2012-01-01" , "measures" : { "PageViews" : 1258143 , "Visits" : 524606 , "Visitors" : 495974 , "NewVisitors" : 254034 , "BounceRate" : 63.9746781394037 , "AvgTimeonSite" : 276.706781356731 , "AvgVisitorsperDay" : 495974 , "PageViewsperVisit" : 2.39826269619486 , "AvgTimeonSiteperVisitor" : 97.029864468702 } , "SubRows" : null } , { "period" : "Day" , "start_date" : "2012-01-02" , "end_date" : "2012-01-02" , "measures" : { "PageViews" : 1576014 , "Visits" : 649237 , "Visitors" : 614465 , "NewVisitors" : 320101 , "BounceRate" : 65.3790526417934 , "AvgTimeonSite" : 275.88984425623 , "AvgVisitorsperDay" : 614465 , "PageViewsperVisit" : 2.427486418673 , "AvgTimeonSiteperVisitor" : 97.7010993303117 } , "SubRows" : null } , { "period" : "Day" , "start_date" : "2012-01-03" , "end_date" : "2012-01-03" , "measures" : { "PageViews" : 1628215 , "Visits" : 664809 , "Visitors" : 629484 , "NewVisitors" : 326187 , "BounceRate" : 65.9521757376931 , "AvgTimeonSite" : 274.289682249817 , "AvgVisitorsperDay" : 629484 , "PageViewsperVisit" : 2.44914704824995 , "AvgTimeonSiteperVisitor" : 95.4439064376537 } , "SubRows" : null } , { "period" : "Day" , "start_date" : "2012-01-04" , "end_date" : "2012-01-04" , "measures" : { "PageViews" : 1622072 , "Visits" : 648066 , "Visitors" : 613411 , "NewVisitors" : 315226 , "BounceRate" : 65.8422136017011 , "AvgTimeonSite" : 272.315030946065 , "AvgVisitorsperDay" : 613411 , "PageViewsperVisit" : 2.50294260152515 , "AvgTimeonSiteperVisitor" : 95.3973388152478 } , "SubRows" : null } , { "period" : "Day" , "start_date" : "2012-01-05" , "end_date" : "2012-01-05" , "measures" : { "PageViews" : 1561292 , "Visits" : 619564 , "Visitors" : 585760 , "NewVisitors" : 298016 , "BounceRate" : 65.9460523852257 , "AvgTimeonSite" : 274.38456436392 , "AvgVisitorsperDay" : 585760 , "PageViewsperVisit" : 2.51998502172495 , "AvgTimeonSiteperVisitor" : 96.0481818492215 } , "SubRows" : null } , { "period" : "Day" , "start_date" : "2012-01-06" , "end_date" : "2012-01-06" , "measures" : { "PageViews" : 1520252 , "Visits" : 608327 , "Visitors" : 575889 , "NewVisitors" : 294054 , "BounceRate" : 65.7352049144621 , "AvgTimeonSite" : 271.518740910547 , "AvgVisitorsperDay" : 575889 , "PageViewsperVisit" : 2.49907040128089 , "AvgTimeonSiteperVisitor" : 95.6368657848995 } , "SubRows" : null } , { "period" : "Day" , "start_date" : "2012-01-07" , "end_date" : "2012-01-07" , "measures" : { "PageViews" : 1357925 , "Visits" : 559847 , "Visitors" : 529301 , "NewVisitors" : 270788 , "BounceRate" : 65.3650015093409 , "AvgTimeonSite" : 278.304308416466 , "AvgVisitorsperDay" : 529301 , "PageViewsperVisit" : 2.42552876053636 , "AvgTimeonSiteperVisitor" : 99.1935042631697 } , "SubRows" : null } ] } } ] }'
+
+
+class WebtrendsTests(TestCase):
+ """Tests for the Webtrends API helper."""
+
+ @patch.object(Webtrends, 'key_metrics')
+ def test_visits(self, key_metrics):
+ """Test Webtrends.visits()."""
+ key_metrics.return_value = KEY_METRICS_JSON_RESPONSE
+
+ visits = Webtrends.visits(date(2012, 01, 01), date(2012, 01, 07))
+
+ eq_(7, len(visits))
+ eq_(495974, visits['2012-01-01'])
+ eq_(529301, visits['2012-01-07'])
View
82 apps/sumo/webtrends.py
@@ -0,0 +1,82 @@
+from datetime import datetime, date
+import json
+from urllib2 import HTTPBasicAuthHandler, build_opener
+
+from django.conf import settings
+
+from sumo.helpers import urlparams
+
+
+class StatsException(Exception):
+ """An error in the stats returned by the third-party analytics package"""
+ def __init__(self, msg):
+ self.msg = msg
+
+
+class StatsIOError(IOError):
+ """An error communicating with WebTrends"""
+
+
+class Webtrends(object):
+ """Webtrends API helper."""
+
+ @classmethod
+ def request(cls, url, start, end, realm='Webtrends Basic Authentication'):
+ """Make an authed request to the webtrends API.
+
+ Make one attempt to fetch and reload the data. If something fails, it's
+ the caller's responsibility to retry.
+ """
+
+ # If start and/or end are date or datetime, convert to string.
+ if isinstance(start, (date, datetime)):
+ start = start.strftime('%Ym%md%d')
+ if isinstance(end, (date, datetime)):
+ end = end.strftime('%Ym%md%d')
+
+ auth_handler = HTTPBasicAuthHandler()
+ auth_handler.add_password(realm=realm,
+ uri=url,
+ user=settings.WEBTRENDS_USER,
+ passwd=settings.WEBTRENDS_PASSWORD)
+ opener = build_opener(auth_handler)
+ url = urlparams(url, start_period=start, end_period=end)
+ try:
+ # TODO: A wrong username or password results in a recursion depth
+ # error.
+ return opener.open(url).read()
+ except IOError, e:
+ raise StatsIOError(*e.args)
+
+ @classmethod
+ def wiki_report(cls, start, end):
+ """Return the json for the wiki article visits report."""
+ return cls.request(settings.WEBTRENDS_WIKI_REPORT_URL, start, end)
+
+ @classmethod
+ def key_metrics(cls, start, end):
+ """Return the json result for the KeyMetrics API call."""
+ url = ('https://ws.webtrends.com/v3/Reporting/profiles/{profile_id}'
+ '/KeyMetrics/?period_type=trend')
+ url = url.format(profile_id=settings.WEBTRENDS_PROFILE_ID)
+ return cls.request(url, start, end, realm='DX')
+
+ @classmethod
+ def visits(cls, start, end):
+ """Return the number of unique visitors.
+
+ Returns a dict with daily numbers:
+ {u'2012-01-22': 404971,
+ u'2012-01-23': 434618,
+ u'2012-01-24': 501687,...}
+ """
+ data = json.loads(cls.key_metrics(start, end))
+ rows = data['data'][0][settings.WEBTRENDS_PROFILE_ID]['SubRows']
+ if not isinstance(rows, list):
+ rows = [rows]
+
+ visits = {}
+ for row in rows:
+ visits[row['start_date']] = row['measures']['Visitors']
+
+ return visits
View
127 media/js/kpi.dashboard.js
@@ -193,8 +193,6 @@ window.StockChartView = Backbone.View.extend({
style: {
width: 200
},
- yDecimals: 1,
- ySuffix: '%',
shared: true,
pointFormat: '<span style="color:{series.color}">{series.prettyName}</span>: <b>{point.y}</b><br/>'
},
@@ -217,6 +215,14 @@ window.StockChartView = Backbone.View.extend({
},
series: []
};
+
+ if (this.options.percent) {
+ this.chartOptions.yAxis.title = {
+ text: '%'
+ };
+ this.chartOptions.tooltip.ySuffix = '%';
+ this.chartOptions.tooltip.yDecimals = 1;
+ }
},
render: function() {
@@ -225,47 +231,68 @@ window.StockChartView = Backbone.View.extend({
if(data) {
_.each(this.options.series, function(series) {
- var seriesData;
+ var mapper = series.mapper,
+ seriesData;
+ if (!mapper) {
+ mapper = function(o){
+ return {
+ x: Date.parse(o['date']),
+ y: o[series.numerator] / o[series.denominator] * 100
+ };
+ };
+ }
- seriesData = _.map(data, function(o){
- return [Date.parse(o['date']),
- o[series.numerator] / o[series.denominator] * 100];
- });
+ seriesData = _.map(data, mapper);
seriesData.reverse();
- // Add the series with 3 different possible groupings:
- // daily, weekly, monthly
- self.chartOptions.series.push({
- name: gettext('Daily'),
- data: seriesData,
- dataGrouping: {
- enabled: false
- }
- });
- self.chartOptions.series.push({
- name: gettext('Weekly'),
- data: seriesData,
- dataGrouping: {
- forced: true,
- units: [['week', [1]]]
- }
- });
- self.chartOptions.series.push({
- name: gettext('Monthly'),
- data: seriesData,
- dataGrouping: {
- forced: true,
- units: [['month', [1]]]
- }
- });
+ if (!series.addGroupings) {
+ self.chartOptions.series.push({
+ name: series.name,
+ data: seriesData,
+ dataGrouping: {
+ enabled: false
+ }
+ });
+ } else {
+ // Add the series with 3 different possible groupings:
+ // daily, weekly, monthly
+ self.chartOptions.series.push({
+ name: gettext('Daily'),
+ data: seriesData,
+ dataGrouping: {
+ enabled: false
+ }
+ });
+ self.chartOptions.series.push({
+ name: gettext('Weekly'),
+ data: seriesData,
+ dataGrouping: {
+ forced: true,
+ units: [['week', [1]]]
+ }
+ });
+ self.chartOptions.series.push({
+ name: gettext('Monthly'),
+ data: seriesData,
+ dataGrouping: {
+ forced: true,
+ units: [['month', [1]]]
+ }
+ });
+ }
self.chart = new Highcharts.StockChart(self.chartOptions);
- self.chart.series[0].prettyName = self.chart.series[1].prettyName = self.chart.series[2].prettyName = series.name;
+ if (!series.addGroupings) {
+ self.chart.series[0].prettyName = series.name;
+ }
+ else {
+ self.chart.series[0].prettyName = self.chart.series[1].prettyName = self.chart.series[2].prettyName = series.name;
- // Hide the weekly and monthly series.
- self.chart.series[1].hide();
- self.chart.series[2].hide();
+ // Hide the weekly and monthly series.
+ self.chart.series[1].hide();
+ self.chart.series[2].hide();
+ }
});
}
return this;
@@ -313,6 +340,10 @@ window.KpiDashboard = Backbone.View.extend({
});
this.elasticCtrChart.name = 'Elastic';
+ this.visitorsChart = new ChartModel([], {
+ url: $(this.el).data('visitors-url')
+ });
+
// Create the views.
this.solvedChartView = new StockChartView({
model: this.solvedChart,
@@ -321,7 +352,8 @@ window.KpiDashboard = Backbone.View.extend({
series: [{
name: gettext('Solved'),
numerator: 'solved',
- denominator: 'questions'
+ denominator: 'questions',
+ addGroupings: true
}]
});
@@ -356,7 +388,8 @@ window.KpiDashboard = Backbone.View.extend({
series: [{
name: gettext('Responsed'),
numerator: 'responded',
- denominator: 'questions'
+ denominator: 'questions',
+ addGroupings: true
}]
});
@@ -425,6 +458,20 @@ window.KpiDashboard = Backbone.View.extend({
});
this.ctrView.addModel(this.elasticCtrChart);
+ this.visitorsView = new StockChartView({
+ model: this.visitorsChart,
+ title: gettext('Daily Unique Visitors'),
+ series: [{
+ name: gettext('Visitors'),
+ mapper: function(o) {
+ return {
+ x: Date.parse(o['date']),
+ y: o['visitors']
+ };
+ }
+ }]
+ });
+
// Render the views.
$(this.el)
.append(this.solvedChartView.render().el)
@@ -433,7 +480,8 @@ window.KpiDashboard = Backbone.View.extend({
.append(this.activeKbContributorsView.render().el)
.append(this.activeAnswerersView.render().el)
.append(this.aoaContributorsView.render().el)
- .append(this.ctrView.render().el);
+ .append(this.ctrView.render().el)
+ .append(this.visitorsView.render().el);
// Load up the models.
@@ -445,6 +493,7 @@ window.KpiDashboard = Backbone.View.extend({
this.voteChart.fetch();
this.sphinxCtrChart.fetch();
this.elasticCtrChart.fetch();
+ this.visitorsChart.fetch();
}
});
View
2 migrations/145-unique-visitor-metric-kind.sql
@@ -0,0 +1,2 @@
+insert into kpi_metrickind (code) values
+ ('general keymetrics:visitors');
View
1 scripts/crontab/crontab.tpl
@@ -28,6 +28,7 @@ HOME = /tmp
42 0 * * * {{ cron }} update_top_contributors
0 21 * * * {{ cron }} cache_most_unhelpful_kb_articles
47 2 * * * {{ cron }} remove_expired_registration_profiles
+0 9 * * * {{ cron }} update_visitors_metric
# Twice per week.
#05 01 * * 1,4 {{ cron }} update_weekly_votes
View
1 scripts/crontab/make-crons.py
@@ -71,6 +71,7 @@
42 0 * * * $CRON update_top_contributors
0 21 * * * $CRON cache_most_unhelpful_kb_articles
47 2 * * * $CRON remove_expired_registration_profiles
+0 9 * * * $CRON update_visitors_metric
# Twice per week.
#05 01 * * 1,4 $CRON update_weekly_votes
View
1 scripts/crontab/prod
@@ -28,6 +28,7 @@ HOME = /tmp
42 0 * * * cd /data/www/support.mozilla.com/kitsune; /usr/bin/python26 manage.py cron update_top_contributors
0 21 * * * cd /data/www/support.mozilla.com/kitsune; /usr/bin/python26 manage.py cron cache_most_unhelpful_kb_articles
47 2 * * * cd /data/www/support.mozilla.com/kitsune; /usr/bin/python26 manage.py cron remove_expired_registration_profiles
+0 9 * * * cd /data/www/support.mozilla.com/kitsune; /usr/bin/python26 manage.py cron update_visitors_metric
# Twice per week.
#05 01 * * 1,4 cd /data/www/support.mozilla.com/kitsune; /usr/bin/python26 manage.py cron update_weekly_votes
View
1 scripts/crontab/support
@@ -28,6 +28,7 @@ HOME = /tmp
42 0 * * * cd /data/www/support.allizom.org/kitsune; /usr/bin/python26 manage.py cron update_top_contributors
0 21 * * * cd /data/www/support.allizom.org/kitsune; /usr/bin/python26 manage.py cron cache_most_unhelpful_kb_articles
47 2 * * * cd /data/www/support.allizom.org/kitsune; /usr/bin/python26 manage.py cron remove_expired_registration_profiles
+0 9 * * * cd /data/www/support.allizom.org/kitsune; /usr/bin/python26 manage.py cron update_visitors_metric
# Twice per week.
#05 01 * * 1,4 cd /data/www/support.allizom.org/kitsune; /usr/bin/python26 manage.py cron update_weekly_votes
View
1 scripts/crontab/support-release
@@ -28,6 +28,7 @@ HOME = /tmp
42 0 * * * cd /data/www/support-release.allizom.org/kitsune; /usr/bin/python26 manage.py cron update_top_contributors
0 21 * * * cd /data/www/support-release.allizom.org/kitsune; /usr/bin/python26 manage.py cron cache_most_unhelpful_kb_articles
47 2 * * * cd /data/www/support-release.allizom.org/kitsune; /usr/bin/python26 manage.py cron remove_expired_registration_profiles
+0 9 * * * cd /data/www/support-release.allizom.org/kitsune; /usr/bin/python26 manage.py cron update_visitors_metric
# Twice per week.
#05 01 * * 1,4 cd /data/www/support-release.allizom.org/kitsune; /usr/bin/python26 manage.py cron update_weekly_votes
View
2 settings.py
@@ -769,12 +769,12 @@ def read_only_mode(env):
CHAT_SERVER = 'https://chat-support.mozilla.com:9091'
CHAT_CACHE_KEY = 'sumo-chat-queue-status'
+WEBTRENDS_PROFILE_ID = 'ABC123' # Profile id for SUMO
WEBTRENDS_WIKI_REPORT_URL = 'https://example.com/see_production.rst'
WEBTRENDS_USER = r'someaccount\someusername'
WEBTRENDS_PASSWORD = 'password'
WEBTRENDS_EPOCH = date(2010, 8, 1) # When WebTrends started gathering stats on
# the KB
-WEBTRENDS_REALM = 'Webtrends Basic Authentication'
MOBILE_COOKIE = 'msumo'
View
3 settings_test.py
@@ -20,3 +20,6 @@
'karma': 'redis://localhost:6383?socket_timeout=0.5&db=2',
'helpfulvotes': 'redis://localhost:6383?socket_timeout=0.5&db=2',
}
+
+# Use fake webtrends settings.
+WEBTRENDS_PROFILE_ID = 'ABC123'

0 comments on commit 4a9aacc

Please sign in to comment.
Something went wrong with that request. Please try again.