Merge branch 'feature/created_courses' into 'master'

Feature/created courses

See merge request lilcity/backend!3
remotes/origin/hasaccess
cfwme 8 years ago
commit 6aa3acdd8c
  1. 3
      .gitignore
  2. 0
      apps/course/__init__.py
  3. 13
      apps/course/admin.py
  4. 6
      apps/course/apps.py
  5. 6
      apps/course/manager.py
  6. 45
      apps/course/migrations/0001_initial.py
  7. 0
      apps/course/migrations/__init__.py
  8. 44
      apps/course/models.py
  9. 42
      apps/course/templates/course/_items.html
  10. 4
      apps/course/templates/course/course_items.html
  11. 53
      apps/course/templates/course/courses.html
  12. 5
      apps/course/templates/course/inclusion/category_items.html
  13. 0
      apps/course/templatetags/__init__.py
  14. 11
      apps/course/templatetags/lilcity_category.py
  15. 3
      apps/course/tests.py
  16. 14
      apps/course/views.py
  17. BIN
      docs/entities-diagram.png
  18. 2
      docs/overview.md
  19. 4
      project/settings.py
  20. 40
      project/templates/lilcity/index.html
  21. 11
      project/urls.py
  22. 4
      requirements.txt

3
.gitignore vendored

@ -109,4 +109,5 @@ venv.bak/
# JavaScript
.map
node_modules
node_modules
db.sqlite3

@ -0,0 +1,13 @@
from django.contrib import admin
from .models import Course, Category
@admin.register(Course)
class CourseAdmin(admin.ModelAdmin):
pass
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
pass

@ -0,0 +1,6 @@
from django.apps import AppConfig
class CourseConfig(AppConfig):
name = 'apps.course'
label = 'lilcity_course'

@ -0,0 +1,6 @@
from django.db import models
class CategoryQuerySet(models.QuerySet):
def all(self):
return self.filter()

@ -0,0 +1,45 @@
# Generated by Django 2.0.1 on 2018-01-22 11:43
from django.db import migrations, models
import django.db.models.deletion
import django.db.models.manager
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Category',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=100, verbose_name='Название категории')),
],
managers=[
('manager', django.db.models.manager.Manager()),
],
),
migrations.CreateModel(
name='Course',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_created=True)),
('title', models.CharField(max_length=100, verbose_name='Название курса')),
('short_description', models.TextField(verbose_name='Краткое описание курса')),
('background', models.ImageField(upload_to='courses', verbose_name='Фон курса')),
('price', models.DecimalField(blank=True, decimal_places=2, help_text='Если цены нету, то курс бесплатный', max_digits=10, null=True, verbose_name='Цена курса')),
('is_highlighted', models.BooleanField(default=False)),
('deferred_start', models.DateTimeField(blank=True, help_text='Заполнить если курс отложенный', null=True, verbose_name='Отложенный запуск курса')),
('update_at', models.DateTimeField(auto_now=True)),
('category', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='course.Category')),
],
options={
'verbose_name': 'Курс',
'verbose_name_plural': 'Курсы',
},
),
]

@ -0,0 +1,44 @@
from django.db import models
from django.utils import timezone
from .manager import CategoryQuerySet
class Course(models.Model):
title = models.CharField("Название курса", max_length=100)
short_description = models.TextField("Краткое описание курса")
background = models.ImageField("Фон курса", upload_to='courses')
price = models.DecimalField("Цена курса", help_text="Если цены нету, то курс бесплатный", max_digits=10, decimal_places=2, null=True, blank=True)
is_highlighted = models.BooleanField(default=False)
deferred_start = models.DateTimeField("Отложенный запуск курса", help_text="Заполнить если курс отложенный", null=True, blank=True)
category = models.ForeignKey("Category", on_delete=models.PROTECT)
created_at = models.DateTimeField(auto_created=True)
update_at = models.DateTimeField(auto_now=True)
def is_free(self):
if self.price:
return False
return True
def is_deferred_start(self):
if not self.deferred_start:
return False
if timezone.now() < self.deferred_start:
return True
return False
class Meta:
verbose_name = "Курс"
verbose_name_plural = "Курсы"
class Category(models.Model):
title = models.CharField("Название категории", max_length=100)
manager = CategoryQuerySet.as_manager()
def __str__(self):
return self.title

@ -0,0 +1,42 @@
{% load static %}
<div class="courses__item"><a class="courses__preview" href="#"><img class="courses__pic" src="{% get_media_prefix %}{{ course.background }}"/>
<div class="courses__view">Подробнее</div>
{% if course.is_highlighted %}
<div class="courses__label courses__label_fav"></div>
{% endif %}
{% if course.is_deferred_start %}
<div class="courses__soon">
<div class="courses__left">До запуска курса осталось:</div>
<div class="courses__time">16 часов 13 минут</div>
</div>
<div class="courses__label courses__label_clock"></div>
{% endif %}
</a>
<div class="courses__details"><a class="courses__theme theme {{ theme_color }}" href="#">АНИМАЦИЯ</a>
{% if not course.is_free %}<div class="courses__price">{{ course.price|floatformat:"-2" }}$</div>{% endif %}
</div>
<a class="courses__title" href="#">{{ course.title }}</a>
<div class="courses__content">{{ course.short_description }}
</div>
<div class="courses__user user">
<div class="user__ava ava"><img class="ava__pic" src={% static "img/user.jpg" %}/></div>
<div class="user__info">
<div class="user__name">Александра Неимоверноумная</div>
<div class="user__meta">
<div class="user__date">SEPT 12, 2017</div>
<a class="user__likes likes" href="#">
<div class="likes__counter">253</div>
<div class="likes__icon">
<svg class="icon icon-like">
<use xlink:href="{% get_static_prefix %}img/sprite.svg#icon-like"></use>
</svg>
<svg class="icon icon-like-fill">
<use xlink:href="{% get_static_prefix %}img/sprite.svg#icon-like-fill"></use>
</svg>
</div>
</a>
</div>
</div>
</div>
</div>

@ -0,0 +1,4 @@
{% for course in course_items %}
{% cycle '' 'theme_green' 'theme_violet' as theme_color silent %}
{% include "course/_items.html" %}
{% endfor %}

@ -0,0 +1,53 @@
{% extends "templates/lilcity/index.html" %}
{% load static %}
{% load category_items from lilcity_category %}
{% block content %}
<div class="main" style="background-image: url({% get_static_prefix %}img/bg-1.jpg);">
<div class="main__center center">
<div class="main__title">Онлайн-курсы LilCity</div>
</div>
</div>
<div class="section">
<div class="section__center center">
<div class="text text_lg">
<p>Учите и развивайте креативное мышление когда и где угодно. Если вам не совсем удобно заниматься с нами в прямом эфире каждый день как в
нашей онлайн-школе, специально для вас мы делаем отдельные уроки в записи, которые вы можете проходить
когда вам будем удобно.</p><img class="text__curve text__curve_four" src="{% get_static_prefix %}img/curve-4.svg" %} width="155"></div>
<div class="head">
<div class="head__title title title_center">Курсы</div>
<div class="head__right">
<div class="head__field field">
<div class="field__wrap">
<div class="field__select select js-select">
<div class="select__head js-select-head">Категории</div>
<div class="select__drop js-select-drop">
{% category_items %}
</div>
<input class="select__input" type="hidden"></div>
</div>
</div>
</div>
</div>
<div class="courses">
<div class="courses__list">
{% include "course/course_items.html" %}
</div>
<div class="courses__load load">
{% if page_obj.has_next %}
<button class="load__btn btn" data-next-page-url="?page={{ page_obj.next_page_number }}">Подгрузить еще</button>
{% endif %}
</div>
</div>
</div>
</div>
<div class="game">
<div class="game__center center">
<div class="game__wrap">
<div class="game__theme">LILCITY</div>
<div class="game__title">Приложения, развивающие игры и интерактивные книги от Lil City.</div>
<a class="game__btn btn btn_dark" href="#">УЗНАТЬ БОЛЬШЕ</a></div>
</div>
<div class="game__preview"><img class="game__pic" src="{% get_static_prefix %}img/icons.png" %}></div>
</div>
{% endblock content %}

@ -0,0 +1,5 @@
{% for category in category_items %}
<div class="select__option js-select-option">
<div class="select__title">{{ category.title }}</div>
</div>
{% endfor %}

@ -0,0 +1,11 @@
from django import template
from ..models import Category
register = template.Library()
@register.inclusion_tag('course/inclusion/category_items.html')
def category_items():
return {"category_items": Category.manager.all()}

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

@ -0,0 +1,14 @@
from django.views.generic import ListView
from .models import Course
class CoursesView(ListView):
model = Course
context_object_name = "course_items"
paginate_by = 6
def get_template_names(self):
if self.request.is_ajax():
return 'course/course_items.html'
return "course/courses.html"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 KiB

@ -45,4 +45,4 @@ CRUD для целей доступа к персистентным хранил
### Диаграмма сущностей проекта
![Диаграмма сущностей проекта.png](entities-diagram.png)
Актуальная версия: [Google Drive](https://drive.google.com/file/d/1OPt3_M0VrVBkRK1M4LTLJ-UMFEwojMzc/view?usp=sharing)

@ -39,11 +39,13 @@ INSTALLED_APPS = [
'django.contrib.staticfiles',
] + [
'anymail',
'active_link',
] + [
'apps.auth.apps',
'apps.user',
'apps.notification',
'apps.payment',
'apps.course',
]
MIDDLEWARE = [
@ -153,3 +155,5 @@ DEFAULT_FROM_EMAIL = "postmaster@mail.9ev.ru"
TWILIO_ACCOUNT = 'ACdf4a96b776cc764bc3ec0f0e136ba550'
TWILIO_TOKEN = '559a6b1fce121759c9af2dcbb3f755ea'
TWILIO_FROM_PHONE = '+37128914409'
ACTIVE_LINK_STRICT = True

@ -1,4 +1,6 @@
{% load static %}
{% load active_link_tags %}
<!DOCTYPE html>
<html>
@ -52,7 +54,7 @@
<use xlink:href={% static "img/sprite.svg" %}#icon-menu></use>
</svg>
</button>
<a class="header__logo logo" href="#"></a>
<a class="header__logo logo" href="/"></a>
<div class="header__wrap js-header-wrap">
<div class="header__top">
<button class="header__close js-header-close">
@ -70,7 +72,7 @@
</form>
</div>
<nav class="header__nav">
<div class="header__group"><a class="header__section header__section_sub js-header-section active" href="#">ОНЛАЙН-ШКОЛА</a>
<div class="header__group"><a class="header__section header__section_sub js-header-section {% active_link 'index' %}" href="{% url 'index' %}">ОНЛАЙН-ШКОЛА</a>
<div class="header__list js-header-list">
<a class="header__link" href="#">
<div class="header__title">О школе</div>
@ -104,7 +106,7 @@
</a>
</div>
</div>
<div class="header__group"><a class="header__section header__section_sub js-header-section" href="#">ВИДЕО-КУРСЫ</a>
<div class="header__group"><a class="header__section header__section_sub js-header-section {% active_link 'courses' %}" href="{% url 'courses' %}">ВИДЕО-КУРСЫ</a>
<div class="header__list js-header-list">
<a class="header__link" href="#">
<div class="header__title">ПЕРСОНАЖ</div>
@ -147,40 +149,12 @@
</div>
</header>
<div class="container">
<div class="center" style="padding-top: 50px; padding-bottom: 50px;">
<div><a href="email/index.html">email.html</a></div>
<br>
<div><a href="ui-kit.html">ui-kit.html</a></div>
<br>
<div><a href="main.html">main.html</a></div>
<div><a href="main-online.html">main-online.html</a></div>
<div><a href="main-online-soon.html">main-online-soon.html</a></div>
<div><a href="courses.html">courses.html</a></div>
<div><a href="result.html">result.html</a></div>
<div><a href="result-empty.html">result-empty.html</a></div>
<div><a href="course.html">course.html</a></div>
<div><a href="course-v2.html">course-v2.html</a></div>
<div><a href="course-soon.html">course-soon.html</a></div>
<div><a href="course-lock.html">course-lock.html</a></div>
<div><a href="course-bought.html">course-bought.html</a></div>
<div><a href="lesson.html">lesson.html</a></div>
<div><a href="profile.html">profile.html</a></div>
<div><a href="profile-settings.html">profile-settings.html</a></div>
<div><a href="profile-alerts.html">profile-alerts.html</a></div>
<div><a href="become-author.html">become-author.html</a></div>
<div><a href="history-transactions.html">history-transactions.html</a></div>
<div><a href="success-payment.html">success-payment.html</a></div>
<div><a href="success-application.html">success-application.html</a></div>
<br>
<div><a href="course-add.html">course-add.html</a></div>
<div><a href="course-add-lessons.html">course-add-lessons.html</a></div>
<div><a href="lesson-add.html">lesson-add.html</a></div>
</div>
{% block content %}{% endblock content %}
</div>
<footer class="footer">
<div class="footer__center center">
<div class="footer__row footer__row_first">
<div class="footer__col footer__col_md"><a class="footer__logo logo" href="#"></a>
<div class="footer__col footer__col_md"><a class="footer__logo logo" href="/"></a>
<div class="footer__content">Первая онлайн-школа креативного мышления Lil City School</div>
</div>
<div class="footer__col">

@ -16,9 +16,18 @@ Including another URLconf
from django.contrib import admin
from django.urls import path, include
from django.views.generic import TemplateView
from django.conf import settings
from apps.course.views import CoursesView
urlpatterns = [
path('admin/', admin.site.urls),
path('auth/', include(('apps.auth.urls', 'lilcity'))),
path('', TemplateView.as_view(template_name="templates/lilcity/index.html")),
path('courses/', CoursesView.as_view(), name='courses'),
path('', TemplateView.as_view(template_name="templates/lilcity/index.html"), name='index'),
]
if settings.DEBUG:
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

@ -4,4 +4,6 @@ django-anymail[mailgun]==1.2
paymentwall-python==1.0.7
twilio==6.10.0
psycopg2==2.7.3.2
facepy==1.0.9
facepy==1.0.9
Pillow==5.0.0
django-active-link==0.1.2

Loading…
Cancel
Save