diff options
Diffstat (limited to 'meetingtools/apps/stats')
-rw-r--r-- | meetingtools/apps/stats/admin.py | 10 | ||||
-rw-r--r-- | meetingtools/apps/stats/forms.py | 8 | ||||
-rw-r--r-- | meetingtools/apps/stats/management/__init__.py | 1 | ||||
-rw-r--r-- | meetingtools/apps/stats/management/commands/__init__.py | 1 | ||||
-rw-r--r-- | meetingtools/apps/stats/management/commands/import_sessions.py | 20 | ||||
-rw-r--r-- | meetingtools/apps/stats/models.py | 66 | ||||
-rw-r--r-- | meetingtools/apps/stats/tasks.py | 33 | ||||
-rw-r--r-- | meetingtools/apps/stats/views.py | 85 |
8 files changed, 223 insertions, 1 deletions
diff --git a/meetingtools/apps/stats/admin.py b/meetingtools/apps/stats/admin.py new file mode 100644 index 0000000..f9ae86e --- /dev/null +++ b/meetingtools/apps/stats/admin.py @@ -0,0 +1,10 @@ +''' +Created on Jan 31, 2011 + +@author: leifj +''' + +from django.contrib import admin +from meetingtools.apps.stats.models import UserMeetingTransaction + +admin.site.register(UserMeetingTransaction)
\ No newline at end of file diff --git a/meetingtools/apps/stats/forms.py b/meetingtools/apps/stats/forms.py index 9fa5225..fbe9d5b 100644 --- a/meetingtools/apps/stats/forms.py +++ b/meetingtools/apps/stats/forms.py @@ -3,9 +3,15 @@ Created on Jan 16, 2012 @author: leifj """ +from django.contrib.auth.models import User +from django.forms import ModelChoiceField from django.forms.forms import Form -from django.forms.fields import DateTimeField +from django.forms.fields import DateTimeField, CharField +from meetingtools.apps.sco.models import ACObject class StatCaledarForm(Form): + tags = CharField(required=False) + user = ModelChoiceField(User.objects,required=False) + sco = ModelChoiceField(ACObject.objects,required=False) begin = DateTimeField(required=False) end = DateTimeField(required=False)
\ No newline at end of file diff --git a/meetingtools/apps/stats/management/__init__.py b/meetingtools/apps/stats/management/__init__.py new file mode 100644 index 0000000..3929ed7 --- /dev/null +++ b/meetingtools/apps/stats/management/__init__.py @@ -0,0 +1 @@ +__author__ = 'leifj' diff --git a/meetingtools/apps/stats/management/commands/__init__.py b/meetingtools/apps/stats/management/commands/__init__.py new file mode 100644 index 0000000..3929ed7 --- /dev/null +++ b/meetingtools/apps/stats/management/commands/__init__.py @@ -0,0 +1 @@ +__author__ = 'leifj' diff --git a/meetingtools/apps/stats/management/commands/import_sessions.py b/meetingtools/apps/stats/management/commands/import_sessions.py new file mode 100644 index 0000000..d98b081 --- /dev/null +++ b/meetingtools/apps/stats/management/commands/import_sessions.py @@ -0,0 +1,20 @@ +from optparse import make_option +from django.core.management import BaseCommand +from meetingtools.apps.stats.tasks import import_acc_sessions +from meetingtools.apps.cluster.models import ACCluster + +__author__ = 'leifj' + +class Command(BaseCommand): + + option_list = BaseCommand.option_list + ( + make_option('--since', + type='int', + dest='since', + default=0, + help='Import all sessions <since> seconds ago'), + ) + + def handle(self, *args, **options): + for acc in ACCluster.objects.all(): + import_acc_sessions(acc,since=options['since'])
\ No newline at end of file diff --git a/meetingtools/apps/stats/models.py b/meetingtools/apps/stats/models.py new file mode 100644 index 0000000..495bd65 --- /dev/null +++ b/meetingtools/apps/stats/models.py @@ -0,0 +1,66 @@ +import logging +from django.contrib.auth.models import User +from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned +from iso8601 import iso8601 +import tagging +from tagging.models import Tag +from meetingtools.apps.sco.models import ACObject, get_sco + +__author__ = 'leifj' + +from django.db import models +from django.db.models import fields, ForeignKey, DateTimeField, IntegerField +import lxml.etree as etree + +class UserMeetingTransaction(models.Model): + sco = ForeignKey(ACObject) + user = ForeignKey(User) + txid = IntegerField() + date_created = DateTimeField() + date_closed = DateTimeField() + + def seconds(self): + delta = self.date_closed - self.date_created + return delta.total_seconds() + + def __unicode__(self): + return "(%d) %d seconds in %s" % (self.txid,self.seconds(),self.sco) + + @staticmethod + def create(acc,row): + txid = int(row.get('transaction-id')) + sco_id = int(row.get('sco-id')) + + status = row.findtext("status") + if not status or status != "completed": + logging.debug("Ignoring transaction %s" % etree.tostring(row)) + return + + txo = None + try: + txo = UserMeetingTransaction.objects.get(sco__acc=acc,txid=txid) + except MultipleObjectsReturned,ex: + logging.error(ex) + except ObjectDoesNotExist: + login = row.findtext("login") + if not login: + raise ValueError("No user for transaction %d" % txid) + user,created = User.objects.get_or_create(username=login) + date_created=iso8601.parse_date(row.findtext("date-created")) + #date_created = date_created.replace(tzinfo=None) + date_closed=iso8601.parse_date(row.findtext("date-closed")) + #date_close = date_closed.replace(tzinfo=None) + txo = UserMeetingTransaction.objects.create(sco=get_sco(acc,sco_id), + txid=txid, + user=user, + date_created=date_created, + date_closed=date_closed) + tags = [] + for group in txo.user.groups.all(): + tags.append("group:%s" % group.name) + + (local,domain) = txo.user.username.split("@") + tags.append("domain:%s" % domain) + Tag.objects.update_tags(txo, ' '.join(tags)) + +tagging.register(UserMeetingTransaction)
\ No newline at end of file diff --git a/meetingtools/apps/stats/tasks.py b/meetingtools/apps/stats/tasks.py new file mode 100644 index 0000000..fb4b5c7 --- /dev/null +++ b/meetingtools/apps/stats/tasks.py @@ -0,0 +1,33 @@ +import logging +from celery.schedules import crontab +from celery.task import periodic_task +from meetingtools.ac import ac_api_client +from meetingtools.apps.cluster.models import ACCluster +from datetime import datetime,timedelta +from meetingtools.apps.stats.models import UserMeetingTransaction + +__author__ = 'leifj' + +def import_acc_sessions(acc,since=0): + with ac_api_client(acc) as api: + p = {'sort': 'asc','sort1': 'date-created','filter-type': 'meeting'} + + begin = None + if since > 0: + begin = datetime.now()-timedelta(seconds=since) + begin = begin.replace(microsecond=0) + + if begin is not None: + p['filter-gte-date-created'] = begin.isoformat() + + r = api.request('report-bulk-consolidated-transactions',p,True) + for tx in r.et.findall(".//row"): + try: + UserMeetingTransaction.create(acc,tx) + except Exception,ex: + logging.error(ex) + +@periodic_task(run_every=crontab(hour="*", minute="*", day_of_week="*")) +def import_sessions(since=3700): + for acc in ACCluster.objects.all(): + import_acc_sessions(acc,since)
\ No newline at end of file diff --git a/meetingtools/apps/stats/views.py b/meetingtools/apps/stats/views.py index 252b827..60628a2 100644 --- a/meetingtools/apps/stats/views.py +++ b/meetingtools/apps/stats/views.py @@ -3,10 +3,12 @@ Created on Jan 16, 2012 @author: leifj """ +import logging from django.contrib.auth.decorators import login_required from django.contrib.humanize.templatetags.humanize import naturalday from django.http import HttpResponseForbidden, HttpResponseBadRequest +from meetingtools.apps.stats.models import UserMeetingTransaction from meetingtools.ac import ac_api_client from iso8601 import iso8601 from time import mktime @@ -19,6 +21,9 @@ def _iso2datesimple(iso): (date,rest) = iso.split("T") return date +def _dt2ts(dt): + return mktime(dt.timetuple())*1000 + def _iso2ts(iso): return mktime(iso8601.parse_date(iso).timetuple())*1000 @@ -126,6 +131,7 @@ def user_minutes_api(request,username=None): #logging.debug("midnight: %d (%d)" % (ts_date_ts,ts_closed)) ms = (ts_closed - ts_date_ts) #logging.debug("nms: %d" % ms) + if curdate is not None and ms > 0: series.append([_date_ts(curdate),int(ms/60000)]) @@ -133,6 +139,85 @@ def user_minutes_api(request,username=None): return json_response({'data': sorted(series,key=lambda x: x[0]), 'rooms': len(rc.keys()), 'minutes': int(t_ms/60000)},request) @login_required +def tagged_minutes_api(request): + form = StatCaledarForm(request.GET) # convenient way to parse dates + if not form.is_valid(): + return HttpResponseBadRequest() + + tags = filter(lambda x: bool(x), form.cleaned_data['tags'].strip().split("+")) + sco = form.cleaned_data['sco'] + begin = form.cleaned_data['begin'] + end = form.cleaned_data['end'] + user = form.cleaned_data['user'] + + qs = UserMeetingTransaction.objects + + if user: + qs = qs.filter(user=user) + if sco: + qs = qs.filter(sco=sco) + if begin: + qs = qs.filter(date_created__gt=begin) + if end: + qs = qs.filter(date_closed__lt=end) + + if len(tags) > 0: + qs = UserMeetingTransaction.tagged.with_all(tags,qs) + + series = [] + d_created = None + d_closed = None + ms = 0 + curdate = None + t_ms = 0 + rc = {} + uc = {} + + for tx in qs.all().prefetch_related("sco").prefetch_related("user"): + rc[tx.sco.id] = True + uc[tx.user.username] = True + ts_created = _dt2ts(tx.date_created) + ts_closed = _dt2ts(tx.date_closed) + + d1 = tx.date_created + if d_created is None: + d_created = d1 + + d2 = tx.date_closed + if d_closed is None: + d_closed = d2 + + if curdate is None: + curdate = d1 + + if curdate != d1: + series.append([_dt2ts(curdate),int(ms/60)]) + ms = 0 + curdate = d1 + + if d1 == d2: #same date + diff = (ts_closed - ts_created) + ms += diff + t_ms += diff + else: # meeting spanned midnight + ts_date_ts = _dt2ts(d2) + ms += ts_date_ts - ts_created + series.append([_dt2ts(d1),int(ms/60)]) + t_ms += ms + curdate = d2 + ms = (ts_closed - ts_date_ts) + + if curdate is not None and ms > 0: + series.append([_date_ts(curdate),int(ms/60)]) + + return json_response({'data': sorted(series,key=lambda x: x[0]), + 'rooms': len(rc.keys()), + 'begin': naturalday(begin), + 'end': naturalday(end), + 'users': len(uc.keys()), + 'minutes': int(t_ms/60)},request) + +@login_required def domain_minutes_api(request,domain): with ac_api_client(request) as api: p = {'sort': 'asc','sort1': 'date-created','filter-type': 'meeting'} |