Browse Source

create

odstrel_modelu_odevzdavatko
Pavel "LEdoian" Turinsky 2 months ago
parent
commit
33381b4307
  1. 99
      odevzdavatko/migrations/0001_create.py
  2. 243
      odevzdavatko/models.py
  3. 3
      seminar/models/__init__.py
  4. 245
      seminar/models/odevzdavatko.py
  5. 1
      split-apps-meta/create.notes

99
odevzdavatko/migrations/0001_create.py

@ -0,0 +1,99 @@
# Generated by Django 4.2.13 on 2024-10-22 22:51
from django.db import migrations, models
import django.utils.timezone
import odevzdavatko.models
def nastav_nove_contenttypes(apps, schema_editor):
ContentType = apps.get_model('contenttypes', 'ContentType')
for m in ('reseni', 'hodnoceni', 'reseni_resitele', 'prilohareseni'):
ContentType.objects.filter(app_label='seminar', model=m).update(app_label='odevzdavatko')
def nastav_stare_contenttypes(apps, schema_editor):
ContentType = apps.get_model('contenttypes', 'ContentType')
for m in ('reseni', 'hodnoceni', 'reseni_resitele', 'prilohareseni'):
ContentType.objects.filter(app_label='odevzdavatko', model=m).update(app_label='seminar')
class Migration(migrations.Migration):
initial = True
dependencies = [
('seminar', '0132_unmanage_odevzdavatko'),
]
operations = [
migrations.CreateModel(
name='Hodnoceni',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('body', models.DecimalField(blank=True, decimal_places=1, max_digits=8, null=True, verbose_name='body')),
('feedback', models.TextField(blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)', verbose_name='zpětná vazba')),
('cislo_body', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='seminar.cislo', verbose_name='číslo pro body')),
('deadline_body', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='seminar.deadline', verbose_name='deadline pro body')),
('problem', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='seminar.problem', verbose_name='problém')),
('reseni', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='odevzdavatko.reseni', verbose_name='řešení')),
],
options={
'verbose_name': 'Hodnocení',
'verbose_name_plural': 'Hodnocení',
'db_table': 'seminar_hodnoceni',
'managed': False,
},
),
migrations.CreateModel(
name='PrilohaReseni',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('vytvoreno', models.DateTimeField(blank=True, default=django.utils.timezone.now, editable=False, verbose_name='vytvořeno')),
('soubor', models.FileField(upload_to=odevzdavatko.models.generate_filename, verbose_name='soubor')),
('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k příloze řešení (plain text), např. o původu', verbose_name='neveřejná poznámka')),
('res_poznamka', models.TextField(blank=True, help_text='Poznámka k příloze řešení, např. co daný soubor obsahuje', verbose_name='poznámka řešitele')),
('reseni', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='prilohy', to='odevzdavatko.reseni', verbose_name='řešení')),
],
options={
'verbose_name': 'Příloha řešení',
'verbose_name_plural': 'Přílohy řešení',
'db_table': 'seminar_priloha_reseni',
'ordering': ['reseni', 'vytvoreno'],
'managed': False,
},
),
migrations.CreateModel(
name='Reseni',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('cas_doruceni', models.DateTimeField(blank=True, default=django.utils.timezone.now, verbose_name='čas_doručení')),
('forma', models.CharField(choices=[('papir', 'Papírové řešení'), ('email', 'Emailem'), ('upload', 'Upload přes web')], default='email', max_length=16, verbose_name='forma řešení')),
('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k řešení (plain text)', verbose_name='neveřejná poznámka')),
('zverejneno', models.BooleanField(default=False, help_text='Udává, zda je řešení zveřejněno', verbose_name='řešení zveřejněno')),
('problem', models.ManyToManyField(help_text='Problém', through='odevzdavatko.Hodnoceni', to='seminar.problem', verbose_name='problém')),
('resitele', models.ManyToManyField(help_text='Seznam autorů řešení', through='odevzdavatko.Reseni_Resitele', to='personalni.resitel', verbose_name='autoři řešení')),
('text_cely', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='reseni_cely_set', to='seminar.reseninode', verbose_name='Plná verze textu řešení')),
],
options={
'verbose_name': 'Řešení',
'verbose_name_plural': 'Řešení',
'db_table': 'seminar_reseni',
'ordering': ['-cas_doruceni'],
'managed': False,
},
),
migrations.CreateModel(
name='Reseni_Resitele',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('reseni', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='odevzdavatko.reseni', verbose_name='řešení')),
('resitele', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='personalni.resitel', verbose_name='řešitel')),
],
options={
'verbose_name': 'Řešení řešitelů',
'verbose_name_plural': 'Řešení řešitelů',
'db_table': 'seminar_reseni_resitele',
'ordering': ['reseni', 'resitele'],
'managed': False,
},
),
migrations.RunPython(nastav_nove_contenttypes, nastav_stare_contenttypes),
]

243
odevzdavatko/models.py

@ -0,0 +1,243 @@
import os
import reversion
from django.contrib.sites.shortcuts import get_current_site
from django.db import models
from django.db.models import Sum
from django.urls import reverse_lazy
from django.utils import timezone
from django.conf import settings
from seminar.models import tvorba as am
from seminar.models import base as bm
from odevzdavatko.utils import vzorecek_na_prepocet, inverze_vzorecku_na_prepocet
from personalni.models import Resitel
@reversion.register(ignore_duplicates=True)
class Reseni(bm.SeminarModelBase):
class Meta:
db_table = 'seminar_reseni'
verbose_name = 'Řešení'
verbose_name_plural = 'Řešení'
#ordering = ['-problem', 'resitele'] # FIXME: Takhle to chceme, ale nefunguje to.
ordering = ['-cas_doruceni']
managed = False
# Interní ID
id = models.AutoField(primary_key = True)
# Ke každé dvojici řešní a problém existuje nanejvýš jedno hodnocení, doplnění vazby.
problem = models.ManyToManyField(am.Problem, verbose_name='problém', help_text='Problém',
through='Hodnoceni')
resitele = models.ManyToManyField(Resitel, verbose_name='autoři řešení',
help_text='Seznam autorů řešení', through='Reseni_Resitele')
cas_doruceni = models.DateTimeField('čas_doručení', default=timezone.now, blank=True)
FORMA_PAPIR = 'papir'
FORMA_EMAIL = 'email'
FORMA_UPLOAD = 'upload'
FORMA_CHOICES = [
(FORMA_PAPIR, 'Papírové řešení'),
(FORMA_EMAIL, 'Emailem'),
(FORMA_UPLOAD, 'Upload přes web'),
]
forma = models.CharField('forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False,
default=FORMA_EMAIL)
text_cely = models.OneToOneField('seminar.ReseniNode', verbose_name='Plná verze textu řešení',
blank=True, null=True, related_name="reseni_cely_set",
on_delete=models.PROTECT)
poznamka = models.TextField('neveřejná poznámka', blank=True,
help_text='Neveřejná poznámka k řešení (plain text)')
zverejneno = models.BooleanField('řešení zveřejněno', default=False,
help_text='Udává, zda je řešení zveřejněno')
def verejne_url(self):
return str(reverse_lazy('odevzdavatko_detail_reseni', args=[self.id]))
def absolute_url(self):
return "https://" + str(get_current_site(None)) + self.verejne_url()
# má OneToOneField s:
# Konfera
# má ForeignKey s:
# Hodnoceni
def sum_body(self):
return self.hodnoceni_set.all().aggregate(Sum('body'))["body__sum"]
def __str__(self):
return "{}({}): {}({})".format(self.resitele.first(),len(self.resitele.all()), self.problem.first() ,len(self.problem.all()))
# NOTE: Potenciální DB HOG (bez select_related)
def deadline_reseni(self):
return am.Deadline.objects.filter(deadline__gte=self.cas_doruceni).order_by("deadline").first()
## Pravdepodobne uz nebude potreba:
# def save(self, *args, **kwargs):
# if ((self.cislo_body is None) and (self.problem.cislo_reseni) and
# (self.problem.typ == Problem.TYP_ULOHA)):
# self.cislo_body = self.problem.cislo_reseni
# super(Reseni, self).save(*args, **kwargs)
class Hodnoceni(bm.SeminarModelBase):
class Meta:
db_table = 'seminar_hodnoceni'
verbose_name = 'Hodnocení'
verbose_name_plural = 'Hodnocení'
managed = False
# Interní ID
id = models.AutoField(primary_key = True)
body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body',
blank=True, null=True)
cislo_body = models.ForeignKey(am.Cislo, verbose_name='číslo pro body',
related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT)
# V ročníku < 26 nastaveno na deadline vygenerovaný pro původní cislo_body
deadline_body = models.ForeignKey(am.Deadline, verbose_name='deadline pro body',
related_name='hodnoceni', blank=True, null=True, on_delete=models.PROTECT)
reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE)
problem = models.ForeignKey(am.Problem, verbose_name='problém',
related_name='hodnoceni', on_delete=models.PROTECT)
feedback = models.TextField('zpětná vazba', blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)')
@property
def body_celkem(self):
# FIXME řeším jen prvního řešitele.
return Hodnoceni.objects.filter(problem=self.problem, reseni__resitele=self.reseni.resitele.first(), body__isnull=False).aggregate(Sum("body"))["body__sum"]
@body_celkem.setter
def body_celkem(self, value):
if value is None:
self.body = None
else:
if self.body is None:
self.body = 0
if self.body_celkem is None:
self.body += value
else:
self.body += value - self.body_celkem
@property
def body_neprepocitane(self):
if self.body is None:
return None
return inverze_vzorecku_na_prepocet(self.body, self.reseni.resitele.count())
@body_neprepocitane.setter
def body_neprepocitane(self, value):
if value is None:
self.body = None
else:
self.body = vzorecek_na_prepocet(value, self.reseni.resitele.count())
@property
def body_neprepocitane_celkem(self):
if self.body_celkem is None:
return None
return inverze_vzorecku_na_prepocet(self.body_celkem, self.reseni.resitele.count())
@body_neprepocitane_celkem.setter
def body_neprepocitane_celkem(self, value):
if value is None:
self.body = None
else:
self.body_celkem = vzorecek_na_prepocet(value, self.reseni.resitele.count())
@property
def body_max(self):
if self.body_neprepocitane_max is None:
return None
return vzorecek_na_prepocet(self.body_neprepocitane_max, self.reseni.resitele.count())
@property
def body_neprepocitane_max(self):
if not isinstance(self.problem.get_real_instance(), am.Uloha):
return None
return self.problem.uloha.max_body
def __str__(self):
return "{}, {}, {}".format(self.problem, self.reseni, self.body)
def generate_filename(self, filename):
return os.path.join(
settings.SEMINAR_RESENI_DIR,
am.aux_generate_filename(self, filename)
)
@reversion.register(ignore_duplicates=True)
class PrilohaReseni(bm.SeminarModelBase):
class Meta:
db_table = 'seminar_priloha_reseni'
verbose_name = 'Příloha řešení'
verbose_name_plural = 'Přílohy řešení'
ordering = ['reseni', 'vytvoreno']
managed = False
# Interní ID
id = models.AutoField(primary_key = True)
reseni = models.ForeignKey(Reseni, verbose_name='řešení', related_name='prilohy',
on_delete=models.CASCADE)
vytvoreno = models.DateTimeField('vytvořeno', default=timezone.now, blank=True, editable=False)
soubor = models.FileField('soubor', upload_to = generate_filename)
poznamka = models.TextField('neveřejná poznámka', blank=True,
help_text='Neveřejná poznámka k příloze řešení (plain text), např. o původu')
res_poznamka = models.TextField('poznámka řešitele', blank=True,
help_text='Poznámka k příloze řešení, např. co daný soubor obsahuje')
def __str__(self):
return str(self.soubor)
def split(self):
"Vrátí cestu rozsekanou po složkách. To se hodí v templatech"
# Věřím, že tohle funguje, případně použít os.path nebo pathlib.
return self.soubor.url.split('/')
# Vazebna tabulka. Mozna se generuje automaticky.
@reversion.register(ignore_duplicates=True)
class Reseni_Resitele(models.Model):
class Meta:
db_table = 'seminar_reseni_resitele'
verbose_name = 'Řešení řešitelů'
verbose_name_plural = 'Řešení řešitelů'
ordering = ['reseni', 'resitele']
managed = False
# Interní ID
id = models.AutoField(primary_key = True)
resitele = models.ForeignKey(Resitel, verbose_name='řešitel', on_delete=models.PROTECT)
reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE)
# podil - jakou merou se ktery resitel podilel na danem reseni
# - pouziti v budoucnu, pokud by resitele nemeli dostat vsichni stejne bodu za spolecne reseni
def __str__(self):
return '{} od {}'.format(self.reseni, self.resitel)
# NOTE: Poteciální DB HOG bez select_related

3
seminar/models/__init__.py

@ -8,6 +8,9 @@ from various.models import Nastaveni
from personalni.models import Organizator, Resitel, Skola, Prijemce, Osoba
from soustredeni.models import Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Konfera, Konfery_Ucastnici
from novinky.models import Novinky
from odevzdavatko.models import Reseni, PrilohaReseni, Reseni_Resitele, Hodnoceni
# Kvůli migr. 0041
from soustredeni.models import generate_filename_konfera
# migr. 0001
from odevzdavatko.models import generate_filename

245
seminar/models/odevzdavatko.py

@ -1,250 +1,7 @@
import os
import reversion
from django.contrib.sites.shortcuts import get_current_site
from django.db import models
from django.db.models import Sum
from django.urls import reverse_lazy
from django.utils import timezone
from django.conf import settings
from seminar.models import tvorba as am
from seminar.models import treenode as tm
from seminar.models import base as bm
from odevzdavatko.utils import vzorecek_na_prepocet, inverze_vzorecku_na_prepocet
from personalni.models import Resitel
@reversion.register(ignore_duplicates=True)
class Reseni(bm.SeminarModelBase):
class Meta:
db_table = 'seminar_reseni'
verbose_name = 'Řešení'
verbose_name_plural = 'Řešení'
#ordering = ['-problem', 'resitele'] # FIXME: Takhle to chceme, ale nefunguje to.
ordering = ['-cas_doruceni']
managed = False
# Interní ID
id = models.AutoField(primary_key = True)
# Ke každé dvojici řešní a problém existuje nanejvýš jedno hodnocení, doplnění vazby.
problem = models.ManyToManyField(am.Problem, verbose_name='problém', help_text='Problém',
related_name='problem_old',
through='Hodnoceni')
resitele = models.ManyToManyField(Resitel, verbose_name='autoři řešení',
related_name='resitele_old',
help_text='Seznam autorů řešení', through='Reseni_Resitele')
cas_doruceni = models.DateTimeField('čas_doručení', default=timezone.now, blank=True)
FORMA_PAPIR = 'papir'
FORMA_EMAIL = 'email'
FORMA_UPLOAD = 'upload'
FORMA_CHOICES = [
(FORMA_PAPIR, 'Papírové řešení'),
(FORMA_EMAIL, 'Emailem'),
(FORMA_UPLOAD, 'Upload přes web'),
]
forma = models.CharField('forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False,
default=FORMA_EMAIL)
text_cely = models.OneToOneField('seminar.ReseniNode', verbose_name='Plná verze textu řešení',
blank=True, null=True, related_name="reseni_cely_set_old",
on_delete=models.PROTECT)
poznamka = models.TextField('neveřejná poznámka', blank=True,
help_text='Neveřejná poznámka k řešení (plain text)')
zverejneno = models.BooleanField('řešení zveřejněno', default=False,
help_text='Udává, zda je řešení zveřejněno')
def verejne_url(self):
return str(reverse_lazy('odevzdavatko_detail_reseni', args=[self.id]))
def absolute_url(self):
return "https://" + str(get_current_site(None)) + self.verejne_url()
# má OneToOneField s:
# Konfera
# má ForeignKey s:
# Hodnoceni
def sum_body(self):
return self.hodnoceni_set.all().aggregate(Sum('body'))["body__sum"]
def __str__(self):
return "{}({}): {}({})".format(self.resitele.first(),len(self.resitele.all()), self.problem.first() ,len(self.problem.all()))
# NOTE: Potenciální DB HOG (bez select_related)
def deadline_reseni(self):
return am.Deadline.objects.filter(deadline__gte=self.cas_doruceni).order_by("deadline").first()
## Pravdepodobne uz nebude potreba:
# def save(self, *args, **kwargs):
# if ((self.cislo_body is None) and (self.problem.cislo_reseni) and
# (self.problem.typ == Problem.TYP_ULOHA)):
# self.cislo_body = self.problem.cislo_reseni
# super(Reseni, self).save(*args, **kwargs)
class Hodnoceni(bm.SeminarModelBase):
class Meta:
db_table = 'seminar_hodnoceni'
verbose_name = 'Hodnocení'
verbose_name_plural = 'Hodnocení'
managed = False
# Interní ID
id = models.AutoField(primary_key = True)
body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body',
blank=True, null=True)
cislo_body = models.ForeignKey(am.Cislo, verbose_name='číslo pro body',
related_name='hodnoceni_old', blank=True, null=True, on_delete=models.PROTECT)
# V ročníku < 26 nastaveno na deadline vygenerovaný pro původní cislo_body
deadline_body = models.ForeignKey(am.Deadline, verbose_name='deadline pro body',
related_name='hodnoceni_old', blank=True, null=True, on_delete=models.PROTECT)
reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE, related_name='hodnoceni_old')
problem = models.ForeignKey(am.Problem, verbose_name='problém',
related_name='hodnoceni_old', on_delete=models.PROTECT)
feedback = models.TextField('zpětná vazba', blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)')
@property
def body_celkem(self):
# FIXME řeším jen prvního řešitele.
return Hodnoceni.objects.filter(problem=self.problem, reseni__resitele=self.reseni.resitele.first(), body__isnull=False).aggregate(Sum("body"))["body__sum"]
@body_celkem.setter
def body_celkem(self, value):
if value is None:
self.body = None
else:
if self.body is None:
self.body = 0
if self.body_celkem is None:
self.body += value
else:
self.body += value - self.body_celkem
@property
def body_neprepocitane(self):
if self.body is None:
return None
return inverze_vzorecku_na_prepocet(self.body, self.reseni.resitele.count())
@body_neprepocitane.setter
def body_neprepocitane(self, value):
if value is None:
self.body = None
else:
self.body = vzorecek_na_prepocet(value, self.reseni.resitele.count())
@property
def body_neprepocitane_celkem(self):
if self.body_celkem is None:
return None
return inverze_vzorecku_na_prepocet(self.body_celkem, self.reseni.resitele.count())
@body_neprepocitane_celkem.setter
def body_neprepocitane_celkem(self, value):
if value is None:
self.body = None
else:
self.body_celkem = vzorecek_na_prepocet(value, self.reseni.resitele.count())
@property
def body_max(self):
if self.body_neprepocitane_max is None:
return None
return vzorecek_na_prepocet(self.body_neprepocitane_max, self.reseni.resitele.count())
@property
def body_neprepocitane_max(self):
if not isinstance(self.problem.get_real_instance(), am.Uloha):
return None
return self.problem.uloha.max_body
def __str__(self):
return "{}, {}, {}".format(self.problem, self.reseni, self.body)
def generate_filename(self, filename):
return os.path.join(
settings.SEMINAR_RESENI_DIR,
am.aux_generate_filename(self, filename)
)
@reversion.register(ignore_duplicates=True)
class PrilohaReseni(bm.SeminarModelBase):
class Meta:
db_table = 'seminar_priloha_reseni'
verbose_name = 'Příloha řešení'
verbose_name_plural = 'Přílohy řešení'
ordering = ['reseni', 'vytvoreno']
managed = False
# Interní ID
id = models.AutoField(primary_key = True)
reseni = models.ForeignKey(Reseni, verbose_name='řešení', related_name='prilohy_old',
on_delete=models.CASCADE)
vytvoreno = models.DateTimeField('vytvořeno', default=timezone.now, blank=True, editable=False)
soubor = models.FileField('soubor', upload_to = generate_filename)
poznamka = models.TextField('neveřejná poznámka', blank=True,
help_text='Neveřejná poznámka k příloze řešení (plain text), např. o původu')
res_poznamka = models.TextField('poznámka řešitele', blank=True,
help_text='Poznámka k příloze řešení, např. co daný soubor obsahuje')
def __str__(self):
return str(self.soubor)
def split(self):
"Vrátí cestu rozsekanou po složkách. To se hodí v templatech"
# Věřím, že tohle funguje, případně použít os.path nebo pathlib.
return self.soubor.url.split('/')
# Vazebna tabulka. Mozna se generuje automaticky.
@reversion.register(ignore_duplicates=True)
class Reseni_Resitele(models.Model):
class Meta:
db_table = 'seminar_reseni_resitele'
verbose_name = 'Řešení řešitelů'
verbose_name_plural = 'Řešení řešitelů'
ordering = ['reseni', 'resitele']
managed = False
# Interní ID
id = models.AutoField(primary_key = True)
resitele = models.ForeignKey(Resitel, verbose_name='řešitel', on_delete=models.PROTECT, related_name='rere_old')
reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE, related_name='rere_old')
# podil - jakou merou se ktery resitel podilel na danem reseni
# - pouziti v budoucnu, pokud by resitele nemeli dostat vsichni stejne bodu za spolecne reseni
def __str__(self):
return '{} od {}'.format(self.reseni, self.resitel)
# NOTE: Poteciální DB HOG bez select_related
from odevzdavatko.models import Reseni
class ReseniNode(tm.TreeNode):
class Meta:

1
split-apps-meta/create.notes

@ -5,5 +5,6 @@ makemigrations
! Doplnit ForeignKeys (Vypadá to, že se dá vesměs zkopírovat předpis z models.py, jen místo prvního fieldu dát `to='app.model'. Dokonce asi funguje použít už novou aplikaci pro vazby v rámci aplikace.)
To samé s ManyToManyFieldy (through= musí taky být 'app.model')
(Zdá se, že jde dobastlit tuhle migraci polozpětně – doplnit co chybělo až podle toho, co vygeneruje migrace po zamanagování nového modelu.)
Alternativa: zagitovat si unmanaged model, upravit ho na `managed = True`, vyrobit migrace, vyrobit je ještě jednou (z nějakého důvodu) a vykrást ty. Pak `models.py` vrátit do unmanaged stavu a soubory s novými migracemi smazat bez náhrady (obdobné vzniknou znovu v případě potřeby).
doplnit závislost na unmanage
migrate

Loading…
Cancel
Save