Commit 71975b7e by Jeff LaJoie Committed by Jeff LaJoie

LEARNER-3879: Adds in publisher entitlement model and version to course

parent df77af18
...@@ -595,7 +595,7 @@ class CourseMarketingSiteDataLoader(AbstractMarketingSiteDataLoader): ...@@ -595,7 +595,7 @@ class CourseMarketingSiteDataLoader(AbstractMarketingSiteDataLoader):
def set_subjects(self, course, data): def set_subjects(self, course, data):
subjects = self._get_objects_by_uuid(Subject, data['field_course_subject']) subjects = self._get_objects_by_uuid(Subject, data['field_course_subject'])
course.subjects.clear() course.subjects.clear()
course.subjects.add(*subjects) course.subjects.add(*subjects) # pylint: disable=not-an-iterable
def set_course_run_staff(self, course_run, data): def set_course_run_staff(self, course_run, data):
staff = self._get_objects_by_uuid(Person, data['field_course_staff']) staff = self._get_objects_by_uuid(Person, data['field_course_staff'])
......
...@@ -13,9 +13,9 @@ from course_discovery.apps.publisher.forms import ( ...@@ -13,9 +13,9 @@ from course_discovery.apps.publisher.forms import (
CourseRunAdminForm, CourseRunStateAdminForm, CourseStateAdminForm, OrganizationExtensionForm, CourseRunAdminForm, CourseRunStateAdminForm, CourseStateAdminForm, OrganizationExtensionForm,
PublisherUserCreationForm, UserAttributesAdminForm PublisherUserCreationForm, UserAttributesAdminForm
) )
from course_discovery.apps.publisher.models import (Course, CourseRun, CourseRunState, CourseState, CourseUserRole, from course_discovery.apps.publisher.models import (Course, CourseEntitlement, CourseRun, CourseRunState, CourseState,
OrganizationExtension, OrganizationUserRole, PublisherUser, Seat, CourseUserRole, OrganizationExtension, OrganizationUserRole,
UserAttributes) PublisherUser, Seat, UserAttributes)
@admin.register(CourseUserRole) @admin.register(CourseUserRole)
...@@ -116,6 +116,12 @@ class SeatAdmin(SimpleHistoryAdmin): ...@@ -116,6 +116,12 @@ class SeatAdmin(SimpleHistoryAdmin):
search_fields = ['course_run__course__title', 'type'] search_fields = ['course_run__course__title', 'type']
@admin.register(CourseEntitlement)
class CourseEntitlementAdmin(SimpleHistoryAdmin):
list_display = ['course', 'mode']
raw_id_fields = ['course']
@admin.register(PublisherUser) @admin.register(PublisherUser)
class PublisherUserAdmin(UserAdmin): class PublisherUserAdmin(UserAdmin):
add_form_template = 'publisher/admin/add_user_form.html' add_form_template = 'publisher/admin/add_user_form.html'
......
...@@ -593,7 +593,7 @@ def send_email_for_seo_review(course, site): ...@@ -593,7 +593,7 @@ def send_email_for_seo_review(course, site):
try: try:
legal_team_users = User.objects.filter(groups__name=LEGAL_TEAM_GROUP_NAME) legal_team_users = User.objects.filter(groups__name=LEGAL_TEAM_GROUP_NAME)
project_coordinator = course.project_coordinator project_coordinator = course.project_coordinator
to_addresses = [user.email for user in legal_team_users] to_addresses = [user.email for user in legal_team_users] # pylint: disable=not-an-iterable
from_address = settings.PUBLISHER_FROM_EMAIL from_address = settings.PUBLISHER_FROM_EMAIL
course_page_path = reverse('publisher:publisher_course_detail', kwargs={'pk': course.id}) course_page_path = reverse('publisher:publisher_course_detail', kwargs={'pk': course.id})
......
# -*- coding: utf-8 -*-
# Generated by Django 1.11.3 on 2018-01-25 18:36
from __future__ import unicode_literals
import django.db.models.deletion
import django_extensions.db.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0007_auto_20171004_1133'),
('publisher', '0063_auto_20171219_1841'),
]
operations = [
migrations.CreateModel(
name='CourseEntitlement',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True, verbose_name='created')),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')),
('mode', models.CharField(choices=[('verified', 'Verified'), ('professional', 'Professional')], max_length=63, verbose_name='Course mode')),
('price', models.DecimalField(decimal_places=2, default=0.0, max_digits=10)),
],
),
migrations.AddField(
model_name='course',
name='version',
field=models.IntegerField(default=0, verbose_name='Workflow Version'),
),
migrations.AddField(
model_name='historicalcourse',
name='version',
field=models.IntegerField(default=0, verbose_name='Workflow Version'),
),
migrations.AddField(
model_name='courseentitlement',
name='course',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='entitlements', to='publisher.Course'),
),
migrations.AddField(
model_name='courseentitlement',
name='currency',
field=models.ForeignKey(default='USD', on_delete=django.db.models.deletion.CASCADE, related_name='publisher_entitlements', to='core.Currency'),
),
migrations.AlterUniqueTogether(
name='courseentitlement',
unique_together=set([('course', 'mode')]),
),
]
...@@ -44,6 +44,12 @@ class ChangedByMixin(models.Model): ...@@ -44,6 +44,12 @@ class ChangedByMixin(models.Model):
class Course(TimeStampedModel, ChangedByMixin): class Course(TimeStampedModel, ChangedByMixin):
""" Publisher Course model. It contains fields related to the course intake form.""" """ Publisher Course model. It contains fields related to the course intake form."""
# Versions for code paths in publisher Course and Course Run Create/Edit
# Is the original version for courses without Entitlements (No mode/price set at Course level)
SEAT_VERSION = 0
# Is the version for Courses that have a mode and price set (a CourseEntitlement), where all course runs must match
ENTITLEMENT_VERSION = 1
title = models.CharField(max_length=255, default=None, null=True, blank=True, verbose_name=_('Course title')) title = models.CharField(max_length=255, default=None, null=True, blank=True, verbose_name=_('Course title'))
number = models.CharField(max_length=50, null=True, blank=True, verbose_name=_('Course number')) number = models.CharField(max_length=50, null=True, blank=True, verbose_name=_('Course number'))
short_description = models.TextField( short_description = models.TextField(
...@@ -84,6 +90,7 @@ class Course(TimeStampedModel, ChangedByMixin): ...@@ -84,6 +90,7 @@ class Course(TimeStampedModel, ChangedByMixin):
keywords = TaggableManager(blank=True, verbose_name='keywords') keywords = TaggableManager(blank=True, verbose_name='keywords')
faq = models.TextField(default=None, null=True, blank=True, verbose_name=_('FAQ')) faq = models.TextField(default=None, null=True, blank=True, verbose_name=_('FAQ'))
video_link = models.URLField(default=None, null=True, blank=True, verbose_name=_('Video Link')) video_link = models.URLField(default=None, null=True, blank=True, verbose_name=_('Video Link'))
version = models.IntegerField(default=SEAT_VERSION, verbose_name='Workflow Version')
# temp fields for data migrations only. # temp fields for data migrations only.
course_metadata_pk = models.PositiveIntegerField(null=True, blank=True, verbose_name=_('Course Metadata Course PK')) course_metadata_pk = models.PositiveIntegerField(null=True, blank=True, verbose_name=_('Course Metadata Course PK'))
...@@ -485,6 +492,33 @@ class Seat(TimeStampedModel, ChangedByMixin): ...@@ -485,6 +492,33 @@ class Seat(TimeStampedModel, ChangedByMixin):
return None return None
class CourseEntitlement(TimeStampedModel):
VERIFIED = 'verified'
PROFESSIONAL = 'professional'
COURSE_MODE_CHOICES = (
(VERIFIED, _('Verified')),
(PROFESSIONAL, _('Professional'))
)
PRICE_FIELD_CONFIG = {
'decimal_places': 2,
'max_digits': 10,
'null': False,
'default': 0.00,
}
course = models.ForeignKey(Course, related_name='entitlements')
mode = models.CharField(max_length=63, choices=COURSE_MODE_CHOICES, verbose_name='Course mode')
price = models.DecimalField(**PRICE_FIELD_CONFIG)
currency = models.ForeignKey(Currency, default='USD', related_name='publisher_entitlements')
class Meta(object):
unique_together = (
('course', 'mode')
)
class UserAttributes(TimeStampedModel): class UserAttributes(TimeStampedModel):
""" Record additional metadata about a user. """ """ Record additional metadata about a user. """
user = models.OneToOneField(User, related_name='attributes') user = models.OneToOneField(User, related_name='attributes')
......
...@@ -644,7 +644,7 @@ class SEOReviewEmailTests(SiteMixin, TestCase): ...@@ -644,7 +644,7 @@ class SEOReviewEmailTests(SiteMixin, TestCase):
self.assertEqual(len(mail.outbox), 1) self.assertEqual(len(mail.outbox), 1)
legal_team_users = User.objects.filter(groups__name=LEGAL_TEAM_GROUP_NAME) legal_team_users = User.objects.filter(groups__name=LEGAL_TEAM_GROUP_NAME)
expected_addresses = [user.email for user in legal_team_users] expected_addresses = [user.email for user in legal_team_users] # pylint: disable=not-an-iterable
self.assertEqual(expected_addresses, mail.outbox[0].to) self.assertEqual(expected_addresses, mail.outbox[0].to)
self.assertEqual(str(mail.outbox[0].subject), expected_subject) self.assertEqual(str(mail.outbox[0].subject), expected_subject)
body = mail.outbox[0].body.strip() body = mail.outbox[0].body.strip()
......
...@@ -7,7 +7,7 @@ msgid "" ...@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-01-12 11:58+0000\n" "POT-Creation-Date: 2018-01-25 21:26+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
...@@ -404,7 +404,7 @@ msgstr "" ...@@ -404,7 +404,7 @@ msgstr ""
msgid "Verified" msgid "Verified"
msgstr "" msgstr ""
#: apps/course_metadata/models.py #: apps/course_metadata/models.py apps/publisher/models.py
msgid "Professional" msgid "Professional"
msgstr "" msgstr ""
......
...@@ -7,7 +7,7 @@ msgid "" ...@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-01-12 11:58+0000\n" "POT-Creation-Date: 2018-01-25 21:26+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
......
...@@ -7,7 +7,7 @@ msgid "" ...@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-01-12 11:58+0000\n" "POT-Creation-Date: 2018-01-25 21:26+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
...@@ -507,7 +507,7 @@ msgstr "Àüdït Ⱡ'σяєм ιρѕ#" ...@@ -507,7 +507,7 @@ msgstr "Àüdït Ⱡ'σяєм ιρѕ#"
msgid "Verified" msgid "Verified"
msgstr "Vérïfïéd Ⱡ'σяєм ιρѕυм ∂#" msgstr "Vérïfïéd Ⱡ'σяєм ιρѕυм ∂#"
#: apps/course_metadata/models.py #: apps/course_metadata/models.py apps/publisher/models.py
msgid "Professional" msgid "Professional"
msgstr "Pröféssïönäl Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#" msgstr "Pröféssïönäl Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#"
......
...@@ -7,7 +7,7 @@ msgid "" ...@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-01-12 11:58+0000\n" "POT-Creation-Date: 2018-01-25 21:26+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment