Commit 6df8f721 authored by Julien Palard's avatar Julien Palard
Browse files

Merge branch 'features/courses' into 'master'

added courses part1

See merge request !5
parents 28f1200a 92f36e7c
# How to contribute
## Requirements
This project requires Python 3.6 at least.
## Install
```
pip install -r requirements.txt
./manage.py migrate
......
......@@ -19,15 +19,17 @@ from django.conf import settings
from django.urls import path, include
from website.api import router
from website.views import (
chat,
dashboard_view,
ProfileView,
index,
ExerciseListView,
ExerciseView,
about,
LessonListView,
LessonView,
ProfileView,
StatsDetailView,
StatsListView,
about,
chat,
dashboard_view,
index,
)
......@@ -42,12 +44,14 @@ urlpatterns = [
path("accounts/", include("registration.backends.default.urls")),
path("dashboard/", dashboard_view, name="dashboard"),
path("exercises/", ExerciseListView.as_view(), name="exercises"),
path("favicon.ico", favicon_view),
path("exercises/<slug:slug>", ExerciseView.as_view(), name="exercise"),
path("favicon.ico", favicon_view),
path("profile/<int:pk>", ProfileView.as_view(), name="profile"),
path("stats/", StatsListView.as_view(), name="stats"),
path("stats/<int:pk>", StatsDetailView.as_view(), name="stats"),
path("chat/", chat, name="chat"),
path("lessons/", LessonListView.as_view(), name="lesson"),
path("lessons/<slug:slug>", LessonView.as_view(), name="lesson"),
]
if settings.DEBUG:
......
from django import forms
from django.contrib import admin
from django_ace import AceWidget
from website.models import Answer, Exercise, Snippet
from website.models import Answer, Exercise, Snippet, Lesson
from website.forms import AnswerForm
from registration.admin import RegistrationAdmin
from registration.models import RegistrationProfile
......@@ -38,12 +38,29 @@ class AdminExerciseForm(forms.ModelForm):
}
class AdminLessonForm(forms.ModelForm):
class Meta:
model = Lesson
fields = ("title", "slug", "is_published", "position", "content")
widgets = {
"content": AceWidget(
mode="markdown", theme="twilight", width="100%", height="800px"
)
}
class ExerciseAdmin(admin.ModelAdmin):
readonly_fields = ("id",)
list_display = ("title", "slug", "is_published", "position")
form = AdminExerciseForm
class LessonAdmin(admin.ModelAdmin):
readonly_fields = ("id",)
list_display = ("title", "slug", "is_published", "position")
form = AdminLessonForm
class AnswerAdmin(admin.ModelAdmin):
readonly_fields = ("user", "created_at", "corrected_at")
list_display = ("user", "exercise", "is_valid", "created_at", "is_corrected")
......@@ -67,6 +84,7 @@ class MyUserAdmin(UserAdmin):
admin.site.register(Answer, AnswerAdmin)
admin.site.register(Exercise, ExerciseAdmin)
admin.site.register(Lesson, LessonAdmin)
admin.site.register(Snippet, SnippetAdmin)
admin.site.unregister(RegistrationProfile)
......
# Generated by Django 2.1 on 2019-01-25 18:46
from django.db import migrations, models
import django_extensions.db.fields
class Migration(migrations.Migration):
dependencies = [
('website', '0008_auto_20190118_2151'),
]
operations = [
migrations.CreateModel(
name='Lesson',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=255)),
('slug', django_extensions.db.fields.AutoSlugField(blank=True, editable=False, populate_from=['title'])),
('content', models.TextField()),
('is_published', models.BooleanField(default=False)),
('position', models.FloatField(default=0)),
],
options={
'ordering': ['position'],
},
),
]
......@@ -58,6 +58,21 @@ class Answer(models.Model):
def get_absolute_url(self):
return reverse("exercise", args=[self.exercise.slug])
class Lesson(models.Model):
title = models.CharField(max_length=255)
slug = AutoSlugField(populate_from=["title"], editable=True)
content = models.TextField()
is_published = models.BooleanField(default=False)
position = models.FloatField(default=0)
class Meta:
ordering = ["position"]
def get_absolute_url(self):
return reverse("lesson", args=[self.slug])
def __str__(self):
return self.title
def cb_new_answer(sender, instance, created, **kwargs):
group = "answers.{}.{}".format(instance.user.id, instance.exercise.id)
......
{% extends "hkis/layout.html" %}
{% load hkis_extra %}
{% load i18n static bootstrap4 %}
{% block title %}{{ object.title }}{% endblock %}
{% block stylesheets %}
<link href="{% static "css/codehilite.css" %}" rel="stylesheet" type="text/css" />
{% endblock %}
{% block container %}
{% if next %}
<div class="float-right">
<a class="btn btn-primary" href="{% url "lesson" next %}">
Next →
</a>
</div>
{% endif %}
{{ object.content|markdown|md_to_bootstrap }}
{% endblock %}
{% extends "hkis/layout.html" %}
{% load i18n %}
{% load static %}
{% load bootstrap4 %}
{% block title %}Python course{% endblock %}
{% block container %}
<h1>Python lessons</h1>
<table class="table table-sm table-hover records_list">
<tbody>
{% for lesson in lessons %}
<tr onclick="if (!event.ctrlKey){location.href='{% url "lesson" lesson.slug %}';}">
<td>
<a href="{% url "lesson" lesson.slug %}">
<h5>{{ lesson.number }}. {{ lesson.title }}</h5>
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
......@@ -14,7 +14,7 @@ from django.contrib.auth.models import User
from django.views.generic.detail import DetailView
from django.views.generic.edit import CreateView, UpdateView
from django.views.generic.list import ListView
from website.models import Exercise, Answer
from website.models import Exercise, Answer, Lesson
from website.forms import AnswerForm
......@@ -46,7 +46,6 @@ class ProfileView(LoginRequiredMixin, UpdateView):
messages.info(self.request, "Profile updated")
return reverse("profile", kwargs={"pk": self.request.user.id})
class ExerciseListView(LoginRequiredMixin, ListView):
model = Exercise
template_name = "hkis/exercises.html"
......@@ -112,6 +111,43 @@ class ExerciseView(LoginRequiredMixin, DetailView):
context["next"] = None
return context
class LessonListView(LoginRequiredMixin, ListView):
model = Lesson
template_name = "hkis/lessons.html"
def get_queryset(self):
self.queryset = Lesson.objects.filter(is_published=True)
return super().get_queryset()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["lessons"] = [
{
**vars(lesson),
"number": i + 1,
}
for i, lesson in enumerate(self.object_list)
]
return context
class LessonView(LoginRequiredMixin, DetailView):
model = Lesson
template_name = "hkis/lesson.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
try:
context["next"] = (
Lesson.objects.filter(position__gt=self.object.position)
.order_by("position")[0]
.slug
)
except IndexError:
context["next"] = None
return context
class StatsListView(UserPassesTestMixin, ListView):
template_name = "hkis/stats_list.html"
......@@ -143,7 +179,7 @@ class StatsDetailView(UserPassesTestMixin, DetailView):
"is_valid": exercice.nb_valid_anwser > 0,
"slug": exercice.slug,
}
for exercice in Exercise.objects.annotate(
for exercice in Lesson.objects.annotate(
nb_anwser=Count(
"answers", filter=Q(answers__user_id=user.id)
),
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment