diff options
Diffstat (limited to 'meetingtools/apps/room/tasks.py')
-rw-r--r-- | meetingtools/apps/room/tasks.py | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/meetingtools/apps/room/tasks.py b/meetingtools/apps/room/tasks.py new file mode 100644 index 0000000..ce9f275 --- /dev/null +++ b/meetingtools/apps/room/tasks.py @@ -0,0 +1,250 @@ +''' +Created on Jan 18, 2012 + +@author: leifj +''' +from celery.task import periodic_task,task +from celery.schedules import crontab +from meetingtools.apps.cluster.models import ACCluster +from meetingtools.ac import ac_api_client +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 +from django.db.models import Q +from django.contrib.humanize.templatetags import humanize +from django.conf import settings +from django.core.mail import send_mail + +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 is not 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 is not 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("found room owned by %s time for and update" % username) + if username is None: + return + + logging.debug(etree.tostring(row)) + logging.debug(etree.tostring(r)) + urlpath = row.findtext("url[0]").strip("/") + name = row.findtext('name[0]') + description = row.findtext('description[0]') + folder_sco_id = 0 + source_sco_id = 0 + + def _ior0(elt,a,dflt): + str = elt.get(a,None) + if str is None or not str: + return dflt + else: + return int(str) + + + for elt in r.findall(".//sco[0]"): + folder_sco_id = _ior0(elt,'folder-id',0) + source_sco_id = _ior0(elt,'source-sco-id',0) + + logging.debug("urlpath=%s, name=%s, folder_sco_id=%s, source_sco_id=%s" % (urlpath,name,folder_sco_id,source_sco_id)) + + if room is None: + if folder_sco_id: + 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: + if folder_sco_id: + room.folder_sco_id = folder_sco_id + room.source_sco_id = source_sco_id + room.description = description + room.urlpath = urlpath + + if room is not None: + room.save() + room.unlock() + else: + if room is not None: + room.unlock() + +def import_acc(acc,since=0): + with ac_api_client(acc) as api: + r = None + if since > 0: + then = datetime.now()-timedelta(seconds=since) + then = then.replace(microsecond=0) + r = api.request('report-bulk-objects',{'filter-type': 'meeting','filter-gt-date-modified': then.isoformat()}) + else: + r = api.request('report-bulk-objects',{'filter-type': 'meeting'}) + + for row in r.et.xpath("//row"): + try: + _import_one_room(acc,api,row) + except Exception,ex: + logging.error(ex) + +@periodic_task(run_every=crontab(hour="*", minute="*", day_of_week="*")) +def import_all_rooms(): + for acc in ACCluster.objects.all(): + import_acc(acc,since=3600) + +def start_user_counts_poll(room,niter): + poll_user_counts.apply_async(args=[room],kwargs={'niter': niter}) + +@task(name='meetingtools.apps.room.tasks.poll_user_counts',rate_limit="10/s") +def poll_user_counts(room,niter=0): + logging.debug("polling user_counts for room %s" % room.name) + with ac_api_client(room.acc) as api: + (nusers,nhosts) = api.poll_user_counts(room) + if nusers > 0: + logging.debug("room occupied by %d users and %d hosts, checking again in 20 ..." % (nusers,nhosts)) + poll_user_counts.apply_async(args=[room],kwargs={'niter': 0},countdown=20) + elif niter > 0: + logging.debug("room empty, will check again in 5 for %d more times ..." % (niter -1)) + poll_user_counts.apply_async(args=[room],kwargs={'niter': niter-1},countdown=5) + return (nusers,nhosts) + +# belts and suspenders - we setup polling for active rooms aswell... +@periodic_task(run_every=crontab(hour="*", minute="*/5", day_of_week="*")) +def import_recent_user_counts(): + for acc in ACCluster.objects.all(): + with ac_api_client(acc) as api: + then = datetime.now()-timedelta(seconds=600) + for room in Room.objects.filter((Q(lastupdated__gt=then) | Q(lastvisited__gt=then)) & Q(acc=acc)): + api.poll_user_counts(room) + +# look for sessions that are newer than the one we know about for a room +@periodic_task(run_every=crontab(hour="*", minute="*", day_of_week="*")) +def import_sessions(): + for room in Room.objects.all(): + with ac_api_client(room.acc) as api: + p = {'sco-id': room.sco_id,'sort-date-created': 'asc'} + if room.lastvisited != None: + last = room.lastvisited + last.replace(microsecond=0) + p['filter-gt-date-created'] = last.isoformat() + r = api.request('report-meeting-sessions',p) + for row in r.et.xpath("//row"): + date_created = iso8601.parse_date(row.findtext("date-created")) + logging.debug("sco_id=%d lastvisited: %s" % (room.sco_id,date_created)) + room.lastvisited = date_created + room.save() + break + +#@periodic_task(run_every=crontab(hour="*", minutes="*/5", day_of_week="*")) +def import_transactions(): + for acc in ACCluster.objects.all(): + then = datetime.now() - timedelta(seconds=600) + then = then.replace(microsecond=0) + with ac_api_client(acc) as api: + seen = {} + r = api.request('report-bulk-consolidated-transactions',{'filter-type':'meeting','sort-date-created': 'asc','filter-gt-date-created': then.isformat()}) + for row in r.et.xpath("//row"): + sco_id = row.get('sco-id') + logging.debug("last session for sco_id=%d" % sco_id) + if not seen.get(sco_id,False): #pick the first session for each room - ie the one last created + seen[sco_id] = True + try: + room = Room.objects.get(acc=acc,sco_id=sco_id) + date_created = iso8601.parse_date(row.findtext("date-created")) + room.lastvisited = date_created + room.save() + except ObjectDoesNotExist: + pass # we only care about rooms we know about here + +@task(name="meetingtools.apps.room.tasks.mail") +def send_message(user,subject,message): + try: + p = user.get_profile() + if p and p.email: + send_mail(subject,message,settings.NOREPLY,[p.email]) + else: + logging.info("User %s has no email address - email not sent" % user.username) + except ObjectDoesNotExist: + logging.info("User %s has no profile - email not sent" % user.username) + except Exception,exc: + logging.error("Error while sending email: \n%s" % exc) + send_message.retry(exc=exc) + +@periodic_task(run_every=crontab(hour="1", minute="5", day_of_week="*")) +def clean_old_rooms(): + for acc in ACCluster.objects.all(): + then = datetime.now() - timedelta(days=30) + then = then.replace(microsecond=0) + with ac_api_client(acc) as api: + for room in Room.objects.filter(lastvisited__lt=then): + logging.debug("room %s was last used %s" % (room.name,humanize.naturalday(room.lastvisited))) + send_message.apply_async([room.creator,"You have an unused meetingroom at %s" % acc.name ,"Do you still need %s (%s)?" % (room.name,room.permalink())])
\ No newline at end of file |