From 24d5c9162c8bceddca1b6b19bf3af1c42f0f7e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Andr=C3=A9s=20Rocha?= Date: Tue, 21 Aug 2012 10:20:26 -0400 Subject: [PATCH 1/8] Created Django App with commands to import software license numbers per class. --- lms/djangoapps/licenses/__init__.py | 0 .../licenses/management/__init__.py | 0 .../licenses/management/commands/__init__.py | 0 .../management/commands/import_serials.py | 77 +++++++++++++++++++ lms/djangoapps/licenses/models.py | 19 +++++ lms/djangoapps/licenses/views.py | 0 lms/envs/common.py | 1 + 7 files changed, 97 insertions(+) create mode 100644 lms/djangoapps/licenses/__init__.py create mode 100644 lms/djangoapps/licenses/management/__init__.py create mode 100644 lms/djangoapps/licenses/management/commands/__init__.py create mode 100644 lms/djangoapps/licenses/management/commands/import_serials.py create mode 100644 lms/djangoapps/licenses/models.py create mode 100644 lms/djangoapps/licenses/views.py diff --git a/lms/djangoapps/licenses/__init__.py b/lms/djangoapps/licenses/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lms/djangoapps/licenses/management/__init__.py b/lms/djangoapps/licenses/management/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lms/djangoapps/licenses/management/commands/__init__.py b/lms/djangoapps/licenses/management/commands/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lms/djangoapps/licenses/management/commands/import_serials.py b/lms/djangoapps/licenses/management/commands/import_serials.py new file mode 100644 index 0000000000..c65cc356f9 --- /dev/null +++ b/lms/djangoapps/licenses/management/commands/import_serials.py @@ -0,0 +1,77 @@ +import os.path + +from optparse import make_option + +from django.utils.html import escape +from django.core.management.base import BaseCommand, CommandError + +from xmodule.modulestore.django import modulestore + +from licenses.models import Software, StudentLicense + + +class Command(BaseCommand): + help = """Imports serial numbers for software used in a course. + + Usage: import_serials course_id software_id serial_file + + serial_file is a text file that list one available serial number per line. + + Example: + import_serials.py MITx/6.002x/2012_Fall matlab /tmp/matlab-serials.txt + """ + + args = "course_id software_id serial_file" + + def handle(self, *args, **options): + """ + """ + course_id, software_name, filename = self._parse_arguments(args) + + software = self._find_software(course_id, software_name) + + self._import_serials(software, filename) + + def _parse_arguments(self, args): + if len(args) != 3: + raise CommandError("Incorrect number of arguments") + + course_id = args[0] + courses = modulestore().get_courses() + known_course_ids = set(c.id for c in courses) + + if course_id not in known_course_ids: + raise CommandError("Unknown course_id") + + software_name = escape(args[1].lower()) + + filename = os.path.abspath(args[2]) + if not os.path.exists(filename): + raise CommandError("Cannot find filename {0}".format(filename)) + + return course_id, software_name, filename + + def _find_software(self, course_id, software_name): + try: + software = Software.objects.get(course_id=course_id, name=software_name) + except Software.DoesNotExist: + software = Software(name=software_name, course_id=course_id) + software.save() + + return software + + def _import_serials(self, software, filename): + print "Importing serial numbers for {0} {1}".format( + software.name, software.course_id) + + known_serials = set(l.serial for l in StudentLicense.objects.filter(software=software)) + + count = 0 + serials = list(l.strip() for l in open(filename)) + for s in serials: + if s not in known_serials: + license = StudentLicense(software=software, serial=s) + license.save() + count += 1 + + print "{0} new serial numbers imported.".format(count) diff --git a/lms/djangoapps/licenses/models.py b/lms/djangoapps/licenses/models.py new file mode 100644 index 0000000000..61f270c163 --- /dev/null +++ b/lms/djangoapps/licenses/models.py @@ -0,0 +1,19 @@ +""" +""" + +from django.db import models + +from student.models import User + + +class Software(models.Model): + name = models.CharField(max_length=255) + full_name = models.CharField(max_length=255) + url = models.CharField(max_length=255) + course_id = models.CharField(max_length=255) + + +class StudentLicense(models.Model): + software = models.ForeignKey(Software, db_index=True) + serial = models.CharField(max_length=255) + user = models.ForeignKey(User, null=True, blank=True) diff --git a/lms/djangoapps/licenses/views.py b/lms/djangoapps/licenses/views.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lms/envs/common.py b/lms/envs/common.py index a927da8e98..9b98e4ecfd 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -626,6 +626,7 @@ INSTALLED_APPS = ( 'certificates', 'instructor', 'psychometrics', + 'licenses', #For the wiki 'wiki', # The new django-wiki from benjaoming From ed88708d716123acac5d8f9ca7304b79d363852a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Andr=C3=A9s=20Rocha?= Date: Tue, 21 Aug 2012 19:04:11 -0400 Subject: [PATCH 2/8] Renamed models and commands for importing serial numbers. --- ...rt_serials.py => import_serial_numbers.py} | 44 ++++++++----------- lms/djangoapps/licenses/models.py | 13 +++--- 2 files changed, 25 insertions(+), 32 deletions(-) rename lms/djangoapps/licenses/management/commands/{import_serials.py => import_serial_numbers.py} (54%) diff --git a/lms/djangoapps/licenses/management/commands/import_serials.py b/lms/djangoapps/licenses/management/commands/import_serial_numbers.py similarity index 54% rename from lms/djangoapps/licenses/management/commands/import_serials.py rename to lms/djangoapps/licenses/management/commands/import_serial_numbers.py index c65cc356f9..846327966d 100644 --- a/lms/djangoapps/licenses/management/commands/import_serials.py +++ b/lms/djangoapps/licenses/management/commands/import_serial_numbers.py @@ -7,20 +7,18 @@ from django.core.management.base import BaseCommand, CommandError from xmodule.modulestore.django import modulestore -from licenses.models import Software, StudentLicense - +from licenses.models import CourseSoftware, UserLicense class Command(BaseCommand): help = """Imports serial numbers for software used in a course. - Usage: import_serials course_id software_id serial_file + Usage: import_serial_numbers serial_file is a text file that list one available serial number per line. Example: - import_serials.py MITx/6.002x/2012_Fall matlab /tmp/matlab-serials.txt + django-admin.py import_serial_numbers MITx/6.002x/2012_Fall matlab /tmp/matlab-serials.txt """ - args = "course_id software_id serial_file" def handle(self, *args, **options): @@ -28,8 +26,8 @@ class Command(BaseCommand): """ course_id, software_name, filename = self._parse_arguments(args) - software = self._find_software(course_id, software_name) - + software, _ = CourseSoftware.objects.get_or_create(course_id=course_id, + name=software_name) self._import_serials(software, filename) def _parse_arguments(self, args): @@ -51,27 +49,21 @@ class Command(BaseCommand): return course_id, software_name, filename - def _find_software(self, course_id, software_name): - try: - software = Software.objects.get(course_id=course_id, name=software_name) - except Software.DoesNotExist: - software = Software(name=software_name, course_id=course_id) - software.save() - - return software def _import_serials(self, software, filename): - print "Importing serial numbers for {0} {1}".format( - software.name, software.course_id) + print "Importing serial numbers for {0}.".format(software) - known_serials = set(l.serial for l in StudentLicense.objects.filter(software=software)) + serials = set(unicode(l.strip()) for l in open(filename)) - count = 0 - serials = list(l.strip() for l in open(filename)) - for s in serials: - if s not in known_serials: - license = StudentLicense(software=software, serial=s) - license.save() - count += 1 + # remove serial numbers we already have + licenses = UserLicense.objects.filter(software=software) + known_serials = set(l.serial for l in licenses) + if known_serials: + serials = serials.difference(known_serials) - print "{0} new serial numbers imported.".format(count) + # add serial numbers them to the database + for serial in serials: + license = UserLicense(software=software, serial=serial) + license.save() + + print "{0} new serial numbers imported.".format(len(serials)) diff --git a/lms/djangoapps/licenses/models.py b/lms/djangoapps/licenses/models.py index 61f270c163..78da5d14cb 100644 --- a/lms/djangoapps/licenses/models.py +++ b/lms/djangoapps/licenses/models.py @@ -1,19 +1,20 @@ """ """ - from django.db import models - from student.models import User -class Software(models.Model): +class CourseSoftware(models.Model): name = models.CharField(max_length=255) full_name = models.CharField(max_length=255) url = models.CharField(max_length=255) course_id = models.CharField(max_length=255) + def __unicode__(self): + return u'{0} for {1}'.format(self.name, self.course_id) -class StudentLicense(models.Model): - software = models.ForeignKey(Software, db_index=True) + +class UserLicense(models.Model): + software = models.ForeignKey(CourseSoftware, db_index=True) + user = models.ForeignKey(User, null=True) serial = models.CharField(max_length=255) - user = models.ForeignKey(User, null=True, blank=True) From b2de8199b7670c795c9d91012ee9085156affe50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Andr=C3=A9s=20Rocha?= Date: Tue, 21 Aug 2012 19:04:53 -0400 Subject: [PATCH 3/8] Added licenses view helper functions. --- lms/djangoapps/licenses/views.py | 60 ++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/lms/djangoapps/licenses/views.py b/lms/djangoapps/licenses/views.py index e69de29bb2..b4ab4ea909 100644 --- a/lms/djangoapps/licenses/views.py +++ b/lms/djangoapps/licenses/views.py @@ -0,0 +1,60 @@ +import logging +from itertools import groupby +from collections import Iterable + +from django.db.models import Q + +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) + + for software, license in user_licenses.iteritems(): + if license is None: + user_licenses[software] = get_or_create_user_license(user, software) + + log.info(user_licenses) + + return user_licenses + + +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 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 4/8] 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 +
From 9cf8c02dc65615e4750b1181a60f8e1dbd5fd4bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Andr=C3=A9s=20Rocha?= Date: Wed, 22 Aug 2012 14:23:36 -0400 Subject: [PATCH 5/8] Added import_serial_numbers command test. --- lms/djangoapps/licenses/tests.py | 85 ++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 lms/djangoapps/licenses/tests.py diff --git a/lms/djangoapps/licenses/tests.py b/lms/djangoapps/licenses/tests.py new file mode 100644 index 0000000000..f06899d2de --- /dev/null +++ b/lms/djangoapps/licenses/tests.py @@ -0,0 +1,85 @@ +import logging +from uuid import uuid4 +from random import shuffle +from tempfile import NamedTemporaryFile + +from django.test import TestCase +from django.core.management import call_command + +from models import CourseSoftware, UserLicense + +COURSE_1 = 'MITx/6.002x/2012_Fall' + +SOFTWARE_1 = 'matlab' +SOFTWARE_2 = 'stata' + +log = logging.getLogger(__name__) + + +class CommandTest(TestCase): + def test_import_serial_numbers(self): + size = 20 + + log.debug('Adding one set of serials for {0}'.format(SOFTWARE_1)) + with generate_serials_file(size) as temp_file: + args = [COURSE_1, SOFTWARE_1, temp_file.name] + call_command('import_serial_numbers', *args) + + log.debug('Adding one set of serials for {0}'.format(SOFTWARE_2)) + with generate_serials_file(size) as temp_file: + args = [COURSE_1, SOFTWARE_2, temp_file.name] + call_command('import_serial_numbers', *args) + + log.debug('There should be only 2 course-software entries') + software_count = CourseSoftware.objects.all().count() + self.assertEqual(2, software_count) + + log.debug('We added two sets of {0} serials'.format(size)) + licenses_count = UserLicense.objects.all().count() + self.assertEqual(2 * size, licenses_count) + + log.debug('Adding more serial numbers to {0}'.format(SOFTWARE_1)) + with generate_serials_file(size) as temp_file: + args = [COURSE_1, SOFTWARE_1, temp_file.name] + call_command('import_serial_numbers', *args) + + log.debug('There should be still only 2 course-software entries') + software_count = CourseSoftware.objects.all().count() + self.assertEqual(2, software_count) + + log.debug('Now we should have 3 sets of 20 serials'.format(size)) + licenses_count = UserLicense.objects.all().count() + self.assertEqual(3 * size, licenses_count) + + cs = CourseSoftware.objects.get(pk=1) + + lics = UserLicense.objects.filter(software=cs)[:size] + known_serials = list(l.serial for l in lics) + known_serials.extend(generate_serials(10)) + + shuffle(known_serials) + + log.debug('Adding some new and old serials to {0}'.format(SOFTWARE_1)) + with NamedTemporaryFile() as f: + f.write('\n'.join(known_serials)) + f.flush() + args = [COURSE_1, SOFTWARE_1, f.name] + call_command('import_serial_numbers', *args) + + log.debug('Check if we added only the new ones') + licenses_count = UserLicense.objects.filter(software=cs).count() + self.assertEqual((2 * size) + 10, licenses_count) + + +def generate_serials(size=20): + return [str(uuid4()) for _ in range(size)] + + +def generate_serials_file(size=20): + serials = generate_serials(size) + + temp_file = NamedTemporaryFile() + temp_file.write('\n'.join(serials)) + temp_file.flush() + + return temp_file From 3808ff85a8f867be5ef4bd7359f6a4593f4a9707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Andr=C3=A9s=20Rocha?= Date: Tue, 28 Aug 2012 11:46:12 -0400 Subject: [PATCH 6/8] Added django command to generate random serial numbers. --- .../commands/generate_serial_numbers.py | 65 +++++++++++++++++++ .../commands/import_serial_numbers.py | 1 - 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 lms/djangoapps/licenses/management/commands/generate_serial_numbers.py diff --git a/lms/djangoapps/licenses/management/commands/generate_serial_numbers.py b/lms/djangoapps/licenses/management/commands/generate_serial_numbers.py new file mode 100644 index 0000000000..7c6b0d310e --- /dev/null +++ b/lms/djangoapps/licenses/management/commands/generate_serial_numbers.py @@ -0,0 +1,65 @@ +import os.path +from uuid import uuid4 +from optparse import make_option + +from django.utils.html import escape +from django.core.management.base import BaseCommand, CommandError + +from xmodule.modulestore.django import modulestore + +from licenses.models import CourseSoftware, UserLicense + + +class Command(BaseCommand): + help = """Generate random serial numbers for software used in a course. + + Usage: generate_serial_numbers + + is the number of numbers to generate. + + Example: + + import_serial_numbers MITx/6.002x/2012_Fall matlab 100 + + """ + args = "course_id software_id count" + + def handle(self, *args, **options): + """ + """ + course_id, software_name, count = self._parse_arguments(args) + + software, _ = CourseSoftware.objects.get_or_create(course_id=course_id, + name=software_name) + self._generate_serials(software, count) + + def _parse_arguments(self, args): + if len(args) != 3: + raise CommandError("Incorrect number of arguments") + + course_id = args[0] + courses = modulestore().get_courses() + known_course_ids = set(c.id for c in courses) + + if course_id not in known_course_ids: + raise CommandError("Unknown course_id") + + software_name = escape(args[1].lower()) + + try: + count = int(args[2]) + except ValueError: + raise CommandError("Invalid argument.") + + return course_id, software_name, count + + def _generate_serials(self, software, count): + print "Generating {0} serials".format(count) + + # add serial numbers them to the database + for _ in xrange(count): + serial = str(uuid4()) + license = UserLicense(software=software, serial=serial) + license.save() + + print "{0} new serial numbers generated.".format(count) diff --git a/lms/djangoapps/licenses/management/commands/import_serial_numbers.py b/lms/djangoapps/licenses/management/commands/import_serial_numbers.py index 465940ce20..a3a8c0bad1 100644 --- a/lms/djangoapps/licenses/management/commands/import_serial_numbers.py +++ b/lms/djangoapps/licenses/management/commands/import_serial_numbers.py @@ -1,5 +1,4 @@ import os.path - from optparse import make_option from django.utils.html import escape From e9ec63e4b6488ad278edfa98f4566d861da796a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Andr=C3=A9s=20Rocha?= Date: Mon, 15 Oct 2012 18:45:36 -0400 Subject: [PATCH 7/8] Add ajax endpoint to retrieve and assign course software licenses --- lms/djangoapps/licenses/models.py | 2 +- lms/djangoapps/licenses/views.py | 53 ++++++++++++++++++++++++++++++- lms/urls.py | 8 +++++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/lms/djangoapps/licenses/models.py b/lms/djangoapps/licenses/models.py index 929fba10ec..d259892f5d 100644 --- a/lms/djangoapps/licenses/models.py +++ b/lms/djangoapps/licenses/models.py @@ -65,7 +65,7 @@ def _create_license(user, software): # 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 = selected.filter(user__isnull=True, software=software)[0] license.user = user license.save() except IndexError: diff --git a/lms/djangoapps/licenses/views.py b/lms/djangoapps/licenses/views.py index 7cf8e6591e..9b32478e0e 100644 --- a/lms/djangoapps/licenses/views.py +++ b/lms/djangoapps/licenses/views.py @@ -1,9 +1,18 @@ import logging +import json +import re +from urlparse import urlparse from collections import namedtuple, defaultdict + from mitxmako.shortcuts import render_to_string -from models import get_courses_licenses, get_or_create_license +from django.contrib.auth.models import User +from django.http import HttpResponse, Http404 +from django.views.decorators.csrf import requires_csrf_token, csrf_protect + +from models import CourseSoftware +from models import get_courses_licenses, get_or_create_license, get_license log = logging.getLogger("mitx.licenses") @@ -33,3 +42,45 @@ def get_licenses_by_course(user, courses): data_by_course[course_id] = render_to_string(template, context) return data_by_course + + +@requires_csrf_token +def user_software_license(request): + if request.method != 'POST' or not request.is_ajax(): + raise Http404 + + # get the course id from the referer + url_path = urlparse(request.META.get('HTTP_REFERER', '')).path + pattern = re.compile('^/courses/(?P[^/]+/[^/]+/[^/]+)/.*/?$') + match = re.match(pattern, url_path) + + if not match: + raise Http404 + course_id = match.groupdict().get('id', '') + + user_id = request.session.get('_auth_user_id') + software_name = request.POST.get('software') + generate = request.POST.get('generate', False) == 'true' + + print user_id, software_name, generate + + try: + software = CourseSoftware.objects.get(name=software_name, + course_id=course_id) + print software + except CourseSoftware.DoesNotExist: + raise Http404 + + user = User.objects.get(id=user_id) + + if generate: + license = get_or_create_license(user, software) + else: + license = get_license(user, software) + + if license: + response = {'serial': license.serial} + else: + response = {'error': 'No serial number found'} + + return HttpResponse(json.dumps(response), mimetype='application/json') diff --git a/lms/urls.py b/lms/urls.py index 89a541ab06..e025478387 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -154,6 +154,14 @@ if settings.COURSEWARE_ENABLED: url(r'^preview/chemcalc', 'courseware.module_render.preview_chemcalc', name='preview_chemcalc'), + # Software Licenses + + # TODO: for now, this is the endpoint of an ajax replay + # service that retrieve and assigns license numbers for + # software assigned to a course. The numbers have to be loaded + # into the database. + url(r'^software-licenses$', 'licenses.views.user_software_license', name="user_software_license"), + url(r'^courses/(?P[^/]+/[^/]+/[^/]+)/xqueue/(?P[^/]*)/(?P.*?)/(?P[^/]*)$', 'courseware.module_render.xqueue_callback', name='xqueue_callback'), From a8cedf8ab9712534e8747d62975c7536e8698cd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Andr=C3=A9s=20Rocha?= Date: Mon, 15 Oct 2012 21:04:10 -0300 Subject: [PATCH 8/8] Update lms/djangoapps/licenses/views.py Remove annoying print statements --- lms/djangoapps/licenses/views.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lms/djangoapps/licenses/views.py b/lms/djangoapps/licenses/views.py index 9b32478e0e..7d804fbd3d 100644 --- a/lms/djangoapps/licenses/views.py +++ b/lms/djangoapps/licenses/views.py @@ -62,8 +62,6 @@ def user_software_license(request): software_name = request.POST.get('software') generate = request.POST.get('generate', False) == 'true' - print user_id, software_name, generate - try: software = CourseSoftware.objects.get(name=software_name, course_id=course_id)