diff --git a/pythonpro/cohorts/__init__.py b/pythonpro/cohorts/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/pythonpro/cohorts/admin.py b/pythonpro/cohorts/admin.py
new file mode 100644
index 00000000..3bed0b41
--- /dev/null
+++ b/pythonpro/cohorts/admin.py
@@ -0,0 +1,9 @@
+from django.contrib import admin
+
+from pythonpro.cohorts.models import Cohort
+
+
+@admin.register(Cohort)
+class ModuleAdmin(admin.ModelAdmin):
+ list_display = 'title start end'.split()
+ ordering = ('-start',)
diff --git a/pythonpro/cohorts/apps.py b/pythonpro/cohorts/apps.py
new file mode 100644
index 00000000..60cc3369
--- /dev/null
+++ b/pythonpro/cohorts/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class CohortsConfig(AppConfig):
+ name = 'pythonpro.cohorts'
diff --git a/pythonpro/cohorts/context_processors.py b/pythonpro/cohorts/context_processors.py
new file mode 100644
index 00000000..d49c2a0e
--- /dev/null
+++ b/pythonpro/cohorts/context_processors.py
@@ -0,0 +1,11 @@
+from pythonpro.cohorts.facade import get_all_cohorts_desc
+
+
+def global_settings(request):
+ # return any necessary values
+ if not request.user.is_authenticated:
+ return {}
+
+ return {
+ 'ALL_COHORTS': get_all_cohorts_desc(),
+ }
diff --git a/pythonpro/cohorts/facade.py b/pythonpro/cohorts/facade.py
new file mode 100644
index 00000000..44c3e072
--- /dev/null
+++ b/pythonpro/cohorts/facade.py
@@ -0,0 +1,5 @@
+from pythonpro.cohorts.models import Cohort as _Cohort
+
+
+def get_all_cohorts_desc():
+ return tuple(_Cohort.objects.order_by('-start'))
diff --git a/pythonpro/cohorts/migrations/0001_initial.py b/pythonpro/cohorts/migrations/0001_initial.py
new file mode 100644
index 00000000..b0ed74b3
--- /dev/null
+++ b/pythonpro/cohorts/migrations/0001_initial.py
@@ -0,0 +1,46 @@
+# Generated by Django 2.0.6 on 2018-06-03 23:26
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+ initial = True
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Cohort',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('title', models.CharField(max_length=50)),
+ ('slug', models.SlugField()),
+ ('image', models.ImageField(upload_to='cohorts/')),
+ ('quote', models.TextField()),
+ ('mail_list', models.URLField()),
+ ('forum_post', models.URLField()),
+ ('start', models.DateField()),
+ ('end', models.DateField()),
+ ],
+ ),
+ migrations.CreateModel(
+ name='CohortStudent',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('cohort', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='cohorts.Cohort')),
+ (
+ 'user',
+ models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL)
+ ),
+ ],
+ ),
+ migrations.AddField(
+ model_name='cohort',
+ name='students',
+ field=models.ManyToManyField(through='cohorts.CohortStudent', to=settings.AUTH_USER_MODEL),
+ ),
+ ]
diff --git a/pythonpro/cohorts/migrations/__init__.py b/pythonpro/cohorts/migrations/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/pythonpro/cohorts/models.py b/pythonpro/cohorts/models.py
new file mode 100644
index 00000000..38c8dc86
--- /dev/null
+++ b/pythonpro/cohorts/models.py
@@ -0,0 +1,25 @@
+from django.contrib.auth import get_user_model
+from django.db import models
+
+# Create your models here.
+from django.urls import reverse
+
+
+class Cohort(models.Model):
+ title = models.CharField(max_length=50)
+ slug = models.SlugField()
+ image = models.ImageField(upload_to='cohorts/')
+ quote = models.TextField()
+ mail_list = models.URLField()
+ forum_post = models.URLField()
+ start = models.DateField()
+ end = models.DateField()
+ students = models.ManyToManyField(get_user_model(), through='CohortStudent')
+
+ def get_absolute_url(self):
+ return reverse('cohorts:detail', kwargs={'slug': self.slug})
+
+
+class CohortStudent(models.Model):
+ cohort = models.ForeignKey(Cohort, on_delete=models.DO_NOTHING)
+ user = models.ForeignKey(get_user_model(), on_delete=models.DO_NOTHING)
diff --git a/pythonpro/cohorts/templates/cohorts/cohort_detail.html b/pythonpro/cohorts/templates/cohorts/cohort_detail.html
new file mode 100644
index 00000000..02eaa96a
--- /dev/null
+++ b/pythonpro/cohorts/templates/cohorts/cohort_detail.html
@@ -0,0 +1,45 @@
+{% extends 'core/base.html' %}
+{% load static %}
+
+{% block title %}Turma {{ cohort.title }}{% endblock %}
+
+{% block body %}
+
+
+
+
Turma {{ cohort.title }}
+

+
+ "{{ cohort.quote }}"
+
+
+
+ Inicio:
+
+
+ {{ cohort.start }}
+
+
+ Fim:
+
+
+ {{ cohort.end }}
+
+
+
+
+
+
+{% endblock body %}
diff --git a/pythonpro/cohorts/tests.py b/pythonpro/cohorts/tests.py
new file mode 100644
index 00000000..56f87f71
--- /dev/null
+++ b/pythonpro/cohorts/tests.py
@@ -0,0 +1,71 @@
+from os import path
+
+import pytest
+from django.core.files.uploadedfile import SimpleUploadedFile
+from django.template.defaultfilters import date
+from django.urls import reverse
+from model_mommy import mommy
+
+from pythonpro import settings
+from pythonpro.cohorts.models import Cohort
+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')
+
+
+@pytest.fixture
+def cohort(client, django_user_model):
+ user = mommy.make(django_user_model)
+ client.force_login(user)
+ image = SimpleUploadedFile(name='renzo-nuccitelli.png', content=open(_img_path, 'rb').read(),
+ content_type='image/png')
+ cohort = mommy.make(Cohort, slug='guido-van-rossum', students=[user], image=image)
+ return cohort
+
+
+@pytest.fixture
+def resp(client, cohort):
+ resp = client.get(reverse('cohorts:detail', kwargs={'slug': cohort.slug}), secure=True)
+ return resp
+
+
+def test_cohort_links_for_logged_user(client, django_user_model):
+ user = mommy.make(django_user_model)
+ client.force_login(user)
+ image = SimpleUploadedFile(name='renzo-nuccitelli.png', content=open(_img_path, 'rb').read(),
+ content_type='image/png')
+ cohorts = mommy.make(Cohort, 4, image=image)
+ resp = client.get('/', secure=True)
+ for c in cohorts:
+ dj_assert_contains(resp, c.get_absolute_url())
+
+
+@pytest.mark.django_db
+def test_cohort_links_not_avaliable_for_no_user(client):
+ image = SimpleUploadedFile(name='renzo-nuccitelli.png', content=open(_img_path, 'rb').read(),
+ content_type='image/png')
+ cohorts = mommy.make(Cohort, 4, image=image)
+ resp = client.get('/', secure=True)
+ for c in cohorts:
+ dj_assert_not_contains(resp, c.get_absolute_url())
+
+
+def test_status_code(resp):
+ assert 200 == resp.status_code
+
+
+@pytest.mark.parametrize('property_name', 'title mail_list forum_post'.split())
+def test_cohort_propeties(cohort, resp, property_name):
+ dj_assert_contains(resp, getattr(cohort, property_name))
+
+
+def test_cohort_img(cohort: Cohort, resp):
+ dj_assert_contains(resp, cohort.image.url)
+
+
+def test_cohort_start(cohort: Cohort, resp):
+ dj_assert_contains(resp, date(cohort.start))
+
+
+def test_cohort_end(cohort: Cohort, resp):
+ dj_assert_contains(resp, date(cohort.end))
diff --git a/pythonpro/cohorts/urls.py b/pythonpro/cohorts/urls.py
new file mode 100644
index 00000000..0882698d
--- /dev/null
+++ b/pythonpro/cohorts/urls.py
@@ -0,0 +1,8 @@
+from django.urls import path
+
+from pythonpro.cohorts import views
+
+app_name = 'cohorts'
+urlpatterns = [
+ path('/', views.detail, name='detail'),
+]
diff --git a/pythonpro/cohorts/views.py b/pythonpro/cohorts/views.py
new file mode 100644
index 00000000..4af7ffcd
--- /dev/null
+++ b/pythonpro/cohorts/views.py
@@ -0,0 +1,8 @@
+from django.shortcuts import render
+
+# Create your views here.
+from pythonpro.cohorts.models import Cohort
+
+
+def detail(request, slug):
+ return render(request, 'cohorts/cohort_detail.html', {'cohort': Cohort.objects.get(slug=slug)})
diff --git a/pythonpro/core/migrations/0005_auto_20180603_2026.py b/pythonpro/core/migrations/0005_auto_20180603_2026.py
new file mode 100644
index 00000000..ba8abe93
--- /dev/null
+++ b/pythonpro/core/migrations/0005_auto_20180603_2026.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.0.6 on 2018-06-03 23:26
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('core', '0004_auto_20180601_1108'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='user',
+ name='first_name',
+ field=models.CharField(max_length=30, verbose_name='first name'),
+ ),
+ ]
diff --git a/pythonpro/core/models.py b/pythonpro/core/models.py
index bd2884a2..3db82547 100644
--- a/pythonpro/core/models.py
+++ b/pythonpro/core/models.py
@@ -52,8 +52,7 @@ def get_full_name(self):
"""
Return the first_name plus the last_name, with a space in between.
"""
- full_name = '%s %s' % (self.first_name, self.last_name)
- return full_name.strip()
+ return self.get_short_name()
def get_short_name(self):
"""Return the short name for the user."""
diff --git a/pythonpro/core/templates/core/base.html b/pythonpro/core/templates/core/base.html
index e50db30d..197e3ad3 100644
--- a/pythonpro/core/templates/core/base.html
+++ b/pythonpro/core/templates/core/base.html
@@ -11,7 +11,7 @@
-
+