Commit 8e407444 by Bill DeRusha Committed by GitHub

Merge pull request #163 from edx/awais786/ECOM-4908-add-models-course-about

ECOM-4892
parents fe9e1736 c1e5b031
from django.contrib import admin
from course_discovery.apps.course_metadata.models import (
Seat, Image, Video, LevelType, Subject, Prerequisite, ExpectedLearningItem, Course, CourseRun, Organization, Person,
Seat, Image, Video, LevelType, Subject, Prerequisite, ExpectedLearningItem, Expertise,
Course, CourseRun, CourseRunSocialNetwork, MajorWork, Organization, Person, PersonSocialNetwork,
CourseOrganization, SyllabusItem, Program
)
......@@ -56,9 +57,9 @@ for model in (Organization, Person,):
admin.site.register(model, KeyNameAdmin)
# Register children of AbstractNamedModel
for model in (LevelType, Subject, Prerequisite,):
for model in (LevelType, Subject, Prerequisite, Expertise, MajorWork):
admin.site.register(model, NamedModelAdmin)
# Register remaining models using basic ModelAdmin classes
for model in (Image, Video, ExpectedLearningItem, SyllabusItem):
for model in (Image, Video, ExpectedLearningItem, SyllabusItem, PersonSocialNetwork, CourseRunSocialNetwork):
admin.site.register(model)
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import sortedm2m.fields
import django_extensions.db.fields
class Migration(migrations.Migration):
dependencies = [
('course_metadata', '0006_auto_20160719_2052'),
]
operations = [
migrations.CreateModel(
name='CourseRunSocialNetwork',
fields=[
('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)),
('created', django_extensions.db.fields.CreationDateTimeField(verbose_name='created', auto_now_add=True)),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')),
('type', models.CharField(choices=[('facebook', 'Facebook'), ('twitter', 'Twitter'), ('blog', 'Blog'), ('others', 'Others')], db_index=True, max_length=15)),
('value', models.CharField(max_length=500)),
('course_run', models.ForeignKey(related_name='course_run_networks', to='course_metadata.CourseRun')),
],
options={
'verbose_name_plural': 'CourseRun SocialNetwork',
},
),
migrations.CreateModel(
name='Expertise',
fields=[
('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)),
('created', django_extensions.db.fields.CreationDateTimeField(verbose_name='created', auto_now_add=True)),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')),
('name', models.CharField(unique=True, max_length=255)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='MajorWork',
fields=[
('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)),
('created', django_extensions.db.fields.CreationDateTimeField(verbose_name='created', auto_now_add=True)),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')),
('name', models.CharField(unique=True, max_length=255)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='PersonSocialNetwork',
fields=[
('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)),
('created', django_extensions.db.fields.CreationDateTimeField(verbose_name='created', auto_now_add=True)),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')),
('type', models.CharField(choices=[('facebook', 'Facebook'), ('twitter', 'Twitter'), ('blog', 'Blog'), ('others', 'Others')], db_index=True, max_length=15)),
('value', models.CharField(max_length=500)),
],
options={
'verbose_name_plural': 'Person SocialNetwork',
},
),
migrations.AddField(
model_name='course',
name='learner_testimonial',
field=models.CharField(help_text='A quote from a learner in the course, demonstrating the value of taking the course', null=True, blank=True, max_length=50),
),
migrations.AddField(
model_name='course',
name='number',
field=models.CharField(help_text='Course number format e.g CS002x, BIO1.1x, BIO1.2x', null=True, blank=True, max_length=50),
),
migrations.AddField(
model_name='historicalcourse',
name='learner_testimonial',
field=models.CharField(help_text='A quote from a learner in the course, demonstrating the value of taking the course', null=True, blank=True, max_length=50),
),
migrations.AddField(
model_name='historicalcourse',
name='number',
field=models.CharField(help_text='Course number format e.g CS002x, BIO1.1x, BIO1.2x', null=True, blank=True, max_length=50),
),
migrations.AddField(
model_name='historicalperson',
name='email',
field=models.EmailField(null=True, blank=True, max_length=255),
),
migrations.AddField(
model_name='historicalperson',
name='username',
field=models.CharField(null=True, blank=True, max_length=255),
),
migrations.AddField(
model_name='person',
name='email',
field=models.EmailField(null=True, blank=True, max_length=255),
),
migrations.AddField(
model_name='person',
name='username',
field=models.CharField(null=True, blank=True, max_length=255),
),
migrations.AddField(
model_name='personsocialnetwork',
name='person',
field=models.ForeignKey(related_name='person_networks', to='course_metadata.Person'),
),
migrations.AddField(
model_name='person',
name='expertises',
field=sortedm2m.fields.SortedManyToManyField(related_name='person_expertise', help_text=None, blank=True, to='course_metadata.Expertise'),
),
migrations.AddField(
model_name='person',
name='major_works',
field=sortedm2m.fields.SortedManyToManyField(related_name='person_works', help_text=None, blank=True, to='course_metadata.MajorWork'),
),
migrations.AlterUniqueTogether(
name='personsocialnetwork',
unique_together=set([('person', 'type')]),
),
migrations.AlterUniqueTogether(
name='courserunsocialnetwork',
unique_together=set([('course_run', 'type')]),
),
]
......@@ -55,6 +55,30 @@ class AbstractMediaModel(TimeStampedModel):
abstract = True
class AbstractSocialNetworkModel(TimeStampedModel):
""" SocialNetwork model. """
FACEBOOK = 'facebook'
TWITTER = 'twitter'
BLOG = 'blog'
OTHERS = 'others'
SOCIAL_NETWORK_CHOICES = (
(FACEBOOK, _('Facebook')),
(TWITTER, _('Twitter')),
(BLOG, _('Blog')),
(OTHERS, _('Others')),
)
type = models.CharField(max_length=15, choices=SOCIAL_NETWORK_CHOICES, db_index=True)
value = models.CharField(max_length=500)
def __str__(self):
return '{type}: {value}'.format(type=self.type, value=self.value)
class Meta(object):
abstract = True
class Image(AbstractMediaModel):
""" Image model. """
height = models.IntegerField(null=True, blank=True)
......@@ -91,6 +115,16 @@ class SyllabusItem(AbstractValueModel):
parent = models.ForeignKey('self', blank=True, null=True, related_name='children')
class Expertise(AbstractNamedModel):
""" Expertise model. """
pass
class MajorWork(AbstractNamedModel):
""" MajorWork model. """
pass
class Organization(TimeStampedModel):
""" Organization model. """
key = models.CharField(max_length=255, unique=True)
......@@ -113,6 +147,10 @@ class Person(TimeStampedModel):
bio = models.TextField(null=True, blank=True)
profile_image = models.ForeignKey(Image, null=True, blank=True)
organizations = models.ManyToManyField(Organization, blank=True)
email = models.EmailField(max_length=255, null=True, blank=True)
username = models.CharField(max_length=255, null=True, blank=True)
expertises = SortedManyToManyField(Expertise, blank=True, related_name='person_expertise')
major_works = SortedManyToManyField(MajorWork, blank=True, related_name='person_works')
history = HistoricalRecords()
......@@ -137,6 +175,17 @@ class Course(TimeStampedModel):
image = models.ForeignKey(Image, default=None, null=True, blank=True)
video = models.ForeignKey(Video, default=None, null=True, blank=True)
marketing_url = models.URLField(max_length=255, null=True, blank=True)
learner_testimonial = models.CharField(
max_length=50, null=True, blank=True, help_text=_(
"A quote from a learner in the course, demonstrating the value of taking the course"
)
)
number = models.CharField(
max_length=50, null=True, blank=True, help_text=_(
"Course number format e.g CS002x, BIO1.1x, BIO1.2x"
)
)
history = HistoricalRecords()
objects = CourseQuerySet.as_manager()
......@@ -437,3 +486,27 @@ class Program(TimeStampedModel):
def __str__(self):
return self.title
class PersonSocialNetwork(AbstractSocialNetworkModel):
""" Person Social Network model. """
person = models.ForeignKey(Person, related_name='person_networks')
class Meta(object):
verbose_name_plural = 'Person SocialNetwork'
unique_together = (
('person', 'type'),
)
class CourseRunSocialNetwork(AbstractSocialNetworkModel):
""" CourseRun Social Network model. """
course_run = models.ForeignKey(CourseRun, related_name='course_run_networks')
class Meta(object):
verbose_name_plural = 'CourseRun SocialNetwork'
unique_together = (
('course_run', 'type'),
)
......@@ -9,7 +9,8 @@ from pytz import UTC
from course_discovery.apps.core.models import Currency
from course_discovery.apps.course_metadata.models import (
Course, CourseRun, Organization, Person, Image, Video, Subject, Seat, Prerequisite, LevelType, Program
Course, CourseRun, Organization, Person, Image, Video, Subject, Seat, Prerequisite, LevelType, Program,
AbstractSocialNetworkModel, CourseRunSocialNetwork, PersonSocialNetwork
)
from course_discovery.apps.ietf_language_tags.models import LanguageTag
......@@ -148,3 +149,22 @@ class ProgramFactory(factory.django.DjangoModelFactory):
category = 'xseries'
status = 'unpublished'
marketing_slug = factory.Sequence(lambda n: 'test-slug-{}'.format(n)) # pylint: disable=unnecessary-lambda
class AbstractSocialNetworkModelFactory(factory.DjangoModelFactory):
type = FuzzyChoice([name for name, __ in AbstractSocialNetworkModel.SOCIAL_NETWORK_CHOICES])
value = FuzzyText()
class PersonSocialNetworkFactory(AbstractSocialNetworkModelFactory):
person = factory.SubFactory(PersonFactory)
class Meta:
model = PersonSocialNetwork
class CourseRunSocialNetworkFactory(AbstractSocialNetworkModelFactory):
course_run = factory.SubFactory(CourseRunFactory)
class Meta:
model = CourseRunSocialNetwork
......@@ -3,6 +3,7 @@ import datetime
import ddt
import mock
import pytz
from django.db import IntegrityError
from django.conf import settings
from django.test import TestCase
......@@ -257,3 +258,47 @@ class ProgramTests(TestCase):
""" Verify the property returns None if the Program has no marketing_slug set. """
program = factories.ProgramFactory(marketing_slug='')
self.assertIsNone(program.marketing_url)
class PersonSocialNetworkTests(TestCase):
"""Tests of the PersonSocialNetwork model."""
def setUp(self):
super(PersonSocialNetworkTests, self).setUp()
self.network = factories.PersonSocialNetworkFactory()
self.person = factories.PersonFactory()
def test_str(self):
"""Verify that a person-social-network is properly converted to a str."""
self.assertEqual(
str(self.network), '{type}: {value}'.format(type=self.network.type, value=self.network.value)
)
def test_unique_constraint(self):
"""Verify that a person-social-network does not allow multiple accounts for same
social network.
"""
factories.PersonSocialNetworkFactory(person=self.person, type='facebook')
with self.assertRaises(IntegrityError):
factories.PersonSocialNetworkFactory(person=self.person, type='facebook')
class CourseSocialNetworkTests(TestCase):
"""Tests of the CourseSocialNetwork model."""
def setUp(self):
super(CourseSocialNetworkTests, self).setUp()
self.network = factories.CourseRunSocialNetworkFactory()
self.course_run = factories.CourseRunFactory()
def test_str(self):
"""Verify that a course-social-network is properly converted to a str."""
self.assertEqual(
str(self.network), '{type}: {value}'.format(type=self.network.type, value=self.network.value)
)
def test_unique_constraint(self):
"""Verify that a course-social-network does not allow multiple accounts for same
social network.
"""
factories.CourseRunSocialNetworkFactory(course_run=self.course_run, type='facebook')
with self.assertRaises(IntegrityError):
factories.CourseRunSocialNetworkFactory(course_run=self.course_run, type='facebook')
from django.contrib import admin
from course_discovery.apps.publisher.models import Course, CourseRun, Seat
admin.site.register(Course)
admin.site.register(CourseRun)
admin.site.register(Seat)
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import sortedm2m.fields
from django.conf import settings
import django.db.models.deletion
import course_discovery.apps.publisher.models
import django_extensions.db.fields
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('core', '0007_auto_20160510_2017'),
('ietf_language_tags', '0002_language_tag_data_migration'),
('course_metadata', '0007_auto_20160720_1749'),
]
operations = [
migrations.CreateModel(
name='Course',
fields=[
('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)),
('created', django_extensions.db.fields.CreationDateTimeField(verbose_name='created', auto_now_add=True)),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')),
('title', models.CharField(default=None, null=True, blank=True, max_length=255)),
('number', models.CharField(null=True, blank=True, max_length=50)),
('short_description', models.CharField(default=None, null=True, blank=True, max_length=255)),
('full_description', models.TextField(default=None, null=True, blank=True)),
('expected_learnings', models.TextField(default=None, null=True, blank=True)),
('syllabus', models.TextField(default=None, null=True, blank=True)),
('prerequisites', models.TextField(default=None, null=True, blank=True)),
('learner_testimonial', models.CharField(null=True, blank=True, max_length=50)),
('level_type', models.ForeignKey(related_name='publisher_courses', to='course_metadata.LevelType', default=None, null=True, blank=True)),
('organizations', models.ManyToManyField(related_name='publisher_courses', blank=True, to='course_metadata.Organization')),
('primary_subject', models.ForeignKey(related_name='publisher_courses_primary', to='course_metadata.Subject')),
('secondary_subject', models.ForeignKey(related_name='publisher_courses_secondary', to='course_metadata.Subject')),
('tertiary_subject', models.ForeignKey(related_name='publisher_courses_tertiary', to='course_metadata.Subject')),
],
options={
'ordering': ('-modified', '-created'),
'get_latest_by': 'modified',
'abstract': False,
},
bases=(models.Model, course_discovery.apps.publisher.models.ChangedByMixin),
),
migrations.CreateModel(
name='CourseRun',
fields=[
('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)),
('created', django_extensions.db.fields.CreationDateTimeField(verbose_name='created', auto_now_add=True)),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')),
('lms_course_id', models.CharField(unique=True, null=True, blank=True, max_length=255)),
('start', models.DateTimeField(null=True, blank=True)),
('end', models.DateTimeField(null=True, blank=True)),
('enrollment_start', models.DateTimeField(null=True, blank=True)),
('enrollment_end', models.DateTimeField(null=True, blank=True)),
('certificate_generation', models.DateTimeField(null=True, blank=True)),
('pacing_type', models.CharField(choices=[('self_paced', 'Self-paced'), ('instructor_paced', 'Instructor-paced')], null=True, blank=True, db_index=True, max_length=255)),
('min_effort', models.PositiveSmallIntegerField(help_text='Estimated minimum number of hours per week needed to complete a course run.', null=True, blank=True)),
('max_effort', models.PositiveSmallIntegerField(help_text='Estimated maximum number of hours per week needed to complete a course run.', null=True, blank=True)),
('length', models.PositiveIntegerField(help_text='Length of course, in number of weeks', null=True, blank=True)),
('is_re_run', models.BooleanField(default=False)),
('is_xseries', models.BooleanField(default=False)),
('xseries_name', models.CharField(max_length=255)),
('is_micromasters', models.BooleanField(default=False)),
('micromasters_name', models.CharField(max_length=255)),
('contacted_partner_manager', models.BooleanField(default=False)),
('seo_review', models.TextField(help_text='SEO review on your course title and short description', default=None, null=True, blank=True)),
('keywords', models.TextField(help_text='Please add top 10 comma separated keywords for your course content', default=None, blank=True)),
('notes', models.TextField(help_text='Please add any additional notes or special instructions for the course About Page.', default=None, null=True, blank=True)),
('target_content', models.BooleanField(default=False)),
('priority', models.CharField(choices=[('L1', 'Level 1'), ('L2', 'Level 2'), ('L3', 'Level 3'), ('L4', 'Level 4'), ('L5', 'Level 5')], null=True, blank=True, max_length=5)),
('course_team_admins', models.TextField(help_text='Comma separated list of edX usernames or emails of admins.', default=None, null=True, blank=True)),
('course_team_additional_staff', models.TextField(help_text='Comma separated list of edX usernames or emails of additional staff.', default=None, null=True, blank=True)),
('course', models.ForeignKey(to='publisher.Course')),
('language', models.ForeignKey(related_name='publisher_course_runs', to='ietf_language_tags.LanguageTag', null=True, blank=True)),
('sponsor', models.ManyToManyField(related_name='publisher_course_runs', blank=True, to='course_metadata.Organization')),
('staff', sortedm2m.fields.SortedManyToManyField(related_name='publisher_course_runs_staffed', help_text=None, blank=True, to='course_metadata.Person')),
('transcript_languages', models.ManyToManyField(related_name='publisher_transcript_course_runs', blank=True, to='ietf_language_tags.LanguageTag')),
],
options={
'ordering': ('-modified', '-created'),
'get_latest_by': 'modified',
'abstract': False,
},
bases=(models.Model, course_discovery.apps.publisher.models.ChangedByMixin),
),
migrations.CreateModel(
name='HistoricalCourse',
fields=[
('id', models.IntegerField(verbose_name='ID', auto_created=True, db_index=True, blank=True)),
('created', django_extensions.db.fields.CreationDateTimeField(verbose_name='created', auto_now_add=True)),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')),
('title', models.CharField(default=None, null=True, blank=True, max_length=255)),
('number', models.CharField(null=True, blank=True, max_length=50)),
('short_description', models.CharField(default=None, null=True, blank=True, max_length=255)),
('full_description', models.TextField(default=None, null=True, blank=True)),
('expected_learnings', models.TextField(default=None, null=True, blank=True)),
('syllabus', models.TextField(default=None, null=True, blank=True)),
('prerequisites', models.TextField(default=None, null=True, blank=True)),
('learner_testimonial', models.CharField(null=True, blank=True, max_length=50)),
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_date', models.DateTimeField()),
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
('history_user', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, null=True)),
('level_type', models.ForeignKey(related_name='+', db_constraint=False, to='course_metadata.LevelType', null=True, blank=True, on_delete=django.db.models.deletion.DO_NOTHING)),
('primary_subject', models.ForeignKey(related_name='+', db_constraint=False, to='course_metadata.Subject', null=True, blank=True, on_delete=django.db.models.deletion.DO_NOTHING)),
('secondary_subject', models.ForeignKey(related_name='+', db_constraint=False, to='course_metadata.Subject', null=True, blank=True, on_delete=django.db.models.deletion.DO_NOTHING)),
('tertiary_subject', models.ForeignKey(related_name='+', db_constraint=False, to='course_metadata.Subject', null=True, blank=True, on_delete=django.db.models.deletion.DO_NOTHING)),
],
options={
'ordering': ('-history_date', '-history_id'),
'verbose_name': 'historical course',
'get_latest_by': 'history_date',
},
),
migrations.CreateModel(
name='HistoricalCourseRun',
fields=[
('id', models.IntegerField(verbose_name='ID', auto_created=True, db_index=True, blank=True)),
('created', django_extensions.db.fields.CreationDateTimeField(verbose_name='created', auto_now_add=True)),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')),
('lms_course_id', models.CharField(null=True, blank=True, db_index=True, max_length=255)),
('start', models.DateTimeField(null=True, blank=True)),
('end', models.DateTimeField(null=True, blank=True)),
('enrollment_start', models.DateTimeField(null=True, blank=True)),
('enrollment_end', models.DateTimeField(null=True, blank=True)),
('certificate_generation', models.DateTimeField(null=True, blank=True)),
('pacing_type', models.CharField(choices=[('self_paced', 'Self-paced'), ('instructor_paced', 'Instructor-paced')], null=True, blank=True, db_index=True, max_length=255)),
('min_effort', models.PositiveSmallIntegerField(help_text='Estimated minimum number of hours per week needed to complete a course run.', null=True, blank=True)),
('max_effort', models.PositiveSmallIntegerField(help_text='Estimated maximum number of hours per week needed to complete a course run.', null=True, blank=True)),
('length', models.PositiveIntegerField(help_text='Length of course, in number of weeks', null=True, blank=True)),
('is_re_run', models.BooleanField(default=False)),
('is_xseries', models.BooleanField(default=False)),
('xseries_name', models.CharField(max_length=255)),
('is_micromasters', models.BooleanField(default=False)),
('micromasters_name', models.CharField(max_length=255)),
('contacted_partner_manager', models.BooleanField(default=False)),
('seo_review', models.TextField(help_text='SEO review on your course title and short description', default=None, null=True, blank=True)),
('keywords', models.TextField(help_text='Please add top 10 comma separated keywords for your course content', default=None, blank=True)),
('notes', models.TextField(help_text='Please add any additional notes or special instructions for the course About Page.', default=None, null=True, blank=True)),
('target_content', models.BooleanField(default=False)),
('priority', models.CharField(choices=[('L1', 'Level 1'), ('L2', 'Level 2'), ('L3', 'Level 3'), ('L4', 'Level 4'), ('L5', 'Level 5')], null=True, blank=True, max_length=5)),
('course_team_admins', models.TextField(help_text='Comma separated list of edX usernames or emails of admins.', default=None, null=True, blank=True)),
('course_team_additional_staff', models.TextField(help_text='Comma separated list of edX usernames or emails of additional staff.', default=None, null=True, blank=True)),
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_date', models.DateTimeField()),
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
('course', models.ForeignKey(related_name='+', db_constraint=False, to='publisher.Course', null=True, blank=True, on_delete=django.db.models.deletion.DO_NOTHING)),
('history_user', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, null=True)),
('language', models.ForeignKey(related_name='+', db_constraint=False, to='ietf_language_tags.LanguageTag', null=True, blank=True, on_delete=django.db.models.deletion.DO_NOTHING)),
],
options={
'ordering': ('-history_date', '-history_id'),
'verbose_name': 'historical course run',
'get_latest_by': 'history_date',
},
),
migrations.CreateModel(
name='HistoricalSeat',
fields=[
('id', models.IntegerField(verbose_name='ID', auto_created=True, db_index=True, blank=True)),
('created', django_extensions.db.fields.CreationDateTimeField(verbose_name='created', auto_now_add=True)),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')),
('type', models.CharField(choices=[('honor', 'Honor'), ('audit', 'Audit'), ('verified', 'Verified'), ('professional', 'Professional (with ID verification)'), ('no-id-professional', 'Professional (no ID verifiation)'), ('credit', 'Credit')], max_length=63)),
('price', models.DecimalField(decimal_places=2, default=0.0, max_digits=10)),
('upgrade_deadline', models.DateTimeField(null=True, blank=True)),
('credit_provider', models.CharField(null=True, blank=True, max_length=255)),
('credit_hours', models.IntegerField(null=True, blank=True)),
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_date', models.DateTimeField()),
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
('course_run', models.ForeignKey(related_name='+', db_constraint=False, to='publisher.CourseRun', null=True, blank=True, on_delete=django.db.models.deletion.DO_NOTHING)),
('currency', models.ForeignKey(related_name='+', db_constraint=False, to='core.Currency', null=True, blank=True, on_delete=django.db.models.deletion.DO_NOTHING)),
('history_user', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, null=True)),
],
options={
'ordering': ('-history_date', '-history_id'),
'verbose_name': 'historical seat',
'get_latest_by': 'history_date',
},
),
migrations.CreateModel(
name='Seat',
fields=[
('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)),
('created', django_extensions.db.fields.CreationDateTimeField(verbose_name='created', auto_now_add=True)),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')),
('type', models.CharField(choices=[('honor', 'Honor'), ('audit', 'Audit'), ('verified', 'Verified'), ('professional', 'Professional (with ID verification)'), ('no-id-professional', 'Professional (no ID verifiation)'), ('credit', 'Credit')], max_length=63)),
('price', models.DecimalField(decimal_places=2, default=0.0, max_digits=10)),
('upgrade_deadline', models.DateTimeField(null=True, blank=True)),
('credit_provider', models.CharField(null=True, blank=True, max_length=255)),
('credit_hours', models.IntegerField(null=True, blank=True)),
('course_run', models.ForeignKey(related_name='seats', to='publisher.CourseRun')),
('currency', models.ForeignKey(related_name='publisher_seats', to='core.Currency')),
],
options={
'ordering': ('-modified', '-created'),
'get_latest_by': 'modified',
'abstract': False,
},
bases=(models.Model, course_discovery.apps.publisher.models.ChangedByMixin),
),
]
import logging
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django_extensions.db.models import TimeStampedModel
from simple_history.models import HistoricalRecords
from sortedm2m.fields import SortedManyToManyField
from course_discovery.apps.core.models import User, Currency
from course_discovery.apps.course_metadata.models import LevelType, Subject, Person, Organization
from course_discovery.apps.course_metadata.models import CourseRun as CourseMetadataCourseRun
from course_discovery.apps.ietf_language_tags.models import LanguageTag
logger = logging.getLogger(__name__)
class ChangedByMixin(object):
changed_by = models.ForeignKey(User, null=True, blank=True)
class Course(TimeStampedModel, ChangedByMixin):
""" Publisher Course model. It contains fields related to the course intake form."""
title = models.CharField(max_length=255, default=None, null=True, blank=True)
number = models.CharField(max_length=50, null=True, blank=True)
short_description = models.CharField(max_length=255, default=None, null=True, blank=True)
full_description = models.TextField(default=None, null=True, blank=True)
organizations = models.ManyToManyField(Organization, blank=True, related_name='publisher_courses')
level_type = models.ForeignKey(LevelType, default=None, null=True, blank=True, related_name='publisher_courses')
expected_learnings = models.TextField(default=None, null=True, blank=True)
syllabus = models.TextField(default=None, null=True, blank=True)
prerequisites = models.TextField(default=None, null=True, blank=True)
learner_testimonial = models.CharField(max_length=50, null=True, blank=True)
primary_subject = models.ForeignKey(Subject, related_name='publisher_courses_primary')
secondary_subject = models.ForeignKey(Subject, related_name='publisher_courses_secondary')
tertiary_subject = models.ForeignKey(Subject, related_name='publisher_courses_tertiary')
history = HistoricalRecords()
def __str__(self):
return self.title
class CourseRun(TimeStampedModel, ChangedByMixin):
""" Publisher CourseRun model. It contains fields related to the course run intake form."""
PRIORITY_LEVEL_1 = 'L1'
PRIORITY_LEVEL_2 = 'L2'
PRIORITY_LEVEL_3 = 'L3'
PRIORITY_LEVEL_4 = 'L4'
PRIORITY_LEVEL_5 = 'L5'
PRIORITY_LEVELS = (
(PRIORITY_LEVEL_1, _('Level 1')),
(PRIORITY_LEVEL_2, _('Level 2')),
(PRIORITY_LEVEL_3, _('Level 3')),
(PRIORITY_LEVEL_4, _('Level 4')),
(PRIORITY_LEVEL_5, _('Level 5')),
)
course = models.ForeignKey(Course)
lms_course_id = models.CharField(max_length=255, unique=True, null=True, blank=True)
start = models.DateTimeField(null=True, blank=True)
end = models.DateTimeField(null=True, blank=True)
enrollment_start = models.DateTimeField(null=True, blank=True)
enrollment_end = models.DateTimeField(null=True, blank=True)
certificate_generation = models.DateTimeField(null=True, blank=True)
pacing_type = models.CharField(
max_length=255, choices=CourseMetadataCourseRun.PACING_CHOICES, db_index=True, null=True, blank=True
)
staff = SortedManyToManyField(Person, blank=True, related_name='publisher_course_runs_staffed')
min_effort = models.PositiveSmallIntegerField(
null=True, blank=True,
help_text=_('Estimated minimum number of hours per week needed to complete a course run.'))
max_effort = models.PositiveSmallIntegerField(
null=True, blank=True,
help_text=_('Estimated maximum number of hours per week needed to complete a course run.'))
language = models.ForeignKey(LanguageTag, null=True, blank=True, related_name='publisher_course_runs')
transcript_languages = models.ManyToManyField(
LanguageTag, blank=True, related_name='publisher_transcript_course_runs'
)
length = models.PositiveIntegerField(
null=True, blank=True, help_text=_("Length of course, in number of weeks")
)
sponsor = models.ManyToManyField(Organization, blank=True, related_name='publisher_course_runs')
is_re_run = models.BooleanField(default=False)
is_xseries = models.BooleanField(default=False)
xseries_name = models.CharField(max_length=255)
is_micromasters = models.BooleanField(default=False)
micromasters_name = models.CharField(max_length=255)
contacted_partner_manager = models.BooleanField(default=False)
seo_review = models.TextField(
default=None, null=True, blank=True, help_text=_("SEO review on your course title and short description")
)
keywords = models.TextField(
default=None, blank=True, help_text=_("Please add top 10 comma separated keywords for your course content")
)
notes = models.TextField(
default=None, null=True, blank=True, help_text=_(
"Please add any additional notes or special instructions for the course About Page."
)
)
target_content = models.BooleanField(default=False)
priority = models.CharField(
max_length=5, choices=PRIORITY_LEVELS, null=True, blank=True
)
course_team_admins = models.TextField(
default=None, blank=True, null=True, help_text=_("Comma separated list of edX usernames or emails of admins.")
)
course_team_additional_staff = models.TextField(
default=None, blank=True, null=True, help_text=_(
"Comma separated list of edX usernames or emails of additional staff."
)
)
history = HistoricalRecords()
def __str__(self):
return '{course}: {start_date}'.format(course=self.course.title, start_date=self.start)
class Seat(TimeStampedModel, ChangedByMixin):
""" Seat model. """
HONOR = 'honor'
AUDIT = 'audit'
VERIFIED = 'verified'
PROFESSIONAL = 'professional'
NO_ID_PROFESSIONAL = 'no-id-professional'
CREDIT = 'credit'
SEAT_TYPE_CHOICES = (
(HONOR, _('Honor')),
(AUDIT, _('Audit')),
(VERIFIED, _('Verified')),
(PROFESSIONAL, _('Professional (with ID verification)')),
(NO_ID_PROFESSIONAL, _('Professional (no ID verifiation)')),
(CREDIT, _('Credit')),
)
PRICE_FIELD_CONFIG = {
'decimal_places': 2,
'max_digits': 10,
'null': False,
'default': 0.00,
}
course_run = models.ForeignKey(CourseRun, related_name='seats')
type = models.CharField(max_length=63, choices=SEAT_TYPE_CHOICES)
price = models.DecimalField(**PRICE_FIELD_CONFIG)
currency = models.ForeignKey(Currency, related_name='publisher_seats')
upgrade_deadline = models.DateTimeField(null=True, blank=True)
credit_provider = models.CharField(max_length=255, null=True, blank=True)
credit_hours = models.IntegerField(null=True, blank=True)
history = HistoricalRecords()
def __str__(self):
return '{course}: {type}'.format(course=self.course_run.course.title, type=self.type)
from datetime import datetime
import factory
from factory.fuzzy import FuzzyText, FuzzyChoice, FuzzyDecimal, FuzzyDateTime, FuzzyInteger
from pytz import UTC
from course_discovery.apps.core.models import Currency
from course_discovery.apps.course_metadata.tests import factories
from course_discovery.apps.course_metadata.models import CourseRun as CourseMetadataCourseRun
from course_discovery.apps.ietf_language_tags.models import LanguageTag
from course_discovery.apps.publisher.models import Course, CourseRun, Seat
class CourseFactory(factory.DjangoModelFactory):
title = FuzzyText(prefix="Test çօմɾʂҽ ")
short_description = FuzzyText(prefix="Test çօմɾʂҽ short description")
full_description = FuzzyText(prefix="Test çօմɾʂҽ FULL description")
number = FuzzyText()
prerequisites = "prereq 1, prereq 2, prereq 3"
expected_learnings = "learning 1, learning 2, learning 3"
syllabus = "week 1: awesomeness"
learner_testimonial = "Best course ever!"
level_type = factory.SubFactory(factories.LevelTypeFactory)
primary_subject = factory.SubFactory(factories.SubjectFactory)
secondary_subject = factory.SubFactory(factories.SubjectFactory)
tertiary_subject = factory.SubFactory(factories.SubjectFactory)
class Meta:
model = Course
class CourseRunFactory(factory.DjangoModelFactory):
course = factory.SubFactory(CourseFactory)
start = FuzzyDateTime(datetime(2014, 1, 1, tzinfo=UTC))
end = FuzzyDateTime(datetime(2014, 1, 1, tzinfo=UTC)).end_dt
enrollment_start = FuzzyDateTime(datetime(2014, 1, 1, tzinfo=UTC))
enrollment_end = FuzzyDateTime(datetime(2014, 1, 1, tzinfo=UTC)).end_dt
certificate_generation = FuzzyDateTime(datetime(2014, 1, 1, tzinfo=UTC))
min_effort = FuzzyInteger(1, 10)
max_effort = FuzzyInteger(10, 20)
language = factory.Iterator(LanguageTag.objects.all())
pacing_type = FuzzyChoice([name for name, __ in CourseMetadataCourseRun.PACING_CHOICES])
length = FuzzyInteger(1, 10)
seo_review = "test-seo-review"
keywords = "Test1, Test2, Test3"
notes = "Testing notes"
class Meta:
model = CourseRun
class SeatFactory(factory.DjangoModelFactory):
type = FuzzyChoice([name for name, __ in Seat.SEAT_TYPE_CHOICES])
price = FuzzyDecimal(0.0, 650.0)
currency = factory.Iterator(Currency.objects.all())
upgrade_deadline = FuzzyDateTime(datetime(2014, 1, 1, tzinfo=UTC))
course_run = factory.SubFactory(CourseRunFactory)
class Meta:
model = Seat
# pylint: disable=no-member
from django.test import TestCase
from course_discovery.apps.publisher.tests import factories
class CourseRunTests(TestCase):
""" Tests for the publisher `CourseRun` model. """
def setUp(self):
super(CourseRunTests, self).setUp()
self.course_run = factories.CourseRunFactory()
def test_str(self):
""" Verify casting an instance to a string returns a string containing the course title and start date. """
self.assertEqual(
str(self.course_run),
'{title}: {date}'.format(
title=self.course_run.course.title, date=self.course_run.start
)
)
class CourseTests(TestCase):
""" Tests for the publisher `Course` model. """
def setUp(self):
super(CourseTests, self).setUp()
self.course = factories.CourseFactory()
def test_str(self):
""" Verify casting an instance to a string returns a string containing the course title. """
self.assertEqual(str(self.course), self.course.title)
class SeatTests(TestCase):
""" Tests for the publisher `Seat` model. """
def setUp(self):
super(SeatTests, self).setUp()
self.seat = factories.SeatFactory()
def test_str(self):
""" Verify casting an instance to a string returns a string containing the course title and seat type. """
self.assertEqual(
str(self.seat),
'{course}: {type}'.format(
course=self.seat.course_run.course.title, type=self.seat.type
)
)
......@@ -51,10 +51,10 @@ PROJECT_APPS = (
'course_discovery.apps.catalogs',
'course_discovery.apps.course_metadata',
'course_discovery.apps.edx_haystack_extensions',
'course_discovery.apps.publisher',
)
INSTALLED_APPS += THIRD_PARTY_APPS
INSTALLED_APPS += PROJECT_APPS
......
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