Skip to content

Commit

Permalink
Merged in features/LMA-534 (pull request grid-synergy#3)
Browse files Browse the repository at this point in the history
Add filters and sorting on index and courses pages

Approved-by: Vladyslav Zherebkin
  • Loading branch information
Oksana Slusarenko authored and Vladyslav Zherebkin committed Apr 5, 2021
2 parents df8e9be + 41ebb49 commit 4c181a4
Show file tree
Hide file tree
Showing 8 changed files with 354 additions and 28 deletions.
36 changes: 35 additions & 1 deletion common/djangoapps/student/views/management.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
from common.djangoapps.entitlements.models import CourseEntitlement
from openedx.core.djangoapps.ace_common.template_context import get_base_template_context
from openedx.core.djangoapps.catalog.utils import get_programs_with_type
from openedx.core.djangoapps.content.course_overviews.models import Category, SubCategory
from openedx.core.djangoapps.embargo import api as embargo_api
from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY
from openedx.core.djangoapps.programs.models import ProgramsApiConfig
Expand Down Expand Up @@ -118,7 +119,36 @@ def index(request, extra_context=None, user=AnonymousUser()):
if extra_context is None:
extra_context = {}

category_id = request.GET.get('category')
subcategory_id = request.GET.get('subcategory')
category = sub_category = None

if category_id:
category = Category.objects.filter(id=category_id).first()

if subcategory_id:
sub_category = SubCategory.objects.filter(id=subcategory_id).first()

courses = get_courses_with_extra_info(user,filter_={'organization': None})

def filter_courses(course):
if category and course.new_category_id != category.id:
return False

if sub_category and course.subcategory_id != sub_category.id:
return False

return True

courses = filter(filter_courses, courses)
categories = Category.objects.prefetch_related('subcategories')
selected_category_name = ''

if category:
selected_category_name = category.name
elif sub_category:
selected_category_name = '{} - {}'.format(sub_category.category.name, sub_category.name)

if configuration_helpers.get_value(
"ENABLE_COURSE_SORTING_BY_START_DATE",
settings.FEATURES["ENABLE_COURSE_SORTING_BY_START_DATE"],
Expand All @@ -127,7 +157,11 @@ def index(request, extra_context=None, user=AnonymousUser()):
else:
courses = sort_by_announcement(courses)

context = {'courses': courses}
context = {
'courses': courses,
'categories': categories,
'selected_category_name': selected_category_name,
}

context['homepage_overlay_html'] = configuration_helpers.get_value('homepage_overlay_html')

Expand Down
2 changes: 0 additions & 2 deletions lms/djangoapps/branding/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@

@ensure_csrf_cookie
@transaction.non_atomic_requests
@cache_if_anonymous()
def index(request):
"""
Redirects to main page -- info page if user authenticated, or marketing if not
Expand Down Expand Up @@ -81,7 +80,6 @@ def index(request):


@ensure_csrf_cookie
@cache_if_anonymous()
def courses(request):
"""
Render the "find courses" page. If the marketing site is enabled, redirect
Expand Down
22 changes: 20 additions & 2 deletions lms/djangoapps/courseware/courses.py
Original file line number Diff line number Diff line change
Expand Up @@ -812,17 +812,35 @@ def sort_by_announcement(courses):

def sort_by_start_date(courses):
"""
Returns a list of courses sorted by their start date, latest first.
Returns a list of courses sorted by their start date.
"""
courses = sorted(
courses,
key=lambda course: (course.has_ended(), course.start is None, course.start),
reverse=False
reverse=True
)

return courses


def sort_by_rating(courses):
courses = sorted(
courses,
key=lambda course: course.ratings or 0,
reverse=True
)
return courses


def sort_by_price(courses):
courses = sorted(
courses,
key=lambda course: course.price,
reverse=True
)
return courses


def get_cms_course_link(course, page='course'):
"""
Returns a link to course_index for editing the course in cms,
Expand Down
80 changes: 67 additions & 13 deletions lms/djangoapps/courseware/views/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,10 @@
get_permission_for_course_about,
get_studio_url,
sort_by_announcement,
sort_by_start_date
sort_by_start_date,
sort_by_rating,
sort_by_price,

)
from lms.djangoapps.courseware.date_summary import verified_upgrade_deadline_link
from lms.djangoapps.courseware.exceptions import CourseAccessRedirect, Redirect
Expand All @@ -94,7 +97,9 @@
from lms.djangoapps.verify_student.services import IDVerificationService
from openedx.core.djangoapps.catalog.utils import get_programs, get_programs_with_type
from openedx.core.djangoapps.certificates import api as auto_certs_api
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.content.course_overviews.models import (
Category, CourseOverview, SubCategory, DifficultyLevel
)
from openedx.core.djangoapps.credit.api import (
get_credit_requirement_status,
is_credit_course,
Expand Down Expand Up @@ -252,11 +257,17 @@ def user_groups(user):


@ensure_csrf_cookie
@cache_if_anonymous()
def courses(request):
"""
Render "find courses" page. The course selection work is done in courseware.courses.
"""
sort = request.GET.get('sort', '')
category_id = request.GET.get('category')
subcategory_id = request.GET.get('subcategory')
difficulty_level_id = request.GET.get('difficulty_level')
mode = request.GET.get('mode', '')
category = sub_category = difficulty_level = None

courses_list = []
filter_ = None
course_discovery_meanings = getattr(settings, 'COURSE_DISCOVERY_MEANINGS', {})
Expand All @@ -274,31 +285,74 @@ def courses(request):
filter_ = {'organization': None}
courses_list = get_courses_with_extra_info(request.user,filter_=filter_)

if configuration_helpers.get_value("ENABLE_COURSE_SORTING_BY_START_DATE",
settings.FEATURES["ENABLE_COURSE_SORTING_BY_START_DATE"]):
if sort == 'latest':
courses_list = sort_by_start_date(courses_list)
elif sort == 'rating':
courses_list = sort_by_rating(courses_list)
elif sort == 'price':
courses_list = sort_by_price(courses_list)
else:
courses_list = sort_by_announcement(courses_list)

# Add marketable programs to the context.
web_couses = []
programs_list = get_programs_with_type(request.site, include_hidden=False)

for course in courses_list:
platform = course.platform_visibility
if platform == None or platform == "Web" or platform == "Both":
web_couses.append(course)
if category_id:
category = Category.objects.filter(id=category_id).first()

programs_list = get_programs_with_type(request.site, include_hidden=False)
if subcategory_id:
sub_category = SubCategory.objects.filter(id=subcategory_id).first()

if difficulty_level_id:
difficulty_level = DifficultyLevel.objects.filter(id=difficulty_level_id).first()

def filter_courses(course):
if course.platform_visibility not in ["Web", "Both", None]:
return False

if category and course.new_category_id != category.id:
return False

if sub_category and course.subcategory_id != sub_category.id:
return False

if difficulty_level and course.difficulty_level != difficulty_level.level.capitalize():
return False

if mode == 'free' and course.price:
return False
elif mode == 'paid' and not course.price:
return False
elif mode == 'discounted' and not course.discount_applicable:
return False

return True

courses_list = filter(filter_courses, courses_list)
categories = Category.objects.prefetch_related('subcategories')
difficulty_levels = DifficultyLevel.objects.all()
selected_category_name = ''

if category:
selected_category_name = category.name
elif sub_category:
selected_category_name = '{} - {}'.format(sub_category.category.name, sub_category.name)

return render_to_response(
"courseware/courses.html",
{
'courses': web_couses,
'courses': courses_list,
'course_discovery_meanings': course_discovery_meanings,
'programs_list': programs_list,
'categories': categories,
'selected_category_name': selected_category_name,
'difficulty_levels': difficulty_levels,
'selected_difficulty_level_id': difficulty_level.id if difficulty_level else '',
'selected_mode': mode,
'sort': sort,
}
)


def _get_course_creator_status(user):
"""
Helper method for returning the course creator status for a particular user,
Expand Down
39 changes: 39 additions & 0 deletions themes/lhub/lms/static/js/sort_and_filter_courses.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
$(document).ready(function() {
$('.js-select-category').on('click', function(ev) {
ev.stopPropagation();
var value = $(ev.currentTarget).data('category-id');
loadFilter('category', value, ['subcategory']);
});

$('.js-select-subcategory').on('click', function(ev) {
ev.stopPropagation();
var value = $(ev.currentTarget).data('subcategory-id');
loadFilter('subcategory', value, ['category']);
});

$('.js-reset-category').on('click', function(ev) {
ev.stopPropagation();
loadFilter('', '', ['category', 'subcategory']);
});

$('.js-select-difficulty-level').on('change', function(ev) {
loadFilter('difficulty_level', this.value, [])
})

$('.js-select-mode').on('change', function(ev) {
loadFilter('mode', this.value, [])
})

$('.js-select-sort').on('change', function(ev) {
loadFilter('sort', this.value, [])
})

function loadFilter(name, value, deleteNames) {
let params = new URLSearchParams(window.location.search);
for (let deleteName of deleteNames) {
params.delete(deleteName);
}
params.set(name, value);
window.location.search = params.toString();
}
});
68 changes: 68 additions & 0 deletions themes/lhub/lms/templates/courseware/courses.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,75 @@
</%block>
% endif

<style>
.selects-wrapper-all {
display: flex;
flex-wrap: wrap;
margin-bottom: 5px;
padding: 0 20px;
}

.select-wrapper {
margin: 0 20px 15px 0;
}

.select-wrapper select {
border: 2px solid #5678a9;
border-radius: 8px;
min-width: 200px;
}

.selects-wrapper-all .select-name {
display: block;
margin-bottom: 5px;
font-size: 14px;
}
</style>

<%block name="pagetitle">${_("Courses")}</%block>
<div class="selects-wrapper-all">
<div class="select-wrapper">
<span class="select-name">${_("Sorting")}</span>
<select class="js-select-sort">
<option value="">---</option>
<option value="rating" ${'selected="selected"' if sort == 'rating' else ''}>
${_("Rating from highest to lowest")}
</option>
<option value="price" ${'selected="selected"' if sort == 'price' else ''}>
${_("Price highest to lowest")}
</option>
<option value="latest" ${'selected="selected"' if sort == 'latest' else ''}>
${_("Newest to oldest ")}
</option>
</select>
</div>
<div class="select-wrapper">
<span class="select-name">${_("Level")}</span>
<select class="js-select-difficulty-level">
<option value="">---</option>
% for difficulty_level in difficulty_levels:
<option value="${difficulty_level.id}" ${'selected="selected"' if difficulty_level.id == selected_difficulty_level_id else ''}>
${difficulty_level.label}
</option>
% endfor
</select>
</div>
<div class="select-wrapper">
<span class="select-name">${_("Payment type")}</span>
<select class="js-select-mode">
<option value="">---</option>
<option value="free" ${'selected="selected"' if selected_mode == 'free' else ''}>
${_("Free")}
</option>
<option value="paid" ${'selected="selected"' if selected_mode == 'paid' else ''}>
${_("Paid")}
</option>
<option value="discounted" ${'selected="selected"' if selected_mode == 'discounted' else ''}>
${_("Discounted")}
</option>
</select>
</div>
</div>

<main id="main" aria-label="Content" tabindex="-1">
<section class="find-courses">
Expand Down
Loading

0 comments on commit 4c181a4

Please sign in to comment.