Commit 4fb3e077 by Awais Committed by Awais Qureshi

Implement the edit course run page.

Add new tests.
Remove tests not require any more.

ECOM-6067
parent a025c2cf
......@@ -134,6 +134,7 @@ class CustomCourseForm(CourseForm):
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
edit_mode = kwargs.pop('edit_mode', None)
organization = kwargs.pop('organization', None)
if organization:
org_extension = OrganizationExtension.objects.get(organization=organization)
......@@ -146,6 +147,11 @@ class CustomCourseForm(CourseForm):
)
super(CustomCourseForm, self).__init__(*args, **kwargs)
if edit_mode:
self.fields['title'].widget = forms.HiddenInput()
self.fields['number'].widget = forms.HiddenInput()
self.fields['team_admin'].widget = forms.HiddenInput()
self.fields['organization'].widget = forms.HiddenInput()
class UpdateCourseForm(BaseCourseForm):
......
""" Tests publisher.utils"""
import ddt
from django.contrib.auth.models import Group
from django.core.urlresolvers import reverse
from django.test import TestCase, RequestFactory
from mock import Mock
......@@ -17,10 +20,12 @@ from course_discovery.apps.publisher.models import OrganizationExtension
from course_discovery.apps.publisher.tests import factories
from course_discovery.apps.publisher.utils import (
is_email_notification_enabled, is_publisher_admin, is_internal_user,
get_internal_users, is_partner_coordinator_user, is_publisher_user
get_internal_users, is_partner_coordinator_user, is_publisher_user,
make_bread_crumbs
)
@ddt.ddt
class PublisherUtilsTests(TestCase):
""" Tests for the publisher utils. """
......@@ -193,3 +198,11 @@ class PublisherUtilsTests(TestCase):
decorated_func(request, self.user)
self.assertTrue(func.called)
def test_make_bread_crumbs(self):
""" Verify the function parse the list of tuple and returns list of dicts."""
links = [(reverse('publisher:publisher_courses_new'), 'Courses'), (None, 'Testing')]
self.assertEqual(
[{'url': '/publisher/courses/new/', 'slug': 'Courses'}, {'url': None, 'slug': 'Testing'}],
make_bread_crumbs(links)
)
......@@ -76,3 +76,21 @@ def is_publisher_user(user):
bool: True, if user is an publisher user; otherwise, False.
"""
return user.groups.exists()
def make_bread_crumbs(links):
""" Returns lists of dicts containing bread-crumbs url and slug.
Arguments:
links (list): List of tuple contains links and slug.
Returns:
list: list containing dicts [{'url':'/courses/', 'slug':'test'}].
"""
return [
{
"url": url,
"slug": slug,
}
for url, slug in links
]
......@@ -20,7 +20,8 @@ from course_discovery.apps.core.models import User
from course_discovery.apps.publisher.choices import PublisherUserRole
from course_discovery.apps.publisher.emails import send_email_for_course_creation
from course_discovery.apps.publisher.forms import (
SeatForm, CustomCourseForm, CustomCourseRunForm, CustomSeatForm, UpdateCourseForm
SeatForm, CustomCourseForm, CustomCourseRunForm,
CustomSeatForm, UpdateCourseForm
)
from course_discovery.apps.publisher import mixins
from course_discovery.apps.publisher.models import (
......@@ -28,7 +29,8 @@ from course_discovery.apps.publisher.models import (
OrganizationExtension, CourseUserRole)
from course_discovery.apps.publisher.utils import (
is_internal_user, get_internal_users, is_publisher_admin,
is_partner_coordinator_user
is_partner_coordinator_user,
make_bread_crumbs
)
from course_discovery.apps.publisher.wrappers import CourseRunWrapper
......@@ -151,21 +153,18 @@ class CourseRunDetailView(mixins.LoginRequiredMixin, mixins.PublisherPermissionM
context['role_widgets'] = self.get_role_widgets_data(course_roles)
context['user_list'] = get_internal_users()
context['breadcrumbs'] = [
{
'url': reverse('publisher:publisher_courses'), 'slug': 'Courses'
},
{
'url': reverse('publisher:publisher_course_detail', kwargs={'pk': course_run.course.id}),
'slug': course_run.course.title
},
{
'url': None,
'slug': '{type}: {start}'.format(
context['breadcrumbs'] = make_bread_crumbs(
[
(reverse('publisher:publisher_courses'), 'Courses'),
(
reverse('publisher:publisher_course_detail', kwargs={'pk': course_run.course.id}),
course_run.course.title
),
(None, '{type}: {start}'.format(
type=course_run.get_pacing_type_display(), start=course_run.start.strftime("%B %d, %Y")
)
}
]
))
]
)
context['can_view_all_tabs'] = mixins.check_roles_access(self.request.user)
context['publisher_hide_features_for_pilot'] = waffle.switch_is_active('publisher_hide_features_for_pilot')
......@@ -180,7 +179,7 @@ class CreateCourseView(mixins.LoginRequiredMixin, mixins.PublisherUserRequiredMi
course_form = CustomCourseForm
run_form = CustomCourseRunForm
seat_form = CustomSeatForm
template_name = 'publisher/add_course_form.html'
template_name = 'publisher/add_update_course_form.html'
success_url = 'publisher:publisher_course_run_detail'
def get_success_url(self, course_id): # pylint: disable=arguments-differ
......@@ -348,15 +347,12 @@ class CourseDetailView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMixi
self.request.user, self.object, OrganizationExtension.EDIT_COURSE
)
context['breadcrumbs'] = [
{
'url': reverse('publisher:publisher_courses'), 'slug': 'Courses'
},
{
'url': None,
'slug': self.object.title
}
]
context['breadcrumbs'] = make_bread_crumbs(
[
(reverse('publisher:publisher_courses'), 'Courses'),
(None, self.object.title),
]
)
return context
......@@ -432,26 +428,122 @@ class CreateCourseRunView(mixins.LoginRequiredMixin, CreateView):
return render(request, self.template_name, context, status=400)
class CourseRunEditView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMixin, mixins.FormValidMixin, UpdateView):
class CourseRunEditView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMixin, UpdateView):
""" Course Run Edit View."""
model = CourseRun
course_form = CustomCourseForm
run_form = CustomCourseRunForm
seat_form = CustomSeatForm
template_name = 'publisher/add_update_course_form.html'
success_url = 'publisher:publisher_course_run_detail'
form_class = CustomCourseRunForm
permission = OrganizationExtension.EDIT_COURSE_RUN
template_name = 'publisher/course_run_form.html'
success_url = 'publisher:publisher_course_runs_edit'
change_state = True
def get_context_data(self, **kwargs):
context = super(CourseRunEditView, self).get_context_data(**kwargs)
if not self.object:
self.object = self.get_object()
context['workflow_state'] = self.object.current_state
context['comment_object'] = self.object
return context
def get_success_url(self):
def get_success_url(self): # pylint: disable=arguments-differ
return reverse(self.success_url, kwargs={'pk': self.object.id})
def get_context_data(self):
course_run = self.get_object()
team_admin_name = course_run.course.course_team_admin
organization = course_run.course.organizations.first()
initial = {
'organization': organization,
'team_admin': team_admin_name,
}
return {
'initial': initial,
'course_run': self.get_object(),
'team_admin_name': team_admin_name.get_full_name(),
'organization_name': organization.name,
'organization': organization,
'publisher_hide_features_for_pilot': waffle.switch_is_active('publisher_hide_features_for_pilot'),
'edit_mode': True,
}
def get(self, request, *args, **kwargs):
context = self.get_context_data()
course_run = context.get('course_run')
course = course_run.course
context['course_form'] = self.course_form(
instance=course,
initial=context.get('initial'),
organization=context.get('organization'),
edit_mode=True
)
context['run_form'] = self.run_form(instance=course_run)
context['seat_form'] = self.seat_form(instance=course_run.seats.first())
context['breadcrumbs'] = make_bread_crumbs(
[
(reverse('publisher:publisher_courses'), 'Courses'),
(reverse('publisher:publisher_course_detail', kwargs={'pk': course.id}), course.title),
(None, '{type}: {start}'.format(
type=course_run.get_pacing_type_display(), start=course_run.start.strftime("%B %d, %Y")
))
]
)
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
user = request.user
context = self.get_context_data()
course_run = context.get('course_run')
course_form = self.course_form(
request.POST, request.FILES,
instance=course_run.course,
initial=context.get('initial'),
organization=context.get('organization'),
edit_mode=True
)
run_form = self.run_form(request.POST, instance=course_run)
seat_form = self.seat_form(request.POST, instance=course_run.seats.first())
if course_form.is_valid() and run_form.is_valid() and seat_form.is_valid():
try:
with transaction.atomic():
course = course_form.save()
course.changed_by = self.request.user
course.save()
course_run = run_form.save()
course_run.changed_by = self.request.user
course_run.save()
run_form.save_m2m()
# If price-type comes with request then save the seat object.
if request.POST.get('type'):
seat_form.save(changed_by=user, course_run=course_run)
# in case of any updating move the course-run state to draft.
if course_run.state.name != State.DRAFT:
course_run.change_state(user=user)
# pylint: disable=no-member
messages.success(request, _('Course run updated successfully.'))
return HttpResponseRedirect(reverse(self.success_url, kwargs={'pk': course_run.id}))
except Exception as e: # pylint: disable=broad-except
# pylint: disable=no-member
error_message = _('An error occurred while saving your changes. {error}').format(error=str(e))
messages.error(request, error_message)
logger.exception('Unable to update course run and seat for course [%s].', course_run.id)
if not messages.get_messages(request):
messages.error(request, _('Please fill all required fields.'))
context.update(
{
'course_form': course_form,
'run_form': run_form,
'seat_form': seat_form
}
)
return render(request, self.template_name, context, status=400)
class CreateSeatView(mixins.LoginRequiredMixin, mixins.FormValidMixin, CreateView):
""" Create Seat View."""
......
......@@ -40,18 +40,6 @@ class CommentsTests(TestCase):
toggle_switch('enable_publisher_email_notifications', True)
def test_course_run_edit_page_with_multiple_comments(self):
""" Verify course-run edit page can load multiple comments"""
self._add_assert_multiple_comments(self.course_run, self.course_run_edit_page)
def test_comment_edit_with_courserun(self):
""" Verify that only comments attached with specific course run appears on edited page. """
comments = self._generate_comments_for_all_content_types()
response = self.client.get(reverse(self.course_run_edit_page, kwargs={'pk': self.course_run.id}))
self.assertContains(response, comments.get(self.course_run).comment)
self.assertNotContains(response, comments.get(self.course).comment)
self.assertNotContains(response, comments.get(self.seat).comment)
def test_comment_edit_with_seat(self):
""" Verify that only comments attached with specific seat appears on edited page. """
comments = self._generate_comments_for_all_content_types()
......@@ -66,12 +54,6 @@ class CommentsTests(TestCase):
self.course, reverse(self.course_edit_page, kwargs={'pk': self.course.id})
)
def test_edit_course_run_comment(self):
""" Verify that course run comment can be edited. """
self._edit_comment_page(
self.course_run, reverse(self.course_run_edit_page, kwargs={'pk': self.course_run.id})
)
def test_edit_seat_comment(self):
""" Verify that seat comment can be edited. """
self._edit_comment_page(
......
......@@ -7,14 +7,14 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-01-26 15:26+0500\n"
"POT-Creation-Date: 2017-01-27 12:46+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"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: \n"
#: static/js/catalogs-change-form.js
msgid "Preview"
......
......@@ -7,14 +7,14 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-01-26 15:26+0500\n"
"POT-Creation-Date: 2017-01-27 12:46+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"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: \n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: static/js/catalogs-change-form.js
......
......@@ -29,11 +29,20 @@ $(document).ready(function(){
loadAdminUsers(org_id);
}
$('#id_is_micromasters').click( function(){
$('#micromasters_name_group').toggle(this.checked);
var microMaster = $('#id_is_micromasters'),
xseries = $('#id_is_xseries');
if (microMaster.is(':checked')) {
toggleMicroMaster(true);
}
if (xseries.is(':checked')) {
toggleXseries(true);
}
microMaster.click( function(){
toggleMicroMaster(this.checked);
});
$('#id_is_xseries').click( function(e){
$('#xseries_name_group').toggle(this.checked);
xseries.click( function(e){
toggleXseries(this.checked)
});
$('#add-new-instructor').click(function(e){
$('#addInstructorModal').show();
......@@ -153,3 +162,13 @@ function renderSelectedInstructor(id, name, image) {
$('.selected-instructor').append(instructorHtml);
}
function toggleMicroMaster (checked) {
// If is-micromaster checkbox value true from db then show the x-micromaster block.
$('#micromasters_name_group').toggle(checked);
}
function toggleXseries(checked) {
// If is-xseries checkbox value true from db then show the x-series block.
$('#xseries_name_group').toggle(checked);
}
......@@ -47,8 +47,6 @@ $(function () {
};
change_fields();
$('#id_type').change(function () {
change_fields(this);
});
......
......@@ -475,6 +475,9 @@ select {
.field-label {
font-weight: bold;
}
.field-value {
margin-bottom: 20px;
}
.help-text {
p {
margin-bottom: 0;
......
......@@ -9,7 +9,13 @@
{% block page_content %}
<div>
<h1 class="hd-1 emphasized">{% trans "Add Course" %}</h1>
<h1 class="hd-1 emphasized">
{% if edit_mode %}
{% trans "Edit Course" %}
{% else %}
{% trans "Add Course" %}
{% endif %}
</h1>
<div class="alert-messages">
{% if messages %}
......@@ -88,26 +94,35 @@
</div>
</div>
<div class="col col-6">
{% if course_form.organization.field.queryset.all.count > 1 %}
<label class="field-label">{{ course_form.organization.label_tag }} </label>
{{ course_form.organization }}
{% if edit_mode %}
<label class="field-label">{{ course_form.organization.label_tag }} </label>
<div class="field-value">
{{ organization_name }}{{ course_form.organization }}</div>
{% else %}
{% with course_form.organization.field.queryset|first as organization %}
<label id="organization-name" class="field-label"
data-org_id="{{ organization.id }}">{{ course_form.organization.label_tag }}
</label>
<span class="read-only-field">{{ organization.name }}</span>
<input id="id_organization" name="organization" type="hidden" value="{{ organization.id }}">
{% endwith %}
{% if course_form.organization.field.queryset.all.count > 1 %}
<label class="field-label">{{ course_form.organization.label_tag }} </label>
{{ course_form.organization }}
{% else %}
{% with course_form.organization.field.queryset|first as organization %}
<label id="organization-name" class="field-label"
data-org_id="{{ organization.id }}">{{ course_form.organization.label_tag }}
</label>
<span class="read-only-field">{{ organization.name }}</span>
<input id="id_organization" name="organization" type="hidden" value="{{ organization.id }}">
{% endwith %}
{% endif %}
{% endif %}
<label class="field-label">
{{ course_form.team_admin.label_tag }}
</label> {{ course_form.team_admin }}
</label>
<div class="field-value">
{% if edit_mode %}{{ team_admin_name }}{% endif %}</div>
{{ course_form.team_admin }}
<label class="field-label ">{{ course_form.title.label }}</label>
{{ course_form.title }}
{% if edit_mode %}<div class="field-value">{{ course_form.title.value }}</div>{% endif %}
{{ course_form.title }}
</div>
</div>
......@@ -165,7 +180,8 @@
</div>
<div class="col col-6">
<label class="field-label ">{{ course_form.number.label_tag }}</label>
{{ course_form.number }}
{% if edit_mode %}{{ course_form.number.value }}{% endif %}
{{ course_form.number }}
</div>
</div>
......@@ -237,7 +253,6 @@
<div class="row">
<div class="col col-6">
<label class="field-label ">{{ seat_form.type.label_tag }}
<span class="required">* {% trans "required" %}</span>
</label>
{{ seat_form.type}}
</div>
......
{% extends 'publisher/base.html' %}
{% load i18n %}
{% block title %}
{% trans "Course Run Form" %}
{% endblock title %}
{% block page_content %}
<div class="layout-full layout publisher-layout">
<div class="publisher-container">
<div class="course-information">
<h4 class="hd-4">{% trans "Course Run Form" %}</h4>
<div class="status-information">
{% if object.id %}
<div class="info-item">
<span class="item">
<span class="heading">{% trans "Status" %}:</span>
<span>{{ workflow_state }}</span>
</span>
</div>
{% endif %}
</div>
<form class="form" method="post" action=""> {% csrf_token %}
<fieldset class="form-group">
{% for field in form %}
{% include "publisher/form_field.html" %}
{% endfor %}
</fieldset>
<button class="btn-brand btn-base" type="submit">{% trans "Save" %}</button>
</form>
</div>
<div class="comment-container">
<span class="item">
{% if object.id %}
<a href="{% url 'publisher:publisher_seats_new' %}" class="btn btn-neutral btn-add">
{% trans "Add Seat" %}
</a>
{% endif %}
</span>
{% include 'comments/comments_list.html' %}
{% include 'comments/add_auth_comments.html' %}
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
{{ form.media }}
{% endblock %}
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