Add CKEditor +configure, first tests, test data generation
This commit is contained in:
		
							parent
							
								
									065461300d
								
							
						
					
					
						commit
						0b88b8022c
					
				
					 7 changed files with 167 additions and 73 deletions
				
			
		|  | @ -90,6 +90,7 @@ INSTALLED_APPS = ( | ||||||
|     'reversion', |     'reversion', | ||||||
|     'django_countries', |     'django_countries', | ||||||
|     'solo', |     'solo', | ||||||
|  |     'ckeditor', | ||||||
| 
 | 
 | ||||||
|     # MaMweb |     # MaMweb | ||||||
|     '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 | # MaM specific | ||||||
| 
 | 
 | ||||||
| SEMINAR_RESENI_DIR = os.path.join(BASE_DIR, 'media', 'reseni') | SEMINAR_RESENI_DIR = os.path.join(BASE_DIR, 'media', 'reseni') | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ from django.conf import settings | ||||||
| urlpatterns = i18n_patterns('', | urlpatterns = i18n_patterns('', | ||||||
| 
 | 
 | ||||||
|     url(r'^admin/', include(admin.site.urls)),  # NOQA |     url(r'^admin/', include(admin.site.urls)),  # NOQA | ||||||
|  |     url(r'^ckeditor/', include('ckeditor.urls')), | ||||||
|     url(r'^', include('seminar.urls')), |     url(r'^', include('seminar.urls')), | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -15,6 +15,7 @@ django-reversion==1.8.5 | ||||||
| django-sekizai==0.8.1 | django-sekizai==0.8.1 | ||||||
| django-countries==3.2 | django-countries==3.2 | ||||||
| django-solo==1.1.0 | django-solo==1.1.0 | ||||||
|  | django-ckeditor==4.4.7 | ||||||
| 
 | 
 | ||||||
| # debug tools/extensions | # debug tools/extensions | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,7 +1,10 @@ | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
| from django.contrib import admin | 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 solo.admin import SingletonModelAdmin | ||||||
|  | from ckeditor.widgets import CKEditorWidget | ||||||
|  | 
 | ||||||
|  | from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni | ||||||
| 
 | 
 | ||||||
| ### Nastaveni | ### Nastaveni | ||||||
| 
 | 
 | ||||||
|  | @ -106,7 +109,15 @@ class ReseniInline(admin.StackedInline): | ||||||
| 
 | 
 | ||||||
| ### Problem | ### 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): | class ProblemAdmin(admin.ModelAdmin): | ||||||
|  |     form = ProblemAdminForm | ||||||
| #    readonly_fields = ['autor'] | #    readonly_fields = ['autor'] | ||||||
|     fieldsets = [ |     fieldsets = [ | ||||||
|         (None,              {'fields': ['nazev', 'typ', 'stav', 'autor']}), |         (None,              {'fields': ['nazev', 'typ', 'stav', 'autor']}), | ||||||
|  |  | ||||||
|  | @ -8,6 +8,12 @@ from django.core.management.base import NoArgsCommand | ||||||
| from django.core.management import call_command | from django.core.management import call_command | ||||||
| from django.conf import settings | 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): | class Command(NoArgsCommand): | ||||||
|     help = "Clear database and load testing data." |     help = "Clear database and load testing data." | ||||||
| 
 | 
 | ||||||
|  | @ -18,76 +24,13 @@ class Command(NoArgsCommand): | ||||||
|             os.rename(dbfile, dbfile + '.old') |             os.rename(dbfile, dbfile + '.old') | ||||||
|             self.stderr.write('Stara databaze prejmenovana na "%s"' % (dbfile + '.old')) |             self.stderr.write('Stara databaze prejmenovana na "%s"' % (dbfile + '.old')) | ||||||
|         call_command('migrate', noinput=True) |         call_command('migrate', noinput=True) | ||||||
|         self.load_testing_data() |         self.stdout.write('Vytvarim uzivatele "admin" (heslo "admin") a pseudo-nahodna data ...') | ||||||
| 
 |         create_test_data() | ||||||
|     def load_testing_data(self): |         self.stdout.write('Vytvoreno %d uzivatelu, %d skol, %d resitelu, %d rocniku, %d cisel, %d problemu, %d reseni.' % | ||||||
|         from seminar import models as sm |                 (User.objects.count(), Skola.objects.count(), Resitel.objects.count(), Rocnik.objects.count(), | ||||||
|         import django.contrib.auth |                 Cislo.objects.count(), Problem.objects.count(), Reseni.objects.count())) | ||||||
|         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())) |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -13,6 +13,10 @@ from django.utils.encoding import force_unicode | ||||||
| from django_countries.fields import CountryField | from django_countries.fields import CountryField | ||||||
| from solo.models import SingletonModel | 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 | # Mělo by být částečně vytaženo z Aesopa | ||||||
| # viz https://ovvp.mff.cuni.cz/wiki/aesop/export-skol. | # viz https://ovvp.mff.cuni.cz/wiki/aesop/export-skol. | ||||||
|  | @ -155,6 +159,11 @@ class Rocnik(models.Model): | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|         return force_unicode(u'%s (%d/%d)' % (self.rocnik, self.prvni_rok, self.prvni_rok+1)) |         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 | @python_2_unicode_compatible | ||||||
| class Cislo(models.Model): | class Cislo(models.Model): | ||||||
|  | @ -320,3 +329,11 @@ class Nastaveni(SingletonModel): | ||||||
|         return u'Nastavení semináře' |         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 | ||||||
|  |  | ||||||
|  | @ -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. |  | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Tomas Gavenciak
						Tomas Gavenciak