From 0d2aa416dd06c99c9c3edbebb778b590f12ba974 Mon Sep 17 00:00:00 2001 From: Leif Johansson Date: Mon, 6 Feb 2012 12:18:33 +0100 Subject: south++ --- .../apps/room/migrations/0001_initial.py | 120 +++++++++++++ .../0002_auto__add_field_room_deleted_sco_id.py | 91 ++++++++++ src/meetingtools/apps/room/migrations/__init__.py | 0 src/meetingtools/apps/room/models.py | 16 ++ src/meetingtools/apps/room/tasks.py | 186 +++++++++++++++++++++ 5 files changed, 413 insertions(+) create mode 100644 src/meetingtools/apps/room/migrations/0001_initial.py create mode 100644 src/meetingtools/apps/room/migrations/0002_auto__add_field_room_deleted_sco_id.py create mode 100644 src/meetingtools/apps/room/migrations/__init__.py create mode 100644 src/meetingtools/apps/room/tasks.py (limited to 'src/meetingtools/apps/room') diff --git a/src/meetingtools/apps/room/migrations/0001_initial.py b/src/meetingtools/apps/room/migrations/0001_initial.py new file mode 100644 index 0000000..4cb1bef --- /dev/null +++ b/src/meetingtools/apps/room/migrations/0001_initial.py @@ -0,0 +1,120 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding model 'Room' + db.create_table('room_room', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('creator', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), + ('name', self.gf('django.db.models.fields.CharField')(max_length=128)), + ('urlpath', self.gf('django.db.models.fields.CharField')(unique=True, max_length=128)), + ('acc', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['cluster.ACCluster'])), + ('self_cleaning', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('allow_host', self.gf('django.db.models.fields.BooleanField')(default=True)), + ('sco_id', self.gf('django.db.models.fields.IntegerField')()), + ('source_sco_id', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)), + ('folder_sco_id', self.gf('django.db.models.fields.IntegerField')()), + ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('user_count', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)), + ('host_count', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)), + ('timecreated', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), + ('lastupdated', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)), + ('lastvisited', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)), + )) + db.send_create_signal('room', ['Room']) + + # Adding unique constraint on 'Room', fields ['acc', 'sco_id'] + db.create_unique('room_room', ['acc_id', 'sco_id']) + + # Adding unique constraint on 'Room', fields ['name', 'folder_sco_id'] + db.create_unique('room_room', ['name', 'folder_sco_id']) + + + def backwards(self, orm): + + # Removing unique constraint on 'Room', fields ['name', 'folder_sco_id'] + db.delete_unique('room_room', ['name', 'folder_sco_id']) + + # Removing unique constraint on 'Room', fields ['acc', 'sco_id'] + db.delete_unique('room_room', ['acc_id', 'sco_id']) + + # Deleting model 'Room' + db.delete_table('room_room') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'cluster.accluster': { + 'Meta': {'object_name': 'ACCluster'}, + 'api_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'default_template_sco_id': ('django.db.models.fields.IntegerField', [], {'unique': 'True', 'blank': 'True'}), + 'domain_match': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'user': ('django.db.models.fields.CharField', [], {'max_length': '128'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'room.room': { + 'Meta': {'unique_together': "(('acc', 'sco_id'), ('name', 'folder_sco_id'))", 'object_name': 'Room'}, + 'acc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cluster.ACCluster']"}), + 'allow_host': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'folder_sco_id': ('django.db.models.fields.IntegerField', [], {}), + 'host_count': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'lastupdated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'lastvisited': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'sco_id': ('django.db.models.fields.IntegerField', [], {}), + 'self_cleaning': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'source_sco_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'timecreated': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'urlpath': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), + 'user_count': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['room'] diff --git a/src/meetingtools/apps/room/migrations/0002_auto__add_field_room_deleted_sco_id.py b/src/meetingtools/apps/room/migrations/0002_auto__add_field_room_deleted_sco_id.py new file mode 100644 index 0000000..bea1f14 --- /dev/null +++ b/src/meetingtools/apps/room/migrations/0002_auto__add_field_room_deleted_sco_id.py @@ -0,0 +1,91 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding field 'Room.deleted_sco_id' + db.add_column('room_room', 'deleted_sco_id', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True), keep_default=False) + + + def backwards(self, orm): + + # Deleting field 'Room.deleted_sco_id' + db.delete_column('room_room', 'deleted_sco_id') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'cluster.accluster': { + 'Meta': {'object_name': 'ACCluster'}, + 'api_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'default_template_sco_id': ('django.db.models.fields.IntegerField', [], {'unique': 'True', 'blank': 'True'}), + 'domain_match': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'user': ('django.db.models.fields.CharField', [], {'max_length': '128'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'room.room': { + 'Meta': {'unique_together': "(('acc', 'sco_id'), ('name', 'folder_sco_id'))", 'object_name': 'Room'}, + 'acc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cluster.ACCluster']"}), + 'allow_host': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'deleted_sco_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'folder_sco_id': ('django.db.models.fields.IntegerField', [], {}), + 'host_count': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'lastupdated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'lastvisited': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'sco_id': ('django.db.models.fields.IntegerField', [], {}), + 'self_cleaning': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'source_sco_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'timecreated': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'urlpath': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), + 'user_count': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['room'] diff --git a/src/meetingtools/apps/room/migrations/__init__.py b/src/meetingtools/apps/room/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/meetingtools/apps/room/models.py b/src/meetingtools/apps/room/models.py index 731e8b9..249b1bf 100644 --- a/src/meetingtools/apps/room/models.py +++ b/src/meetingtools/apps/room/models.py @@ -35,6 +35,12 @@ class FileLock(object): def __delete__(self): raise AttributeError +class RoomLockedException(Exception): + def __init__(self, value): + self.value = value + def __str__(self): + return repr(self.value) + class Room(models.Model): creator = ForeignKey(User,editable=False) name = CharField(max_length=128) @@ -44,6 +50,7 @@ class Room(models.Model): allow_host = BooleanField(verbose_name="Allow first participant to become host?",default=True) sco_id = IntegerField(verbose_name="Adobe Connect Room") source_sco_id = IntegerField(verbose_name="Template",blank=True,null=True) + deleted_sco_id = IntegerField(verbose_name="Previous Room ID",editable=False,blank=True,null=True) folder_sco_id = IntegerField(verbose_name="Adobe Connect Room Folder",editable=False) description = TextField(blank=True,null=True) user_count = IntegerField(verbose_name="User Count At Last Visit",editable=False,blank=True,null=True) @@ -66,6 +73,15 @@ class Room(models.Model): if msg: f.write(msg) f.close() + + def trylock(self,raise_on_locked=True): + if self.is_locked(): + if raise_on_locked: + raise RoomLockedException,"room %s is locked" % self.__unicode__() + else: + return False + self.lock() #race!! - must use flock + return True def unlock(self): os.remove(self._lockf()) diff --git a/src/meetingtools/apps/room/tasks.py b/src/meetingtools/apps/room/tasks.py new file mode 100644 index 0000000..2042e36 --- /dev/null +++ b/src/meetingtools/apps/room/tasks.py @@ -0,0 +1,186 @@ +''' +Created on Jan 18, 2012 + +@author: leifj +''' +from celery.decorators import periodic_task +from celery.schedules import crontab +from meetingtools.apps.cluster.models import ACCluster +from meetingtools.ac import ac_api_client, ac_api_client_nocache,\ + ac_api_client_direct +from meetingtools.apps.room.models import Room +import iso8601 +from django.contrib.auth.models import User +from django.core.cache import cache +from django.core.exceptions import ObjectDoesNotExist +import logging +from datetime import datetime,timedelta +from lxml import etree + +def _owner_username(api,sco): + logging.debug(sco) + key = '_sco_owner_%s' % sco.get('sco-id') + logging.debug(key) + try: + if cache.get(key) is None: + fid = sco.get('folder-id') + if not fid: + logging.debug("No folder-id") + return None + + folder_id = int(fid) + r = api.request('sco-info',{'sco-id':folder_id},False) + if r.status_code() == 'no-data': + return None + + parent = r.et.xpath("//sco")[0] + logging.debug("p %s",repr(parent)) + logging.debug("r %s",etree.tostring(parent)) + name = None + if parent: + logging.debug("parent: %s" % parent) + if parent.findtext('name') == 'User Meetings': + name = sco.findtext('name') + else: + name = _owner_username(api,parent) + + cache.set(key,name) + + return cache.get(key) + except Exception,e: + logging.debug(e) + return None + +def _extended_info(api,sco_id): + r = api.request('sco-info',{'sco-id':sco_id},False) + if r.status_code == 'no-data': + return None + return (r.et,_owner_username(api,r.et.xpath('//sco')[0])) + +def _import_one_room(acc,api,row): + sco_id = int(row.get('sco-id')) + last = iso8601.parse_date(row.findtext("date-modified[0]")) + room = None + + try: + room = Room.objects.get(acc=acc,deleted_sco_id=sco_id) + if room != None: + return # We hit a room in the process of being cleaned - let it simmer until next pass + except ObjectDoesNotExist: + pass + except Exception,e: + logging.debug(e) + return + + try: + logging.debug("finding acc=%s,sco_id=%d in our DB" % (acc,sco_id)) + room = Room.objects.get(acc=acc,sco_id=sco_id) + if room.deleted_sco_id != None: + return # We hit a room in the process of being cleaned - let it simmer until next pass + room.trylock() + except ObjectDoesNotExist: + pass + + last = last.replace(tzinfo=None) + lastupdated = None + if room: + lastupdated = room.lastupdated.replace(tzinfo=None) # make the compare work - very ugly + logging.debug("last %s" % last) + logging.debug("lastupdated %s" % lastupdated) + if not room or lastupdated < last: + (r,username) = _extended_info(api, sco_id) + logging.debug("owner: %s" % username) + if username is None: + return + + urlpath = row.findtext("url[0]").strip("/") + name = row.findtext('name[0]') + description = row.findtext('description[0]') + folder_sco_id = int(r.xpath('//sco[0]/@folder-id') or 0) or None + source_sco_id = int(r.xpath('//sco[0]/@source-sco-id') or 0) or None + + if room == None: + user,created = User.objects.get_or_create(username=username) + if created: + user.set_unusable_password() + room = Room.objects.create(acc=acc,sco_id=sco_id,creator=user,name=name,description=description,folder_sco_id=folder_sco_id,source_sco_id=source_sco_id,urlpath=urlpath) + room.trylock() + else: + room.folder_sco_id = folder_sco_id + room.source_sco_id = source_sco_id + room.description = description + room.urlpath = urlpath + + userlist = api.request('meeting-usermanager-user-list',{'sco-id': room.sco_id},False) + if userlist.status_code() == 'ok': + room.user_count = int(userlist.et.xpath("count(.//userdetails)")) + room.host_count = int(userlist.et.xpath("count(.//userdetails/role[text() = 'host'])")) + + room.save() + room.unlock() + else: + room.unlock() + +def _import_acc(acc): + api = ac_api_client_direct(acc) + backthen = datetime.now()-timedelta(seconds=6000000) + backthen = backthen.replace(microsecond=0) + logging.debug(backthen.isoformat()) + r = api.request('report-bulk-objects',{'filter-type': 'meeting','filter-gt-date-modified': backthen.isoformat()}) + for row in r.et.xpath("//row"): + _import_one_room(acc,api,row) + + +@periodic_task(run_every=crontab(hour="*", minute="*/1", day_of_week="*")) +def import_all_rooms(): + for acc in ACCluster.objects.all(): + _import_acc(acc) + +def _import_meeting_room_session(api,acc,sco_id,row=None,room=None): + try: + if room == None: + room = Room.objects.get(acc=acc,sco_id=sco_id) + if row and row.findtext("date-end"): + room.user_count = 0 + room.host_count = 0 + else: + userlist = api.request('meeting-usermanager-user-list',{'sco-id': room.sco_id},False) + if userlist.status_code() == 'ok': + room.user_count = int(userlist.et.xpath("count(.//userdetails)")) + room.host_count = int(userlist.et.xpath("count(.//userdetails/role[text() = 'host'])")) + elif userlist.status_code() == 'no-access' and userlist.subcode() == 'not-available': #no active session + room.user_count = 0 + room.host_count = 0 + room.save() + except ObjectDoesNotExist: + pass + +def _import_meeting_sessions_acc(acc): + api = ac_api_client_direct(acc) + backthen = datetime.now()-timedelta(seconds=600000) + backthen = backthen.replace(microsecond=0) + logging.debug(backthen.isoformat()) + r = api.request('report-meeting-sessions',{'filter-gt-date-created': backthen.isoformat()}) + seen = {} + for row in r.et.xpath("//row"): + try: + sco_id = int(row.get('sco-id')) + _import_meeting_room_session(api,acc,sco_id,row,None) + seen[sco_id] = True + except Exception,ex: + logging.error(ex) + + for room in Room.objects.filter(acc=acc,user_count=None): + logging.debug("checking sessions on room: %s" % room) + if seen.get(room.sco_id,False): + continue + try: + logging.debug("importing sessions") + _import_meeting_room_session(api,acc,room.sco_id,None,room) + except Exception,ex: + logging.error(ex) + +@periodic_task(run_every=crontab(hour="*", minute="*/1", day_of_week="*")) +def _import_meeting_sessions(): + for acc in ACCluster.objects.all(): + _import_meeting_sessions_acc(acc) \ No newline at end of file -- cgit v1.1