Skip to content

Commit fe40699

Browse files
joaogarciadelimarenzon
authored andcommitted
Implemented previous button in contents
close #1746
1 parent 70f7603 commit fe40699

File tree

4 files changed

+139
-4
lines changed

4 files changed

+139
-4
lines changed

Pipfile.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pythonpro/modules/models.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class Content(OrderedModel):
1414
description = models.TextField()
1515
slug = models.SlugField(unique=True)
1616
_next_content_cache = _NoneCache
17+
_previous_content_cache = _NoneCache
1718

1819
class Meta:
1920
abstract = True
@@ -55,6 +56,24 @@ def _next_content_query_set(self):
5556
"""Must provide a query set for next content"""
5657
raise NotImplementedError()
5758

59+
def previous_content(self):
60+
if self._previous_content_cache is not _NoneCache:
61+
return self._previous_content_cache
62+
previous = self
63+
while previous is not None:
64+
try:
65+
self._previous_content_cache = previous._previous_content_query_set().get()
66+
break
67+
except ObjectDoesNotExist:
68+
previous = previous.parent()
69+
else:
70+
self._previous_content_cache = None
71+
return self._previous_content_cache
72+
73+
def _previous_content_query_set(self):
74+
"""Must provide a query set for previous content"""
75+
raise NotImplementedError()
76+
5877
def module_slug(self):
5978
if self.parent() is None:
6079
return self.slug
@@ -95,6 +114,9 @@ def get_absolute_url(self):
95114
def _next_content_query_set(self):
96115
return Module.objects.filter(order=self.order + 1)
97116

117+
def _previous_content_query_set(self):
118+
return Module.objects.filter(order=self.order - 1)
119+
98120

99121
class Section(Content):
100122
module = models.ForeignKey('Module', on_delete=models.CASCADE)
@@ -112,6 +134,9 @@ def parent(self):
112134
def _next_content_query_set(self):
113135
return Section.objects.filter(module=self.module, order=self.order + 1)
114136

137+
def _previous_content_query_set(self):
138+
return Section.objects.filter(module=self.module, order=self.order - 1)
139+
115140

116141
class Chapter(Content):
117142
section = models.ForeignKey('Section', on_delete=models.CASCADE)
@@ -129,6 +154,9 @@ def parent(self):
129154
def _next_content_query_set(self):
130155
return Chapter.objects.filter(section=self.section, order=self.order + 1)
131156

157+
def _previous_content_query_set(self):
158+
return Chapter.objects.filter(section=self.section, order=self.order - 1)
159+
132160

133161
class Topic(Content):
134162
chapter = models.ForeignKey('Chapter', on_delete=models.CASCADE)
@@ -147,3 +175,6 @@ def parent(self):
147175

148176
def _next_content_query_set(self):
149177
return Topic.objects.filter(chapter=self.chapter, order=self.order + 1)
178+
179+
def _previous_content_query_set(self):
180+
return Topic.objects.filter(chapter=self.chapter, order=self.order - 1)

pythonpro/modules/templates/topics/topic_detail.html

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,22 @@ <h1 class="mt-4 mb-3">{{ topic.title }}</h1>
9595
frameborder="0"
9696
webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
9797
</div>
98+
<div class="row mt-5 mb-5">
99+
{% if topic.previous_content %}
100+
<div class="col-md text-md-left text-center">
101+
<a class="btn btn-success" href="{{ topic.previous_content.get_absolute_url }}">
102+
&laquo; <strong>{{ topic.previous_content.title }}</strong>
103+
</a>
104+
</div>
105+
{% endif %}
98106
{% if topic.next_content %}
99-
<div class="mt-5 mb-5" style="float: right">
100-
<a class="btn btn-success" href="{{ topic.next_content.get_absolute_url }}">
101-
Próximo Conteúdo: <strong>{{ topic.next_content.title }}</strong> &raquo;
107+
<div class="col-md text-md-right text-center mt-md-0 mt-2">
108+
<a class="btn btn-primary" href="{{ topic.next_content.get_absolute_url }}">
109+
<strong>{{ topic.next_content.title }}</strong> &raquo;
102110
</a>
103111
</div>
104112
{% endif %}
113+
</div>
105114
<div id='discourse-comments'></div>
106115
<script type="text/javascript">
107116
DiscourseEmbed = {
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import pytest
2+
from django.urls import reverse
3+
from model_mommy import mommy
4+
5+
from pythonpro.django_assertions import dj_assert_contains, dj_assert_not_contains
6+
from pythonpro.modules.models import Chapter, Module, Section, Topic
7+
8+
9+
@pytest.fixture
10+
def module(db):
11+
return mommy.make(Module)
12+
13+
14+
@pytest.fixture
15+
def section(module):
16+
return mommy.make(Section, module=module)
17+
18+
19+
@pytest.fixture
20+
def chapter(section):
21+
return mommy.make(Chapter, section=section)
22+
23+
24+
@pytest.fixture
25+
def topics(chapter):
26+
return [mommy.make(Topic, chapter=chapter, order=i) for i in range(2)]
27+
28+
29+
@pytest.fixture
30+
def resp(client_with_member, django_user_model, topics):
31+
next_topic = topics[1]
32+
return client_with_member.get(
33+
reverse('modules:topic_detail',
34+
kwargs={'module_slug': next_topic.module_slug(), 'topic_slug': next_topic.slug}),
35+
secure=True)
36+
37+
38+
@pytest.fixture
39+
def resp_first_topic(client_with_member, topics, logged_user):
40+
first_topic = topics[0]
41+
yield client_with_member.get(
42+
reverse(
43+
'modules:topic_detail', kwargs={'module_slug': first_topic.module_slug(), 'topic_slug': first_topic.slug}
44+
),
45+
secure=True
46+
)
47+
48+
49+
def test_topic_with_previous_topic(resp, topics):
50+
previous_topic, _ = topics
51+
dj_assert_contains(resp, previous_topic.get_absolute_url())
52+
53+
54+
def test_first_topic(resp_first_topic):
55+
dj_assert_not_contains(resp_first_topic, 'Conteúdo Anterior')
56+
57+
58+
def test_first_topic_previous_chapter(section):
59+
"""Assert previous Chapter as previous content for the first Topic"""
60+
previous_chapter, next_chapter = [mommy.make(Chapter, section=section, order=order) for order in range(2)]
61+
topic = mommy.make(Topic, chapter=next_chapter)
62+
assert previous_chapter == topic.previous_content()
63+
64+
65+
def test_topic_previous_section(module):
66+
"""Assert previous Section as previous content for the first Topic"""
67+
previous_section, next_section = [mommy.make(Section, module=module, order=order) for order in range(2)]
68+
chapter = mommy.make(Chapter, section=next_section)
69+
topic = mommy.make(Topic, chapter=chapter)
70+
assert previous_section == topic.previous_content()
71+
72+
73+
@pytest.mark.django_db
74+
def test_topic_previous_module():
75+
"""Assert previous Module as previous content for the first Topic"""
76+
previous_module, next_module = [mommy.make(Module, order=order) for order in range(2)]
77+
section = mommy.make(Section, module=next_module)
78+
chapter = mommy.make(Chapter, section=section)
79+
topic = mommy.make(Topic, chapter=chapter)
80+
assert previous_module == topic.previous_content()
81+
82+
83+
def test_previous_topic_first_none(chapter):
84+
"""Assert None as previous content for the First Topic of First Chapter of First Section of First Module"""
85+
topic = mommy.make(Topic, chapter=chapter)
86+
assert topic.previous_content() is None
87+
88+
89+
def test_cache(chapter, mocker):
90+
"""Assert cache is used when calling previous content multiple times"""
91+
topic = mommy.make(Topic, chapter=chapter)
92+
mocker.spy(topic, '_previous_content_query_set')
93+
for _ in range(3):
94+
topic.previous_content()
95+
assert topic._previous_content_query_set.call_count == 1

0 commit comments

Comments
 (0)