Skip to content

Commit 2fd8b47

Browse files
renzonrenzon
authored andcommitted
Implemented Cohorts's Live Classes
close #425 Changed modules/sections/chapters/topics to auto populate slug as well
1 parent 9f42e91 commit 2fd8b47

File tree

9 files changed

+133
-20
lines changed

9 files changed

+133
-20
lines changed

pythonpro/cohorts/admin.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,22 @@
11
from django.contrib import admin
2+
from django.utils.safestring import mark_safe
23

3-
from pythonpro.cohorts.models import Cohort
4+
from pythonpro.cohorts.models import Cohort, LiveClass
5+
6+
7+
class ClassInline(admin.TabularInline):
8+
extra = 1
9+
model = LiveClass
410

511

612
@admin.register(Cohort)
713
class ModuleAdmin(admin.ModelAdmin):
8-
list_display = 'title start end'.split()
14+
inlines = [ClassInline]
15+
prepopulated_fields = {'slug': ('title',)}
16+
list_display = 'title start end page_link'.split()
917
ordering = ('-start',)
18+
19+
def page_link(self, cohort):
20+
return mark_safe(f'<a href="{cohort.get_absolute_url()}">See on Page</a>')
21+
22+
page_link.short_description = 'page'

pythonpro/cohorts/facade.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
1-
from pythonpro.cohorts.models import Cohort as _Cohort
1+
from django.db.models import Prefetch
2+
3+
from pythonpro.cohorts.models import Cohort as _Cohort, LiveClass
24

35

46
def get_all_cohorts_desc():
57
return tuple(_Cohort.objects.order_by('-start'))
8+
9+
10+
def find_cohort(slug):
11+
return _Cohort.objects.filter(slug=slug).prefetch_related(Prefetch(
12+
'liveclass_set',
13+
queryset=LiveClass.objects.order_by('start'),
14+
to_attr='classes'
15+
)).get()
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Generated by Django 2.0.6 on 2018-06-04 17:53
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('cohorts', '0001_initial'),
11+
]
12+
13+
operations = [
14+
migrations.CreateModel(
15+
name='LiveClass',
16+
fields=[
17+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18+
('start', models.DateTimeField()),
19+
('vimeo_id', models.CharField(blank=True, max_length=11)),
20+
('cohort', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cohorts.Cohort')),
21+
],
22+
),
23+
]

pythonpro/cohorts/models.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,9 @@ def get_absolute_url(self):
2323
class CohortStudent(models.Model):
2424
cohort = models.ForeignKey(Cohort, on_delete=models.DO_NOTHING)
2525
user = models.ForeignKey(get_user_model(), on_delete=models.DO_NOTHING)
26+
27+
28+
class LiveClass(models.Model):
29+
start = models.DateTimeField()
30+
vimeo_id = models.CharField(max_length=11, db_index=False, blank=True)
31+
cohort = models.ForeignKey(Cohort, models.CASCADE)

pythonpro/cohorts/templates/cohorts/cohort_detail.html

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,28 @@ <h2 class="mt-5">Intruções</h2>
3838
<dt>Passo 2</dt>
3939
<dd>Se apresente no <a href="{{ cohort.forum_post }}" target="_blank">post do fórum</a></dd>
4040
<dt>Passo 3</dt>
41-
<dd>Entre no nosso <a href="https://t.me/joinchat/DZ2HMkC_wRSHCm5YwIM1UQ" target="_blank">grupo do Telegram</a> para tirar dúvidas e interagir!</dd>
41+
<dd>Entre no nosso <a href="https://t.me/joinchat/DZ2HMkC_wRSHCm5YwIM1UQ" target="_blank">grupo do
42+
Telegram</a> para tirar dúvidas e interagir!
43+
</dd>
44+
</div>
45+
</div>
46+
<div class="row">
47+
<div class="col">
48+
<h2 class="mt-3">Aulas ao Vivo</h2>
49+
{% for class in cohort.classes %}
50+
<dt>{{ class.start }}</dt>
51+
<dd>
52+
{% if class.vimeo_id %}
53+
<div class="embed-container mb-3">
54+
<iframe src="https://player.vimeo.com/video/{{ class.vimeo_id }}" width="640"
55+
height="360" frameborder="0"
56+
webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
57+
</div>
58+
{% else %}
59+
Upload de Vídeo Pendente
60+
{% endif %}
61+
</dd>
62+
{% endfor %}
4263
</div>
4364
</div>
4465
</div>

pythonpro/cohorts/tests/__init__.py

Whitespace-only changes.
Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import operator
2+
from datetime import datetime, timedelta
13
from os import path
24

35
import pytest
@@ -7,7 +9,8 @@
79
from model_mommy import mommy
810

911
from pythonpro import settings
10-
from pythonpro.cohorts.models import Cohort
12+
from pythonpro.cohorts import facade
13+
from pythonpro.cohorts.models import Cohort, LiveClass
1114
from pythonpro.django_assertions import dj_assert_contains, dj_assert_not_contains
1215

1316
_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):
8285

8386
def test_cohort_end(cohort: Cohort, resp):
8487
dj_assert_contains(resp, date(cohort.end))
88+
89+
90+
@pytest.fixture
91+
def live_classes(cohort):
92+
now = datetime.now()
93+
return [
94+
mommy.make(LiveClass, cohort=cohort, vimeo_id=str(i), start=now + timedelta(days=i)) for i in range(100, 105)
95+
]
96+
97+
98+
@pytest.fixture
99+
def resp_with_classes(live_classes, cohort, client):
100+
assert len(live_classes) > 0
101+
return client.get(reverse('cohorts:detail', kwargs={'slug': cohort.slug}), secure=True)
102+
103+
104+
def test_live_classes_are_sorted(live_classes: list, cohort):
105+
live_classes.sort(key=operator.attrgetter('start'))
106+
db_cohort = facade.find_cohort(slug=cohort.slug)
107+
assert live_classes == db_cohort.classes
108+
109+
110+
def test_live_classes_datetime(resp_with_classes, live_classes):
111+
for live_class in live_classes:
112+
dj_assert_contains(resp_with_classes, date(live_class.start))
113+
114+
115+
def test_live_classes_vimeo(resp_with_classes, live_classes):
116+
for live_class in live_classes:
117+
dj_assert_contains(resp_with_classes, live_class.vimeo_id)

pythonpro/cohorts/views.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
from django.contrib.auth.decorators import login_required
22
from django.shortcuts import render
33

4-
# Create your views here.
5-
from pythonpro.cohorts.models import Cohort
4+
from pythonpro.cohorts import facade
65

76

87
@login_required
98
def detail(request, slug):
10-
return render(request, 'cohorts/cohort_detail.html', {'cohort': Cohort.objects.get(slug=slug)})
9+
return render(request, 'cohorts/cohort_detail.html', {'cohort': facade.find_cohort(slug=slug)})

pythonpro/modules/admin.py

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,34 @@
11
from django.contrib import admin
2+
from django.utils.safestring import mark_safe
23
from ordered_model.admin import OrderedModelAdmin
34

45
from pythonpro.modules.models import Section, Module, Chapter, Topic
56

67

7-
class ModuleAdmin(OrderedModelAdmin):
8-
list_display = 'title slug order move_up_down_links'.split()
8+
class BaseAdmin(OrderedModelAdmin):
9+
prepopulated_fields = {'slug': ('title',)}
910

11+
def page_link(self, content):
12+
return mark_safe(f'<a href="{content.get_absolute_url()}">See on Page</a>')
1013

11-
class SectionAdmin(OrderedModelAdmin):
12-
list_display = 'title slug module order move_up_down_links'.split()
14+
page_link.short_description = 'page'
1315

1416

15-
class ChapterAdmin(OrderedModelAdmin):
16-
list_display = 'title slug section order move_up_down_links'.split()
17+
@admin.register(Module)
18+
class ModuleAdmin(BaseAdmin):
19+
list_display = 'title slug order move_up_down_links page_link'.split()
1720

1821

19-
class TopicAdmin(OrderedModelAdmin):
20-
list_display = 'title slug chapter order move_up_down_links'.split()
22+
@admin.register(Section)
23+
class SectionAdmin(BaseAdmin):
24+
list_display = 'title slug module order move_up_down_links page_link'.split()
2125

2226

23-
admin.site.register(Module, ModuleAdmin)
24-
admin.site.register(Section, SectionAdmin)
25-
admin.site.register(Chapter, ChapterAdmin)
26-
admin.site.register(Topic, TopicAdmin)
27+
@admin.register(Chapter)
28+
class ChapterAdmin(BaseAdmin):
29+
list_display = 'title slug section order move_up_down_links page_link'.split()
30+
31+
32+
@admin.register(Topic)
33+
class TopicAdmin(BaseAdmin):
34+
list_display = 'title slug chapter order move_up_down_links page_link'.split()

0 commit comments

Comments
 (0)