From d8c7a21c360556915f4c6b79c9284e714624fdbd Mon Sep 17 00:00:00 2001 From: renzon Date: Mon, 4 Jun 2018 15:41:27 -0300 Subject: [PATCH] Implemented Cohorts's Live Classes close #425 Changed modules/sections/chapters/topics to auto populate slug as well --- pythonpro/cohorts/admin.py | 17 +++++++-- pythonpro/cohorts/facade.py | 12 ++++++- .../cohorts/migrations/0002_liveclass.py | 23 ++++++++++++ pythonpro/cohorts/models.py | 6 ++++ .../templates/cohorts/cohort_detail.html | 23 +++++++++++- pythonpro/cohorts/tests/__init__.py | 0 .../{tests.py => tests/test_cohorts.py} | 35 ++++++++++++++++++- pythonpro/cohorts/views.py | 5 ++- pythonpro/modules/admin.py | 32 ++++++++++------- 9 files changed, 133 insertions(+), 20 deletions(-) create mode 100644 pythonpro/cohorts/migrations/0002_liveclass.py create mode 100644 pythonpro/cohorts/tests/__init__.py rename pythonpro/cohorts/{tests.py => tests/test_cohorts.py} (72%) diff --git a/pythonpro/cohorts/admin.py b/pythonpro/cohorts/admin.py index 3bed0b41..e5e60767 100644 --- a/pythonpro/cohorts/admin.py +++ b/pythonpro/cohorts/admin.py @@ -1,9 +1,22 @@ from django.contrib import admin +from django.utils.safestring import mark_safe -from pythonpro.cohorts.models import Cohort +from pythonpro.cohorts.models import Cohort, LiveClass + + +class ClassInline(admin.TabularInline): + extra = 1 + model = LiveClass @admin.register(Cohort) class ModuleAdmin(admin.ModelAdmin): - list_display = 'title start end'.split() + inlines = [ClassInline] + prepopulated_fields = {'slug': ('title',)} + list_display = 'title start end page_link'.split() ordering = ('-start',) + + def page_link(self, cohort): + return mark_safe(f'See on Page') + + page_link.short_description = 'page' diff --git a/pythonpro/cohorts/facade.py b/pythonpro/cohorts/facade.py index 44c3e072..7385a9fa 100644 --- a/pythonpro/cohorts/facade.py +++ b/pythonpro/cohorts/facade.py @@ -1,5 +1,15 @@ -from pythonpro.cohorts.models import Cohort as _Cohort +from django.db.models import Prefetch + +from pythonpro.cohorts.models import Cohort as _Cohort, LiveClass def get_all_cohorts_desc(): return tuple(_Cohort.objects.order_by('-start')) + + +def find_cohort(slug): + return _Cohort.objects.filter(slug=slug).prefetch_related(Prefetch( + 'liveclass_set', + queryset=LiveClass.objects.order_by('start'), + to_attr='classes' + )).get() diff --git a/pythonpro/cohorts/migrations/0002_liveclass.py b/pythonpro/cohorts/migrations/0002_liveclass.py new file mode 100644 index 00000000..90c467a1 --- /dev/null +++ b/pythonpro/cohorts/migrations/0002_liveclass.py @@ -0,0 +1,23 @@ +# Generated by Django 2.0.6 on 2018-06-04 17:53 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('cohorts', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='LiveClass', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('start', models.DateTimeField()), + ('vimeo_id', models.CharField(blank=True, max_length=11)), + ('cohort', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cohorts.Cohort')), + ], + ), + ] diff --git a/pythonpro/cohorts/models.py b/pythonpro/cohorts/models.py index 38c8dc86..219f2d40 100644 --- a/pythonpro/cohorts/models.py +++ b/pythonpro/cohorts/models.py @@ -23,3 +23,9 @@ def get_absolute_url(self): class CohortStudent(models.Model): cohort = models.ForeignKey(Cohort, on_delete=models.DO_NOTHING) user = models.ForeignKey(get_user_model(), on_delete=models.DO_NOTHING) + + +class LiveClass(models.Model): + start = models.DateTimeField() + vimeo_id = models.CharField(max_length=11, db_index=False, blank=True) + cohort = models.ForeignKey(Cohort, models.CASCADE) diff --git a/pythonpro/cohorts/templates/cohorts/cohort_detail.html b/pythonpro/cohorts/templates/cohorts/cohort_detail.html index 02eaa96a..73d67ccd 100644 --- a/pythonpro/cohorts/templates/cohorts/cohort_detail.html +++ b/pythonpro/cohorts/templates/cohorts/cohort_detail.html @@ -38,7 +38,28 @@

Intruções

Passo 2
Se apresente no post do fórum
Passo 3
-
Entre no nosso grupo do Telegram para tirar dúvidas e interagir!
+
Entre no nosso grupo do + Telegram para tirar dúvidas e interagir! +
+ + +
+
+

Aulas ao Vivo

+ {% for class in cohort.classes %} +
{{ class.start }}
+
+ {% if class.vimeo_id %} +
+ +
+ {% else %} + Upload de Vídeo Pendente + {% endif %} +
+ {% endfor %}
diff --git a/pythonpro/cohorts/tests/__init__.py b/pythonpro/cohorts/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pythonpro/cohorts/tests.py b/pythonpro/cohorts/tests/test_cohorts.py similarity index 72% rename from pythonpro/cohorts/tests.py rename to pythonpro/cohorts/tests/test_cohorts.py index 5e5ba8d9..2358de62 100644 --- a/pythonpro/cohorts/tests.py +++ b/pythonpro/cohorts/tests/test_cohorts.py @@ -1,3 +1,5 @@ +import operator +from datetime import datetime, timedelta from os import path import pytest @@ -7,7 +9,8 @@ from model_mommy import mommy from pythonpro import settings -from pythonpro.cohorts.models import Cohort +from pythonpro.cohorts import facade +from pythonpro.cohorts.models import Cohort, LiveClass from pythonpro.django_assertions import dj_assert_contains, dj_assert_not_contains _img_path = path.join(settings.BASE_DIR, 'pythonpro', 'core', 'static', 'img', 'instructors', 'renzo-nuccitelli.png') @@ -82,3 +85,33 @@ def test_cohort_start(cohort: Cohort, resp): def test_cohort_end(cohort: Cohort, resp): dj_assert_contains(resp, date(cohort.end)) + + +@pytest.fixture +def live_classes(cohort): + now = datetime.now() + return [ + mommy.make(LiveClass, cohort=cohort, vimeo_id=str(i), start=now + timedelta(days=i)) for i in range(100, 105) + ] + + +@pytest.fixture +def resp_with_classes(live_classes, cohort, client): + assert len(live_classes) > 0 + return client.get(reverse('cohorts:detail', kwargs={'slug': cohort.slug}), secure=True) + + +def test_live_classes_are_sorted(live_classes: list, cohort): + live_classes.sort(key=operator.attrgetter('start')) + db_cohort = facade.find_cohort(slug=cohort.slug) + assert live_classes == db_cohort.classes + + +def test_live_classes_datetime(resp_with_classes, live_classes): + for live_class in live_classes: + dj_assert_contains(resp_with_classes, date(live_class.start)) + + +def test_live_classes_vimeo(resp_with_classes, live_classes): + for live_class in live_classes: + dj_assert_contains(resp_with_classes, live_class.vimeo_id) diff --git a/pythonpro/cohorts/views.py b/pythonpro/cohorts/views.py index 13ae33d8..104c4de9 100644 --- a/pythonpro/cohorts/views.py +++ b/pythonpro/cohorts/views.py @@ -1,10 +1,9 @@ from django.contrib.auth.decorators import login_required from django.shortcuts import render -# Create your views here. -from pythonpro.cohorts.models import Cohort +from pythonpro.cohorts import facade @login_required def detail(request, slug): - return render(request, 'cohorts/cohort_detail.html', {'cohort': Cohort.objects.get(slug=slug)}) + return render(request, 'cohorts/cohort_detail.html', {'cohort': facade.find_cohort(slug=slug)}) diff --git a/pythonpro/modules/admin.py b/pythonpro/modules/admin.py index d4ec764b..a3ee8c08 100644 --- a/pythonpro/modules/admin.py +++ b/pythonpro/modules/admin.py @@ -1,26 +1,34 @@ from django.contrib import admin +from django.utils.safestring import mark_safe from ordered_model.admin import OrderedModelAdmin from pythonpro.modules.models import Section, Module, Chapter, Topic -class ModuleAdmin(OrderedModelAdmin): - list_display = 'title slug order move_up_down_links'.split() +class BaseAdmin(OrderedModelAdmin): + prepopulated_fields = {'slug': ('title',)} + def page_link(self, content): + return mark_safe(f'See on Page') -class SectionAdmin(OrderedModelAdmin): - list_display = 'title slug module order move_up_down_links'.split() + page_link.short_description = 'page' -class ChapterAdmin(OrderedModelAdmin): - list_display = 'title slug section order move_up_down_links'.split() +@admin.register(Module) +class ModuleAdmin(BaseAdmin): + list_display = 'title slug order move_up_down_links page_link'.split() -class TopicAdmin(OrderedModelAdmin): - list_display = 'title slug chapter order move_up_down_links'.split() +@admin.register(Section) +class SectionAdmin(BaseAdmin): + list_display = 'title slug module order move_up_down_links page_link'.split() -admin.site.register(Module, ModuleAdmin) -admin.site.register(Section, SectionAdmin) -admin.site.register(Chapter, ChapterAdmin) -admin.site.register(Topic, TopicAdmin) +@admin.register(Chapter) +class ChapterAdmin(BaseAdmin): + list_display = 'title slug section order move_up_down_links page_link'.split() + + +@admin.register(Topic) +class TopicAdmin(BaseAdmin): + list_display = 'title slug chapter order move_up_down_links page_link'.split()