From 0b88b8022c4ac3b6efce84c285dc0f1afb3226bc Mon Sep 17 00:00:00 2001 From: Tomas Gavenciak Date: Sat, 14 Mar 2015 22:27:04 +0100 Subject: [PATCH] Add CKEditor +configure, first tests, test data generation --- mamweb/settings_common.py | 45 ++++++++++++++ mamweb/urls.py | 1 + requirements.txt | 1 + seminar/admin.py | 13 +++- seminar/management/commands/testdata.py | 83 ++++--------------------- seminar/models.py | 17 +++++ seminar/tests.py | 80 +++++++++++++++++++++++- 7 files changed, 167 insertions(+), 73 deletions(-) diff --git a/mamweb/settings_common.py b/mamweb/settings_common.py index f6ebadae..c5b186c1 100644 --- a/mamweb/settings_common.py +++ b/mamweb/settings_common.py @@ -90,6 +90,7 @@ INSTALLED_APPS = ( 'reversion', 'django_countries', 'solo', + 'ckeditor', # MaMweb 'mamweb', @@ -97,9 +98,53 @@ INSTALLED_APPS = ( ) +DEBUG_TOOLBAR_CONFIG = { + 'SHOW_COLLAPSED': True, +} + +SUMMERNOTE_CONFIG = { + 'iframe': False, + 'airMode': False, + 'attachment_require_authentication': True, + 'width': '80%', +# 'height': '30em', + 'toolbar': [ + ['style', ['style']], + ['font', ['bold', 'italic', 'superscript', 'subscript', 'clear']], + ['color', ['color']], + ['para', ['ul', 'ol', 'paragraph']], + ['table', ['table']], + ['insert', ['link', 'picture', 'hr']], + ['view', ['fullscreen', 'codeview']], + ['help', ['help']], + ] +} + +CKEDITOR_UPLOAD_PATH = "uploads/" +CKEDITOR_IMAGE_BACKEND = 'pillow' +#CKEDITOR_JQUERY_URL = '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js' +CKEDITOR_CONFIGS = { + 'default': { + 'toolbar': [ + ['Source', 'ShowBlocks', '-', 'Maximize'], + ['Bold', 'Italic', 'Subscript', 'Superscript', '-', 'RemoveFormat'], + ['NumberedList','BulletedList','-','Blockquote','-','JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'], + ['Link', 'Unlink', 'Anchor', '-', 'Image', 'Table', 'HorizontalRule'], + ['Format'], + + ], +# 'toolbar': 'full', + 'height': '40em', + 'width': '100%', + 'toolbarStartupExpanded': False + }, +} + # MaM specific SEMINAR_RESENI_DIR = os.path.join(BASE_DIR, 'media', 'reseni') + + diff --git a/mamweb/urls.py b/mamweb/urls.py index 7e22c28f..de51d3da 100644 --- a/mamweb/urls.py +++ b/mamweb/urls.py @@ -7,6 +7,7 @@ from django.conf import settings urlpatterns = i18n_patterns('', url(r'^admin/', include(admin.site.urls)), # NOQA + url(r'^ckeditor/', include('ckeditor.urls')), url(r'^', include('seminar.urls')), ) diff --git a/requirements.txt b/requirements.txt index b1422055..21fc890d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,6 +15,7 @@ django-reversion==1.8.5 django-sekizai==0.8.1 django-countries==3.2 django-solo==1.1.0 +django-ckeditor==4.4.7 # debug tools/extensions diff --git a/seminar/admin.py b/seminar/admin.py index 87488893..aadf7562 100644 --- a/seminar/admin.py +++ b/seminar/admin.py @@ -1,7 +1,10 @@ # -*- coding: utf-8 -*- from django.contrib import admin -from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni +from django import forms from solo.admin import SingletonModelAdmin +from ckeditor.widgets import CKEditorWidget + +from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni ### Nastaveni @@ -106,7 +109,15 @@ class ReseniInline(admin.StackedInline): ### Problem +class ProblemAdminForm(forms.ModelForm): + text_problemu = forms.CharField(widget=CKEditorWidget()) + text_problemu_org = forms.CharField(widget=CKEditorWidget()) + class Meta: + model = Problem + exclude = [] + class ProblemAdmin(admin.ModelAdmin): + form = ProblemAdminForm # readonly_fields = ['autor'] fieldsets = [ (None, {'fields': ['nazev', 'typ', 'stav', 'autor']}), diff --git a/seminar/management/commands/testdata.py b/seminar/management/commands/testdata.py index dfba605f..775161c5 100644 --- a/seminar/management/commands/testdata.py +++ b/seminar/management/commands/testdata.py @@ -8,6 +8,12 @@ from django.core.management.base import NoArgsCommand from django.core.management import call_command from django.conf import settings +from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni +from seminar.tests import create_test_data +import django.contrib.auth +User = django.contrib.auth.get_user_model() + + class Command(NoArgsCommand): help = "Clear database and load testing data." @@ -18,76 +24,13 @@ class Command(NoArgsCommand): os.rename(dbfile, dbfile + '.old') self.stderr.write('Stara databaze prejmenovana na "%s"' % (dbfile + '.old')) call_command('migrate', noinput=True) - self.load_testing_data() - - def load_testing_data(self): - from seminar import models as sm - import django.contrib.auth - User = django.contrib.auth.get_user_model() - - self.stderr.write('Vytvarim uzivatele "admin" (heslo "admin")') - - # pevna pseudo-nahodnost - rnd = random.Random(x=42) - - # users - admin = User.objects.create_superuser(username='admin', email='', password='admin') - - self.stderr.write('Vytvarim pseudo-nahodna data') - - orgs = [] - for org in ['anet', 'bara', 'cyril', 'david', 'eva', 'filip']: - o = User.objects.create_user(username=org, password=org) - o.first_name = org.capitalize() - o.save() - orgs.append(o) - - # skoly - sm.Skola.objects.create(mesto = u'Praha', stat='CZ', psc='101 00', ulice=u'Krátká 5', nazev=u'První SŠ') - sm.Skola.objects.create(mesto = u'Praha', stat='CZ', psc='102 00', ulice=u'Dlouhá 5', nazev=u'Druhá SŠ') - sm.Skola.objects.create(mesto = u'Praha', stat='CZ', psc='103 00', ulice=u'Široká 3', nazev=u'Třetí SŠ') - sm.Skola.objects.create(mesto = u'Ostrava', stat='CZ', psc='700 00', ulice=u'Hluboká 42', nazev=u'Hutní gympl') - sm.Skola.objects.create(mesto = u'Humenné', stat='SK', psc='012 34', ulice=u'Pltká 1', nazev=u'Sredná škuola') - - # resitele - for i in range(20): - skola = rnd.choice(sm.Skola.objects.all()) - pohlavi = rnd.randint(0,1) - sm.Resitel.objects.create(skola = skola, datum_prihlaseni = datetime.date(rnd.randint(2002, 2014), rnd.randint(1,12), 1), - jmeno = u'Řešitel' if pohlavi else u'Řešitelka', prijmeni = 'Číslo-%s' % (i), rok_maturity = rnd.randint(2015, 2019), - stat = skola.stat, zasilat = sm.Resitel.ZASILAT_NIKAM, pohlavi_muz = pohlavi) - - # rocniky - for ri in range(17, 22): - r = sm.Rocnik.objects.create(prvni_rok = 1993 + ri, rocnik = ri) - - # cisla - cisel = rnd.randint(4, 6) - cs = {} - for ci in range(1, cisel + 1): - vydano = datetime.date(r.prvni_rok, ci + 6, 1) - deadline = datetime.date(r.prvni_rok, ci + 8, 1) if ci + 2 < cisel else None - c = sm.Cislo.objects.create(rocnik = r, cislo = str(ci), datum_vydani=vydano, datum_deadline=deadline) - cs[ci] = c - - # problemy resene v ci - seq='#ABCDEFGHIJKLMNOPQRSTUVWXYZ' - if ci >= 3: - for pi in range(1, 4): - p = sm.Problem.objects.create(autor = rnd.choice(orgs), cislo_zadani=cs[ci-2], cislo_reseni=cs[ci], - opravovatel = rnd.choice(orgs), kod = str(pi), nazev = u'Dummy úloha %s-%s' % (seq[ci], seq[pi]), - stav = sm.Problem.STAV_ZADANY, typ = sm.Problem.TYP_ULOHA, body = rnd.randint(1, 5)) - - for resi in range(rnd.randint(0, 7)): - res = sm.Reseni.objects.create(problem = p, resitel = rnd.choice(sm.Resitel.objects.all()), - body = rnd.randint(0, p.body), cislo_body = cs[ci]) - - nastaveni = sm.Nastaveni.objects.create(aktualni_rocnik = sm.Rocnik.objects.last(), - aktualni_cislo = sm.Cislo.objects.last()) - - self.stderr.write('Vytvoreno %d uzivatelu, %d skol, %d resitelu, %d rocniku, %d cisel, %d problemu, %d reseni.' % - (User.objects.count(), sm.Skola.objects.count(), sm.Resitel.objects.count(), sm.Rocnik.objects.count(), - sm.Cislo.objects.count(), sm.Problem.objects.count(), sm.Reseni.objects.count())) + self.stdout.write('Vytvarim uzivatele "admin" (heslo "admin") a pseudo-nahodna data ...') + create_test_data() + self.stdout.write('Vytvoreno %d uzivatelu, %d skol, %d resitelu, %d rocniku, %d cisel, %d problemu, %d reseni.' % + (User.objects.count(), Skola.objects.count(), Resitel.objects.count(), Rocnik.objects.count(), + Cislo.objects.count(), Problem.objects.count(), Reseni.objects.count())) + + diff --git a/seminar/models.py b/seminar/models.py index dbfa5f22..42ce27e5 100644 --- a/seminar/models.py +++ b/seminar/models.py @@ -13,6 +13,10 @@ from django.utils.encoding import force_unicode from django_countries.fields import CountryField from solo.models import SingletonModel +from ckeditor.fields import RichTextField +from redactor.fields import RedactorField + + # # Mělo by být částečně vytaženo z Aesopa # viz https://ovvp.mff.cuni.cz/wiki/aesop/export-skol. @@ -155,6 +159,11 @@ class Rocnik(models.Model): def __str__(self): return force_unicode(u'%s (%d/%d)' % (self.rocnik, self.prvni_rok, self.prvni_rok+1)) + def roman(self): + if self.rocnik.isdigit(): + return force_unicode(roman(int(self.rocnik))) + else: + return force_unicode(self.rocnik) @python_2_unicode_compatible class Cislo(models.Model): @@ -320,3 +329,11 @@ class Nastaveni(SingletonModel): return u'Nastavení semináře' +def roman(num): + ints = (1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1) + nums = ('M', 'CM', 'D', 'CD','C', 'XC','L','XL','X','IX','V','IV','I') + res = "" + for i, n in zip(ints, nums): + res += n * (num // i) + num %= i + return res diff --git a/seminar/tests.py b/seminar/tests.py index 7ce503c2..2b3338cf 100644 --- a/seminar/tests.py +++ b/seminar/tests.py @@ -1,3 +1,79 @@ -from django.test import TestCase +# -*- coding: utf-8 -*- + +import datetime +import random +from unittest import TestCase + +import django.contrib.auth +from django.db import transaction +from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni + +User = django.contrib.auth.get_user_model() + +@transaction.atomic +def create_test_data(): + # pevna pseudo-nahodnost + rnd = random.Random(x=42) + + # users + admin = User.objects.create_superuser(username='admin', email='', password='admin') + + orgs = [] + for org in ['anet', 'bara', 'cyril', 'david', 'eva', 'filip']: + o = User.objects.create_user(username=org, password=org) + o.first_name = org.capitalize() + o.save() + orgs.append(o) + + # skoly + Skola.objects.create(mesto = u'Praha', stat='CZ', psc='101 00', ulice=u'Krátká 5', nazev=u'První SŠ') + Skola.objects.create(mesto = u'Praha', stat='CZ', psc='102 00', ulice=u'Dlouhá 5', nazev=u'Druhá SŠ') + Skola.objects.create(mesto = u'Praha', stat='CZ', psc='103 00', ulice=u'Široká 3', nazev=u'Třetí SŠ') + Skola.objects.create(mesto = u'Ostrava', stat='CZ', psc='700 00', ulice=u'Hluboká 42', nazev=u'Hutní gympl') + Skola.objects.create(mesto = u'Humenné', stat='SK', psc='012 34', ulice=u'Pltká 1', nazev=u'Sredná škuola') + + # resitele + for i in range(20): + skola = rnd.choice(Skola.objects.all()) + pohlavi = rnd.randint(0,1) + Resitel.objects.create(skola = skola, datum_prihlaseni = datetime.date(rnd.randint(2002, 2014), rnd.randint(1,12), 1), + jmeno = u'Řešitel' if pohlavi else u'Řešitelka', prijmeni = 'Číslo-%s' % (i), rok_maturity = rnd.randint(2015, 2019), + stat = skola.stat, zasilat = Resitel.ZASILAT_NIKAM, pohlavi_muz = pohlavi) + + # rocniky + for ri in range(17, 22): + r = Rocnik.objects.create(prvni_rok = 1993 + ri, rocnik = ri) + + # cisla + cisel = rnd.randint(4, 6) + cs = {} + for ci in range(1, cisel + 1): + vydano = datetime.date(r.prvni_rok, ci + 6, 1) + deadline = datetime.date(r.prvni_rok, ci + 8, 1) if ci + 2 < cisel else None + c = Cislo.objects.create(rocnik = r, cislo = str(ci), datum_vydani=vydano, datum_deadline=deadline) + cs[ci] = c + + # problemy resene v ci + seq='#ABCDEFGHIJKLMNOPQRSTUVWXYZ' + if ci >= 3: + for pi in range(1, 4): + p = Problem.objects.create(autor = rnd.choice(orgs), cislo_zadani=cs[ci-2], cislo_reseni=cs[ci], + opravovatel = rnd.choice(orgs), kod = str(pi), nazev = u'Dummy úloha %s-%s' % (seq[ci], seq[pi]), + stav = Problem.STAV_ZADANY, typ = Problem.TYP_ULOHA, body = rnd.randint(1, 5)) + + for resi in range(rnd.randint(0, 7)): + res = Reseni.objects.create(problem = p, resitel = rnd.choice(Resitel.objects.all()), + body = rnd.randint(0, p.body), cislo_body = cs[ci]) + + nastaveni = Nastaveni.objects.create(aktualni_rocnik = Rocnik.objects.last(), + aktualni_cislo = Cislo.objects.last()) + + +class SeminarTests(TestCase): + def setUp(self): + create_test_data() + + def test_rocniky(self): + r19 = Rocnik.objects.get(rocnik=19) + self.assertEqual(r19.roman(), 'XIX') -# Create your tests here.