Commit 952062f9 by Awais Committed by Awais Qureshi

Managment command for importing courses into publisher app.

ECOM-7809
parent 6e6f8741
......@@ -2,10 +2,10 @@ from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import Group
from guardian.admin import GuardedModelAdmin
from guardian.shortcuts import assign_perm
from course_discovery.apps.publisher.assign_permissions import assign_permissions
from course_discovery.apps.publisher.choices import InternalUserRole
from course_discovery.apps.publisher.constants import (LEGAL_TEAM_GROUP_NAME, PARTNER_MANAGER_GROUP_NAME,
from course_discovery.apps.publisher.constants import (PARTNER_MANAGER_GROUP_NAME,
PROJECT_COORDINATOR_GROUP_NAME, PUBLISHER_GROUP_NAME,
REVIEWER_GROUP_NAME)
from course_discovery.apps.publisher.forms import (CourseRunAdminForm, CourseUserRoleForm, OrganizationUserRoleForm,
......@@ -28,42 +28,7 @@ class OrganizationExtensionAdmin(GuardedModelAdmin):
def save_model(self, request, obj, form, change):
obj.save()
# Assign EDIT/VIEW permissions to organization group.
course_team_permissions = [
OrganizationExtension.VIEW_COURSE,
OrganizationExtension.EDIT_COURSE,
OrganizationExtension.VIEW_COURSE_RUN,
OrganizationExtension.EDIT_COURSE_RUN
]
self.assign_permissions(obj, obj.group, course_team_permissions)
# Assign EDIT_COURSE permission to Marketing Reviewers group.
marketing_permissions = [
OrganizationExtension.EDIT_COURSE,
OrganizationExtension.VIEW_COURSE,
OrganizationExtension.VIEW_COURSE_RUN
]
self.assign_permissions(obj, Group.objects.get(name=REVIEWER_GROUP_NAME), marketing_permissions)
# Assign EDIT_COURSE_RUN permission to Project Coordinators group.
pc_permissions = [
OrganizationExtension.VIEW_COURSE,
OrganizationExtension.EDIT_COURSE_RUN,
OrganizationExtension.VIEW_COURSE_RUN
]
self.assign_permissions(obj, Group.objects.get(name=PROJECT_COORDINATOR_GROUP_NAME), pc_permissions)
# Assign view permissions to Legal Team group.
legal_team_permissions = [
OrganizationExtension.VIEW_COURSE,
OrganizationExtension.VIEW_COURSE_RUN
]
self.assign_permissions(obj, Group.objects.get(name=LEGAL_TEAM_GROUP_NAME), legal_team_permissions)
def assign_permissions(self, obj, group, permissions):
for permission in permissions:
assign_perm(permission, group, obj)
assign_permissions(obj)
@admin.register(UserAttributes)
......
from django.contrib.auth.models import Group
from guardian.shortcuts import assign_perm
from course_discovery.apps.publisher.constants import (LEGAL_TEAM_GROUP_NAME, PROJECT_COORDINATOR_GROUP_NAME,
REVIEWER_GROUP_NAME)
from course_discovery.apps.publisher.models import OrganizationExtension
def assign_permissions(organization_extension):
# Assign EDIT/VIEW permissions to organization group.
course_team_permissions = [
OrganizationExtension.VIEW_COURSE,
OrganizationExtension.EDIT_COURSE,
OrganizationExtension.VIEW_COURSE_RUN,
OrganizationExtension.EDIT_COURSE_RUN
]
assign_permissions_to_group(organization_extension, organization_extension.group, course_team_permissions)
# Assign EDIT_COURSE permission to Marketing Reviewers group.
marketing_permissions = [
OrganizationExtension.EDIT_COURSE,
OrganizationExtension.VIEW_COURSE,
OrganizationExtension.VIEW_COURSE_RUN
]
assign_permissions_to_group(organization_extension, Group.objects.get(name=REVIEWER_GROUP_NAME),
marketing_permissions)
# Assign EDIT_COURSE_RUN permission to Project Coordinators group.
pc_permissions = [
OrganizationExtension.VIEW_COURSE,
OrganizationExtension.EDIT_COURSE_RUN,
OrganizationExtension.VIEW_COURSE_RUN
]
assign_permissions_to_group(organization_extension, Group.objects.get(name=PROJECT_COORDINATOR_GROUP_NAME),
pc_permissions)
# Assign view permissions to Legal Team group.
legal_team_permissions = [
OrganizationExtension.VIEW_COURSE,
OrganizationExtension.VIEW_COURSE_RUN
]
assign_permissions_to_group(organization_extension, Group.objects.get(name=LEGAL_TEAM_GROUP_NAME),
legal_team_permissions)
def assign_permissions_to_group(organization_extension, group, permissions):
for permission in permissions:
assign_perm(permission, group, organization_extension)
import logging
from django.contrib.auth.models import Group
from course_discovery.apps.publisher.assign_permissions import assign_permissions
from course_discovery.apps.publisher.choices import CourseRunStateChoices, CourseStateChoices, PublisherUserRole
from course_discovery.apps.publisher.models import (Course, CourseRun, CourseRunState, CourseState, CourseUserRole,
OrganizationExtension, Seat)
logger = logging.getLogger(__name__)
def process_course(meta_data_course):
""" Create or update the course."""
# if course has more than 1 organization don't import that course. Just log the entry.
# that course will import manually.
organizations = meta_data_course.authoring_organizations.all()
try:
available_organization = organizations_requirements(organizations, meta_data_course)
if not available_organization:
return
create_or_update_course(meta_data_course, available_organization)
except: # pylint: disable=bare-except
logger.error('Exception appear for course-id [%s].', meta_data_course.uuid)
def create_or_update_course(meta_data_course, available_organization):
defaults = {
'title': meta_data_course.title, 'number': meta_data_course.number,
'short_description': meta_data_course.short_description,
'full_description': meta_data_course.full_description,
'level_type': meta_data_course.level_type
}
publisher_course, created = Course.objects.update_or_create(
course_metadata_pk=meta_data_course.id,
defaults=defaults
)
for i, subject in enumerate(meta_data_course.subjects.all()):
if i == 0:
publisher_course.primary_subject = subject
elif i == 1:
publisher_course.secondary_subject = subject
elif i == 2:
publisher_course.tertiary_subject = subject
publisher_course.save()
if created:
if available_organization:
publisher_course.organizations.add(available_organization)
# assign course-user-roles
assign_course_user_roles(publisher_course, available_organization)
# marked course as approved with related fields.
state, created = CourseState.objects.get_or_create(course=publisher_course)
if created:
state.approved_by_role = PublisherUserRole.MarketingReviewer
state.owner_role = PublisherUserRole.MarketingReviewer
state.marketing_reviewed = True
state.name = CourseStateChoices.Approved
state.save()
logger.info('Import course with id [%s], number [%s].', publisher_course.id, publisher_course.number)
# create canonical course-run against the course.
create_course_runs(meta_data_course, publisher_course)
def create_course_runs(meta_data_course, publisher_course):
# create or update canonical course-run for the course.
canonical_course_run = meta_data_course.canonical_course_run
if canonical_course_run and canonical_course_run.key:
defaults = {
'course': publisher_course,
'start': canonical_course_run.start, 'end': canonical_course_run.end,
'enrollment_start': canonical_course_run.enrollment_start,
'enrollment_end': canonical_course_run.enrollment_end,
'min_effort': canonical_course_run.min_effort, 'max_effort': canonical_course_run.max_effort,
'language': canonical_course_run.language, 'pacing_type': canonical_course_run.pacing_type,
'length': canonical_course_run.weeks_to_complete,
'card_image_url': canonical_course_run.card_image_url,
'lms_course_id': canonical_course_run.key
}
publisher_course_run, created = CourseRun.objects.update_or_create(
lms_course_id=canonical_course_run.key, defaults=defaults
)
# add many to many fields.
publisher_course_run.transcript_languages.add(*canonical_course_run.transcript_languages.all())
publisher_course_run.staff.add(*canonical_course_run.staff.all())
publisher_course_run.language = canonical_course_run.language
# Initialize workflow for Course-run.
if created:
state, created = CourseRunState.objects.get_or_create(course_run=publisher_course_run)
if created:
state.approved_by_role = PublisherUserRole.ProjectCoordinator
state.owner_role = PublisherUserRole.Publisher
state.preview_accepted = True
state.name = CourseRunStateChoices.Published
state.save()
logger.info(
'Import course-run with id [%s], lms_course_id [%s].',
publisher_course_run.id, publisher_course_run.lms_course_id
)
# create seat against the course-run.
create_seats(canonical_course_run, publisher_course_run)
else:
logger.warning(
'Canonical course-run not found for metadata course [%s].', meta_data_course.uuid
)
def create_seats(metadata_course_run, publisher_course_run):
# create or update canonical course-run related seats..
seats = metadata_course_run.seats.all()
if seats:
for metadata_seat in seats:
defaults = {
'price': metadata_seat.price, 'currency': metadata_seat.currency,
'credit_provider': metadata_seat.credit_provider, 'credit_hours': metadata_seat.credit_hours,
'upgrade_deadline': metadata_seat.upgrade_deadline
}
seat, created = Seat.objects.update_or_create(
course_run=publisher_course_run,
type=metadata_seat.type,
defaults=defaults
)
if created:
logger.info(
'Import seat with id [%s], type [%s].',
seat.id, metadata_course_run.type
)
else:
logger.warning(
'No seats found for course-run [%s].', metadata_course_run.uuid
)
def assign_course_user_roles(course, organization):
# Assign the course user roles against each organization users.
for user_role in organization.organization_user_roles.all():
CourseUserRole.add_course_roles(course, user_role.role, user_role.user)
def organizations_requirements(organizations, meta_data_course):
""" Before adding course make sure organization exists and has OrganizationExtension
object also.
"""
if organizations.count() > 1:
logger.warning(
'Course has more than 1 organization. Course uuid is [%s].', meta_data_course.uuid
)
return None
available_organization = organizations.first()
if not available_organization:
logger.warning(
'Course has no organization. Course uuid is [%s].', meta_data_course.uuid
)
return None
if available_organization.key.strip() == "":
logger.warning(
'Organization key has empty value [%s].', available_organization.uuid
)
return None
group, __ = Group.objects.get_or_create(
name__iexact=available_organization.key, defaults={'name': available_organization.key}
)
organization_extension = OrganizationExtension.objects.filter(organization=available_organization)
if not organization_extension.exists():
organization_extension, __ = OrganizationExtension.objects.get_or_create(
organization=available_organization,
group=group
)
else:
organization_extension = organization_extension.first()
assign_permissions(organization_extension)
return organization_extension.organization
import logging
from django.core.management import BaseCommand
from course_discovery.apps.course_metadata.models import Course
from course_discovery.apps.publisher.dataloader.create_courses import process_course
logger = logging.getLogger(__name__)
class Command(BaseCommand):
help = 'Import courses into publisher app.'
def add_arguments(self, parser):
parser.add_argument(
'--start_id',
action='store',
dest='start_id',
default=None,
required=True,
help='The Primary key value starting id to import the courses.'
)
parser.add_argument(
'--end_id',
action='store',
dest='end_id',
default=None,
required=True,
help='To this id courses will be imported.'
)
def handle(self, *args, **options):
""" Import the course according to the given range. But in prod for multiple runs there are multiple
courses. During import just pick the latest course and do not import the old ones.
"""
start_id = options.get('start_id')
end_id = options.get('end_id')
for course in Course.objects.filter(id__range=(start_id, end_id)):
process_course(course)
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-05-24 19:09
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('publisher', '0049_auto_20170518_1017'),
]
operations = [
migrations.AddField(
model_name='courserun',
name='card_image_url',
field=models.URLField(blank=True, null=True, verbose_name='canonical course run image'),
),
migrations.AddField(
model_name='historicalcourserun',
name='card_image_url',
field=models.URLField(blank=True, null=True, verbose_name='canonical course run image'),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-05-25 10:49
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('publisher', '0050_auto_20170524_1909'),
]
operations = [
migrations.AddField(
model_name='course',
name='course_metadata_pk',
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='Course Metadata Course PK'),
),
migrations.AddField(
model_name='historicalcourse',
name='course_metadata_pk',
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='Course Metadata Course PK'),
),
]
......@@ -89,6 +89,9 @@ class Course(TimeStampedModel, ChangedByMixin):
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'))
# temp fields for data migrations only.
course_metadata_pk = models.PositiveIntegerField(null=True, blank=True, verbose_name=_('Course Metadata Course PK'))
history = HistoricalRecords()
def __str__(self):
......@@ -261,6 +264,10 @@ class CourseRun(TimeStampedModel, ChangedByMixin):
video_language = models.ForeignKey(LanguageTag, null=True, blank=True, related_name='video_language')
preview_url = models.URLField(null=True, blank=True)
# temporary field to save the canonical course run image. In 2nd script this url field
# will be used to download the image and save into course model --> course image.
card_image_url = models.URLField(null=True, blank=True, verbose_name='canonical course run image')
history = HistoricalRecords()
def __str__(self):
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-05-22 17:08+0500\n"
"POT-Creation-Date: 2017-05-25 17:32+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -765,6 +765,10 @@ msgid "Video Link"
msgstr ""
#: apps/publisher/models.py
msgid "Course Metadata Course PK"
msgstr ""
#: apps/publisher/models.py
msgid "Level 1"
msgstr ""
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-05-22 17:08+0500\n"
"POT-Creation-Date: 2017-05-25 17:32+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-05-22 17:08+0500\n"
"POT-Creation-Date: 2017-05-25 17:32+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -899,6 +899,10 @@ msgid "Video Link"
msgstr "Vïdéö Lïnk Ⱡ'σяєм ιρѕυм ∂σłσ#"
#: apps/publisher/models.py
msgid "Course Metadata Course PK"
msgstr "Çöürsé Métädätä Çöürsé PK Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕ#"
#: apps/publisher/models.py
msgid "Level 1"
msgstr "Lévél 1 Ⱡ'σяєм ιρѕυм #"
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-05-22 17:08+0500\n"
"POT-Creation-Date: 2017-05-25 17:32+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\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