Commit 4d22f572 by Waqas Khalid

Merge pull request #3738 from mlkwaqas/waqas/for545-user-last-thread-not-remembered

Waqas/for545 user last thread not remembered
parents 708bb8a5 9e6333f6
......@@ -6,7 +6,6 @@ import re
import urlparse
from .http import StubHttpRequestHandler, StubHttpService
class StubCommentsServiceHandler(StubHttpRequestHandler):
@property
......@@ -23,26 +22,41 @@ class StubCommentsServiceHandler(StubHttpRequestHandler):
"/api/v1/comments/(?P<comment_id>\\w+)$": self.do_comment,
"/api/v1/(?P<commentable_id>\\w+)/threads$": self.do_commentable,
}
if self.match_pattern(pattern_handlers):
return
self.send_response(404, content="404 Not Found")
def match_pattern(self, pattern_handlers):
path = urlparse.urlparse(self.path).path
for pattern in pattern_handlers:
match = re.match(pattern, path)
if match:
pattern_handlers[pattern](**match.groupdict())
return
self.send_response(404, content="404 Not Found")
return True
return None
def do_PUT(self):
if self.path.startswith('/set_config'):
return StubHttpRequestHandler.do_PUT(self)
pattern_handlers = {
"/api/v1/users/(?P<user_id>\\d+)$": self.do_put_user,
}
if self.match_pattern(pattern_handlers):
return
self.send_response(204, "")
def do_put_user(self, user_id):
self.server.config['default_sort_key'] = self.post_dict.get("default_sort_key", "date")
self.send_json_response({'username': self.post_dict.get("username"), 'external_id': self.post_dict.get("external_id")})
def do_DELETE(self):
self.send_json_response({})
def do_user(self, user_id):
response = {
"id": user_id,
"default_sort_key": self.server.config.get("default_sort_key", "date"),
"upvoted_ids": [],
"downvoted_ids": [],
"subscribed_thread_ids": [],
......
......@@ -3,6 +3,36 @@ describe "DiscussionThreadListView", ->
beforeEach ->
setFixtures """
<script type="text/template" id="thread-list-item-template">
<a href="<%- id %>" data-id="<%- id %>">
<span class="title"><%- title %></span>
<span class="comments-count">
<%
var fmt;
var data = {
'span_sr_open': '<span class="sr">',
'span_close': '</span>',
'unread_comments_count': unread_comments_count,
'comments_count': comments_count
};
if (unread_comments_count > 0) {
fmt = '%(comments_count)s %(span_sr_open)scomments (%(unread_comments_count)s unread comments)%(span_close)s';
} else {
fmt = '%(comments_count)s %(span_sr_open)scomments %(span_close)s';
}
print(interpolate(fmt, data, true));
%>
</span>
<span class="votes-count">+<%=
interpolate(
'%(votes_up_count)s%(span_sr_open)s votes %(span_close)s',
{'span_sr_open': '<span class="sr">', 'span_close': '</span>', 'votes_up_count': votes['up_count']},
true
)
%></span>
</a>
</script>
<script type="text/template" id="thread-list-template">
<div class="browse-search">
<div class="home"></div>
......@@ -14,7 +44,14 @@ describe "DiscussionThreadListView", ->
</form>
</div>
</div>
<div class="sort-bar"></div>
<div class="sort-bar">
<span class="sort-label" id="sort-label">Sort by:</span>
<ul role="radiogroup" aria-labelledby="sort-label">
<li><a href="#" role="radio" aria-checked="false" data-sort="date">date</a></li>
<li><a href="#" role="radio" aria-checked="false" data-sort="votes">votes</a></li>
<li><a href="#" role="radio" aria-checked="false" data-sort="comments">comments</a></li>
</ul>
</div>
<div class="search-alerts"></div>
<div class="post-list-wrapper">
<ul class="post-list"></ul>
......@@ -33,6 +70,11 @@ describe "DiscussionThreadListView", ->
</script>
<div class="sidebar"></div>
"""
@threads = [
{id: "1", title: "Thread1", body: "dummy body", votes: {up_count: '20'}, unread_comments_count:0, comments_count:1, created_at: '2013-04-03T20:08:39Z',},
{id: "2", title: "Thread2", body: "dummy body", votes: {up_count: '42'}, unread_comments_count:0, comments_count:2, created_at: '2013-04-03T20:07:39Z',},
{id: "3", title: "Thread3", body: "dummy body", votes: {up_count: '12'}, unread_comments_count:0, comments_count:3, created_at: '2013-04-03T20:06:39Z',},
]
window.$$course_id = "TestOrg/TestCourse/TestRun"
window.user = new DiscussionUser({id: "567", upvoted_ids: []})
......@@ -98,3 +140,65 @@ describe "DiscussionThreadListView", ->
spyOn(@view, "renderThread")
@view.collection.trigger("change", new Thread({id: 1}))
expect(@view.clearSearchAlerts).toHaveBeenCalled()
makeView = (discussion) ->
return new DiscussionThreadListView(
el: $(".sidebar"),
collection: discussion
)
checkThreadsOrdering = (view, sort_order, type) ->
expect(view.$el.find(".post-list .list-item").children().length).toEqual(3)
expect(view.$el.find(".post-list .list-item:nth-child(1) .title").text()).toEqual(sort_order[0])
expect(view.$el.find(".post-list .list-item:nth-child(2) .title").text()).toEqual(sort_order[1])
expect(view.$el.find(".post-list .list-item:nth-child(3) .title").text()).toEqual(sort_order[2])
expect(view.$el.find(".sort-bar a.active").text()).toEqual(type)
describe "thread rendering should be correct", ->
checkRender = (threads, type, sort_order) ->
discussion = new Discussion(threads, {pages: 1, sort: type})
view = makeView(discussion)
view.render()
checkThreadsOrdering(view, sort_order, type)
it "with sort preference date", ->
checkRender(@threads, "date", [ "Thread1", "Thread2", "Thread3"])
it "with sort preference votes", ->
checkRender(@threads, "votes", [ "Thread2", "Thread1", "Thread3"])
it "with sort preference comments", ->
checkRender(@threads, "comments", [ "Thread3", "Thread2", "Thread1"])
describe "Sort click should be correct", ->
changeSorting = (threads, selected_type, new_type, sort_order) ->
discussion = new Discussion(threads, {pages: 1, sort: selected_type})
view = makeView(discussion)
view.render()
expect(view.$el.find(".sort-bar a.active").text()).toEqual(selected_type)
sorted_threads = []
if new_type == 'date'
sorted_threads = [threads[0], threads[1], threads[2]]
else if new_type == 'comments'
sorted_threads = [threads[2], threads[1], threads[0]]
else if new_type == 'votes'
sorted_threads = [threads[1], threads[0], threads[2]]
$.ajax.andCallFake((params) =>
params.success(
{"discussion_data":sorted_threads, page:1, num_pages:1}
)
{always: ->}
)
view.$el.find(".sort-bar a[data-sort='"+new_type+"']").click()
expect($.ajax).toHaveBeenCalled()
expect(view.sortBy).toEqual(new_type)
checkThreadsOrdering(view, sort_order, new_type)
it "with sort preference date", ->
changeSorting(@threads, "comments", "date", ["Thread1", "Thread2", "Thread3"])
it "with sort preference votes", ->
changeSorting(@threads, "date", "votes", ["Thread2", "Thread1", "Thread3"])
it "with sort preference comments", ->
changeSorting(@threads, "votes", "comments", ["Thread3", "Thread2", "Thread1"])
......@@ -5,9 +5,10 @@ if Backbone?
initialize: (models, options={})->
@pages = options['pages'] || 1
@current_page = 1
@sort_preference = options['sort']
@bind "add", (item) =>
item.discussion = @
@comparator = @sortByDateRecentFirst
@setSortComparator(@sort_preference)
@on "thread:remove", (thread) =>
@remove(thread)
......@@ -17,6 +18,12 @@ if Backbone?
hasMorePages: ->
@current_page < @pages
setSortComparator: (sortBy) ->
switch sortBy
when 'date' then @comparator = @sortByDateRecentFirst
when 'votes' then @comparator = @sortByVotes
when 'comments' then @comparator = @sortByComments
addThread: (thread, options) ->
# TODO: Check for existing thread with same ID in a faster way
if not @find(thread.id)
......
......@@ -6,12 +6,13 @@ if Backbone?
element = $(elem)
window.$$course_id = element.data("course-id")
user_info = element.data("user-info")
sort_preference = element.data("sort-preference")
threads = element.data("threads")
thread_pages = element.data("thread-pages")
content_info = element.data("content-info")
window.user = new DiscussionUser(user_info)
Content.loadContentInfos(content_info)
discussion = new Discussion(threads, pages: thread_pages)
discussion = new Discussion(threads, {pages: thread_pages, sort: sort_preference})
new DiscussionRouter({discussion: discussion})
Backbone.history.start({pushState: true, root: "/courses/#{$$course_id}/discussion/forum/"})
DiscussionProfileApp =
......
......@@ -132,6 +132,9 @@ if Backbone?
@displayedCollection.on "reset", @renderThreads
@displayedCollection.on "thread:remove", @renderThreads
@renderThreads()
sort_element = @$('.sort-bar a[data-sort="' + this.collection.sort_preference + '"]')
sort_element.attr('aria-checked',true)
sort_element.addClass('active')
@
renderThreads: =>
......@@ -413,18 +416,14 @@ if Backbone?
@loadMorePages(event)
sortThreads: (event) ->
activeSort = @$(".sort-bar a[class='active']")
activeSort = @$(".sort-bar a.active")
activeSort.removeClass("active")
activeSort.attr("aria-checked", "false")
newSort = $(event.target)
newSort.addClass("active")
newSort.attr("aria-checked", "true")
@sortBy = newSort.data("sort")
@displayedCollection.comparator = switch @sortBy
when 'date' then @displayedCollection.sortByDateRecentFirst
when 'votes' then @displayedCollection.sortByVotes
when 'comments' then @displayedCollection.sortByComments
@displayedCollection.setSortComparator(@sortBy)
@retrieveFirstPage(event)
performSearch: (event) ->
......
......@@ -125,4 +125,3 @@ class SearchResultFixture(DiscussionContentFixture):
def get_config_data(self):
return {"search_result": json.dumps(self.result)}
......@@ -167,6 +167,38 @@ class DiscussionThreadPage(PageObject, DiscussionPageMixin):
).fulfill()
class DiscussionSortPreferencePage(CoursePage):
"""
Page that contain the discussion board with sorting options
"""
def __init__(self, browser, course_id):
super(DiscussionSortPreferencePage, self).__init__(browser, course_id)
self.url_path = "discussion/forum"
def is_browser_on_page(self):
"""
Return true if the browser is on the right page else false.
"""
return self.q(css="body.discussion .sort-bar").present
def get_selected_sort_preference_text(self):
"""
Return the text of option that is selected for sorting.
"""
return self.q(css="body.discussion .sort-bar a.active").text[0].lower()
def change_sort_preference(self, sort_by):
"""
Change the option of sorting by clicking on new option.
"""
self.q(css="body.discussion .sort-bar a[data-sort='{0}']".format(sort_by)).click()
def refresh_page(self):
"""
Reload the page.
"""
self.browser.refresh()
class DiscussionTabSingleThreadPage(CoursePage):
def __init__(self, browser, course_id, thread_id):
super(DiscussionTabSingleThreadPage, self).__init__(browser, course_id)
......
......@@ -12,7 +12,8 @@ from ..pages.lms.discussion import (
InlineDiscussionPage,
InlineDiscussionThreadPage,
DiscussionUserProfilePage,
DiscussionTabHomePage
DiscussionTabHomePage,
DiscussionSortPreferencePage,
)
from ..fixtures.course import CourseFixture, XBlockFixtureDesc
from ..fixtures.discussion import (
......@@ -22,7 +23,7 @@ from ..fixtures.discussion import (
Thread,
Response,
Comment,
SearchResult
SearchResult,
)
......@@ -458,3 +459,52 @@ class DiscussionSearchAlertTest(UniqueCourseTest):
self.setup_corrected_text(None)
self.page.perform_search()
self.check_search_alert_messages([])
class DiscussionSortPreferenceTest(UniqueCourseTest):
"""
Tests for the discussion page displaying a single thread.
"""
def setUp(self):
super(DiscussionSortPreferenceTest, self).setUp()
# Create a course to register for.
CourseFixture(**self.course_info).install()
AutoAuthPage(self.browser, course_id=self.course_id).visit()
self.sort_page = DiscussionSortPreferencePage(self.browser, self.course_id)
self.sort_page.visit()
def test_default_sort_preference(self):
"""
Test to check the default sorting preference of user. (Default = date )
"""
selected_sort = self.sort_page.get_selected_sort_preference_text()
self.assertEqual(selected_sort, "date")
def test_change_sort_preference(self):
"""
Test that if user sorting preference is changing properly.
"""
selected_sort = ""
for sort_type in ["votes", "comments", "date"]:
self.assertNotEqual(selected_sort, sort_type)
self.sort_page.change_sort_preference(sort_type)
selected_sort = self.sort_page.get_selected_sort_preference_text()
self.assertEqual(selected_sort, sort_type)
def test_last_preference_saved(self):
"""
Test that user last preference is saved.
"""
selected_sort = ""
for sort_type in ["votes", "comments", "date"]:
self.assertNotEqual(selected_sort, sort_type)
self.sort_page.change_sort_preference(sort_type)
selected_sort = self.sort_page.get_selected_sort_preference_text()
self.assertEqual(selected_sort, sort_type)
self.sort_page.refresh_page()
selected_sort = self.sort_page.get_selected_sort_preference_text()
self.assertEqual(selected_sort, sort_type)
......@@ -118,6 +118,7 @@ def make_mock_request_impl(text, thread_id="dummy_thread_id"):
data = make_mock_thread_data(text, thread_id, True)
elif "/users/" in url:
data = {
"default_sort_key": "date",
"upvoted_ids": [],
"downvoted_ids": [],
"subscribed_thread_ids": [],
......
......@@ -3,9 +3,9 @@ import logging
import xml.sax.saxutils as saxutils
from django.contrib.auth.decorators import login_required
from django.http import Http404
from django.core.context_processors import csrf
from django.contrib.auth.models import User
from django.http import Http404
from django.utils.translation import ugettext as _
from django.views.decorators.http import require_GET
import newrelic.agent
......@@ -225,7 +225,8 @@ def forum_form_discussion(request, course_id):
'cohorts': cohorts,
'user_cohort': user_cohort_id,
'cohorted_commentables': cohorted_commentables,
'is_course_cohorted': is_course_cohorted(course_id)
'is_course_cohorted': is_course_cohorted(course_id),
'sort_preference': user.default_sort_key,
}
# print "start rendering.."
return render_to_response('discussion/index.html', context)
......@@ -296,7 +297,6 @@ def single_thread(request, course_id, discussion_id, thread_id):
cohorts = get_course_cohorts(course_id)
cohorted_commentables = get_cohorted_commentables(course_id)
user_cohort = get_cohort_id(request.user, course_id)
context = {
'discussion_id': discussion_id,
'csrf': csrf(request)['csrf_token'],
......@@ -316,9 +316,9 @@ def single_thread(request, course_id, discussion_id, thread_id):
'flag_moderator': cached_has_permission(request.user, 'openclose_thread', course.id) or has_access(request.user, 'staff', course),
'cohorts': cohorts,
'user_cohort': get_cohort_id(request.user, course_id),
'cohorted_commentables': cohorted_commentables
'cohorted_commentables': cohorted_commentables,
'sort_preference': cc_user.default_sort_key,
}
return render_to_response('discussion/index.html', context)
@require_GET
......
......@@ -29,7 +29,7 @@
<div class="sort-bar">
<span class="sort-label" id="sort-label">${_("Sort by:")}</span>
<ul role="radiogroup" aria-labelledby="sort-label">
<li><a href="#" role="radio" aria-checked="true" class="active" data-sort="date">${_("date")}</a></li>
<li><a href="#" role="radio" aria-checked="false" data-sort="date">${_("date")}</a></li>
<li><a href="#" role="radio" aria-checked="false" data-sort="votes">${_("votes")}</a></li>
<li><a href="#" role="radio" aria-checked="false" data-sort="comments">${_("comments")}</a></li>
</ul>
......
......@@ -25,7 +25,7 @@
<%include file="_new_post.html" />
<section class="discussion container" id="discussion-container" data-roles="${roles}" data-course-id="${course_id}" data-user-info="${user_info}" data-threads="${threads}" data-thread-pages="${thread_pages}" data-content-info="${annotated_content_info}" data-flag-moderator="${flag_moderator}">
<section class="discussion container" id="discussion-container" data-roles="${roles}" data-course-id="${course_id}" data-user-info="${user_info}" data-threads="${threads}" data-thread-pages="${thread_pages}" data-content-info="${annotated_content_info}" data-sort-preference="${sort_preference}" data-flag-moderator="${flag_moderator}">
<div class="discussion-body">
<div class="sidebar"></div>
<div class="discussion-column">
......
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