Tagy u korektur

This commit is contained in:
Jonas Havelka 2025-02-11 18:54:15 +01:00
parent 326be3eaa0
commit 773cd7d419
10 changed files with 101 additions and 5 deletions

View file

@ -1,6 +1,6 @@
from django.contrib import admin from django.contrib import admin
from reversion.admin import VersionAdmin from reversion.admin import VersionAdmin
from korektury.models import KorekturovanePDF, Oprava from korektury.models import KorekturovanePDF, Oprava, KorekturaTag
from django.core.mail import EmailMessage from django.core.mail import EmailMessage
from django.urls import reverse from django.urls import reverse
@ -66,6 +66,7 @@ admin.site.register(KorekturovanePDF, KorekturovanePDFAdmin)
class OpravaAdmin(admin.ModelAdmin): class OpravaAdmin(admin.ModelAdmin):
model = Oprava model = Oprava
filter_horizontal = ("informovani_orgove", ) filter_horizontal = ("informovani_orgove", "tagy",)
admin.site.register(Oprava, OpravaAdmin) admin.site.register(Oprava, OpravaAdmin)
admin.site.register(KorekturaTag)

View file

@ -6,7 +6,7 @@ from django.views.decorators.csrf import csrf_exempt
from rest_framework import serializers from rest_framework import serializers
from korektury.utils import send_email_notification_komentar from korektury.utils import send_email_notification_komentar
from korektury.models import Oprava, KorekturovanePDF, Komentar from korektury.models import Oprava, KorekturovanePDF, Komentar, KorekturaTag
from personalni.models import Organizator, Osoba from personalni.models import Organizator, Osoba
@ -45,6 +45,12 @@ class KomentarSerializer(serializers.ModelSerializer):
ret["text"] = linebreaks(ret["text"], autoescape=True) # Autora není třeba escapovat, ten se vkládá jako text. ret["text"] = linebreaks(ret["text"], autoescape=True) # Autora není třeba escapovat, ten se vkládá jako text.
return ret return ret
class KorekturaTagSerializer(serializers.ModelSerializer):
class Meta:
model = KorekturaTag
fields = '__all__'
class OpravaSerializer(serializers.ModelSerializer): class OpravaSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Oprava model = Oprava
@ -53,6 +59,7 @@ class OpravaSerializer(serializers.ModelSerializer):
def to_representation(self, instance): def to_representation(self, instance):
ret = super().to_representation(instance) ret = super().to_representation(instance)
ret["komentare"] = [KomentarSerializer(komentar).data for komentar in instance.komentar_set.all()] ret["komentare"] = [KomentarSerializer(komentar).data for komentar in instance.komentar_set.all()]
ret["tagy"] = [KorekturaTagSerializer(tag).data for tag in instance.tagy.all()]
return ret return ret
# komentar_set = serializers.ListField(child=KomentarSerializer()) # komentar_set = serializers.ListField(child=KomentarSerializer())
@ -82,6 +89,7 @@ def opravy_a_komentare_view(request, pdf_id: int, **kwargs):
if oprava_id != -1: if oprava_id != -1:
oprava = get_object_or_404(Oprava, id=oprava_id) oprava = get_object_or_404(Oprava, id=oprava_id)
else: else:
tagy = list(map(int, q.get('tagy').split(",")))
pdf = get_object_or_404(KorekturovanePDF, id=pdf_id) pdf = get_object_or_404(KorekturovanePDF, id=pdf_id)
oprava = Oprava.objects.create( oprava = Oprava.objects.create(
pdf=pdf, pdf=pdf,
@ -89,6 +97,7 @@ def opravy_a_komentare_view(request, pdf_id: int, **kwargs):
x=x, x=x,
y=y, y=y,
) )
oprava.tagy.add(*KorekturaTag.objects.filter(id__in=tagy))
Komentar.objects.create(oprava=oprava, autor=autor, text=text) Komentar.objects.create(oprava=oprava, autor=autor, text=text)

View file

@ -0,0 +1,27 @@
# Generated by Django 4.2.16 on 2025-02-11 16:07
import colorfield.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('korektury', '0026_oprava_informovani_orgove'),
]
operations = [
migrations.CreateModel(
name='KorekturaTag',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('nazev', models.CharField(help_text='Název daného tagu, <20 znaků', max_length=20, verbose_name='název tagu')),
('barva', colorfield.fields.ColorField(default='#FFFFFF', image_field=None, max_length=25, samples=None, verbose_name='barva daného tagu')),
],
),
migrations.AddField(
model_name='oprava',
name='tagy',
field=models.ManyToManyField(blank=True, default=None, to='korektury.korekturatag'),
),
]

View file

@ -1,4 +1,7 @@
import os import os
from colorfield.fields import ColorField
from django.db import models from django.db import models
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
@ -131,6 +134,14 @@ class KorekturovanePDF(models.Model):
return reverse('korektury', kwargs={'pdf': self.id}) return reverse('korektury', kwargs={'pdf': self.id})
class KorekturaTag(models.Model):
nazev = models.CharField("název tagu", blank = False, max_length=20, help_text="Název daného tagu, <20 znaků")
barva = ColorField("barva daného tagu", default="#FFFFFF")
def __str__(self):
return self.nazev
@reversion.register(ignore_duplicates=True) @reversion.register(ignore_duplicates=True)
class Oprava(models.Model): class Oprava(models.Model):
class Meta: class Meta:
@ -164,6 +175,8 @@ class Oprava(models.Model):
related_name='informovan_o_opravach', related_name='informovan_o_opravach',
) )
tagy = models.ManyToManyField(KorekturaTag, blank=True, default=None,)
@reversion.register(ignore_duplicates=True) @reversion.register(ignore_duplicates=True)
class Komentar(models.Model): class Komentar(models.Model):

View file

@ -137,6 +137,14 @@ form {
opacity: 80%; opacity: 80%;
} }
.korektury-tag {
border-radius: 5px;
margin: 2px;
padding: 2px;
&[data-selected="false"] { opacity: 0.5; }
}
/**** ROZLIŠENÍ MEZI LOKÁLNÍM, TESTOVACÍM A PRODUKČNÍM WEBEM ****/ /**** ROZLIŠENÍ MEZI LOKÁLNÍM, TESTOVACÍM A PRODUKČNÍM WEBEM ****/
body.localweb, body.testweb, body.suprodweb { body.localweb, body.testweb, body.suprodweb {

View file

@ -5,6 +5,11 @@
<br/> <br/>
<textarea id="commform-text" cols=40 rows=10 name="txt"></textarea> <textarea id="commform-text" cols=40 rows=10 name="txt"></textarea>
<br/> <br/>
<span id="commform-tagy">
{% for tag in tagy %}
<button type="button" class="korektury-tag" value="{{tag.id}}" data-selected="false" style="background: {{ tag.barva }};">{{tag.nazev}}</button>
{% endfor %}
</span>
</div> </div>
<script> <script>
@ -14,6 +19,7 @@
this.text = document.getElementById('commform-text'); this.text = document.getElementById('commform-text');
this.submit_button = document.getElementById('commform-submit'); this.submit_button = document.getElementById('commform-submit');
const close_button = document.getElementById('commform-close'); const close_button = document.getElementById('commform-close');
this.tagy = document.getElementById('commform-tagy');
// ctrl-enter submits form // ctrl-enter submits form
@ -23,6 +29,12 @@
close_button.addEventListener("click", _ => { this.close(); }); close_button.addEventListener("click", _ => { this.close(); });
this.submit_button.addEventListener("click", _ => { this.submit(); }); this.submit_button.addEventListener("click", _ => { this.submit(); });
for (const tag of this.tagy.getElementsByTagName("button")) tag.addEventListener("click", event => { this.toggle_tag(event); });
}
toggle_tag(event) {
const button = event.target;
button.dataset.selected = String(button.dataset.selected === "false");
} }
@ -54,6 +66,7 @@
this.text.value = text; this.text.value = text;
// show form // show form
if (oprava_id === -1 && komentar_id === -1) this.tagy.style.display = 'unset'; else this.tagy.style.display = 'none';
this._show(img_id, x, y); this._show(img_id, x, y);
} }
@ -65,6 +78,13 @@
data.append('img_id', this.imgID); data.append('img_id', this.imgID);
data.append('oprava_id', this.oprava_id); data.append('oprava_id', this.oprava_id);
data.append('komentar_id', this.komentar_id); data.append('komentar_id', this.komentar_id);
if (this.oprava_id === -1 && this.komentar_id === -1) {
const tagy = [];
for (const tag of this.tagy.getElementsByTagName("button")) {
if (tag.dataset.selected !== "false") tagy.push(tag.value);
}
data.append('tagy', String(tagy));
}
data.append('text', this.text.value); data.append('text', this.text.value);

View file

@ -11,6 +11,10 @@
{# data-opravastatus='{{o.status}}' #} {# data-opravastatus='{{o.status}}' #}
data-opravazobrazit='true' data-opravazobrazit='true'
> >
<div class='corr-tagy'>
{# {% for tag in o.tagy %} <span style="background:{{ tag.barva }}>{{ tag.text }}<span/> #}
</div>
<div class='corr-body'> <div class='corr-body'>
{# {% for k in o.komentare %} {% include "korektury/korekturovatko/__komentar.html" %} <hr> {% endfor %} #} {# {% for k in o.komentare %} {% include "korektury/korekturovatko/__komentar.html" %} <hr> {% endfor %} #}
</div> </div>
@ -65,7 +69,7 @@
else return new Oprava(oprava_data); else return new Oprava(oprava_data);
} }
#komentare; #komentare; #tagy;
htmlElement; pointer; htmlElement; pointer;
id; x; y; img_id; zobrazit = true; {# oprava_data; #} id; x; y; img_id; zobrazit = true; {# oprava_data; #}
@ -73,6 +77,7 @@
this.htmlElement = preoprava.cloneNode(true); this.htmlElement = preoprava.cloneNode(true);
this.pointer = prepointer.cloneNode(true); this.pointer = prepointer.cloneNode(true);
this.#komentare = this.htmlElement.getElementsByClassName('corr-body')[0]; this.#komentare = this.htmlElement.getElementsByClassName('corr-body')[0];
this.#tagy = this.htmlElement.getElementsByClassName('corr-tagy')[0];
this.id = oprava_data['id']; this.id = oprava_data['id'];
this.htmlElement.id = 'op' + this.id; this.htmlElement.id = 'op' + this.id;
@ -103,6 +108,14 @@
update(oprava_data) { update(oprava_data) {
{# this.oprava_data = oprava_data; #} {# this.oprava_data = oprava_data; #}
this.set_status(oprava_data['status']); this.set_status(oprava_data['status']);
this.#tagy.innerHTML = "";
for (const tag of oprava_data["tagy"]) {
const span = document.createElement("span");
span.innerHTML = tag["nazev"];
span.classList.add("korektury-tag");
span.style.backgroundColor = tag["barva"];
this.#tagy.appendChild(span);
}
return this; return this;
}; };

View file

@ -5,7 +5,7 @@ from django.http import HttpResponseForbidden
from django.db.models import Count,Q from django.db.models import Count,Q
from .utils import send_email_notification_komentar from .utils import send_email_notification_komentar
from .models import Oprava,Komentar,KorekturovanePDF, Organizator from .models import Oprava, Komentar, KorekturovanePDF, Organizator, KorekturaTag
class KorekturyListView(generic.ListView): class KorekturyListView(generic.ListView):
model = KorekturovanePDF model = KorekturovanePDF
@ -150,5 +150,7 @@ class KorekturyView(generic.TemplateView):
context['opravy'] = opravy context['opravy'] = opravy
context['zasluhy'] = zasluhy context['zasluhy'] = zasluhy
context['tagy'] = KorekturaTag.objects.all()
return context return context

View file

@ -129,6 +129,8 @@ INSTALLED_APPS = (
'rest_framework', 'rest_framework',
'rest_framework.authtoken', 'rest_framework.authtoken',
'colorfield',
# MaMweb # MaMweb
'mamweb', 'mamweb',
'seminar', 'seminar',

View file

@ -25,6 +25,7 @@ django_reverse_admin # Lepší handlování OneToOne fieldů v adminu
django-rest-framework django-rest-framework
django-webpack-loader django-webpack-loader
django-rest-polymorphic django-rest-polymorphic
django-colorfield # Field pro ukládání barvy (např. tagy v korekturovátku)
# debug tools/extensions # debug tools/extensions