├── .gitignore ├── LICENSE.txt ├── README.rst ├── setup.py └── user_metrics ├── __init__.py ├── admin.py ├── api.py ├── management ├── __init__.py └── commands │ ├── __init__.py │ └── metrics_aggregate_by_user.py ├── migrations ├── 0001_initial.py └── __init__.py ├── models.py └── utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* 2 | *.pyc -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Reiner Márquez Alvarez and contributors 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of copyright holders nor the names of its 14 | contributors may be used to endorse or promote products derived 15 | from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS 21 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Django User Metrics 2 | =================== 3 | 4 | django app that allows capture application metrics by each user individually, so after you can generate reports with aggregation of results 5 | by day or by week for each user 6 | 7 | soon more documentation -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | from setuptools import setup, find_packages 3 | 4 | def read(fname): 5 | return open(os.path.join(os.path.dirname(__file__), fname)).read() 6 | 7 | setup( 8 | name = "django-user-metrics", 9 | version = "0.1", 10 | description = "capture metrics for each user", 11 | long_description = read('README.rst'), 12 | url = 'https://github.com/rmaceissoft/django-user-metrics', 13 | author = 'Reiner Marquez', 14 | author_email = 'rmaceissoft@gmail.com', 15 | packages = find_packages(exclude=['tests', 'example', 'docs']), 16 | classifiers = [ 17 | 'Development Status :: 4 - Beta', 18 | 'Environment :: Console', 19 | 'Framework :: Django', 20 | 'Intended Audience :: Developers', 21 | 'Intended Audience :: Information Technology', 22 | 'License :: OSI Approved :: BSD License', 23 | 'Operating System :: OS Independent', 24 | 'Programming Language :: Python', 25 | ], 26 | ) -------------------------------------------------------------------------------- /user_metrics/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmaceissoft/django-user-metrics/eab89f402c3cf60ce802df2011ca3f77571079f5/user_metrics/__init__.py -------------------------------------------------------------------------------- /user_metrics/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from user_metrics.models import Metric 3 | 4 | admin.site.register(Metric) 5 | -------------------------------------------------------------------------------- /user_metrics/api.py: -------------------------------------------------------------------------------- 1 | from user_metrics.models import Metric, MetricItem 2 | 3 | def put_metric(slug, user, count=1, **kwargs): 4 | """ Increment a metric by a given user """ 5 | 6 | try: 7 | metric = Metric.objects.get(slug=slug) 8 | except Metric.DoesNotExist: 9 | metric = Metric.objects.create(slug=slug, name=slug) 10 | 11 | MetricItem.objects.create( 12 | metric = metric, 13 | user = user, 14 | count = count 15 | ) -------------------------------------------------------------------------------- /user_metrics/management/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'reiner' 2 | -------------------------------------------------------------------------------- /user_metrics/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'reiner' 2 | -------------------------------------------------------------------------------- /user_metrics/management/commands/metrics_aggregate_by_user.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from django.core.management.base import NoArgsCommand 3 | 4 | from user_metrics.models import Metric, MetricItem, MetricDay, MetricWeek 5 | from user_metrics.utils import week_for_date 6 | 7 | class Command(NoArgsCommand): 8 | help = "Aggregate Application Metrics" 9 | 10 | requires_model_validation = True 11 | 12 | def handle_noargs(self, **options): 13 | """ Aggregate Metrics by User """ 14 | 15 | # Aggregate Items 16 | items = MetricItem.objects.all() 17 | 18 | for item in items: 19 | # Daily Aggregation 20 | day, create = MetricDay.objects.get_or_create( 21 | metric = item.metric, 22 | user = item.user, 23 | date_up = item.date_up 24 | ) 25 | 26 | day.count = day.count + item.count 27 | day.save() 28 | 29 | # Weekly Aggregation 30 | week_date = week_for_date(item.date_up) 31 | week, create = MetricWeek.objects.get_or_create( 32 | metric = item.metric, 33 | user = item.user, 34 | date_up = week_date 35 | ) 36 | 37 | week.count = week.count + item.count 38 | week.save() 39 | 40 | # Kill off our items 41 | items.delete() 42 | -------------------------------------------------------------------------------- /user_metrics/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | import datetime 3 | from south.db import db 4 | from south.v2 import SchemaMigration 5 | from django.db import models 6 | 7 | class Migration(SchemaMigration): 8 | 9 | def forwards(self, orm): 10 | 11 | # Adding model 'Metric' 12 | db.create_table('user_metrics_metric', ( 13 | ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), 14 | ('slug', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=100, db_index=True)), 15 | ('name', self.gf('django.db.models.fields.CharField')(max_length=90)), 16 | )) 17 | db.send_create_signal('user_metrics', ['Metric']) 18 | 19 | # Adding model 'MetricItem' 20 | db.create_table('user_metrics_metricitem', ( 21 | ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), 22 | ('metric', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['user_metrics.Metric'])), 23 | ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), 24 | ('count', self.gf('django.db.models.fields.IntegerField')(default=1)), 25 | ('date_up', self.gf('django.db.models.fields.DateField')(default=datetime.date.today)), 26 | )) 27 | db.send_create_signal('user_metrics', ['MetricItem']) 28 | 29 | # Adding model 'MetricDay' 30 | db.create_table('user_metrics_metricday', ( 31 | ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), 32 | ('metric', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['user_metrics.Metric'])), 33 | ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), 34 | ('count', self.gf('django.db.models.fields.IntegerField')(default=0)), 35 | ('date_up', self.gf('django.db.models.fields.DateField')(default=datetime.date.today)), 36 | )) 37 | db.send_create_signal('user_metrics', ['MetricDay']) 38 | 39 | # Adding model 'MetricWeek' 40 | db.create_table('user_metrics_metricweek', ( 41 | ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), 42 | ('metric', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['user_metrics.Metric'])), 43 | ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), 44 | ('count', self.gf('django.db.models.fields.IntegerField')(default=0)), 45 | ('date_up', self.gf('django.db.models.fields.DateField')(default=datetime.date.today)), 46 | )) 47 | db.send_create_signal('user_metrics', ['MetricWeek']) 48 | 49 | 50 | def backwards(self, orm): 51 | 52 | # Deleting model 'Metric' 53 | db.delete_table('user_metrics_metric') 54 | 55 | # Deleting model 'MetricItem' 56 | db.delete_table('user_metrics_metricitem') 57 | 58 | # Deleting model 'MetricDay' 59 | db.delete_table('user_metrics_metricday') 60 | 61 | # Deleting model 'MetricWeek' 62 | db.delete_table('user_metrics_metricweek') 63 | 64 | 65 | models = { 66 | 'auth.group': { 67 | 'Meta': {'object_name': 'Group'}, 68 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 69 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), 70 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) 71 | }, 72 | 'auth.permission': { 73 | 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, 74 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 75 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), 76 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 77 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) 78 | }, 79 | 'auth.user': { 80 | 'Meta': {'object_name': 'User'}, 81 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 82 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), 83 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 84 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), 85 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 86 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 87 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 88 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 89 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 90 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 91 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), 92 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), 93 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '75'}) 94 | }, 95 | 'contenttypes.contenttype': { 96 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, 97 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 98 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 99 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 100 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 101 | }, 102 | 'user_metrics.metric': { 103 | 'Meta': {'object_name': 'Metric'}, 104 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 105 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '90'}), 106 | 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100', 'db_index': 'True'}) 107 | }, 108 | 'user_metrics.metricday': { 109 | 'Meta': {'object_name': 'MetricDay'}, 110 | 'count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), 111 | 'date_up': ('django.db.models.fields.DateField', [], {'default': 'datetime.date.today'}), 112 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 113 | 'metric': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['user_metrics.Metric']"}), 114 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) 115 | }, 116 | 'user_metrics.metricitem': { 117 | 'Meta': {'object_name': 'MetricItem'}, 118 | 'count': ('django.db.models.fields.IntegerField', [], {'default': '1'}), 119 | 'date_up': ('django.db.models.fields.DateField', [], {'default': 'datetime.date.today'}), 120 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 121 | 'metric': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['user_metrics.Metric']"}), 122 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) 123 | }, 124 | 'user_metrics.metricweek': { 125 | 'Meta': {'object_name': 'MetricWeek'}, 126 | 'count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), 127 | 'date_up': ('django.db.models.fields.DateField', [], {'default': 'datetime.date.today'}), 128 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 129 | 'metric': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['user_metrics.Metric']"}), 130 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) 131 | } 132 | } 133 | 134 | complete_apps = ['user_metrics'] 135 | -------------------------------------------------------------------------------- /user_metrics/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmaceissoft/django-user-metrics/eab89f402c3cf60ce802df2011ca3f77571079f5/user_metrics/migrations/__init__.py -------------------------------------------------------------------------------- /user_metrics/models.py: -------------------------------------------------------------------------------- 1 | from datetime import date 2 | from django.db import models 3 | from django.contrib.auth.models import User 4 | 5 | class Metric(models.Model): 6 | """ holds the types of metrics 7 | """ 8 | slug = models.SlugField(unique=True, max_length=100, db_index=True) 9 | name = models.CharField(max_length=90) 10 | 11 | def __unicode__(self): return self.name 12 | 13 | 14 | class MetricItem(models.Model): 15 | """ more atomic representation of a metric by each user 16 | """ 17 | metric = models.ForeignKey(Metric) 18 | user = models.ForeignKey(User) 19 | 20 | count = models.IntegerField(default=1) 21 | date_up = models.DateField(default=date.today) 22 | 23 | def __unicode__(self): 24 | '%d %s by %s at %s' % (self.count, self.metric.name, self.user, self.date_up) 25 | 26 | 27 | class MetricDay(models.Model): 28 | """ represent aggregation of metrics daily 29 | """ 30 | metric = models.ForeignKey(Metric) 31 | user = models.ForeignKey(User) 32 | 33 | count = models.IntegerField(default=0) 34 | date_up = models.DateField(default=date.today) 35 | 36 | 37 | class MetricWeek(models.Model): 38 | """ represent aggregation of metric weekly 39 | """ 40 | metric = models.ForeignKey(Metric) 41 | user = models.ForeignKey(User) 42 | 43 | count = models.IntegerField(default=0) 44 | date_up = models.DateField(default=date.today) -------------------------------------------------------------------------------- /user_metrics/utils.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | from user_metrics.models import MetricWeek 4 | 5 | def week_for_date(date): 6 | return date - datetime.timedelta(days=date.weekday()) 7 | 8 | 9 | def total_weeks_aggregated(): 10 | """ 11 | return total of weeks aggregates to MetricWeek model 12 | """ 13 | try: 14 | first_week = MetricWeek.objects.all().order_by('-date_up')[0].date_up 15 | except IndexError: 16 | first_week = week_for_date(datetime.date.today()) 17 | return (datetime.date.today() - first_week).days / 7 18 | 19 | --------------------------------------------------------------------------------