Remove "Licenses" djangoapp
This commit is contained in:
@@ -1875,33 +1875,6 @@ CREATE TABLE `instructor_task_instructortask` (
|
|||||||
CONSTRAINT `instructor_task_in_requester_id_3383acfe2fe42391_fk_auth_user_id` FOREIGN KEY (`requester_id`) REFERENCES `auth_user` (`id`)
|
CONSTRAINT `instructor_task_in_requester_id_3383acfe2fe42391_fk_auth_user_id` FOREIGN KEY (`requester_id`) REFERENCES `auth_user` (`id`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
DROP TABLE IF EXISTS `licenses_coursesoftware`;
|
|
||||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
|
||||||
/*!40101 SET character_set_client = utf8 */;
|
|
||||||
CREATE TABLE `licenses_coursesoftware` (
|
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
|
||||||
`name` varchar(255) NOT NULL,
|
|
||||||
`full_name` varchar(255) NOT NULL,
|
|
||||||
`url` varchar(255) NOT NULL,
|
|
||||||
`course_id` varchar(255) NOT NULL,
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
|
||||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
|
||||||
DROP TABLE IF EXISTS `licenses_userlicense`;
|
|
||||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
|
||||||
/*!40101 SET character_set_client = utf8 */;
|
|
||||||
CREATE TABLE `licenses_userlicense` (
|
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
|
||||||
`serial` varchar(255) NOT NULL,
|
|
||||||
`software_id` int(11) NOT NULL,
|
|
||||||
`user_id` int(11) DEFAULT NULL,
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
KEY `licen_software_id_209fd400b7c7a3ca_fk_licenses_coursesoftware_id` (`software_id`),
|
|
||||||
KEY `licenses_userlicense_user_id_7d98c37aa4438a34_fk_auth_user_id` (`user_id`),
|
|
||||||
CONSTRAINT `licen_software_id_209fd400b7c7a3ca_fk_licenses_coursesoftware_id` FOREIGN KEY (`software_id`) REFERENCES `licenses_coursesoftware` (`id`),
|
|
||||||
CONSTRAINT `licenses_userlicense_user_id_7d98c37aa4438a34_fk_auth_user_id` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
|
||||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
|
||||||
DROP TABLE IF EXISTS `lms_xblock_xblockasidesconfig`;
|
DROP TABLE IF EXISTS `lms_xblock_xblockasidesconfig`;
|
||||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
/*!40101 SET character_set_client = utf8 */;
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
from uuid import uuid4
|
|
||||||
|
|
||||||
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
|
|
||||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
|
||||||
help = """Generate random serial numbers for software used in a course.
|
|
||||||
|
|
||||||
Usage: generate_serial_numbers <course_id> <software_name> <count>
|
|
||||||
|
|
||||||
<count> 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]
|
|
||||||
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
|
|
||||||
if not modulestore().has_course(course_key):
|
|
||||||
raise CommandError("Unknown course_id")
|
|
||||||
|
|
||||||
software_name = escape(args[1].lower())
|
|
||||||
|
|
||||||
try:
|
|
||||||
count = int(args[2])
|
|
||||||
except ValueError:
|
|
||||||
raise CommandError("Invalid <count> argument.")
|
|
||||||
|
|
||||||
return course_key, 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)
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
import os.path
|
|
||||||
|
|
||||||
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
|
|
||||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
|
||||||
help = """Imports serial numbers for software used in a course.
|
|
||||||
|
|
||||||
Usage: import_serial_numbers <course_id> <software_name> <file>
|
|
||||||
|
|
||||||
<file> is a text file that list one available serial number per line.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
import_serial_numbers MITx/6.002x/2012_Fall 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, _ = CourseSoftware.objects.get_or_create(course_id=course_id,
|
|
||||||
name=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]
|
|
||||||
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
|
|
||||||
if not modulestore().has_course(course_key):
|
|
||||||
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_key, software_name, filename
|
|
||||||
|
|
||||||
def _import_serials(self, software, filename):
|
|
||||||
print "Importing serial numbers for {0}.".format(software)
|
|
||||||
|
|
||||||
serials = set(unicode(l.strip()) for l in open(filename))
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
# 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))
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
from django.conf import settings
|
|
||||||
import xmodule_django.models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='CourseSoftware',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
|
||||||
('name', models.CharField(max_length=255)),
|
|
||||||
('full_name', models.CharField(max_length=255)),
|
|
||||||
('url', models.CharField(max_length=255)),
|
|
||||||
('course_id', xmodule_django.models.CourseKeyField(max_length=255)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='UserLicense',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
|
||||||
('serial', models.CharField(max_length=255)),
|
|
||||||
('software', models.ForeignKey(to='licenses.CourseSoftware')),
|
|
||||||
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, null=True)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
import logging
|
|
||||||
|
|
||||||
from django.db import models, transaction
|
|
||||||
|
|
||||||
from student.models import User
|
|
||||||
|
|
||||||
from xmodule_django.models import CourseKeyField
|
|
||||||
|
|
||||||
log = logging.getLogger("edx.licenses")
|
|
||||||
|
|
||||||
|
|
||||||
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 = CourseKeyField(max_length=255)
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return u'{0} for {1}'.format(self.name, self.course_id)
|
|
||||||
|
|
||||||
|
|
||||||
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:
|
|
||||||
# TODO: temporary fix for when somehow a user got more that one license.
|
|
||||||
# The proper fix should use Meta.unique_together in the UserLicense model.
|
|
||||||
licenses = UserLicense.objects.filter(user=user, software=software)
|
|
||||||
license = licenses[0] if licenses else None
|
|
||||||
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.atomic():
|
|
||||||
selected = UserLicense.objects.select_for_update()
|
|
||||||
license = selected.filter(user__isnull=True, software=software)[0]
|
|
||||||
license.user = user
|
|
||||||
license.save()
|
|
||||||
except IndexError:
|
|
||||||
# there are no free licenses
|
|
||||||
log.error('No serial numbers available for %s', software)
|
|
||||||
license = None
|
|
||||||
# TODO [rocha]look if someone has unenrolled from the class
|
|
||||||
# and already has a serial number
|
|
||||||
|
|
||||||
return license
|
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
"""Tests for License package"""
|
|
||||||
import logging
|
|
||||||
import json
|
|
||||||
|
|
||||||
from uuid import uuid4
|
|
||||||
from random import shuffle
|
|
||||||
from tempfile import NamedTemporaryFile
|
|
||||||
import factory
|
|
||||||
from factory.django import DjangoModelFactory
|
|
||||||
|
|
||||||
from django.test import TestCase
|
|
||||||
from django.test.client import Client
|
|
||||||
from django.core.management import call_command
|
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from nose.tools import assert_true
|
|
||||||
|
|
||||||
from licenses.models import CourseSoftware, UserLicense
|
|
||||||
|
|
||||||
from student.tests.factories import UserFactory
|
|
||||||
from xmodule.modulestore.tests.factories import CourseFactory
|
|
||||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
|
||||||
|
|
||||||
COURSE_1 = 'edX/toy/2012_Fall'
|
|
||||||
|
|
||||||
SOFTWARE_1 = 'matlab'
|
|
||||||
SOFTWARE_2 = 'stata'
|
|
||||||
|
|
||||||
SERIAL_1 = '123456abcde'
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class CourseSoftwareFactory(DjangoModelFactory):
|
|
||||||
'''Factory for generating CourseSoftware objects in database'''
|
|
||||||
class Meta(object):
|
|
||||||
model = CourseSoftware
|
|
||||||
|
|
||||||
name = SOFTWARE_1
|
|
||||||
full_name = SOFTWARE_1
|
|
||||||
url = SOFTWARE_1
|
|
||||||
course_id = COURSE_1
|
|
||||||
|
|
||||||
|
|
||||||
class UserLicenseFactory(DjangoModelFactory):
|
|
||||||
'''
|
|
||||||
Factory for generating UserLicense objects in database
|
|
||||||
|
|
||||||
By default, the user assigned is null, indicating that the
|
|
||||||
serial number has not yet been assigned.
|
|
||||||
'''
|
|
||||||
class Meta(object):
|
|
||||||
model = UserLicense
|
|
||||||
|
|
||||||
user = None
|
|
||||||
software = factory.SubFactory(CourseSoftwareFactory)
|
|
||||||
serial = SERIAL_1
|
|
||||||
|
|
||||||
|
|
||||||
class LicenseTestCase(TestCase):
|
|
||||||
'''Tests for licenses.views'''
|
|
||||||
def setUp(self):
|
|
||||||
'''creates a user and logs in'''
|
|
||||||
|
|
||||||
super(LicenseTestCase, self).setUp()
|
|
||||||
# self.setup_viewtest_user()
|
|
||||||
self.user = UserFactory(username='test',
|
|
||||||
email='test@edx.org', password='test_password')
|
|
||||||
self.client = Client()
|
|
||||||
assert_true(self.client.login(username='test', password='test_password'))
|
|
||||||
self.software = CourseSoftwareFactory()
|
|
||||||
|
|
||||||
def test_get_license(self):
|
|
||||||
UserLicenseFactory(user=self.user, software=self.software)
|
|
||||||
response = self.client.post(reverse('user_software_license'),
|
|
||||||
{'software': SOFTWARE_1, 'generate': 'false'},
|
|
||||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
|
|
||||||
HTTP_REFERER='/courses/{0}/some_page'.format(COURSE_1))
|
|
||||||
self.assertEqual(200, response.status_code)
|
|
||||||
json_returned = json.loads(response.content)
|
|
||||||
self.assertFalse('error' in json_returned)
|
|
||||||
self.assertTrue('serial' in json_returned)
|
|
||||||
self.assertEquals(json_returned['serial'], SERIAL_1)
|
|
||||||
|
|
||||||
def test_get_nonexistent_license(self):
|
|
||||||
response = self.client.post(reverse('user_software_license'),
|
|
||||||
{'software': SOFTWARE_1, 'generate': 'false'},
|
|
||||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
|
|
||||||
HTTP_REFERER='/courses/{0}/some_page'.format(COURSE_1))
|
|
||||||
self.assertEqual(200, response.status_code)
|
|
||||||
json_returned = json.loads(response.content)
|
|
||||||
self.assertFalse('serial' in json_returned)
|
|
||||||
self.assertTrue('error' in json_returned)
|
|
||||||
|
|
||||||
def test_create_nonexistent_license(self):
|
|
||||||
'''Should not assign a license to an unlicensed user when none are available'''
|
|
||||||
response = self.client.post(reverse('user_software_license'),
|
|
||||||
{'software': SOFTWARE_1, 'generate': 'true'},
|
|
||||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
|
|
||||||
HTTP_REFERER='/courses/{0}/some_page'.format(COURSE_1))
|
|
||||||
self.assertEqual(200, response.status_code)
|
|
||||||
json_returned = json.loads(response.content)
|
|
||||||
self.assertFalse('serial' in json_returned)
|
|
||||||
self.assertTrue('error' in json_returned)
|
|
||||||
|
|
||||||
def test_create_license(self):
|
|
||||||
'''Should assign a license to an unlicensed user if one is unassigned'''
|
|
||||||
# create an unassigned license
|
|
||||||
UserLicenseFactory(software=self.software)
|
|
||||||
response = self.client.post(reverse('user_software_license'),
|
|
||||||
{'software': SOFTWARE_1, 'generate': 'true'},
|
|
||||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
|
|
||||||
HTTP_REFERER='/courses/{0}/some_page'.format(COURSE_1))
|
|
||||||
self.assertEqual(200, response.status_code)
|
|
||||||
json_returned = json.loads(response.content)
|
|
||||||
self.assertFalse('error' in json_returned)
|
|
||||||
self.assertTrue('serial' in json_returned)
|
|
||||||
self.assertEquals(json_returned['serial'], SERIAL_1)
|
|
||||||
|
|
||||||
def test_get_license_from_wrong_course(self):
|
|
||||||
response = self.client.post(reverse('user_software_license'),
|
|
||||||
{'software': SOFTWARE_1, 'generate': 'false'},
|
|
||||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
|
|
||||||
HTTP_REFERER='/courses/{0}/some_page'.format('some/other/course'))
|
|
||||||
self.assertEqual(404, response.status_code)
|
|
||||||
|
|
||||||
def test_get_license_from_non_ajax(self):
|
|
||||||
response = self.client.post(reverse('user_software_license'),
|
|
||||||
{'software': SOFTWARE_1, 'generate': 'false'},
|
|
||||||
HTTP_REFERER='/courses/{0}/some_page'.format(COURSE_1))
|
|
||||||
self.assertEqual(404, response.status_code)
|
|
||||||
|
|
||||||
def test_get_license_without_software(self):
|
|
||||||
response = self.client.post(reverse('user_software_license'),
|
|
||||||
{'generate': 'false'},
|
|
||||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
|
|
||||||
HTTP_REFERER='/courses/{0}/some_page'.format(COURSE_1))
|
|
||||||
self.assertEqual(404, response.status_code)
|
|
||||||
|
|
||||||
def test_get_license_without_login(self):
|
|
||||||
self.client.logout()
|
|
||||||
response = self.client.post(reverse('user_software_license'),
|
|
||||||
{'software': SOFTWARE_1, 'generate': 'false'},
|
|
||||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
|
|
||||||
HTTP_REFERER='/courses/{0}/some_page'.format(COURSE_1))
|
|
||||||
# if we're not logged in, we should be referred to the login page
|
|
||||||
self.assertEqual(302, response.status_code)
|
|
||||||
|
|
||||||
|
|
||||||
class CommandTest(ModuleStoreTestCase):
|
|
||||||
'''Test management command for importing serial numbers'''
|
|
||||||
def setUp(self):
|
|
||||||
super(CommandTest, self).setUp()
|
|
||||||
|
|
||||||
course = CourseFactory.create()
|
|
||||||
self.course_id = course.id
|
|
||||||
|
|
||||||
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 = [self.course_id.to_deprecated_string(), 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 = [self.course_id.to_deprecated_string(), 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 = [self.course_id.to_deprecated_string(), 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 %s serials",
|
|
||||||
size,
|
|
||||||
)
|
|
||||||
licenses_count = UserLicense.objects.all().count()
|
|
||||||
self.assertEqual(3 * size, licenses_count)
|
|
||||||
|
|
||||||
software = CourseSoftware.objects.get(pk=1)
|
|
||||||
|
|
||||||
lics = UserLicense.objects.filter(software=software)[: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 tmpfile:
|
|
||||||
tmpfile.write('\n'.join(known_serials))
|
|
||||||
tmpfile.flush()
|
|
||||||
args = [self.course_id.to_deprecated_string(), SOFTWARE_1, tmpfile.name]
|
|
||||||
call_command('import_serial_numbers', *args)
|
|
||||||
|
|
||||||
log.debug('Check if we added only the new ones')
|
|
||||||
licenses_count = UserLicense.objects.filter(software=software).count()
|
|
||||||
self.assertEqual((2 * size) + 10, licenses_count)
|
|
||||||
|
|
||||||
|
|
||||||
def generate_serials(size=20):
|
|
||||||
'''generate a list of serial numbers'''
|
|
||||||
return [str(uuid4()) for _ in range(size)]
|
|
||||||
|
|
||||||
|
|
||||||
def generate_serials_file(size=20):
|
|
||||||
'''output list of generated serial numbers to a temp file'''
|
|
||||||
serials = generate_serials(size)
|
|
||||||
|
|
||||||
temp_file = NamedTemporaryFile()
|
|
||||||
temp_file.write('\n'.join(serials))
|
|
||||||
temp_file.flush()
|
|
||||||
|
|
||||||
return temp_file
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
import logging
|
|
||||||
import json
|
|
||||||
import re
|
|
||||||
from urlparse import urlparse
|
|
||||||
from collections import namedtuple, defaultdict
|
|
||||||
|
|
||||||
|
|
||||||
from edxmako.shortcuts import render_to_string
|
|
||||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
|
||||||
|
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.http import HttpResponse, Http404
|
|
||||||
from django.views.decorators.csrf import requires_csrf_token
|
|
||||||
|
|
||||||
from licenses.models import CourseSoftware
|
|
||||||
from licenses.models import get_courses_licenses, get_or_create_license, get_license
|
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger("edx.licenses")
|
|
||||||
|
|
||||||
|
|
||||||
License = namedtuple('License', 'software serial')
|
|
||||||
|
|
||||||
|
|
||||||
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:
|
|
||||||
licenses[software] = get_or_create_license(user, software)
|
|
||||||
|
|
||||||
course_id = software.course_id
|
|
||||||
serial = license.serial if license else None
|
|
||||||
licenses_by_course[course_id].append(License(software, serial))
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
return data_by_course
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@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<id>[^/]+/[^/]+/[^/]+)/.*/?$')
|
|
||||||
match = re.match(pattern, url_path)
|
|
||||||
|
|
||||||
if not match:
|
|
||||||
raise Http404
|
|
||||||
course_id = match.groupdict().get('id', '')
|
|
||||||
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
|
|
||||||
|
|
||||||
user_id = request.session.get('_auth_user_id')
|
|
||||||
software_name = request.POST.get('software')
|
|
||||||
generate = request.POST.get('generate', False) == 'true'
|
|
||||||
|
|
||||||
try:
|
|
||||||
software = CourseSoftware.objects.get(name=software_name,
|
|
||||||
course_id=course_key)
|
|
||||||
except CourseSoftware.DoesNotExist:
|
|
||||||
raise Http404
|
|
||||||
|
|
||||||
try:
|
|
||||||
user = User.objects.get(id=user_id)
|
|
||||||
except User.DoesNotExist:
|
|
||||||
raise Http404
|
|
||||||
|
|
||||||
if generate:
|
|
||||||
software_license = get_or_create_license(user, software)
|
|
||||||
else:
|
|
||||||
software_license = get_license(user, software)
|
|
||||||
|
|
||||||
if software_license:
|
|
||||||
response = {'serial': software_license.serial}
|
|
||||||
else:
|
|
||||||
response = {'error': 'No serial number found'}
|
|
||||||
|
|
||||||
return HttpResponse(json.dumps(response), content_type='application/json')
|
|
||||||
@@ -1831,7 +1831,6 @@ INSTALLED_APPS = (
|
|||||||
'instructor',
|
'instructor',
|
||||||
'instructor_task',
|
'instructor_task',
|
||||||
'open_ended_grading',
|
'open_ended_grading',
|
||||||
'licenses',
|
|
||||||
'openedx.core.djangoapps.course_groups',
|
'openedx.core.djangoapps.course_groups',
|
||||||
'bulk_email',
|
'bulk_email',
|
||||||
'branding',
|
'branding',
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
<%! from django.utils.translation import ugettext as _ %>
|
|
||||||
<dl>
|
|
||||||
% for license in licenses:
|
|
||||||
<dt> ${license.software.name}: </dt>
|
|
||||||
% if license.serial:
|
|
||||||
<dd> ${license.serial} </dd>
|
|
||||||
% else:
|
|
||||||
<dd> ${_("None Available")} </dd>
|
|
||||||
% endif
|
|
||||||
% endfor
|
|
||||||
</dl>
|
|
||||||
@@ -310,14 +310,6 @@ if settings.COURSEWARE_ENABLED:
|
|||||||
name='xblock_resource_url',
|
name='xblock_resource_url',
|
||||||
),
|
),
|
||||||
|
|
||||||
# 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(
|
url(
|
||||||
r'^courses/{}/xqueue/(?P<userid>[^/]*)/(?P<mod_id>.*?)/(?P<dispatch>[^/]*)$'.format(
|
r'^courses/{}/xqueue/(?P<userid>[^/]*)/(?P<mod_id>.*?)/(?P<dispatch>[^/]*)$'.format(
|
||||||
settings.COURSE_ID_PATTERN
|
settings.COURSE_ID_PATTERN
|
||||||
|
|||||||
Reference in New Issue
Block a user