Commit d2c31cf8 by Awais

ECOM-5248

Adding inline fields.
parent edf1589e
from django.contrib import admin
from simple_history.admin import SimpleHistoryAdmin
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from simple_history.admin import SimpleHistoryAdmin
from course_discovery.apps.course_metadata.forms import ProgramAdminForm
from course_discovery.apps.course_metadata.models import * # pylint: disable=wildcard-import
......@@ -14,6 +17,27 @@ class PositionInline(admin.TabularInline):
extra = 0
class FaqsInline(admin.TabularInline):
model = Program.faq.through
exclude = ('sort_value',)
extra = 1
verbose_name_plural = 'Faqs'
class IndividualEndorsementInline(admin.TabularInline):
model = Program.individual_endorsements.through
exclude = ('sort_value',)
extra = 1
verbose_name_plural = 'Individual Endorsement'
class CorporateEndorsementsInline(admin.TabularInline):
model = Program.corporate_endorsements.through
exclude = ('sort_value',)
extra = 1
verbose_name_plural = 'Corporate Endorsement'
@admin.register(Course)
class CourseAdmin(admin.ModelAdmin):
list_display = ('uuid', 'key', 'title',)
......@@ -38,12 +62,35 @@ class CourseRunAdmin(admin.ModelAdmin):
@admin.register(Program)
class ProgramAdmin(admin.ModelAdmin):
list_display = ('uuid', 'title', 'marketing_slug', 'type',)
form = ProgramAdminForm
inlines = [FaqsInline, IndividualEndorsementInline, CorporateEndorsementsInline]
list_display = ('id', 'uuid', 'title', 'category', 'type', 'partner', 'status',)
list_filter = ('partner', 'type',)
ordering = ('uuid', 'title',)
readonly_fields = ('uuid',)
ordering = ('uuid', 'title', 'status')
readonly_fields = ('uuid', 'course_runs', 'excluded_course_runs',)
search_fields = ('uuid', 'title', 'marketing_slug')
filter_horizontal = ('job_outlook_items', 'expected_learning_items', 'credit_backing_organizations',)
# ordering the field display on admin page.
fields = (
'title', 'status', 'banner_image_url', 'card_image_url', 'overview', 'video',
)
fields += (
'courses', 'course_runs', 'excluded_course_runs'
)
fields += filter_horizontal
def response_add(self, request, obj, post_url_continue=None):
return HttpResponseRedirect(reverse('admin_metadata:update_course_runs', kwargs={'pk': obj.pk}))
def response_change(self, request, obj):
if '_continue'in request.POST or '_save' in request.POST:
return HttpResponseRedirect(reverse('admin_metadata:update_course_runs', kwargs={'pk': obj.pk}))
else:
return HttpResponseRedirect(reverse('admin:course_metadata_program_add'))
@admin.register(ProgramType)
class ProgramTypeAdmin(admin.ModelAdmin):
......
from django import forms
from django.forms.util import ErrorList
from course_discovery.apps.course_metadata.models import Program, CourseRun
class ProgramAdminForm(forms.ModelForm):
class Meta:
model = Program
exclude = (
'subtitle', 'category', 'type', 'marketing_slug', 'weeks_to_complete',
'min_hours_effort_per_week', 'max_hours_effort_per_week',
'authoring_organizations',
)
class CourseRunSelectionForm(forms.ModelForm):
class Meta:
model = Program
fields = ('excluded_course_runs',)
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, initial=None, error_class=ErrorList,
label_suffix=':', empty_permitted=False, instance=None):
super(CourseRunSelectionForm, self).__init__(
data, files, auto_id, prefix,
initial, error_class, label_suffix,
empty_permitted, instance
)
query_set = [course.pk for course in instance.courses.all()]
self.fields["excluded_course_runs"].widget = forms.widgets.CheckboxSelectMultiple()
self.fields["excluded_course_runs"].help_text = ""
self.fields['excluded_course_runs'].queryset = CourseRun.objects.filter(
course__id__in=query_set
)
......@@ -512,6 +512,9 @@ class Endorsement(TimeStampedModel):
endorser = models.ForeignKey(Person, blank=False, null=False)
quote = models.TextField(blank=False, null=False)
def __str__(self):
return self.endorser.full_name
class CorporateEndorsement(TimeStampedModel):
corporation_name = models.CharField(max_length=128, blank=False, null=False)
......@@ -519,6 +522,9 @@ class CorporateEndorsement(TimeStampedModel):
image = models.ForeignKey(Image, blank=True, null=True)
individual_endorsements = SortedManyToManyField(Endorsement)
def __str__(self):
return self.corporation_name
class FAQ(TimeStampedModel):
question = models.TextField(blank=False, null=False)
......@@ -528,6 +534,9 @@ class FAQ(TimeStampedModel):
verbose_name = _('FAQ')
verbose_name_plural = _('FAQs')
def __str__(self):
return self.question
class ProgramType(TimeStampedModel):
name = models.CharField(max_length=32, unique=True, null=False, blank=False)
......
import ddt
from django.core.urlresolvers import reverse
from django.test import TestCase
from course_discovery.apps.course_metadata.tests import factories
from course_discovery.apps.core.tests.factories import UserFactory, USER_PASSWORD
# pylint: disable=no-member
@ddt.ddt
class AdminTests(TestCase):
""" Tests Admin page."""
def setUp(self):
super(AdminTests, self).setUp()
self.user = UserFactory(is_staff=True, is_superuser=True)
self.client.login(username=self.user.username, password=USER_PASSWORD)
self.course_runs = factories.CourseRunFactory.create_batch(3)
self.courses = [course_run.course for course_run in self.course_runs]
self.excluded_course_run = factories.CourseRunFactory(course=self.courses[0])
self.program = factories.ProgramFactory(
courses=self.courses, excluded_course_runs=[self.excluded_course_run]
)
def test_program_detail_form(self):
""" Verify in admin panel program detail form load successfully. """
response = self.client.get(reverse('admin:course_metadata_program_change', args=(self.program.id,)))
self.assertEqual(response.status_code, 200)
def test_custom_course_selection_page(self):
""" Verify that course selection page loads successfully. """
response = self.client.get(reverse('admin_metadata:update_course_runs', args=(self.program.id,)))
self.assertEqual(response.status_code, 200)
def test_custom_course_selection_page_with_invalid_id(self):
""" Verify that course selection page will return 404 for invalid program id. """
response = self.client.get(reverse('admin_metadata:update_course_runs', args=(10,)))
self.assertEqual(response.status_code, 404)
def test_custom_course_selection_page_with_non_staff(self):
""" Verify that course selection page will return 404 for non authorized user. """
self.client.logout()
self.user.is_superuser = False
self.user.is_staff = False
self.user.save()
self.client.login(username=self.user.username, password=USER_PASSWORD)
response = self.client.get(reverse('admin_metadata:update_course_runs', args=(self.program.id,)))
self.assertEqual(response.status_code, 404)
def test_page_loads_only_course_related_runs(self):
""" Verify that course selection page loads only all course runs. Also marked checkboxes with
excluded courses runs only.
"""
# add some new courses and course runs
factories.CourseRunFactory.create_batch(2)
response = self.client.get(reverse('admin_metadata:update_course_runs', args=(self.program.id,)))
html = '<input checked="checked" id="id_excluded_course_runs_0" '
html += 'name="excluded_course_runs" type="checkbox" value="{id}" />'.format(
id=self.excluded_course_run.id
)
self.assertContains(response, html)
for run in self.course_runs:
self.assertContains(response, run.key)
def test_page_with_post_new_course_run(self):
""" Verify that course selection page with posting the data. """
self.assertEqual(1, self.program.excluded_course_runs.all().count())
self.assertEqual(3, len(self.program.course_runs.all()))
params = {
'excluded_course_runs': [self.excluded_course_run.id, self.course_runs[0].id],
}
post_url = reverse('admin_metadata:update_course_runs', args=(self.program.id,))
response = self.client.post(post_url, params)
self.assertRedirects(
response,
expected_url=reverse('admin:course_metadata_program_change', args=(self.program.id,)),
status_code=302,
target_status_code=200
)
self.assertEqual(2, self.program.excluded_course_runs.all().count())
self.assertEqual(2, len(self.program.course_runs.all()))
def test_page_with_post_without_course_run(self):
""" Verify that course selection page without posting any selected excluded check run. """
self.assertEqual(1, self.program.excluded_course_runs.all().count())
params = {
'excluded_course_runs': [],
}
post_url = reverse('admin_metadata:update_course_runs', args=(self.program.id,))
response = self.client.post(post_url, params)
self.assertRedirects(
response,
expected_url=reverse('admin:course_metadata_program_change', args=(self.program.id,)),
status_code=302,
target_status_code=200
)
self.assertEqual(0, self.program.excluded_course_runs.all().count())
self.assertEqual(4, len(self.program.course_runs.all()))
response = self.client.get(reverse('admin_metadata:update_course_runs', args=(self.program.id,)))
self.assertNotContains(response, '<input checked="checked")')
......@@ -11,13 +11,15 @@ from django.test import TestCase
from freezegun import freeze_time
from course_discovery.apps.core.models import Currency
from course_discovery.apps.core.tests.helpers import make_image_file
from course_discovery.apps.core.utils import SearchQuerySetWrapper
from course_discovery.apps.course_metadata.models import (
AbstractNamedModel, AbstractMediaModel, AbstractValueModel, Course, CourseRun, SeatType,
AbstractMediaModel, AbstractNamedModel, AbstractValueModel,
CorporateEndorsement, Course, CourseRun, Endorsement,
FAQ, SeatType
)
from course_discovery.apps.course_metadata.tests import factories
from course_discovery.apps.course_metadata.tests.factories import CourseRunFactory
from course_discovery.apps.core.tests.helpers import make_image_file
from course_discovery.apps.course_metadata.tests.factories import CourseRunFactory, ImageFactory
from course_discovery.apps.ietf_language_tags.models import LanguageTag
......@@ -428,3 +430,40 @@ class ProgramTypeTests(TestCase):
def test_str(self):
program_type = factories.ProgramTypeFactory()
self.assertEqual(str(program_type), program_type.name)
class EndorsementTests(TestCase):
""" Tests of the Endorsement model. """
def setUp(self):
super(EndorsementTests, self).setUp()
self.person = factories.PersonFactory()
self.endorsement = Endorsement.objects.create(
endorser=self.person,
quote='test quote'
)
def test_str(self):
self.assertEqual(str(self.endorsement), self.person.full_name)
class CorporateEndorsementTests(TestCase):
""" Tests of the CorporateEndorsement model. """
def setUp(self):
super(CorporateEndorsementTests, self).setUp()
self.corporation_name = 'test org'
self.individual_endorsements = CorporateEndorsement.objects.create(
corporation_name=self.corporation_name,
statement='test statement',
image=ImageFactory()
)
def test_str(self):
self.assertEqual(str(self.individual_endorsements), self.corporation_name)
class FAQTests(TestCase):
""" Tests of the FAQ model. """
def test_str(self):
question = 'test question'
faq = FAQ.objects.create(question=question, answer='test')
self.assertEqual(str(faq), question)
"""
URLs for the admin autocomplete lookups.
"""
from django.conf.urls import url
from course_discovery.apps.course_metadata.views import CourseRunSelectionAdmin
urlpatterns = [
url(r'^update_course_runs/(?P<pk>\d+)/$', CourseRunSelectionAdmin.as_view(), name='update_course_runs',),
]
from django.views.generic import TemplateView
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect, Http404
from django.views.generic import TemplateView, UpdateView
from course_discovery.apps.course_metadata.forms import CourseRunSelectionForm
from course_discovery.apps.course_metadata.models import Program
class QueryPreviewView(TemplateView):
......@@ -7,3 +13,25 @@ class QueryPreviewView(TemplateView):
class SearchDemoView(TemplateView):
template_name = 'demo/search.html'
# pylint: disable=attribute-defined-outside-init
class CourseRunSelectionAdmin(UpdateView):
""" Create Course View."""
model = Program
template_name = 'metadata/admin/course_run.html'
form_class = CourseRunSelectionForm
def get_context_data(self, **kwargs):
if self.request.user.is_authenticated() and self.request.user.is_staff:
return super(CourseRunSelectionAdmin, self).get_context_data(**kwargs)
raise Http404
def form_valid(self, form):
self.object = form.save()
message = 'The program course runs was changed successfully.'
messages.add_message(self.request, messages.SUCCESS, message)
return HttpResponseRedirect(self.get_success_url())
def get_success_url(self):
return reverse('admin:course_metadata_program_change', args=(self.object.id,))
{% extends 'admin/base_site.html' %}
{% load i18n %}
{% block title %}
{% trans "CourseRun Selection Form" %}
{% endblock title %}
{% block content %}
<form class="form" method="post" action="">
{% csrf_token %}
<fieldset class="module aligned ">
{% for field in form %}
<div class="form-row field-overview">
<label for="{{ field.name }}">{{ field.label }}</label>
<div class="form-row">
{{ field }}
</div>
</div>
{% endfor %}
</fieldset>
{% block submit_buttons_bottom %}
<div class="submit-row">
<a href="{% url 'admin:course_metadata_program_changelist' %}">
<input type="button" name="Cancel" value="{% trans 'Cancel' %}">
</a>
<input type="submit" value="{% trans 'Save Course Run' %}" class="default" name="_save" />
</div>
{% endblock %}
</form>
{% endblock %}
......@@ -27,6 +27,7 @@ from course_discovery.apps.course_metadata.views import QueryPreviewView
admin.autodiscover()
urlpatterns = auth_urlpatterns + [
url(r'^admin/course_metadata/', include('course_discovery.apps.course_metadata.urls', namespace='admin_metadata')),
url(r'^admin/', include(admin.site.urls)),
url(r'^api/', include('course_discovery.apps.api.urls', namespace='api')),
# Use the same auth views for all logins, including those originating from the browseable API.
......
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