diff --git a/pythonpro/dashboard/templates/dashboard/home.html b/pythonpro/dashboard/templates/dashboard/home.html index c0fd9a24..68ad8a6f 100644 --- a/pythonpro/dashboard/templates/dashboard/home.html +++ b/pythonpro/dashboard/templates/dashboard/home.html @@ -12,32 +12,38 @@
-

Dashboard

-

Confira seus últimos acessos

+

Dashboard

+

Confira os dados consolidados por tópico

- + + + - {% for interaction in interactions %} + {% for topic in topics %} - - - - + + + + + + {% empty %} - + {% endfor %} @@ -45,5 +51,6 @@

Dashboard

DataTópicoMóduloAula Parada ? Tempo ?Visualizações ?
{{ interaction.creation|date:"d/m/Y" }} {{ interaction.creation|time:"H:i:s" }}{{ interaction.get_topic_title }}{{ interaction.max_watched_time|duration }}{{ interaction.total_watched_time|duration }}{{ topic.last_interaction|date:"d/m/Y" }} {{ topic.last_interaction|time:"H:i:s" }}{{topic.calculated_module.title}}{{ topic.title }}{{ topic.max_watched_time|duration }}{{ topic.total_watched_time|duration }}{{ topic.interactions_count }}
Você ainda não assistiu nenhum tópicoAinda não existem dados agregados
+
{% endblock body %} \ No newline at end of file diff --git a/pythonpro/dashboard/tests/test_dashboard.py b/pythonpro/dashboard/tests/test_dashboard.py deleted file mode 100644 index 6f792a4e..00000000 --- a/pythonpro/dashboard/tests/test_dashboard.py +++ /dev/null @@ -1,31 +0,0 @@ -import pytest -from django.urls import reverse -from model_mommy import mommy - -from pythonpro.dashboard.models import TopicInteraction -from pythonpro.django_assertions import dj_assert_contains - - -@pytest.fixture -def interactions(logged_user, topic): - return mommy.make(TopicInteraction, 100, user=logged_user, topic=topic) - - -@pytest.fixture -def resp(client_with_lead, interactions): - return client_with_lead.get( - reverse('dashboard:home'), - secure=True - ) - - -def test_topic_interaction_status_code(resp): - return resp.status_code == 200 - - -def test_topic_url_is_present(resp, topic): - dj_assert_contains(resp, reverse('topics:detail', kwargs={'slug': topic.slug})) - - -def test_topic_title_is_present(resp, topic): - dj_assert_contains(resp, topic.title) diff --git a/pythonpro/dashboard/tests/test_dashboard/__init__.py b/pythonpro/dashboard/tests/test_dashboard/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pythonpro/dashboard/tests/test_dashboard/test_topic_interaction_aggregate.py b/pythonpro/dashboard/tests/test_dashboard/test_topic_interaction_aggregate.py new file mode 100644 index 00000000..cd693c3a --- /dev/null +++ b/pythonpro/dashboard/tests/test_dashboard/test_topic_interaction_aggregate.py @@ -0,0 +1,116 @@ +from datetime import datetime +from typing import List + +import pytest +import pytz +from django.urls import reverse +from django.utils import timezone +from freezegun import freeze_time +from model_mommy import mommy + +from pythonpro.dashboard.models import TopicInteraction +from pythonpro.dashboard.templatetags.dashboard_tags import duration +from pythonpro.django_assertions import dj_assert_contains +from pythonpro.modules.models import Topic + + +@pytest.fixture +def interactions(logged_user, topic): + with freeze_time("2019-07-22 00:00:00"): + first_interaction = mommy.make( + TopicInteraction, + user=logged_user, + topic=topic, + topic_duration=125, + total_watched_time=125, + max_watched_time=95 + ) + + with freeze_time("2019-07-22 01:00:00"): + second_interaction = mommy.make( + TopicInteraction, + user=logged_user, + topic=topic, + topic_duration=125, + total_watched_time=34, + max_watched_time=14 + ) + with freeze_time("2019-07-22 00:30:00"): + third_interaction = mommy.make( + TopicInteraction, + user=logged_user, + topic=topic, + topic_duration=125, + total_watched_time=64, + max_watched_time=34 + ) + return [ + first_interaction, + second_interaction, + third_interaction, + ] + + +@pytest.fixture +def resp(client_with_lead, interactions): + return client_with_lead.get( + reverse('dashboard:home'), + secure=True + ) + + +def test_status_code(resp): + return resp.status_code == 200 + + +def test_topic_title_is_present(resp, topic): + dj_assert_contains(resp, topic.title) + + +def test_table_instructions(resp, topic): + dj_assert_contains(resp, 'Confira os dados consolidados por tópico') + + +def test_topic_url(resp, topic: Topic): + dj_assert_contains(resp, topic.get_absolute_url()) + + +def test_module_table_row(resp, topic: Topic): + module = topic.find_module() + dj_assert_contains(resp, f'{module.title}') + + +def test_max_creation(resp, interactions): + tz = timezone.get_current_timezone() + last_interaction_utc = datetime(2019, 7, 22, 1, 0, 0, tzinfo=pytz.utc) + last_interaction_local = last_interaction_utc.astimezone(tz).strftime('%d/%m/%Y %H:%M:%S') + dj_assert_contains(resp, last_interaction_local) + + +def test_max_watched_time(resp, interactions: List[TopicInteraction]): + max_watched_time = max(interaction.max_watched_time for interaction in interactions) + max_watched_time_str = duration(max_watched_time) + dj_assert_contains(resp, max_watched_time_str) + + +def test_total_watched_time(resp, interactions: List[TopicInteraction]): + total_watched_time = sum(interaction.total_watched_time for interaction in interactions) + total_watched_time_str = duration(total_watched_time) + dj_assert_contains(resp, total_watched_time_str) + + +def test_interactions_count(resp, interactions: List[TopicInteraction]): + interactions_count = len(interactions) + dj_assert_contains(resp, f'{interactions_count}') + + +@pytest.fixture +def resp_without_interactions(client_with_lead): + return client_with_lead.get( + reverse('dashboard:home'), + secure=True + ) + + +def test_not_existing_aggregation_msg_is_present(resp_without_interactions, topic): + dj_assert_contains(resp_without_interactions, "Ainda não existem dados agregados") diff --git a/pythonpro/dashboard/views.py b/pythonpro/dashboard/views.py index f697feea..c4931254 100644 --- a/pythonpro/dashboard/views.py +++ b/pythonpro/dashboard/views.py @@ -1,18 +1,42 @@ from django.contrib.auth.decorators import login_required +from django.db.models import Count, Max, Sum from django.http import JsonResponse from django.shortcuts import render from django.views.decorators.csrf import csrf_exempt from pythonpro.dashboard.facade import has_watched_any_topic from pythonpro.dashboard.forms import TopicInteractionForm -from pythonpro.dashboard.models import TopicInteraction from pythonpro.domain import user_facade +from pythonpro.modules.models import Topic @login_required def home(request): - interactions = TopicInteraction.objects.select_related('topic').filter(user=request.user).order_by('-creation')[:20] - return render(request, 'dashboard/home.html', {'interactions': interactions}) + topics = list( + Topic.objects.filter( + topicinteraction__user=request.user + ).annotate( + last_interaction=Max('topicinteraction__creation') + ).annotate( + max_watched_time=Max('topicinteraction__max_watched_time') + ).annotate( + total_watched_time=Sum('topicinteraction__total_watched_time') + ).annotate( + interactions_count=Count('topicinteraction') + ).order_by('-last_interaction').select_related('chapter').select_related('chapter__section').select_related( + 'chapter__section__module')[:20] + ) + + for topic in topics: + topic.calculated_module = topic.find_module() + + return render( + request, + 'dashboard/home.html', + { + 'topics': topics, + } + ) @login_required diff --git a/pythonpro/modules/models.py b/pythonpro/modules/models.py index 490439a9..81069cdc 100644 --- a/pythonpro/modules/models.py +++ b/pythonpro/modules/models.py @@ -75,9 +75,12 @@ def _previous_content_query_set(self): raise NotImplementedError() def module_slug(self): + return self.find_module().slug + + def find_module(self): if self.parent() is None: - return self.slug - return self.parent().module_slug() + return self + return self.parent().find_module() class ContentWithTitleMixin(Content):