Skip to content

Commit

Permalink
usage endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewelwell committed May 10, 2019
1 parent 64a3b56 commit 948df49
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 6 deletions.
2 changes: 2 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ The application relies on the following environment variables to run:
* `DATABASE_URL`: required by develop and master environments, should be a standard format database url e.g. postgres://user:password@host:port/db_name
* `DJANGO_SECRET_KEY`: see 'Creating a secret key' section below
* `GOOGLE_ANALYTICS_KEY`: if google analytics is required, add your tracking code
* `GOOGLE_SERVICE_ACCOUNT`: service account json for accessing the google API, used for getting usage of an organisation - needs access to analytics.readonly scope
* `GA_TABLE_ID`: GA table ID (view) to query when looking for organisation usage

### Creating a secret key
It is important to also set an environment variable on whatever platform you are using for
Expand Down
2 changes: 1 addition & 1 deletion src/analytics/middleware.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from threading import Thread

from .utils import track_request
from .track import track_request


class GoogleAnalyticsMiddleware:
Expand Down
37 changes: 37 additions & 0 deletions src/analytics/query.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import json

from apiclient.discovery import build
from django.conf import settings
from google.oauth2 import service_account

GA_SCOPES = ['https://www.googleapis.com/auth/analytics.readonly']
GA_API_NAME = 'analytics'
GA_API_VERSION = 'v3'


def get_service():
"""
Get the google service object to use to query the API
"""
credentials = service_account.Credentials.from_service_account_info(
json.loads(settings.GOOGLE_SERVICE_ACCOUNT), scopes=GA_SCOPES)

# Build the service object.
return build(GA_API_NAME, GA_API_VERSION, credentials=credentials)


def get_events_for_organisation(organisation):
"""
Get number of tracked events for last 30 days for an organisation
:return: number of events as integer
"""
ga_response = get_service().data().ga().get(
ids=settings.GA_TABLE_ID,
start_date='30daysAgo',
end_date='today',
metrics='ga:totalEvents',
dimensions='ga:date',
filters=f'ga:eventCategory=={organisation.get_unique_slug()}').execute()

return int(ga_response['totalsForAllResults']['ga:totalEvents'])
File renamed without changes.
6 changes: 6 additions & 0 deletions src/app/settings/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@

# Google Analytics Configuration
GOOGLE_ANALYTICS_KEY = os.environ.get('GOOGLE_ANALYTICS_KEY', '')
GOOGLE_SERVICE_ACCOUNT = os.environ.get('GOOGLE_SERVICE_ACCOUNT')
if not GOOGLE_SERVICE_ACCOUNT:
warnings.warn("GOOGLE_SERVICE_ACCOUNT not configured, getting organisation usage will not work")
GA_TABLE_ID = os.environ.get('GA_TABLE_ID')
if not GA_TABLE_ID:
warnings.warn("GA_TABLE_ID not configured, getting organisation usage will not work")

if 'DJANGO_ALLOWED_HOSTS' in os.environ:
ALLOWED_HOSTS = os.environ['DJANGO_ALLOWED_HOSTS'].split(',')
Expand Down
8 changes: 3 additions & 5 deletions src/features/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from rest_framework.response import Response
from rest_framework.schemas import AutoSchema

from analytics.utils import track_event
from analytics.track import track_event
from environments.models import Environment, Identity
from projects.models import Project
from .models import FeatureState, Feature
Expand Down Expand Up @@ -223,17 +223,15 @@ def get(self, request, identifier=None, *args, **kwargs):

return Response(error_response, status=status.HTTP_400_BAD_REQUEST)

ga_event_category = str(environment.project.organisation.id) + "-" + environment.project.organisation.name

if identifier:
track_event(ga_event_category, "identity_flags")
track_event(environment.project.organisation.get_unique_slug(), "identity_flags")

identity, _ = Identity.objects.get_or_create(
identifier=identifier,
environment=environment,
)
else:
track_event(ga_event_category, "flags")
track_event(environment.project.organisation.get_unique_slug(), "flags")
identity = None

kwargs = {
Expand Down
4 changes: 4 additions & 0 deletions src/organisations/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ class Organisation(models.Model):
free_to_use_subscription = models.BooleanField(default=True)
plan = models.CharField(max_length=20, null=True, blank=True)
subscription_date = models.DateTimeField('SubscriptionDate', blank=True, null=True)

class Meta:
ordering = ['id']

def __str__(self):
return "Org %s" % self.name

def get_unique_slug(self):
return str(self.id) + "-" + self.name
15 changes: 15 additions & 0 deletions src/organisations/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from rest_framework.exceptions import ValidationError
from rest_framework.response import Response

from analytics.query import get_events_for_organisation
from projects.serializers import ProjectSerializer
from organisations.serializers import OrganisationSerializer
from users.models import Invite
Expand Down Expand Up @@ -74,6 +75,20 @@ def invite(self, request, pk):
else:
raise ValidationError(invites_serializer.errors)

@action(detail=True, methods=["GET"])
def usage(self, request, pk):
organisation = self.get_object()

try:
events = get_events_for_organisation(organisation)
except (TypeError, ValueError):
# TypeError can be thrown when getting service account if not configured
# ValueError can be thrown if GA returns a value that cannot be converted to integer
return Response({"error": "Couldn't get number of events for organisation."},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)

return Response({"events": events}, status=status.HTTP_200_OK)


class InviteViewSet(viewsets.ModelViewSet):
serializer_class = InviteListSerializer
Expand Down

0 comments on commit 948df49

Please sign in to comment.