From 516c41f342c54559cb1e82976083ee063cc6e16b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Andr=C3=A9s=20Rocha?= Date: Wed, 22 Aug 2012 11:53:04 -0400 Subject: [PATCH] Added html view of serial numbers. Refactored code. --- .../commands/import_serial_numbers.py | 10 +-- lms/djangoapps/licenses/models.py | 64 ++++++++++++++++- lms/djangoapps/licenses/views.py | 71 ++++++------------- lms/templates/licenses/serial_numbers.html | 10 +++ 4 files changed, 100 insertions(+), 55 deletions(-) create mode 100644 lms/templates/licenses/serial_numbers.html diff --git a/lms/djangoapps/licenses/management/commands/import_serial_numbers.py b/lms/djangoapps/licenses/management/commands/import_serial_numbers.py index 846327966d..465940ce20 100644 --- a/lms/djangoapps/licenses/management/commands/import_serial_numbers.py +++ b/lms/djangoapps/licenses/management/commands/import_serial_numbers.py @@ -9,15 +9,18 @@ from xmodule.modulestore.django import modulestore from licenses.models import CourseSoftware, UserLicense + class Command(BaseCommand): help = """Imports serial numbers for software used in a course. - Usage: import_serial_numbers + Usage: import_serial_numbers - serial_file is a text file that list one available serial number per line. + is a text file that list one available serial number per line. Example: - django-admin.py import_serial_numbers MITx/6.002x/2012_Fall matlab /tmp/matlab-serials.txt + + import_serial_numbers MITx/6.002x/2012_Fall matlab serials.txt + """ args = "course_id software_id serial_file" @@ -49,7 +52,6 @@ class Command(BaseCommand): return course_id, software_name, filename - def _import_serials(self, software, filename): print "Importing serial numbers for {0}.".format(software) diff --git a/lms/djangoapps/licenses/models.py b/lms/djangoapps/licenses/models.py index 78da5d14cb..929fba10ec 100644 --- a/lms/djangoapps/licenses/models.py +++ b/lms/djangoapps/licenses/models.py @@ -1,8 +1,11 @@ -""" -""" -from django.db import models +import logging + +from django.db import models, transaction + from student.models import User +log = logging.getLogger("mitx.licenses") + class CourseSoftware(models.Model): name = models.CharField(max_length=255) @@ -18,3 +21,58 @@ class UserLicense(models.Model): software = models.ForeignKey(CourseSoftware, db_index=True) user = models.ForeignKey(User, null=True) serial = models.CharField(max_length=255) + + +def get_courses_licenses(user, courses): + course_ids = set(course.id for course in courses) + all_software = CourseSoftware.objects.filter(course_id__in=course_ids) + + assigned_licenses = UserLicense.objects.filter(software__in=all_software, + user=user) + + licenses = dict.fromkeys(all_software, None) + for license in assigned_licenses: + licenses[license.software] = license + + log.info(assigned_licenses) + log.info(licenses) + + return licenses + + +def get_license(user, software): + try: + license = UserLicense.objects.get(user=user, software=software) + except UserLicense.DoesNotExist: + license = None + + return license + + +def get_or_create_license(user, software): + license = get_license(user, software) + if license is None: + license = _create_license(user, software) + + return license + + +def _create_license(user, software): + license = None + + try: + # find one license that has not been assigned, locking the + # table/rows with select_for_update to prevent race conditions + with transaction.commit_on_success(): + selected = UserLicense.objects.select_for_update() + license = selected.filter(user__isnull=True)[0] + license.user = user + license.save() + except IndexError: + # there are no free licenses + log.error('No serial numbers available for {0}', software) + license = None + # TODO [rocha]look if someone has unenrolled from the class + # and already has a serial number + + return license diff --git a/lms/djangoapps/licenses/views.py b/lms/djangoapps/licenses/views.py index b4ab4ea909..7cf8e6591e 100644 --- a/lms/djangoapps/licenses/views.py +++ b/lms/djangoapps/licenses/views.py @@ -1,60 +1,35 @@ import logging -from itertools import groupby -from collections import Iterable +from collections import namedtuple, defaultdict -from django.db.models import Q +from mitxmako.shortcuts import render_to_string + +from models import get_courses_licenses, get_or_create_license -from models import CourseSoftware, UserLicense log = logging.getLogger("mitx.licenses") -def get_or_create_courses_licenses(user, courses): - user_licenses = get_courses_licenses(user, courses) +License = namedtuple('License', 'software serial') - for software, license in user_licenses.iteritems(): + +def get_licenses_by_course(user, courses): + licenses = get_courses_licenses(user, courses) + licenses_by_course = defaultdict(list) + + # create missing licenses and group by course_id + for software, license in licenses.iteritems(): if license is None: - user_licenses[software] = get_or_create_user_license(user, software) + licenses[software] = get_or_create_license(user, software) - log.info(user_licenses) + course_id = software.course_id + serial = license.serial if license else None + licenses_by_course[course_id].append(License(software, serial)) - return user_licenses + # render elements + data_by_course = {} + for course_id, licenses in licenses_by_course.iteritems(): + context = {'licenses': licenses} + template = 'licenses/serial_numbers.html' + data_by_course[course_id] = render_to_string(template, context) - -def get_courses_licenses(user, courses): - course_ids = set(course.id for course in courses) - all_software = CourseSoftware.objects.filter(course_id__in=course_ids) - - user_licenses = dict.fromkeys(all_software, None) - - assigned_licenses = UserLicense.objects.filter(software__in=all_software, user=user) - assigned_by_software = {lic.software:lic for lic in assigned_licenses} - - for software, license in assigned_by_software.iteritems(): - user_licenses[software] = license - - return user_licenses - - -def get_or_create_user_license(user, software): - license = None - try: - # Find a licenses associated with the user or with no user - # associated. - query = (Q(user__isnull=True) | Q(user=user)) & Q(software=software) - - # TODO fix a race condition in this code when more than one - # user is getting a license assigned - - license = UserLicense.objects.filter(query)[0] - - if license.user is not user: - license.user = user - license.save() - - except IndexError: - # TODO look if someone has unenrolled from the class and already has a serial number - log.error('No serial numbers available for {0}', software) - - - return license + return data_by_course diff --git a/lms/templates/licenses/serial_numbers.html b/lms/templates/licenses/serial_numbers.html new file mode 100644 index 0000000000..18f0ff8a9b --- /dev/null +++ b/lms/templates/licenses/serial_numbers.html @@ -0,0 +1,10 @@ +
+% for license in licenses: +
${license.software.name}:
+ % if license.serial: +
${license.serial}
+ % else: +
None Available
+ % endif +% endfor +