From 37e11ed58c817c57bee46df0fe065392c59ecd0a Mon Sep 17 00:00:00 2001 From: gfawcett Date: Wed, 28 Jul 2010 03:17:48 +0000 Subject: [PATCH] sakai (linktool) integration almost done... git-svn-id: svn://svn.open-ils.org/ILS-Contrib/servres/trunk@939 6d9bc8c9-1ec2-4278-b937-99fde70a366f --- conifer/integration/clew-memberships | 53 ++++++++++++ conifer/integration/clew-sections-for-site | 10 +-- conifer/integration/linktool/app.py | 96 ++++++++++++++++------ .../integration/linktool/templates/associate.xhtml | 36 ++++++-- .../linktool/templates/linktoolmaster.xhtml | 4 + .../linktool/templates/new_site_cannot.xhtml | 23 ++++++ conifer/integration/uwindsor.py | 7 +- conifer/syrup/models.py | 13 ++- conifer/urls.py | 7 +- 9 files changed, 206 insertions(+), 43 deletions(-) create mode 100644 conifer/integration/clew-memberships create mode 100644 conifer/integration/linktool/templates/new_site_cannot.xhtml diff --git a/conifer/integration/clew-memberships b/conifer/integration/clew-memberships new file mode 100644 index 0000000..ead1dcf --- /dev/null +++ b/conifer/integration/clew-memberships @@ -0,0 +1,53 @@ +#!/usr/local/bin/python-oracle-cgi-wrapper +# -*- mode: python -*- + +import os +import cgi +import cx_Oracle +import simplejson +import re + +CONN_STRING = '***/***@********' +conn = cx_Oracle.connect(CONN_STRING) +#import cgitb; cgitb.enable(display=True) + +def memberships(uid): + q = ("select realm_id, role_name, S.title, DBMS_LOB.SUBSTR(P.value, 64) " + "from sakai_realm_rl_gr G " + "left join sakai_realm R on G.realm_key = R.realm_key " + "left join sakai_user_id_map M on G.user_id=M.user_id " + "left join sakai_site S on S.site_id = substr(R.realm_id, 7) " + "left join sakai_realm_role RR on G.role_key = RR.role_key " + "left join sakai_site_property P on P.site_id = S.site_id and P.name='term_code' " + "where eid=:1 and active=1 " + "and regexp_like(realm_id, '^/site/.{36}$') ") + cursor = conn.cursor() + cursor.execute(q, (uid,)) + coursepat = re.compile(r'^(\d\d-\d\d-\d\d\d)') + for realm, role, title, term in cursor.fetchall(): + site = realm[6:] + tmp = coursepat.match(title) + course = tmp and tmp.group(1) or None + # term: convert 'W08+S08' to ['2008W', '2008S'] + try: + terms = ['20%s%s' % (t[-2:], t[0]) for t in term.split('+')] + except: + terms = [] + yield {'site': site, 'role': role, 'title': title, 'terms': terms, + 'course': course} + +form = cgi.FieldStorage() +uid = form['uid'].value +compress = 'deflate' in os.environ.get('HTTP_ACCEPT_ENCODING', '') +resp = simplejson.dumps(list(memberships(uid))) + +print 'Status: 200 OK' +print 'Content-Type: application/json' +if compress: + import zlib + print 'Content-Encoding: deflate' + print + print zlib.compress(resp) +else: + print + print resp diff --git a/conifer/integration/clew-sections-for-site b/conifer/integration/clew-sections-for-site index a4912bc..8fb6d05 100755 --- a/conifer/integration/clew-sections-for-site +++ b/conifer/integration/clew-sections-for-site @@ -1,18 +1,14 @@ -#!/Users/graham/bin/python-oracle-cgi-wrapper +#!/usr/local/bin/python-oracle-cgi-wrapper # -*- mode: python -*- import os import cx_Oracle -import re -import sys -import warnings import simplejson as json -import time import cgitb - #cgitb.enable(display=True) -conn = cx_Oracle.connect('sakai/PASSWORD@clew') +CONN_STRING = '***/***@********' +conn = cx_Oracle.connect(CONN_STRING) try: siteid = os.environ['PATH_INFO'][1:] diff --git a/conifer/integration/linktool/app.py b/conifer/integration/linktool/app.py index 40cf88b..05cb929 100644 --- a/conifer/integration/linktool/app.py +++ b/conifer/integration/linktool/app.py @@ -1,5 +1,6 @@ from conifer.here import HERE from conifer.plumbing.genshi_support import TemplateSet +from datetime import date, timedelta from django.http import (HttpResponse, HttpResponseRedirect, HttpResponseNotFound, HttpResponseForbidden) @@ -13,40 +14,87 @@ g = TemplateSet([HERE('integration/linktool/templates'), {'models': models, '_': _}) -def linktool_welcome(request, command=u''): +def linktool_welcome(request): user = authenticate(request=request) if user is None: return HttpResponseForbidden('You are not allowed here.') else: login(request, user) + _role = request.GET['role'] + extrole = request.session['clew-role'] = callhook('decode_role', _role) or _role extsite = request.session['clew-site'] = request.GET['site'] - extrole = request.session['clew-role'] = request.GET['role'] + related_sites = list(models.Site.objects.filter(group__external_id=extsite)) if len(related_sites) == 1: - return HttpResponse("" - "Redirecting..." - "" % ( - request.META['SCRIPT_NAME'] or '/', - related_sites[0].id)) + site_url = related_sites[0].site_url() + html = ("" + "Redirecting..." + "") % site_url + return HttpResponse(html) elif len(related_sites): return g.render('whichsite.xhtml', **locals()) - elif extrole == 'Instructor': - # This isn't quite right yet. I want to give the instructor a - # chance to associate with an existing unassociated site in the - # same term as the Sakai site. I don't want them to associate with - # a site that's in an older Term, but should give them the change - # to copy/reuse an old site. Or, they can make a brand-new Site if - # they want. - - # This reminds me that it should be a warning to edit an old site - # (one in a past Term). Otherwise, profs will add items to old - # sites, and think they are actually ordering stuff. - - possibles = models.Site.taught_by(user) - if possibles: - return g.render('associate.xhtml', **locals()) - else: - return g.render('create_new.xhtml', **locals()) + elif extrole == 'INSTR': + # TODO: This reminds me that it should be a warning to + # edit an old site (one in a past Term). Otherwise, profs + # will add items to old sites, and think they are actually + # ordering stuff. + today = date.today() + possibles = list(models.Site.taught_by(user)) + current = [s for s in possibles if s.term.midpoint() >= today] + ancient = [s for s in possibles if s.term.midpoint() < today] + return g.render('associate.xhtml', **locals()) else: + # TODO: implement me return g.render('choose_dest.xhtml', **locals()) + +def linktool_new_site(request): + extsite = request.session['clew-site'] + extrole = request.session['clew-role'] + assert extrole == 'INSTR' + assert request.user.can_create_sites(), \ + 'Sorry, but you are not allowed to create sites.' + extgroups = callhook('external_memberships', request.user.username) + extsite = [d for d in extgroups if d['group'] == extsite][0] + coursecode = extsite['course'] + termcode = extsite['terms'][0] + try: + course = models.Course.objects.get(code=coursecode) + term = models.Term.objects.get(code=termcode) + except: + # note, this doesn't have to be an exception. I could provide + # them with a form to specify the correct course and term + # codes. But for now, we bail. + return g.render('new_site_cannot.xhtml', **locals()) + site = models.Site.objects.create( + course = course, + term = term, + owner = request.user, + service_desk = models.ServiceDesk.default()) + group = models.Group.objects.create( + site = site, + external_id = extsite) + models.Membership.objects.create( + group = group, + user = request.user, + role = 'INSTR') + return HttpResponseRedirect(site.site_url()) + +def linktool_associate(request): + site = models.Site.objects.get(pk=request.GET['site']) + assert site in request.user.sites(role='INSTR'), \ + 'Not an instructor on this site! Cannot copy.' + assert request.user.can_create_sites(), \ + 'Sorry, but you are not allowed to create sites.' + today = date.today() + assert site.term.midpoint() >= today, \ + 'Sorry, but you cannot associate to such an old site.' + return HttpResponse('associate: not implemented yet') + +def linktool_copy_old(request): + site = models.Site.objects.get(pk=request.GET['site']) + assert site in request.user.sites(role='INSTR'), \ + 'Not an instructor on this site! Cannot copy.' + assert request.user.can_create_sites(), \ + 'Sorry, but you are not allowed to create sites.' + return HttpResponse('copy old: not implemented yet') diff --git a/conifer/integration/linktool/templates/associate.xhtml b/conifer/integration/linktool/templates/associate.xhtml index d162dba..3a04ac8 100644 --- a/conifer/integration/linktool/templates/associate.xhtml +++ b/conifer/integration/linktool/templates/associate.xhtml @@ -2,17 +2,39 @@ xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:py="http://genshi.edgewall.org/"> - + + + -

Associate?

+

No associated reserves list.

There is currently no set of reserves materials associated with this site. As an instructor in this site, you can choose one of the following options:

-

todo: finish this...

- +
  • +
    Copy a reserves list I've used in the past
    + +
  • +
  • +
    Create a new reserves list for this site
    +
  • + + diff --git a/conifer/integration/linktool/templates/linktoolmaster.xhtml b/conifer/integration/linktool/templates/linktoolmaster.xhtml index 7bcb8c3..c668621 100644 --- a/conifer/integration/linktool/templates/linktoolmaster.xhtml +++ b/conifer/integration/linktool/templates/linktoolmaster.xhtml @@ -10,6 +10,10 @@ ${select('*')} + diff --git a/conifer/integration/linktool/templates/new_site_cannot.xhtml b/conifer/integration/linktool/templates/new_site_cannot.xhtml new file mode 100644 index 0000000..063b10a --- /dev/null +++ b/conifer/integration/linktool/templates/new_site_cannot.xhtml @@ -0,0 +1,23 @@ + + + + testing + + + +

    Cannot create site...

    +

    Sorry, but your site's course code (${coursecode or 'unknown'}) + and/or term code (${termcode or 'unknown'}) do not match any known + codes in the reserves system.

    +

    + + Go back + +

    + + diff --git a/conifer/integration/uwindsor.py b/conifer/integration/uwindsor.py index c46f09e..8721202 100644 --- a/conifer/integration/uwindsor.py +++ b/conifer/integration/uwindsor.py @@ -120,9 +120,14 @@ def external_person_lookup(userid): """ return uwindsor_campus_info.call('person_lookup', userid) +def decode_role(role): + if role == 'Instructor': + return 'INSTR' + else: + return 'STUDT' def external_memberships(userid, include_titles=False): memberships = uwindsor_campus_info.call('membership_ids', userid) for m in memberships: - m['role'] = 'INSTR' if m['role'] == 'Instructor' else 'STUDT' + m['role'] = decode_role(m['role']) return memberships diff --git a/conifer/syrup/models.py b/conifer/syrup/models.py index 8a903ba..493d08b 100644 --- a/conifer/syrup/models.py +++ b/conifer/syrup/models.py @@ -42,9 +42,12 @@ class BaseModel(m.Model): class UserExtensionMixin(object): - def sites(self): + def sites(self, role=None): self.maybe_refresh_external_memberships() - return Site.objects.filter(group__membership__user=self.id) + sites = Site.objects.filter(group__membership__user=self.id) + if role: + sites = sites.filter(group__membership__role=role) + return sites def can_create_sites(self): return self.is_staff or \ @@ -122,6 +125,10 @@ class ServiceDesk(BaseModel): def __unicode__(self): return self.name + @classmethod + def default(cls): + return cls.objects.get(pk=Config.get('default.desk', 1)) + class Term(BaseModel): code = m.CharField(max_length=64, unique=True) name = m.CharField(max_length=256) @@ -134,6 +141,8 @@ class Term(BaseModel): def __unicode__(self): return '%s: %s' % (self.code, self.name) + def midpoint(self): + return self.start + timedelta(days=(self.start-self.finish).days/2) class Department(BaseModel): name = m.CharField(max_length=256) diff --git a/conifer/urls.py b/conifer/urls.py index eeac6c3..ec9578c 100644 --- a/conifer/urls.py +++ b/conifer/urls.py @@ -42,5 +42,8 @@ if not settings.DEBUG: if settings.LINKTOOL_AUTHENTICATION: urlpatterns += patterns( 'conifer.integration.linktool.app', - (r'^linktool-welcome/(?P.*)$', 'linktool_welcome')) - + (r'^linktool-welcome/$', 'linktool_welcome'), + (r'^linktool-welcome/new_site$', 'linktool_new_site'), + (r'^linktool-welcome/copy_old$', 'linktool_copy_old'), + (r'^linktool-welcome/associate$', 'linktool_associate'), + ) -- 2.11.0