Commit 4ac7f58f by Renzo Lucioni Committed by GitHub

Merge pull request #409 from edx/clintonb/program-admin-quick-fix

Fixed bug on program admin page
parents df21d5db 8b2b0a2b
from django.contrib import admin, messages
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from course_discovery.apps.course_metadata.forms import ProgramAdminForm
from course_discovery.apps.course_metadata.models import * # pylint: disable=wildcard-import
......@@ -85,38 +87,47 @@ class ProgramAdmin(admin.ModelAdmin):
'authoring_organizations', 'credit_backing_organizations'
)
fields += filter_horizontal
save_error = None
save_error = False
def custom_course_runs_display(self, obj):
return ", ".join([str(run) for run in obj.course_runs])
return mark_safe('<br>'.join([str(run) for run in obj.course_runs]))
custom_course_runs_display.short_description = "Included course runs"
custom_course_runs_display.short_description = _('Included course runs')
def _redirect_course_run_update_page(self, obj):
""" Returns a response redirect to a page where the user can update the
course runs for the program being edited.
Returns:
HttpResponseRedirect
"""
return HttpResponseRedirect(reverse('admin_metadata:update_course_runs', kwargs={'pk': obj.pk}))
def response_add(self, request, obj, post_url_continue=None):
if self.save_error:
return self.response_post_save_add(request, obj)
else:
return HttpResponseRedirect(reverse('admin_metadata:update_course_runs', kwargs={'pk': obj.pk}))
return self._redirect_course_run_update_page(obj)
def response_change(self, request, obj):
if self.save_error:
return self.response_post_save_change(request, obj)
else:
if any(status in request.POST for status in ['_continue', '_save']):
return HttpResponseRedirect(reverse('admin_metadata:update_course_runs', kwargs={'pk': obj.pk}))
return self._redirect_course_run_update_page(obj)
else:
return HttpResponseRedirect(reverse('admin:course_metadata_program_add'))
def save_model(self, request, obj, form, change):
try:
# courses are ordered by django id, but form.cleaned_data is ordered correctly
obj.courses = form.cleaned_data.get('courses')
obj.authoring_organizations = form.cleaned_data.get('authoring_organizations')
obj.credit_backing_organizations = form.cleaned_data.get('credit_backing_organizations')
obj.save()
super().save_model(request, obj, form, change)
self.save_error = False
except ProgramPublisherException as ex:
messages.add_message(request, messages.ERROR, ex.message)
except ProgramPublisherException:
# TODO Redirect the user back to the form so that he/she can try again.
logger.exception('An error occurred while publishing the program [%s] to the marketing site.', obj.uuid)
msg = _('An error occurred while publishing the program to the marketing site. Please try again. '
'If the error persists, please contact the Engineering Team.')
messages.add_message(request, messages.ERROR, msg)
self.save_error = True
class Media:
......
from dal import widgets
from dal import autocomplete
from django import forms
from django.core.exceptions import ValidationError
from django.forms.utils import ErrorList
......@@ -8,32 +8,30 @@ from course_discovery.apps.course_metadata.choices import ProgramStatus
from course_discovery.apps.course_metadata.models import Program, CourseRun
class HackDjangoAutocompleteMixin(object):
# It seems to me there is an issue with the select 2 widget in django autocomplete.
# When the widget loads selected choices it loads them in order of django id, not the order
# they are stored in in the database. This workaround works, but not sure what approach
# would be less hacky. Perhaps opening a PR to the django autocomplete repo if this is
# fact an issue?
def filter_choices_to_render_with_order_preserved(self, selected_choices):
"""
Preserves ordering of selected_choices when creating the choices queryset.
class QuerySetSelectMixin2(widgets.WidgetMixin):
See https://codybonney.com/creating-a-queryset-from-a-list-while-preserving-order-using-django.
def filter_choices_to_render(self, selected_choices):
# preserve ordering of selected_choices in queryset
# https://codybonney.com/creating-a-queryset-from-a-list-while-preserving-order-using-django/
clauses = ' '.join(['WHEN id={} THEN {}'.format(pk, i) for i, pk in enumerate(selected_choices)])
ordering = 'CASE {} END'.format(clauses)
self.choices.queryset = self.choices.queryset.filter(
pk__in=[c for c in selected_choices if c]
).extra(select={'ordering': ordering}, order_by=('ordering',))
django-autocomplete's definition of this method on QuerySetSelectMixin loads selected choices in
order of primary key instead of the order in which the choices are actually stored.
"""
clauses = ' '.join(['WHEN id={} THEN {}'.format(pk, i) for i, pk in enumerate(selected_choices)])
ordering = 'CASE {} END'.format(clauses)
self.choices.queryset = self.choices.queryset.filter(
pk__in=[c for c in selected_choices if c]
).extra(select={'ordering': ordering}, order_by=('ordering',))
widgets.QuerySetSelectMixin = QuerySetSelectMixin2
class ProgramAdminForm(HackDjangoAutocompleteMixin, forms.ModelForm):
class ProgramAdminForm(forms.ModelForm):
class Meta:
model = Program
fields = '__all__'
from dal import autocomplete
# Monkey patch filter_choices_to_render with our own definition which preserves ordering.
autocomplete.ModelSelect2Multiple.filter_choices_to_render = filter_choices_to_render_with_order_preserved
widgets = {
'courses': autocomplete.ModelSelect2Multiple(
url='admin_metadata:course-autocomplete',
......
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect, Http404
from django.utils.translation import ugettext_lazy as _
from django.views.generic import TemplateView, UpdateView
from course_discovery.apps.course_metadata.forms import CourseRunSelectionForm
......@@ -25,7 +26,10 @@ class CourseRunSelectionAdmin(UpdateView):
def get_context_data(self, **kwargs):
if self.request.user.is_authenticated() and self.request.user.is_staff:
context = super(CourseRunSelectionAdmin, self).get_context_data(**kwargs)
context['program_id'] = self.object.id
context.update({
'program_id': self.object.id,
'title': _('Change program excluded course runs')
})
return context
raise Http404
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-10-28 14:40+0500\n"
"POT-Creation-Date: 2016-10-31 19:34-0400\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"
......@@ -196,6 +196,16 @@ msgstr ""
msgid "Partners"
msgstr ""
#: apps/course_metadata/admin.py
msgid "Included course runs"
msgstr ""
#: apps/course_metadata/admin.py
msgid ""
"An error occurred while publishing the program to the marketing site. Please"
" try again. If the error persists, please contact the Engineering Team."
msgstr ""
#: apps/course_metadata/choices.py apps/publisher/models.py
msgid "Published"
msgstr ""
......@@ -391,6 +401,10 @@ msgstr ""
msgid "The description of credit redemption for courses in program"
msgstr ""
#: apps/course_metadata/views.py
msgid "Change program excluded course runs"
msgstr ""
#: apps/edx_haystack_extensions/models.py
msgid "Function Score"
msgstr ""
......@@ -620,7 +634,7 @@ msgid ""
"trademarks or trademarks of %(link_start)sedX Inc.%(link_end)s"
msgstr ""
#: templates/footer.html templates/footer.html.py
#: templates/footer.html
msgid "Powered by Open edX"
msgstr ""
......@@ -658,10 +672,6 @@ msgid "Sign Out"
msgstr ""
#: templates/metadata/admin/course_run.html
msgid "CourseRun Selection Form"
msgstr ""
#: templates/metadata/admin/course_run.html
msgid "Cancel"
msgstr ""
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-10-28 14:40+0500\n"
"POT-Creation-Date: 2016-10-31 19:34-0400\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: 2016-10-28 14:40+0500\n"
"POT-Creation-Date: 2016-10-31 19:34-0400\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"
......@@ -237,6 +237,24 @@ msgstr "Pärtnér Ⱡ'σяєм ιρѕυм #"
msgid "Partners"
msgstr "Pärtnérs Ⱡ'σяєм ιρѕυм ∂#"
#: apps/course_metadata/admin.py
msgid "Included course runs"
msgstr "Ìnçlüdéd çöürsé rüns Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, #"
#: apps/course_metadata/admin.py
msgid ""
"An error occurred while publishing the program to the marketing site. Please"
" try again. If the error persists, please contact the Engineering Team."
msgstr ""
"Àn érrör öççürréd whïlé püßlïshïng thé prögräm tö thé märkétïng sïté. Pléäsé"
" trý ägäïn. Ìf thé érrör pérsïsts, pléäsé çöntäçt thé Éngïnéérïng Téäm. "
"Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α∂ιριѕι¢ιηg єłιт, ѕє∂ ∂σ єιυѕмσ∂ "
"тємρσя ιη¢ι∂ι∂υηт υт łαвσяє єт ∂σłσяє мαgηα αłιqυα. υт єηιм α∂ мιηιм νєηιαм,"
" qυιѕ ησѕтяυ∂ єχєя¢ιтαтιση υłłαм¢σ łαвσяιѕ ηιѕι υт αłιqυιρ єχ єα ¢σммσ∂σ "
"¢σηѕєqυαт. ∂υιѕ αυтє ιяυяє ∂σłσя ιη яєρяєнєη∂єяιт ιη νσłυρтαтє νєłιт єѕѕє "
"¢ιłłυм ∂σłσяє єυ ƒυgιαт ηυłłα ραяιαтυя. єχ¢єρтєυя ѕιηт σ¢¢αє¢αт ¢υρι∂αтαт "
"ηση ρяσι∂єηт, ѕυηт ιη ¢υłρα qυι σƒƒι¢ια ∂єѕєяυηт мσłłιт αηιм #"
#: apps/course_metadata/choices.py apps/publisher/models.py
msgid "Published"
msgstr "Püßlïshéd Ⱡ'σяєм ιρѕυм ∂σł#"
......@@ -484,6 +502,11 @@ msgstr ""
"Thé désçrïptïön öf çrédït rédémptïön för çöürsés ïn prögräm Ⱡ'σяєм ιρѕυм "
"∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α#"
#: apps/course_metadata/views.py
msgid "Change program excluded course runs"
msgstr ""
"Çhängé prögräm éxçlüdéd çöürsé rüns Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєт#"
#: apps/edx_haystack_extensions/models.py
msgid "Function Score"
msgstr "Fünçtïön Sçöré Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт#"
......@@ -741,7 +764,7 @@ msgstr ""
"trädémärks ör trädémärks öf %(link_start)sédX Ìnç.%(link_end)s Ⱡ'σяєм ιρѕυм "
"∂σł#"
#: templates/footer.html templates/footer.html.py
#: templates/footer.html
msgid "Powered by Open edX"
msgstr "Pöwéréd ßý Öpén édX Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт,#"
......@@ -779,10 +802,6 @@ msgid "Sign Out"
msgstr "Sïgn Öüt Ⱡ'σяєм ιρѕυм ∂#"
#: templates/metadata/admin/course_run.html
msgid "CourseRun Selection Form"
msgstr "ÇöürséRün Séléçtïön Förm Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢ση#"
#: templates/metadata/admin/course_run.html
msgid "Cancel"
msgstr "Çänçél Ⱡ'σяєм ιρѕυ#"
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-10-28 14:40+0500\n"
"POT-Creation-Date: 2016-10-31 19:34-0400\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"
......
{% extends 'admin/base_site.html' %}
{% load i18n %}
{% block title %}
{% trans "CourseRun Selection Form" %}
{% endblock title %}
{% block content %}
{% block bodyclass %}
change-program-excluded-course-runs-form
{% endblock %}
{% block content %}
<form class="form" method="post" action="">
{% csrf_token %}
<fieldset class="module aligned ">
......@@ -30,5 +30,4 @@
</div>
{% endblock %}
</form>
{% 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