| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438 | 
							- # encoding: utf-8
 
- import datetime
 
- import json
 
- from django import forms
 
- from django.core.urlresolvers import reverse
 
- from django.forms import Widget
 
- from django.utils import formats
 
- from django.utils.html import format_html
 
- from django.utils.safestring import mark_safe
 
- from django.utils.text import capfirst
 
- from googleapiclient.discovery import build
 
- import httplib2
 
- from jet.dashboard.modules import DashboardModule
 
- from oauth2client.client import flow_from_clientsecrets, OAuth2Credentials, AccessTokenRefreshError, Storage
 
- from django.utils.translation import ugettext_lazy as _
 
- from django.conf import settings
 
- from django.utils.encoding import force_text
 
- try:
 
-     from django.utils.encoding import force_unicode
 
- except ImportError:
 
-     from django.utils.encoding import force_text as force_unicode
 
- try:
 
-     from django.forms.utils import flatatt
 
- except ImportError:
 
-     from django.forms.util import flatatt
 
- JET_MODULE_GOOGLE_ANALYTICS_CLIENT_SECRETS_FILE = getattr(
 
-     settings,
 
-     'JET_MODULE_GOOGLE_ANALYTICS_CLIENT_SECRETS_FILE',
 
-     ''
 
- )
 
- class ModuleCredentialStorage(Storage):
 
-     def __init__(self, module):
 
-         self.module = module
 
-     def locked_get(self):
 
-         pass
 
-     def locked_put(self, credentials):
 
-         pass
 
-     def locked_delete(self):
 
-         pass
 
-     def get(self):
 
-         try:
 
-             settings = json.loads(self.module.settings)
 
-             credential = settings['credential']
 
-             return OAuth2Credentials.from_json(credential)
 
-         except (ValueError, KeyError):
 
-             return None
 
-     def put(self, credentials):
 
-         self.module.update_settings({'credential': credentials.to_json()})
 
-     def delete(self):
 
-         self.module.pop_settings(('credential',))
 
- class GoogleAnalyticsClient:
 
-     credential = None
 
-     analytics_service = None
 
-     def __init__(self, storage=None, redirect_uri=None):
 
-         self.FLOW = flow_from_clientsecrets(
 
-             JET_MODULE_GOOGLE_ANALYTICS_CLIENT_SECRETS_FILE,
 
-             scope='https://www.googleapis.com/auth/analytics.readonly',
 
-             redirect_uri=redirect_uri
 
-         )
 
-         if storage is not None:
 
-             credential = storage.get()
 
-             credential.set_store(storage)
 
-             self.set_credential(credential)
 
-     def get_oauth_authorize_url(self, state=''):
 
-         self.FLOW.params['state'] = state
 
-         authorize_url = self.FLOW.step1_get_authorize_url()
 
-         return authorize_url
 
-     def set_credential(self, credential):
 
-         self.credential = credential
 
-         self.set_analytics_service(self.credential)
 
-     def set_credential_from_request(self, request):
 
-         self.set_credential(self.FLOW.step2_exchange(request.GET))
 
-     def set_analytics_service(self, credential):
 
-         http = httplib2.Http()
 
-         http = credential.authorize(http)
 
-         self.analytics_service = build('analytics', 'v3', http=http)
 
-     def api_profiles(self):
 
-         if self.analytics_service is None:
 
-             return None, None
 
-         try:
 
-             profiles = self.analytics_service.management().profiles().list(
 
-                 accountId='~all',
 
-                 webPropertyId='~all'
 
-             ).execute()
 
-             return profiles['items'], None
 
-         except (TypeError, KeyError) as e:
 
-             return None, e
 
-     def api_ga(self, profile_id, date1, date2, group=None):
 
-         if self.analytics_service is None:
 
-             return None, None
 
-         if group == 'day':
 
-             dimensions = 'ga:date'
 
-         elif group == 'week':
 
-             dimensions = 'ga:year,ga:week'
 
-         elif group == 'month':
 
-             dimensions = 'ga:year,ga:month'
 
-         else:
 
-             dimensions = ''
 
-         try:
 
-             data = self.analytics_service.data().ga().get(
 
-                 ids='ga:' + profile_id,
 
-                 start_date=date1.strftime('%Y-%m-%d'),
 
-                 end_date=date2.strftime('%Y-%m-%d'),
 
-                 metrics='ga:users,ga:sessions,ga:pageviews',
 
-                 dimensions=dimensions
 
-             ).execute()
 
-             return data, None
 
-         except TypeError as e:
 
-             return None, e
 
- class CredentialWidget(Widget):
 
-     module = None
 
-     def render(self, name, value, attrs=None):
 
-         if value and len(value) > 0:
 
-             link = '<a href="%s">%s</a>' % (
 
-                 reverse('jet-dashboard:google-analytics-revoke', kwargs={'pk': self.module.model.pk}),
 
-                 force_text(_('Revoke access'))
 
-             )
 
-         else:
 
-             link = '<a href="%s">%s</a>' % (
 
-                 reverse('jet-dashboard:google-analytics-grant', kwargs={'pk': self.module.model.pk}),
 
-                 force_text(_('Grant access'))
 
-             )
 
-         attrs = self.build_attrs({
 
-             'type': 'hidden',
 
-             'name': 'credential',
 
-         })
 
-         attrs['value'] = force_unicode(value) if value else ''
 
-         return format_html('%s<input{} />' % link, flatatt(attrs))
 
- class GoogleAnalyticsSettingsForm(forms.Form):
 
-     credential = forms.CharField(label=_('Access'), widget=CredentialWidget)
 
-     counter = forms.ChoiceField(label=_('Counter'))
 
-     period = forms.ChoiceField(label=_('Statistics period'), choices=(
 
-         (0, _('Today')),
 
-         (6, _('Last week')),
 
-         (30, _('Last month')),
 
-         (31 * 3 - 1, _('Last quarter')),
 
-         (364, _('Last year')),
 
-     ))
 
-     def set_module(self, module):
 
-         self.fields['credential'].widget.module = module
 
-         self.set_counter_choices(module)
 
-     def set_counter_choices(self, module):
 
-         counters = module.counters()
 
-         if counters is not None:
 
-             self.fields['counter'].choices = (('', '-- %s --' % force_text(_('none'))),)
 
-             self.fields['counter'].choices.extend(map(lambda x: (x['id'], x['websiteUrl']), counters))
 
-         else:
 
-             label = force_text(_('grant access first')) if module.credential is None else force_text(_('counters loading failed'))
 
-             self.fields['counter'].choices = (('', '-- %s -- ' % label),)
 
- class GoogleAnalyticsChartSettingsForm(GoogleAnalyticsSettingsForm):
 
-     show = forms.ChoiceField(label=_('Show'), choices=(
 
-         ('ga:users', capfirst(_('users'))),
 
-         ('ga:sessions', capfirst(_('sessions'))),
 
-         ('ga:pageviews', capfirst(_('views'))),
 
-     ))
 
-     group = forms.ChoiceField(label=_('Group'), choices=(
 
-         ('day', _('By day')),
 
-         ('week', _('By week')),
 
-         ('month', _('By month')),
 
-     ))
 
- class GoogleAnalyticsPeriodVisitorsSettingsForm(GoogleAnalyticsSettingsForm):
 
-     group = forms.ChoiceField(label=_('Group'), choices=(
 
-         ('day', _('By day')),
 
-         ('week', _('By week')),
 
-         ('month', _('By month')),
 
-     ))
 
- class GoogleAnalyticsBase(DashboardModule):
 
-     settings_form = GoogleAnalyticsSettingsForm
 
-     ajax_load = True
 
-     contrast = True
 
-     period = None
 
-     credential = None
 
-     counter = None
 
-     error = None
 
-     storage = None
 
-     def __init__(self, title=None, period=None, **kwargs):
 
-         kwargs.update({'period': period})
 
-         super(GoogleAnalyticsBase, self).__init__(title, **kwargs)
 
-     def settings_dict(self):
 
-         return {
 
-             'period': self.period,
 
-             'credential': self.credential,
 
-             'counter': self.counter
 
-         }
 
-     def load_settings(self, settings):
 
-         try:
 
-             self.period = int(settings.get('period'))
 
-         except TypeError:
 
-             self.period = 0
 
-         self.credential = settings.get('credential')
 
-         self.storage = ModuleCredentialStorage(self.model)
 
-         self.counter = settings.get('counter')
 
-     def init_with_context(self, context):
 
-         raise NotImplementedError('subclasses of GoogleAnalytics must provide a init_with_context() method')
 
-     def counters(self):
 
-         try:
 
-             client = GoogleAnalyticsClient(self.storage)
 
-             profiles, exception = client.api_profiles()
 
-             return profiles
 
-         except Exception:
 
-             return None
 
-     def get_grouped_date(self, data, group):
 
-         if group == 'week':
 
-             date = datetime.datetime.strptime(
 
-                 '%s-%s-%s' % (data['ga_year'], data['ga_week'], '0'),
 
-                 '%Y-%W-%w'
 
-             )
 
-         elif group == 'month':
 
-             date = datetime.datetime.strptime(data['ga_year'] + data['ga_month'], '%Y%m')
 
-         else:
 
-             date = datetime.datetime.strptime(data['ga_date'], '%Y%m%d')
 
-         return date
 
-     def format_grouped_date(self, data, group):
 
-         date = self.get_grouped_date(data, group)
 
-         if group == 'week':
 
-             date = u'%s — %s' % (
 
-                 (date - datetime.timedelta(days=6)).strftime('%d.%m'),
 
-                 date.strftime('%d.%m')
 
-             )
 
-         elif group == 'month':
 
-             date = date.strftime('%b, %Y')
 
-         else:
 
-             date = formats.date_format(date, 'DATE_FORMAT')
 
-         return date
 
-     def counter_attached(self):
 
-         if self.credential is None:
 
-             self.error = mark_safe(_('Please <a href="%s">attach Google account and choose Google Analytics counter</a> to start using widget') % reverse('jet-dashboard:update_module', kwargs={'pk': self.model.pk}))
 
-             return False
 
-         elif self.counter is None:
 
-             self.error = mark_safe(_('Please <a href="%s">select Google Analytics counter</a> to start using widget') % reverse('jet-dashboard:update_module', kwargs={'pk': self.model.pk}))
 
-             return False
 
-         else:
 
-             return True
 
-     def api_ga(self, group=None):
 
-         if self.counter_attached():
 
-             date1 = datetime.datetime.now() - datetime.timedelta(days=self.period)
 
-             date2 = datetime.datetime.now()
 
-             try:
 
-                 client = GoogleAnalyticsClient(self.storage)
 
-                 result, exception = client.api_ga(self.counter, date1, date2, group)
 
-                 if exception is not None:
 
-                         raise exception
 
-                 return result
 
-             except Exception as e:
 
-                 error = _('API request failed.')
 
-                 if isinstance(e, AccessTokenRefreshError):
 
-                     error += _(' Try to <a href="%s">revoke and grant access</a> again') % reverse('jet-dashboard:update_module', kwargs={'pk': self.model.pk})
 
-                 self.error = mark_safe(error)
 
- class GoogleAnalyticsVisitorsTotals(GoogleAnalyticsBase):
 
-     """
 
-     Google Analytics widget that shows total number of users, sessions and viewers for a particular period of time.
 
-     Period may be following: Today, Last week, Last month, Last quarter, Last year
 
-     """
 
-     title = _('Google Analytics visitors totals')
 
-     template = 'jet.dashboard/modules/google_analytics_visitors_totals.html'
 
-     #: Which period should be displayed. Allowed values - integer of days
 
-     period = None
 
-     def __init__(self, title=None, period=None, **kwargs):
 
-         kwargs.update({'period': period})
 
-         super(GoogleAnalyticsVisitorsTotals, self).__init__(title, **kwargs)
 
-     def init_with_context(self, context):
 
-         result = self.api_ga()
 
-         if result is not None:
 
-             try:
 
-                 self.children.append({'title': _('users'), 'value': result['totalsForAllResults']['ga:users']})
 
-                 self.children.append({'title': _('sessions'), 'value': result['totalsForAllResults']['ga:sessions']})
 
-                 self.children.append({'title': _('views'), 'value': result['totalsForAllResults']['ga:pageviews']})
 
-             except KeyError:
 
-                 self.error = _('Bad server response')
 
- class GoogleAnalyticsVisitorsChart(GoogleAnalyticsBase):
 
-     """
 
-     Google Analytics widget that shows users/sessions/viewer chart for a particular period of time.
 
-     Data is grouped by day, week or month
 
-     Period may be following: Today, Last week, Last month, Last quarter, Last year
 
-     """
 
-     title = _('Google Analytics visitors chart')
 
-     template = 'jet.dashboard/modules/google_analytics_visitors_chart.html'
 
-     style = 'overflow-x: auto;'
 
-     #: Which period should be displayed. Allowed values - integer of days
 
-     period = None
 
-     #: What data should be shown. Possible values: ``ga:users``, ``ga:sessions``, ``ga:pageviews``
 
-     show = None
 
-     #: Sets grouping of data. Possible values: ``day``, ``week``, ``month``
 
-     group = None
 
-     settings_form = GoogleAnalyticsChartSettingsForm
 
-     class Media:
 
-         js = ('jet.dashboard/vendor/chart.js/Chart.min.js', 'jet.dashboard/dashboard_modules/google_analytics.js')
 
-     def __init__(self, title=None, period=None, show=None, group=None, **kwargs):
 
-         kwargs.update({'period': period, 'show': show, 'group': group})
 
-         super(GoogleAnalyticsVisitorsChart, self).__init__(title, **kwargs)
 
-     def settings_dict(self):
 
-         settings = super(GoogleAnalyticsVisitorsChart, self).settings_dict()
 
-         settings['show'] = self.show
 
-         settings['group'] = self.group
 
-         return settings
 
-     def load_settings(self, settings):
 
-         super(GoogleAnalyticsVisitorsChart, self).load_settings(settings)
 
-         self.show = settings.get('show')
 
-         self.group = settings.get('group')
 
-     def init_with_context(self, context):
 
-         result = self.api_ga(self.group)
 
-         if result is not None:
 
-             try:
 
-                 for data in result['rows']:
 
-                     row_data = {}
 
-                     i = 0
 
-                     for column in result['columnHeaders']:
 
-                         row_data[column['name'].replace(':', '_')] = data[i]
 
-                         i += 1
 
-                     date = self.get_grouped_date(row_data, self.group)
 
-                     self.children.append((date, row_data[self.show.replace(':', '_')]))
 
-             except KeyError:
 
-                 self.error = _('Bad server response')
 
- class GoogleAnalyticsPeriodVisitors(GoogleAnalyticsBase):
 
-     """
 
-     Google Analytics widget that shows users, sessions and viewers for a particular period of time.
 
-     Data is grouped by day, week or month
 
-     Period may be following: Today, Last week, Last month, Last quarter, Last year
 
-     """
 
-     title = _('Google Analytics period visitors')
 
-     template = 'jet.dashboard/modules/google_analytics_period_visitors.html'
 
-     #: Which period should be displayed. Allowed values - integer of days
 
-     period = None
 
-     #: Sets grouping of data. Possible values: ``day``, ``week``, ``month``
 
-     group = None
 
-     contrast = False
 
-     settings_form = GoogleAnalyticsPeriodVisitorsSettingsForm
 
-     def __init__(self, title=None, period=None, group=None, **kwargs):
 
-         kwargs.update({'period': period, 'group': group})
 
-         super(GoogleAnalyticsPeriodVisitors, self).__init__(title, **kwargs)
 
-     def settings_dict(self):
 
-         settings = super(GoogleAnalyticsPeriodVisitors, self).settings_dict()
 
-         settings['group'] = self.group
 
-         return settings
 
-     def load_settings(self, settings):
 
-         super(GoogleAnalyticsPeriodVisitors, self).load_settings(settings)
 
-         self.group = settings.get('group')
 
-     def init_with_context(self, context):
 
-         result = self.api_ga(self.group)
 
-         if result is not None:
 
-             try:
 
-                 for data in reversed(result['rows']):
 
-                     row_data = {}
 
-                     i = 0
 
-                     for column in result['columnHeaders']:
 
-                         row_data[column['name'].replace(':', '_')] = data[i]
 
-                         i += 1
 
-                     date = self.format_grouped_date(row_data, self.group)
 
-                     self.children.append((date, row_data))
 
-             except KeyError:
 
-                 self.error = _('Bad server response')
 
 
  |