Commit 30719a27 by Waheed Ahmed

Implement the roles field in orgs table

ECOM-6216
parent 3a495a7d
from django.utils.translation import ugettext_lazy as _
from djchoices import DjangoChoices, ChoiceItem
class PublisherUserRole(DjangoChoices):
PartnerCoordinator = ChoiceItem('partner_coordinator', _('Partner Coordinator'))
MarketingReviewer = ChoiceItem('marketing_reviewer', _('Marketing Reviewer'))
Publisher = ChoiceItem('publisher', _('Publisher'))
CourseTeam = ChoiceItem('course_team', _('Course Team'))
# Name of the administrative group for the Publisher app # Name of the administrative group for the Publisher app
ADMIN_GROUP_NAME = 'Publisher Admins' ADMIN_GROUP_NAME = 'Publisher Admins'
# Permissions/Roles
COORDINATOR = 'partner_coordinator'
REVIEWER = 'marketing_reviewer'
PUBLISHER = 'publisher'
# -*- coding: utf-8 -*-
# Generated by Django 1.9.11 on 2016-12-01 15:01
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django_extensions.db.fields
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('publisher', '0016_auto_20161129_0910'),
]
operations = [
migrations.CreateModel(
name='CourseUserRole',
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')),
('role', models.CharField(choices=[('partner_coordinator', 'Partner Coordinator'), ('marketing_reviewer', 'Marketing Reviewer'), ('publisher', 'Publisher'), ('course_team', 'Course Team')], max_length=63, verbose_name='Course Role')),
('changed_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='course_user_roles', to='publisher.Course')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='course_user_roles', to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='HistoricalCourseUserRole',
fields=[
('id', models.IntegerField(auto_created=True, blank=True, db_index=True, 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')),
('role', models.CharField(choices=[('partner_coordinator', 'Partner Coordinator'), ('marketing_reviewer', 'Marketing Reviewer'), ('publisher', 'Publisher'), ('course_team', 'Course Team')], max_length=63, verbose_name='Course Role')),
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_date', models.DateTimeField()),
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
('changed_by', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)),
('course', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='publisher.Course')),
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
('user', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ('-history_date', '-history_id'),
'verbose_name': 'historical course user role',
'get_latest_by': 'history_date',
},
),
migrations.AlterField(
model_name='historicalorganizationuserrole',
name='role',
field=models.CharField(choices=[('partner_coordinator', 'Partner Coordinator'), ('marketing_reviewer', 'Marketing Reviewer'), ('publisher', 'Publisher'), ('course_team', 'Course Team')], max_length=63, verbose_name='Organization Role'),
),
migrations.AlterField(
model_name='organizationuserrole',
name='organization',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='organization_user_roles', to='course_metadata.Organization'),
),
migrations.AlterField(
model_name='organizationuserrole',
name='role',
field=models.CharField(choices=[('partner_coordinator', 'Partner Coordinator'), ('marketing_reviewer', 'Marketing Reviewer'), ('publisher', 'Publisher'), ('course_team', 'Course Team')], max_length=63, verbose_name='Organization Role'),
),
migrations.AlterUniqueTogether(
name='courseuserrole',
unique_together=set([('course', 'user', 'role')]),
),
]
...@@ -19,11 +19,10 @@ from course_discovery.apps.course_metadata.choices import CourseRunPacing ...@@ -19,11 +19,10 @@ from course_discovery.apps.course_metadata.choices import CourseRunPacing
from course_discovery.apps.course_metadata.models import LevelType, Subject, Person, Organization from course_discovery.apps.course_metadata.models import LevelType, Subject, Person, Organization
from course_discovery.apps.course_metadata.utils import UploadToFieldNamePath from course_discovery.apps.course_metadata.utils import UploadToFieldNamePath
from course_discovery.apps.ietf_language_tags.models import LanguageTag from course_discovery.apps.ietf_language_tags.models import LanguageTag
from course_discovery.apps.publisher.constants import COORDINATOR, REVIEWER, PUBLISHER from course_discovery.apps.publisher.choices import PublisherUserRole
from course_discovery.apps.publisher.emails import send_email_for_change_state from course_discovery.apps.publisher.emails import send_email_for_change_state
from course_discovery.apps.publisher.utils import is_email_notification_enabled from course_discovery.apps.publisher.utils import is_email_notification_enabled
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -365,15 +364,12 @@ class UserAttributes(TimeStampedModel): ...@@ -365,15 +364,12 @@ class UserAttributes(TimeStampedModel):
class OrganizationUserRole(TimeStampedModel): class OrganizationUserRole(TimeStampedModel):
""" User Roles model for Organization. """ """ User Roles model for Organization. """
ROLES_TYPE_CHOICES = (
(COORDINATOR, _('Partner Coordinator')),
(REVIEWER, _('Reviewer')),
(PUBLISHER, _('Publisher')),
)
organization = models.ForeignKey(Organization, related_name='user_roles') organization = models.ForeignKey(Organization, related_name='organization_user_roles')
user = models.ForeignKey(User, related_name='organization_user_roles') user = models.ForeignKey(User, related_name='organization_user_roles')
role = models.CharField(max_length=63, choices=ROLES_TYPE_CHOICES, verbose_name='Role Type') role = models.CharField(
max_length=63, choices=PublisherUserRole.choices, verbose_name=_('Organization Role')
)
history = HistoricalRecords() history = HistoricalRecords()
...@@ -388,3 +384,26 @@ class OrganizationUserRole(TimeStampedModel): ...@@ -388,3 +384,26 @@ class OrganizationUserRole(TimeStampedModel):
user=self.user, user=self.user,
role=self.role role=self.role
) )
class CourseUserRole(TimeStampedModel, ChangedByMixin):
""" User Course Roles model. """
course = models.ForeignKey(Course, related_name='course_user_roles')
user = models.ForeignKey(User, related_name='course_user_roles')
role = models.CharField(
max_length=63, choices=PublisherUserRole.choices, verbose_name=_('Course Role')
)
history = HistoricalRecords()
class Meta:
unique_together = (
('course', 'user', 'role'),
)
def __str__(self):
return '{course}: {user}: {role}'.format(
course=self.course,
user=self.user,
role=self.role
)
...@@ -11,8 +11,9 @@ from course_discovery.apps.core.tests.factories import UserFactory ...@@ -11,8 +11,9 @@ from course_discovery.apps.core.tests.factories import UserFactory
from course_discovery.apps.course_metadata.choices import CourseRunPacing from course_discovery.apps.course_metadata.choices import CourseRunPacing
from course_discovery.apps.course_metadata.tests import factories from course_discovery.apps.course_metadata.tests import factories
from course_discovery.apps.ietf_language_tags.models import LanguageTag from course_discovery.apps.ietf_language_tags.models import LanguageTag
from course_discovery.apps.publisher.choices import PublisherUserRole
from course_discovery.apps.publisher.models import ( from course_discovery.apps.publisher.models import (
Course, CourseRun, OrganizationUserRole, Seat, State, UserAttributes Course, CourseRun, CourseUserRole, OrganizationUserRole, Seat, State, UserAttributes
) )
...@@ -91,7 +92,16 @@ class UserAttributeFactory(factory.DjangoModelFactory): ...@@ -91,7 +92,16 @@ class UserAttributeFactory(factory.DjangoModelFactory):
class OrganizationUserRoleFactory(factory.DjangoModelFactory): class OrganizationUserRoleFactory(factory.DjangoModelFactory):
organization = factory.SubFactory(factories.OrganizationFactory) organization = factory.SubFactory(factories.OrganizationFactory)
user = factory.SubFactory(UserFactory) user = factory.SubFactory(UserFactory)
role = FuzzyChoice([name for name, __ in OrganizationUserRole.ROLES_TYPE_CHOICES]) role = FuzzyChoice(PublisherUserRole.values.keys())
class Meta: class Meta:
model = OrganizationUserRole model = OrganizationUserRole
class CourseUserRoleFactory(factory.DjangoModelFactory):
course = factory.SubFactory(CourseFactory)
user = factory.SubFactory(UserFactory)
role = FuzzyChoice(PublisherUserRole.values.keys())
class Meta:
model = CourseUserRole
...@@ -6,9 +6,8 @@ from django.test import TestCase ...@@ -6,9 +6,8 @@ from django.test import TestCase
from django_fsm import TransitionNotAllowed from django_fsm import TransitionNotAllowed
from course_discovery.apps.core.tests.factories import UserFactory from course_discovery.apps.core.tests.factories import UserFactory
from course_discovery.apps.course_metadata.tests.factories import OrganizationFactory from course_discovery.apps.publisher.choices import PublisherUserRole
from course_discovery.apps.publisher.constants import COORDINATOR from course_discovery.apps.publisher.models import State, Course, CourseUserRole, OrganizationUserRole
from course_discovery.apps.publisher.models import State, Course
from course_discovery.apps.publisher.tests import factories from course_discovery.apps.publisher.tests import factories
...@@ -199,15 +198,10 @@ class OrganizationUserRoleTests(TestCase): ...@@ -199,15 +198,10 @@ class OrganizationUserRoleTests(TestCase):
def setUp(self): def setUp(self):
super(OrganizationUserRoleTests, self).setUp() super(OrganizationUserRoleTests, self).setUp()
self.user = UserFactory() self.org_user_role = factories.OrganizationUserRoleFactory(role=PublisherUserRole.PartnerCoordinator)
self.organization = OrganizationFactory()
self.role = COORDINATOR
self.org_user_role = factories.OrganizationUserRoleFactory(
user=self.user, organization=self.organization, role=COORDINATOR
)
def test_str(self): def test_str(self):
"""Verify that a organization-user-role is properly converted to a str.""" """Verify that a OrganizationUserRole is properly converted to a str."""
self.assertEqual( self.assertEqual(
str(self.org_user_role), '{organization}: {user}: {role}'.format( str(self.org_user_role), '{organization}: {user}: {role}'.format(
organization=self.org_user_role.organization, organization=self.org_user_role.organization,
...@@ -217,8 +211,32 @@ class OrganizationUserRoleTests(TestCase): ...@@ -217,8 +211,32 @@ class OrganizationUserRoleTests(TestCase):
) )
def test_unique_constraint(self): def test_unique_constraint(self):
"""Verify that a organization-user-role not allow same user roles under one organization.""" """ Verify a user cannot have multiple rows for the same organization-role combination. """
with self.assertRaises(IntegrityError):
OrganizationUserRole.objects.create(
user=self.org_user_role.user,
organization=self.org_user_role.organization,
role=self.org_user_role.role
)
class CourseUserRoleTests(TestCase):
"""Tests of the CourseUserRole model."""
def setUp(self):
super(CourseUserRoleTests, self).setUp()
self.course_user_role = factories.CourseUserRoleFactory(role=PublisherUserRole.PartnerCoordinator)
def test_str(self):
"""Verify that a CourseUserRole is properly converted to a str."""
expected_str = '{course}: {user}: {role}'.format(
course=self.course_user_role.course, user=self.course_user_role.user, role=self.course_user_role.role
)
self.assertEqual(str(self.course_user_role), expected_str)
def test_unique_constraint(self):
""" Verify a user cannot have multiple rows for the same course-role combination."""
with self.assertRaises(IntegrityError): with self.assertRaises(IntegrityError):
factories.OrganizationUserRoleFactory( CourseUserRole.objects.create(
user=self.user, organization=self.organization, role=COORDINATOR course=self.course_user_role.course, user=self.course_user_role.user, role=self.course_user_role.role
) )
...@@ -7,14 +7,14 @@ msgid "" ...@@ -7,14 +7,14 @@ 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: 2016-11-29 15:24+0500\n" "POT-Creation-Date: 2016-12-01 20:12+0500\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"
"Language: \n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: \n"
#: apps/api/filters.py #: apps/api/filters.py
#, python-brace-format #, python-brace-format
...@@ -409,6 +409,22 @@ msgstr "" ...@@ -409,6 +409,22 @@ msgstr ""
msgid "JSON string containing an elasticsearch function score config." msgid "JSON string containing an elasticsearch function score config."
msgstr "" msgstr ""
#: apps/publisher/choices.py
msgid "Partner Coordinator"
msgstr ""
#: apps/publisher/choices.py
msgid "Marketing Reviewer"
msgstr ""
#: apps/publisher/choices.py
msgid "Publisher"
msgstr ""
#: apps/publisher/choices.py
msgid "Course Team"
msgstr ""
#: apps/publisher/emails.py #: apps/publisher/emails.py
#, python-brace-format #, python-brace-format
msgid "Course Run {title}-{pacing_type}-{start} state has been changed." msgid "Course Run {title}-{pacing_type}-{start} state has been changed."
...@@ -560,15 +576,11 @@ msgid "Professional (no ID verifiation)" ...@@ -560,15 +576,11 @@ msgid "Professional (no ID verifiation)"
msgstr "" msgstr ""
#: apps/publisher/models.py #: apps/publisher/models.py
msgid "Partner Coordinator" msgid "Organization Role"
msgstr ""
#: apps/publisher/models.py
msgid "Reviewer"
msgstr "" msgstr ""
#: apps/publisher/models.py #: apps/publisher/models.py
msgid "Publisher" msgid "Course Role"
msgstr "" msgstr ""
#: apps/publisher/views.py #: apps/publisher/views.py
......
...@@ -7,14 +7,14 @@ msgid "" ...@@ -7,14 +7,14 @@ 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: 2016-11-29 15:24+0500\n" "POT-Creation-Date: 2016-12-01 20:12+0500\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"
"Language: \n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: \n"
#: static/js/catalogs-change-form.js #: static/js/catalogs-change-form.js
msgid "Preview" msgid "Preview"
......
...@@ -7,14 +7,14 @@ msgid "" ...@@ -7,14 +7,14 @@ 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: 2016-11-29 15:24+0500\n" "POT-Creation-Date: 2016-12-01 20:12+0500\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"
"Language: \n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: \n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: apps/api/filters.py #: apps/api/filters.py
...@@ -513,6 +513,22 @@ msgstr "" ...@@ -513,6 +513,22 @@ msgstr ""
"JSÖN strïng çöntäïnïng än élästïçséärçh fünçtïön sçöré çönfïg. Ⱡ'σяєм ιρѕυм " "JSÖN strïng çöntäïnïng än élästïçséärçh fünçtïön sçöré çönfïg. Ⱡ'σяєм ιρѕυм "
"∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α#" "∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α#"
#: apps/publisher/choices.py
msgid "Partner Coordinator"
msgstr "Pärtnér Çöördïnätör Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт,#"
#: apps/publisher/choices.py
msgid "Marketing Reviewer"
msgstr "Märkétïng Révïéwér Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт#"
#: apps/publisher/choices.py
msgid "Publisher"
msgstr "Püßlïshér Ⱡ'σяєм ιρѕυм ∂σł#"
#: apps/publisher/choices.py
msgid "Course Team"
msgstr "Çöürsé Téäm Ⱡ'σяєм ιρѕυм ∂σłσя #"
#: apps/publisher/emails.py #: apps/publisher/emails.py
#, python-brace-format #, python-brace-format
msgid "Course Run {title}-{pacing_type}-{start} state has been changed." msgid "Course Run {title}-{pacing_type}-{start} state has been changed."
...@@ -680,16 +696,12 @@ msgstr "" ...@@ -680,16 +696,12 @@ msgstr ""
"Pröféssïönäl (nö ÌD vérïfïätïön) Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тє#" "Pröféssïönäl (nö ÌD vérïfïätïön) Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тє#"
#: apps/publisher/models.py #: apps/publisher/models.py
msgid "Partner Coordinator" msgid "Organization Role"
msgstr "Pärtnér Çöördïnätör Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт,#" msgstr "Örgänïzätïön Rölé Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмє#"
#: apps/publisher/models.py
msgid "Reviewer"
msgstr "Révïéwér Ⱡ'σяєм ιρѕυм ∂#"
#: apps/publisher/models.py #: apps/publisher/models.py
msgid "Publisher" msgid "Course Role"
msgstr "Püßlïshér Ⱡ'σяєм ιρѕυм ∂σł#" msgstr "Çöürsé Rölé Ⱡ'σяєм ιρѕυм ∂σłσя #"
#: apps/publisher/views.py #: apps/publisher/views.py
msgid "Course created successfully." msgid "Course created successfully."
......
...@@ -7,14 +7,14 @@ msgid "" ...@@ -7,14 +7,14 @@ 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: 2016-11-29 15:24+0500\n" "POT-Creation-Date: 2016-12-01 20:12+0500\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"
"Language: \n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: \n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: static/js/catalogs-change-form.js #: static/js/catalogs-change-form.js
......
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