Compare commits

...

56 commits

Author SHA1 Message Date
7676b0ef60 Merge branch 'korekturovatko-full' into korekturovatko 2025-01-22 20:28:31 +01:00
95b46541c0 Uhlazení JavaScriptu… 2025-01-22 20:28:24 +01:00
0af99d4f3e Aktualizace všech komentářů jako funkce (a aktualizace, ne vytvoření) 2025-01-22 20:23:12 +01:00
f369110cd3 Stránky PDF zvlášť 2025-01-22 19:36:44 +01:00
c54e11f25a Uhlazení JavaScriptu 2025-01-22 19:31:42 +01:00
e205ca52d3 Korektury načtené z API (místo v templatu)… 2025-01-22 18:38:28 +01:00
44a8649d0e Vytváření korektur z jednoho prototypu 2025-01-22 15:39:34 +01:00
a4175f836e Zvýrazňování čar (pointrů) pomocí atributu místo třídy 2025-01-22 11:25:19 +01:00
2c627b3d60 Když už jsem u toho, tak event.keyCode -> event.code 2025-01-21 13:01:57 +01:00
5f904b5c66 Vytažení commform konstant z funkce showform (budu potřebovat pro submit) 2025-01-21 12:58:23 +01:00
94ca903cec Chybějící const 2025-01-21 12:01:47 +01:00
50936e2b50 Prázdné okno editace komentáře klidně zavřít 2025-01-21 11:53:12 +01:00
4a35a63f31 Zbavení se deprecated věci 2025-01-21 10:49:14 +01:00
4006ecd6b8 A ještě jedno uhlazení CSS korekturovátka 2025-01-21 10:24:43 +01:00
ce2d183446 Ještě jedno uhlazení CSS korekturovátka 2025-01-21 10:23:48 +01:00
1ff69f943e Uhlazení CSS korekturovátka 2025-01-21 10:15:42 +01:00
050ebba03b Nepoužívaná css 2025-01-21 10:06:11 +01:00
443a226943 box -> oprava 2025-01-21 10:05:27 +01:00
7da9aa5fe3 Upozorňovat při zavírání okénka editace komentáře 2025-01-21 09:40:42 +01:00
e257f31ea5 x,y uložené v opravě (nemusíme tahat z čáry (pointru)) 2025-01-21 09:28:46 +01:00
87466ce2b6 box -> oprava 2025-01-21 09:23:20 +01:00
291f39990d Vyčištění IDček v html opravy 2025-01-21 09:12:36 +01:00
b637aed7ab Zapomenutý switch podle document.body.class místo podle atributu 2025-01-21 08:22:34 +01:00
3e2469fc45 Tady je těch prázných stringů nějak moc 2025-01-21 08:20:34 +01:00
d5c57da921 Čára (pointer) jako atribut opravy místo grepování "opid-pointer" 2025-01-21 08:17:12 +01:00
370dd7d841 Už nemáme text v opravě, takže nemá smysl ho editovat 2025-01-21 08:12:42 +01:00
d818ce251b Box_edit za pomoci objektu opravy místo id 2025-01-21 08:10:13 +01:00
4121de260e Ha, tohle jsem chtěl tady 2025-01-21 07:48:05 +01:00
a8b7788d35 Merge branch 'master' into korekturovatko 2025-01-21 07:40:42 +01:00
9aa3a5154d Přesnější query (aby nebral i jiné formuláře) 2025-01-21 07:39:55 +01:00
7a4c8239f6 Čára (pointer) opravy nemá vůbec interagovat s klikáním 2025-01-21 07:37:36 +01:00
2deccfada4 Oprava URL 2025-01-20 23:48:54 +01:00
d2e199e509 Dynamický update stavu opravy 2025-01-20 23:40:06 +01:00
9b0fe3d32f Tohle tu nějak zůstalo 2025-01-20 22:35:46 +01:00
af41ca5784 Čistka scrollování 2025-01-20 22:11:53 +01:00
ad2d1e676c Zobrazení tlačítek podle atributu (ne v podle stavu v templatech) 2025-01-20 22:11:02 +01:00
00e0ed0a50 Skrývání korektur pomocí atributu 2025-01-20 21:45:37 +01:00
5563eb681c Status korektury jako atribut a ne třída 2025-01-20 21:06:33 +01:00
3b74772949 Přeházení JavaScriptových věcí 2025-01-20 19:12:27 +01:00
ee69bf4c4f Drobný úklid 2025-01-20 18:38:47 +01:00
dd86fc1fcb Dynamický update stavu 2024-12-12 13:52:55 +01:00
341ae7ce45 Rozlišení lokálního/testovacího/produkčního webu v korekturovátku 2024-12-12 12:51:32 +01:00
7906c87733 Zanášení a zastaralé pomocí atributu místo třídy 2024-12-12 12:50:56 +01:00
daf24ff981 Rozstřílení korekturovátka (html) 2024-12-12 12:33:33 +01:00
0dde05f102 Odstranění textu a autora z opravy (přesun do prvního komentáře) 2024-12-12 11:50:17 +01:00
3528a44d3c Úprava chování korektury (schovává text korektury!!!) 2024-12-12 11:24:17 +01:00
524593b7ef Merge branch 'master' into korekturovatko 2024-12-10 19:03:22 +01:00
22e88daf02 Korektury api init 2024-12-03 20:10:41 +01:00
04508206bb Funkce na posílání e-mailu rozhodně nemá být uvnitř View 2024-12-03 19:41:12 +01:00
8d09fd5389 Status opravy řešit přes enum 2024-12-03 19:10:36 +01:00
9df34c22e0 Moderní přístup k choices (umožňuje např. vytáhnout seznam všech hodnot) 2024-12-03 19:01:33 +01:00
d3d5484d0e Form se nikde nepoužívá 2024-12-03 18:37:23 +01:00
62160e8440 PDF už dostáváme v URL, není to potřeba shánět znovu 2024-12-03 18:33:26 +01:00
1e6e6118a7 Nepoužívat náhodné stringy, když už máme nějaké definované… 2024-12-03 18:20:25 +01:00
c1440687aa Nepotřebné importy… 2024-12-03 17:51:33 +01:00
69e870f958 Podle mě funguje… 2024-12-03 17:50:48 +01:00
24 changed files with 901 additions and 779 deletions

View file

7
korektury/api/apps.py Normal file
View file

@ -0,0 +1,7 @@
from django.apps import AppConfig
class ApiConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'korektury.api'
label = 'korektury_api' # Protože jedno api už máme.

View file

9
korektury/api/urls.py Normal file
View file

@ -0,0 +1,9 @@
from django.urls import path
from personalni.utils import org_required
from . import views
urlpatterns = [
path('<int:pdf_id>/stav', org_required(views.korektury_stav_view), name='korektury_api_pdf_stav'),
path('oprava/stav', org_required(views.oprava_stav_view), name='korektury_api_oprava_stav'),
path('<int:pdf_id>/opravy_a_komentare', org_required(views.opravy_a_komentare_view), name='korektury_api_opravy_a_komentare'),
]

65
korektury/api/views.py Normal file
View file

@ -0,0 +1,65 @@
from django.http import HttpResponseForbidden, JsonResponse
from django.shortcuts import get_object_or_404
from django.utils.html import linebreaks
from django.views.decorators.csrf import csrf_exempt
from rest_framework import serializers
from korektury.utils import send_email_notification_komentar
from korektury.models import Oprava, KorekturovanePDF, Komentar
from personalni.models import Organizator, Osoba
def korektury_stav_view(request, pdf_id: int, **kwargs):
q = request.POST
pdf = get_object_or_404(KorekturovanePDF, id=pdf_id)
status = q.get('state')
if status is not None:
assert status in KorekturovanePDF.STATUS.values
pdf.status = status
pdf.save()
return JsonResponse({'status': pdf.status})
def oprava_stav_view(request, **kwargs):
q = request.POST
op_id_str = q.get('id')
assert op_id_str is not None
op_id = int(op_id_str)
op = get_object_or_404(Oprava, id=op_id)
status = q.get('action')
if status is not None:
assert status in Oprava.STATUS.values
op.status = status
op.save()
return JsonResponse({'status': op.status})
class KomentarSerializer(serializers.ModelSerializer):
class Meta:
model = Komentar
fields = '__all__'
def to_representation(self, instance):
ret = super().to_representation(instance)
ret["autor"] = str(instance.autor)
ret["text"] = linebreaks(ret["text"], autoescape=True) # Autora není třeba escapovat, ten se vkládá jako text.
return ret
class OpravaSerializer(serializers.ModelSerializer):
class Meta:
model = Oprava
fields = '__all__'
def to_representation(self, instance):
ret = super().to_representation(instance)
ret["komentare"] = [KomentarSerializer(komentar).data for komentar in instance.komentar_set.all()]
return ret
# komentar_set = serializers.ListField(child=KomentarSerializer())
def opravy_a_komentare_view(request, pdf_id: int, **kwargs):
q = request.POST
opravy = Oprava.objects.filter(pdf=pdf_id).all()
# Serializovat list je prý security vulnerability, tedy je přidán slovník pro bezpečnost
return JsonResponse({"context": [OpravaSerializer(oprava).data for oprava in opravy]})

View file

@ -1,14 +0,0 @@
from django import forms
class OpravaForm(forms.Form):
""" formulář k přidání opravy (:class:`korektury.models.Oprava`) """
text = forms.CharField(max_length=256)
autor = forms.CharField(max_length=20)
x = forms.IntegerField()
y = forms.IntegerField()
scroll = forms.CharField(max_length=256)
pdf = forms.CharField(max_length=256)
img_id = forms.CharField(max_length=256)
id = forms.CharField(max_length=256)
action = forms.CharField(max_length=256)

View file

@ -0,0 +1,45 @@
# Generated by Django 4.2.16 on 2024-12-12 10:25
from django.db import migrations
import datetime
from django.utils import timezone
def oprava2komentar(apps, schema_editor):
Oprava = apps.get_model('korektury', 'Oprava')
Komentar = apps.get_model('korektury', 'Komentar')
for o in Oprava.objects.all():
Komentar.objects.create(oprava=o, text=o.text, autor=o.autor, cas=timezone.make_aware(datetime.datetime.fromtimestamp(0)))
def komentar2oprava(apps, schema_editor):
Oprava = apps.get_model('korektury', 'Oprava')
Komentar = apps.get_model('korektury', 'Komentar')
for o in Oprava.objects.all():
k = Komentar.objects.filter(oprava=o).first()
o.text = k.text
o.autor = k.autor
o.save()
k.delete()
class Migration(migrations.Migration):
dependencies = [
('korektury', '0024_vic_orgu_k_pdf'),
]
operations = [
migrations.RunPython(oprava2komentar, komentar2oprava),
migrations.RemoveField(
model_name='oprava',
name='autor',
),
migrations.RemoveField(
model_name='oprava',
name='text',
),
]

View file

@ -52,16 +52,13 @@ class KorekturovanePDF(models.Model):
stran = models.IntegerField(u'počet stran', help_text='Počet stran PDF',
default=0)
STATUS_PRIDAVANI = 'pridavani'
STATUS_ZANASENI = 'zanaseni'
STATUS_ZASTARALE = 'zastarale'
STATUS_CHOICES = (
(STATUS_PRIDAVANI, u'Přidávání korektur'),
(STATUS_ZANASENI, u'Korektury jsou zanášeny'),
(STATUS_ZASTARALE, u'Stará verze, nekorigovat'),
)
status = models.CharField(u'stav PDF',max_length=16, choices=STATUS_CHOICES, blank=False,
default = STATUS_PRIDAVANI)
class STATUS(models.TextChoices):
PRIDAVANI = 'pridavani', 'Přidávání korektur'
ZANASENI = 'zanaseni', 'Korektury jsou zanášeny'
ZASTARALE = 'zastarale', 'Stará verze, nekorigovat'
status = models.CharField(u'stav PDF',max_length=16, choices=STATUS.choices, blank=False, default = STATUS.PRIDAVANI)
poslat_mail = models.BooleanField('Poslat mail o novém PDF', default=True,
help_text='Určuje, zda se má o nově nahraném PDF poslat e-mail do mam-org. Při upravování existujícího souboru už nemá žádný vliv.',
@ -152,32 +149,13 @@ class Oprava(models.Model):
x = models.IntegerField(u'x-ová souřadnice bugu')
y = models.IntegerField(u'y-ová souřadnice bugu')
STATUS_K_OPRAVE = 'k_oprave'
STATUS_OPRAVENO = 'opraveno'
STATUS_NENI_CHYBA = 'neni_chyba'
STATUS_K_ZANESENI = 'k_zaneseni'
STATUS_CHOICES = (
(STATUS_K_OPRAVE, u'K opravě'),
(STATUS_OPRAVENO, u'Opraveno'),
(STATUS_NENI_CHYBA, u'Není chyba'),
(STATUS_K_ZANESENI, u'K zanesení do TeXu'),
)
status = models.CharField(u'stav opravy',max_length=16, choices=STATUS_CHOICES, blank=False,
default = STATUS_K_OPRAVE)
autor = models.ForeignKey(Organizator, blank = True,
help_text='Autor opravy',
null = True, on_delete=models.SET_NULL)
text = models.TextField(u'text opravy',blank = True, help_text='Text opravy')
# def __init__(self,dictionary):
# for k,v in dictionary.items():
# setattr(self,k,v)
def __str__(self):
return '{} od {}: {}'.format(self.status,self.autor,self.text)
class STATUS(models.TextChoices):
K_OPRAVE = 'k_oprave', 'K opravě'
OPRAVENO = 'opraveno', 'Opraveno'
NENI_CHYBA = 'neni_chyba', 'Není chyba'
K_ZANESENI = 'k_zaneseni', 'K zanesení do TeXu'
status = models.CharField(u'stav opravy',max_length=16, choices=STATUS.choices, blank=False, default = STATUS.K_OPRAVE)
@reversion.register(ignore_duplicates=True)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 B

After

Width:  |  Height:  |  Size: 307 B

View file

@ -1,136 +1,159 @@
body,
.adding{
background: #f3f3f3;
color: black;
}
.comitting
{
background: yellow;
}
.deprecated {
background: red;
.textzanaseni { display:none; }
.textzastarale { display:none; }
#prekomentar, #preoprava, #prepointer { display: none; }
body {
&[data-status="pridavani"] {
background: #f3f3f3;
}
&[data-status="zanaseni"] {
background: yellow;
.textzanaseni { display: unset; }
}
&[data-status="zastarale"] {
background: red;
.textzastarale { display: unset; }
}
}
img{background:white;}
/* Barvy korektur */
.k_oprave {
[data-opravastatus="k_oprave"] {
--rgb: 255, 0, 0;
[value="k_oprave"] { display: none }
[value="notcomment"] { display: none }
}
.opraveno {
[data-opravastatus="opraveno"] {
--rgb: 0, 0, 255;
[value="opraveno"] { display: none }
[value="comment"] { display: none }
}
.neni_chyba {
[data-opravastatus="neni_chyba"] {
--rgb: 128, 128, 128;
[value="neni_chyba"] { display: none }
[value="comment"] { display: none }
}
.k_zaneseni {
[data-opravastatus="k_zaneseni"] {
--rgb: 0, 255, 0;
[value="k_zaneseni"] { display: none }
[value="notcomment"] { display: none }
}
.pointer-hi,
/* Skrývání korektur */
[data-opravazobrazit="false"] {
.corr-body { display: none; }
.corr-buttons { display: none; }
.toggle-button { transform: rotate(180deg); }
}
/* Čára od textu k místu korektury */
.pointer{
position:absolute;
/*border-bottom-left-radius: 10px; */
border-left: 2px solid yellow;
border-bottom: 2px solid yellow;
border-color: rgb(var(--rgb),var(--alpha));
border-bottom-left-radius: 10px;
border-left: 1px solid rgb(var(--rgb),var(--alpha));
border-bottom: 1px solid rgb(var(--rgb),var(--alpha));
pointer-events: none;
--alpha: 0.35;
/* Zvýraznění čáry při najetí na korekturu */
&[data-highlight="true"] {
border-width: 3px;
--alpha: 1;
}
}
.pointer {
border-width: 1px;
--alpha: 0.35;
}
.pointer-hi {
border-width: 3px;
--alpha: 1;
}
.box:hover{
border-width:3px;
margin: 0px;
}
.box {
/* Korektura samotná */
.oprava {
margin: 1px;
background-color: white;
width:300px;
/*position:absolute;*/
width: 300px;
padding: 3px;
border: 2px solid black;
border: 2px solid rgb(var(--rgb));
border-radius: 10px;
border-color: rgb(var(--rgb));
position: absolute;
&:hover {
border-width:3px;
margin: 0;
}
button, img {
border: 1px solid white;
background-color:transparent;
margin:0;
padding: 1px;
&:hover {
border: 1px solid black;
}
}
button img { pointer-events: none; }
.corr-header {
overflow: auto;
}
.author {
font-weight: bold;
float: left;
margin-top: 3px;
}
.float-right{
float:right;
}
}
form {
display:inline;
}
.float-right{
float:right;
}
/* Zobrazované PDF */
.imgdiv {
position:relative;
left:0px;
top:0px;
left:0;
top:0;
}
/* Přidávání korektury / úprava komentáře */
#commform-div {
display: none;
position: absolute;
background-color: white;
border: 1px solid;
padding: 3px;
/*
width: 310;
height: 220;
*/
z-index: 10;
border: 4px solid red;
border-radius: 10px;
background-color: white;
opacity: 80%;
}
.close-button{
background-color: yellow;
}
.box button,
.box img,
.box-done button,
.box-done img,
.box-ready button,
.box-ready img,
.box-wontfix button,
.box-wontfix img{
border: 1px solid white;
background-color:transparent;
margin:0;
padding: 1px;
}
.box button:hover,
.box img:hover,
.box-done img:hover,
.box-done button:hover,
.box-ready img:hover,
.box-ready button:hover,
.box-wontfix img:hover,
.box-wontfix button:hover{
border: 1px solid black;
}
.comment hr {
height: 0px;
}
.corr-header {
overflow: auto;
}
.author {
font-weight: bold;
float: left;
margin-top: 3px;
/**** ROZLIŠENÍ MEZI LOKÁLNÍM, TESTOVACÍM A PRODUKČNÍM WEBEM ****/
body.localweb, body.testweb, body.suprodweb {
&:before, &:after {
content: "";
position: fixed;
width: 20px;
height: 100%;
top: 0;
z-index: -1000;
}
&:before { left: 0; }
&:after { right: 0; }
}
body.localweb { &:before, &:after { background: greenyellow; } }
body.testweb { &:before, &:after { background: darkorange; } }
body.suprodweb { &:before, &:after { background: red; } }
/****************************************************************/

View file

@ -1,283 +1,46 @@
const W_SKIP = 10;
const H_SKIP = 5;
const POINTER_MIN_H = 30;
function place_comments_one_div(img_id, comments)
{
var img = document.getElementById(img_id);
if( img == null ) {
return;
const img = document.getElementById("img-"+img_id);
if( img == null ) return;
const comments_sorted = comments.sort((a, b) => a.y - b.y);
const par = img.parentNode;
const w = img.clientWidth;
let bott_max = 0;
for (const oprava of comments_sorted) {
const x = oprava.x;
const y = oprava.y;
const htmlElement = oprava.htmlElement;
const pointer = oprava.pointer;
par.appendChild(pointer);
par.appendChild(htmlElement);
const delta_y = (y > bott_max) ? 0: bott_max - y + H_SKIP;
pointer.style.left = x;
pointer.style.top = y;
pointer.style.width = w - x + W_SKIP;
pointer.style.height = POINTER_MIN_H + delta_y;
htmlElement.style.left = w + W_SKIP;
htmlElement.style.top = y + delta_y;
bott_max = Math.max(bott_max, htmlElement.offsetTop + htmlElement.offsetHeight);
}
var par = img.parentNode;
var w = img.clientWidth;
var h = img.clientHeight;
var w_skip = 10;
var h_skip = 5;
var pointer_min_h = 30;
var bott_max = 0;
var comments_sorted = comments.sort(function (a,b) {
return a[2] - b[2];
//pokus o hezci kladeni poiteru, ale nic moc
if( a[3] < b[3] ) {
return (a[2] + pointer_min_h)- b[2];
} else {
return (a[2] - pointer_min_h)- b[2];
}
});
//console.log("w:" + w);
for (c in comments_sorted) {
var id = comments_sorted[c][0];
var x = comments_sorted[c][1];
var y = comments_sorted[c][2];
var el = document.getElementById(id);
var elp = document.getElementById(id + "-pointer");
if( el == null || elp == null ) {
continue;
}
par.appendChild(elp);
par.appendChild(el);
var delta_y = (y > bott_max) ? 0: bott_max - y + h_skip;
elp.style.left = x;
elp.style.top = y ;
elp.style.width = w - x + w_skip;
elp.style.height = pointer_min_h + delta_y;
elp.img_id = img_id;
el.img_id = img_id;
el.style.position = 'absolute';
el.style.left = w + w_skip;
el.style.top = y + delta_y;
var bott = el.offsetTop + el.offsetHeight;
bott_max = ( bott_max > bott ) ? bott_max : bott;
//console.log( "par.w:" + par.style.width);
}
if( par.offsetHeight < bott_max ) {
//par.style.height = bott_max;
//alert("preteklo to:"+ par.offsetHeight +",mx:" + bott_max );
par.style.height = bott_max;
}
if (par.offsetHeight < bott_max) par.style.height = bott_max;
}
function place_comments() {
for (var i=0; i < comments.length-1; i++) {
place_comments_one_div(comments[i][0], comments[i][1])
for (let [img_id, opravy] of Object.entries(comments)) {
place_comments_one_div(img_id, opravy)
}
}
// ctrl-enter submits form
function textarea_onkey(ev)
{
//console.log("ev:" + ev.keyCode + "," + ev.ctrlKey);
if( (ev.keyCode == 13 || ev.keyCode == 10 ) && ev.ctrlKey ) {
var form = document.getElementById('commform');
if( form ) {
save_scroll(form);
//form.action ='';
form.submit();
}
return true;
}
return false;
}
//hide comment form
function close_commform() {
var formdiv = document.getElementById('commform-div');
if( formdiv == null ) {
alert("form null");
return true;
}
formdiv.style.display = 'none';
return false;
}
// show comment form, when clicked to image
function img_click(element, ev) {
var body_class = document.body.className;
switch(body_class){
case "comitting":
if (!confirm("Právě jsou zanášeny korektury, opravdu chcete přidat novou?"))
return;
break;
case "deprecated":
if (!confirm("Toto PDF je již zastaralé, opravdu chcete vytvořit korekturu?"))
return;
break;
}
var dx, dy;
var par = element.parentNode;
if( ev.pageX != null ) {
dx = ev.pageX - par.offsetLeft;
dy = ev.pageY - par.offsetTop;
} else { //IE
dx = ev.offsetX;
dy = ev.offsetY;
}
var img_id = element.id;
if( element.img_id != null ) {
// click was to '-pointer'
img_id = element.img_id;
}
return show_form(img_id, dx, dy, '', '', '', '');
}
// hide or show text of correction
function toggle_visibility(oid){
var buttondiv = document.getElementById(oid+'-buttons')
var text = document.getElementById(oid+'-body');
if (text.style.display == 'none'){
text.style.display = 'block';
buttondiv.style.display = 'inline-block';
}else {
text.style.display = 'none';
buttondiv.style.display = 'none';
}
for (var i=0;i<comments.length-1;i++){
place_comments_one_div(comments[i][0], comments[i][1])
}
}
// show comment form, when 'edit' or 'comment' button pressed
function box_edit(oid, action)
{
var divpointer = document.getElementById(oid + '-pointer');
var text;
if (action == 'update') {
var text_el = document.getElementById(oid + '-text');
text = text_el.textContent; // FIXME původně tu bylo innerHTML.unescapeHTML()
} else {
text = '';
}
var dx = parseInt(divpointer.style.left);
var dy = parseInt(divpointer.style.top);
var divbox = document.getElementById(oid);
//alert('not yet 2:' + text + text_el); // + divpointer.style.top "x" + divpo );
id = oid.substring(2);
return show_form(divbox.img_id, dx, dy, id, text, action);
}
// show comment form when 'update-comment' button pressed
function update_comment(oid,ktid)
{
var divpointer = document.getElementById(oid + '-pointer');
var dx = parseInt(divpointer.style.left);
var dy = parseInt(divpointer.style.top);
var divbox = document.getElementById(oid);
var text = document.getElementById(ktid).textContent; // FIXME původně tu bylo innerHTML.unescapeHTML()
return show_form(divbox.img_id, dx, dy, ktid.substring(2), text, 'update-comment');
}
//fill up comment form and show him
function show_form(img_id, dx, dy, id, text, action) {
var form = document.getElementById('commform');
var formdiv = document.getElementById('commform-div');
var textarea = document.getElementById('commform-text');
var inputX = document.getElementById('commform-x');
var inputY = document.getElementById('commform-y');
var inputImgId = document.getElementById('commform-img-id');
var inputId = document.getElementById('commform-id');
var inputAction = document.getElementById('commform-action');
var img = document.getElementById(img_id);
if( formdiv == null || textarea == null ) {
alert("form null");
return 1;
}
//form.action = "#" + img_id;
// set hidden values
inputX.value = dx;
inputY.value = dy;
inputImgId.value = img_id;
inputId.value = id;
inputAction.value = action;
textarea.value = text;
//textarea.value = "dxy:"+ dx + "x" + dy + "\n" + 'id:' + img_id;
// show form
formdiv.style.display = 'block';
formdiv.style.left = dx;
formdiv.style.top = dy;
img.parentNode.appendChild(formdiv);
textarea.focus();
return true;
}
function box_onmouseover(box)
{
var id = box.id;
var pointer = document.getElementById(box.id + '-pointer');
pointer.classList.remove('pointer');
pointer.classList.add('pointer-hi');
}
function box_onmouseout(box)
{
var id = box.id;
var pointer = document.getElementById(box.id + '-pointer');
pointer.classList.remove('pointer-hi');
pointer.classList.add('pointer');
}
function save_scroll(form)
{
//alert('save_scroll:' + document.body.scrollTop);
form.scroll.value = document.body.scrollTop;
//alert('save_scroll:' + form.scroll.value);
return true;
}
function toggle_corrections(aclass)
{
var stylesheets = document.styleSheets;
var ssheet = null;
for (var i=0;i<stylesheets.length; i++){
if (stylesheets[i].title === "opraf-css"){
ssheet = stylesheets[i];
break;
}
}
if (! ssheet){
return;
}
for (var i=0;i<ssheet.cssRules.length;i++){
var rule = ssheet.cssRules[i];
if (rule.selectorText === '.'+aclass){
if (rule.style.display === ""){
rule.style.display = "none";
} else {
rule.style.display = "";
}
}
}
place_comments();
}
String.prototype.unescapeHTML = function () {
return(
this.replace(/&amp;/g,'&').
replace(/&gt;/g,'>').
replace(/&lt;/g,'<').
replace(/&quot;/g,'"')
);
};

View file

@ -0,0 +1,64 @@
<div id="commform-div" style="display: none">
<form action='' id="commform" method="POST">
{% csrf_token %}
<input size="24" name="au" value="{{user.first_name}} {{user.last_name}}" readonly/>
<input type=submit value="Oprav!"/>
<button type="button" onclick="close_commform()">Zavřít</button>
<br/>
<textarea id="commform-text" cols=40 rows=10 name="txt"></textarea>
<br/>
<input type="hidden" size="3" id="commform-x" name="x"/>
<input type="hidden" size="3" id="commform-y" name="y"/>
<input type="hidden" size="3" id="commform-img-id" name="img-id"/>
<input type="hidden" size="3" id="commform-id" name="id"/>
<input type="hidden" size="3" id="commform-action" name="action"/>
</form>
</div>
<script>
const commform = document.getElementById('commform');
commform._div = document.getElementById('commform-div');
commform._text = document.getElementById('commform-text');
commform._x = document.getElementById('commform-x');
commform._y = document.getElementById('commform-y');
commform._imgID = document.getElementById('commform-img-id');
commform._id = document.getElementById('commform-id');
commform._action = document.getElementById('commform-action');
// ctrl-enter submits form
commform._text.addEventListener("keydown", ev => {
if (ev.code === "Enter" && ev.ctrlKey) commform.submit();
});
//hide comment form
function close_commform() {
commform._div.style.display = 'none';
return false;
}
//fill up comment form and show him
function show_form(img_id, dx, dy, id, text, action) {
const img = document.getElementById("img-" + img_id);
if (commform._div.style.display !== 'none' && commform._text.value !== "" && !confirm("Zavřít předchozí okénko přidávání korektury / editace komentáře?")) return 1;
// set hidden values
commform._x.value = dx;
commform._y.value = dy;
commform._imgID.value = img_id;
commform._id.value = id;
commform._action.value = action;
commform._text.value = text;
// show form
commform._div.style.display = 'block';
commform._div.style.left = dx;
commform._div.style.top = dy;
img.parentNode.appendChild(commform._div);
commform._text.focus();
return true;
}
</script>

View file

@ -0,0 +1,86 @@
{% load static %}
<div class='comment' id='prekomentar' {# id='k{{k.id}}' #}>
<div class='corr-header'>
<div class='author'>{# {{k.autor}} #}</div>
<div class='float-right'>
<button type='button' style='display: none' class='del-comment' title='Smaž komentář'>
<img src='{% static "korektury/imgs/delete.png" %}' alt='del'/>
</button>
<button type='button' class='update-comment' title='Uprav komentář'>
<img src='{% static "korektury/imgs/edit.png"%}' alt='edit'/>
</button>
</div>
</div>
<div class='komtext'>{# {{k.text|linebreaks}} #}</div>
</div>
<script>
const prekomentar = document.getElementById('prekomentar');
const komentare = {};
class Komentar {
static update_or_create(komentar_data, oprava) {
const id = komentar_data['id'];
if (id in komentare) komentare[id].update(komentar_data);
else new Komentar(komentar_data, oprava);
}
#autor; #text;
htmlElement;
id; oprava; {# komentar_data; #}
/**
*
* @param komentar_data
* @param {Oprava} oprava
*/
constructor(komentar_data, oprava) {
this.htmlElement = prekomentar.cloneNode(true);
this.#autor = this.htmlElement.getElementsByClassName('author')[0];
this.#text = this.htmlElement.getElementsByClassName('komtext')[0];
this.id = komentar_data['id'];
this.htmlElement.id = 'k' + this.id;
this.oprava = oprava;
this.oprava.add_komentar_htmlElement(this.htmlElement);
this.update(komentar_data);
this.htmlElement.getElementsByClassName('update-comment')[0].addEventListener('click', _ => this.#update_comment());
this.htmlElement.getElementsByClassName('del-comment')[0].addEventListener('click', _ => this.#delete_comment());
komentare[this.id] = this;
}
update(komentar_data) {
{# this.komentar_data = komentar_data; #}
this.set_autor(komentar_data['autor']);
this.set_text(komentar_data['text']);
};
set_autor(autor) {this.#autor.textContent=autor;};
set_text(text) {
this.#text.innerHTML=text;
};
// show comment form when 'update-comment' button pressed
#update_comment() {
return show_form(this.oprava.img_id, this.oprava.x, this.oprava.y, this.id, this.#text.textContent, 'update-comment');
}
#delete_comment() {
if (confirm('Opravdu smazat komentář?')) {
throw {name : 'NotImplementedError', message: '(Webaři jsou) too lazy to implement'};
}
}
}
</script>

View file

@ -0,0 +1,145 @@
{% load static %}
<div id='prepointer' {# id='op{{o.id}}-pointer' #}
class='pointer'
data-highlight='false'
{# data-opravastatus='{{o.status}}' #}
></div>
<div id='preoprava' {# name='op{{o.id}}' id='op{{o.id}}' #}
class='oprava'
{# data-opravastatus='{{o.status}}' #}
data-opravazobrazit='true'
>
<div class='corr-body'>
{# {% for k in o.komentare %} {% include "korektury/korekturovatko/__komentar.html" %} <hr> {% endfor %} #}
</div>
<div class='corr-header'>
<span class='float-right'>
<span class='corr-buttons'>
<button type='button' style='display: none' class='del' title='Smaž opravu'>
<img src='{% static "korektury/imgs/delete.png"%}' alt='🗑️'/>
</button>
<button type='button' class='action' value='k_oprave' title='Označ jako neopravené'>
<img src='{% static "korektury/imgs/undo.png"%}' alt='↪'/>
</button>
<button type='button' class='action' value='opraveno' title='Označ jako opravené'>
<img src='{% static "korektury/imgs/check.png"%}' alt='✔️'/>
</button>
<button type='button' class='action' value='neni_chyba' title='Označ, že se nebude měnit'>
<img src='{% static "korektury/imgs/cross.png" %}' alt='❌'/>
</button>
<button type='button' class='action' value='k_zaneseni' title='Označ jako připraveno k zanesení'>
<img src='{% static "korektury/imgs/tex.png" %}' alt='TeX'/>
</button>
<button type='button' class='notcomment' title='Korekturu nelze komentovat, protože už je uzavřená' disabled=''>
<img src='{% static "korektury/imgs/comment-gr.png" %}' alt='💭'/>
</button>
<button type='button' class='comment' title='Komentovat'>
<img src='{% static "korektury/imgs/comment.png" %}' alt='💭'/>
</button>
</span>
<button type='button' class='toggle-vis' title='Skrýt/Zobrazit'>
<img class='toggle-button' src='{% static "korektury/imgs/hide.png" %}' alt='⬆'/>
</button>
</span>
</div>
</div>
<script>
const preoprava = document.getElementById('preoprava');
const prepointer = document.getElementById('prepointer');
const opravy = {};
class Oprava {
static update_or_create(oprava_data) {
const id = oprava_data['id'];
if (id in opravy) return opravy[id].update(oprava_data);
else return new Oprava(oprava_data);
}
#komentare;
htmlElement; pointer;
id; x; y; img_id; zobrazit = true; {# oprava_data; #}
constructor(oprava_data) {
this.htmlElement = preoprava.cloneNode(true);
this.pointer = prepointer.cloneNode(true);
this.#komentare = this.htmlElement.getElementsByClassName('corr-body')[0];
this.id = oprava_data['id'];
this.htmlElement.id = 'op' + this.id;
this.pointer.id = 'op' + this.id + '-pointer';
this.x = oprava_data['x'];
this.y = oprava_data['y'];
this.img_id = oprava_data['strana'];
this.update(oprava_data);
this.htmlElement.getElementsByClassName('toggle-vis')[0].addEventListener('click', _ => this.#toggle_visibility());
for (const button of this.htmlElement.getElementsByClassName('action'))
button.addEventListener('click', async event => this.#zmenStavKorektury(event));
this.htmlElement.getElementsByClassName('comment')[0].addEventListener('click', _ => this.#comment())
this.htmlElement.getElementsByClassName('del')[0].addEventListener('click', _ => this.#delete());
this.htmlElement.addEventListener('mouseover', _ => this.pointer.dataset.highlight = 'true');
this.htmlElement.addEventListener('mouseout', _ => this.pointer.dataset.highlight = 'false');
opravy[this.id] = this;
if (this.img_id in comments) comments[this.img_id].push(this); else alert("Někdo korekturoval stranu, která neexistuje. Dejte vědět webařům :)");
}
update(oprava_data) {
{# this.oprava_data = oprava_data; #}
this.set_status(oprava_data['status']);
return this;
};
set_status(status) {
this.htmlElement.dataset.opravastatus=status;
this.pointer.dataset.opravastatus=status;
};
add_komentar_htmlElement(htmlElement) {
this.#komentare.appendChild(htmlElement);
this.#komentare.appendChild(document.createElement('hr'));
}
// hide or show text of correction
#toggle_visibility(){
this.zobrazit = !this.zobrazit;
this.htmlElement.dataset.opravazobrazit = String(this.zobrazit);
place_comments()
}
// show comment form, when 'comment' button pressed
#comment() { return show_form(this.img_id, this.x, this.y, this.id, "", "comment"); }
#zmenStavKorektury(event) {
const data = new FormData(CSRF_FORM);
data.append('id', this.id);
data.append('action', event.target.value);
fetch('{% url "korektury_api_oprava_stav" %}', {method: 'POST', body: data})
.then(response => {
if (!response.ok) {alert('Něco se nepovedlo:' + response.statusText);}
else response.json().then(data => this.set_status(data['status']));
})
.catch(error => {alert('Něco se nepovedlo:' + error);});
}
#delete() {
if (confirm('Opravdu smazat korekturu?')) {
throw {name : 'NotImplementedError', message: '(Webaři jsou) too lazy to implement'};
}
}
}
</script>

View file

@ -0,0 +1,50 @@
{% for i in img_indexes %}
<div class='imgdiv'>
<img
id='img-{{i}}'
width='1021' height='1448'
src='/media/korektury/img/{{img_prefix}}-{{i}}.png'
alt='Strana {{ i|add:1 }}'
class="strana"
/>
</div>
<hr/>
{% endfor %}
<script>
// Mapování stránka -> korektury
const comments = {
{% for s in opravy_strany %}
{{s.strana}}: []{% if not forloop.last %},{% endif %}
{% endfor %}
};
// show comment form, when clicked to image
for (const image of document.getElementsByClassName('strana')) {
image.addEventListener('click', ev => {
switch (document.body.dataset.status) {
case 'zanaseni':
if (!confirm('Právě jsou zanášeny korektury, opravdu chcete přidat novou?'))
return;
break;
case 'zastarale':
if (!confirm('Toto PDF je již zastaralé, opravdu chcete vytvořit korekturu?'))
return;
break;
}
let dx, dy;
const par = image.parentNode;
if (ev.pageX != null) {
dx = ev.pageX - par.offsetLeft;
dy = ev.pageY - par.offsetTop;
} else { //IE a další
dx = ev.offsetX;
dy = ev.offsetY;
}
const img_id = image.id;
return show_form(img_id, dx, dy, '', '', '');
});
}
</script>

View file

@ -0,0 +1,40 @@
{% include "korektury/korekturovatko/__edit_komentar.html" %}
{% include "korektury/korekturovatko/__stranky.html" %}
{# {% for o in opravy %} {% include "korektury/korekturovatko/__oprava.html" %} {% endfor %} #}
{% include "korektury/korekturovatko/__oprava.html" %}
{% include "korektury/korekturovatko/__komentar.html" %}
<script>
/**
*
* @param {RequestInit} data
* @param {Boolean} catchError
*/
function update_all(data={}, catchError=true) { // FIXME není mi jasné, zda v {} nemá být `cache: "no-store"`, aby prohlížeč necachoval GET.
fetch('{% url "korektury_api_opravy_a_komentare" pdf.id %}', data)
.then(response => {
if (!response.ok && catchError) {alert('Něco se nepovedlo:' + response.statusText);}
else response.json().then(data => {
for (const oprava_data of data["context"]) {
const oprava = Oprava.update_or_create(oprava_data);
for (const komentar_data of oprava_data["komentare"]) {
Komentar.update_or_create(komentar_data, oprava);
}
}
place_comments();
});
})
.catch(error => {if (catchError) alert('Něco se nepovedlo:' + error);});
}
update_all();
</script>
<form id='CSRF_form' style='display: none'>{% csrf_token %}</form>
<script>
const CSRF_FORM = document.getElementById('CSRF_form');
</script>

View file

@ -0,0 +1,51 @@
Zobrazit:
<input type="checkbox"
id="k_oprave_checkbox"
name="k_oprave_checkbox"
onchange="toggle_corrections('k_oprave')" checked>
<label for="k_oprave_checkbox">K opravě ({{k_oprave_cnt}})</label>
<input type="checkbox"
id="opraveno_checkbox"
name="opraveno_checkbox"
onchange="toggle_corrections('opraveno')" checked>
<label for="opraveno_checkbox">Opraveno ({{opraveno_cnt}})</label>
<input type="checkbox"
id="neni_chyba_checkbox"
name="neni_chyba_checkbox"
onchange="toggle_corrections('neni_chyba')" checked>
<label for="neni_chyba_checkbox">Není chyba ({{neni_chyba_cnt}})</label>
<input type="checkbox"
id="k_zaneseni_checkbox"
name="k_zaneseni_checkbox"
onchange="toggle_corrections('k_zaneseni')" checked>
<label for="k_zaneseni_checkbox">K zanesení ({{k_zaneseni_cnt}})</label>
<hr/>
<script>
function toggle_corrections(aclass)
{
const stylesheets = document.styleSheets;
let ssheet = null;
for (let i=0; i<stylesheets.length; i++){
if (stylesheets[i].title === "opraf-css"){
ssheet = stylesheets[i];
break;
}
}
if (! ssheet){
return;
}
for (let i=0; i<ssheet.cssRules.length; i++){
const rule = ssheet.cssRules[i];
if (rule.selectorText === '[data-opravastatus="'+aclass+'"]'){
if (rule.style.display === ""){
rule.style.display = "none";
} else {
rule.style.display = "";
}
}
}
place_comments();
}
</script>

View file

@ -0,0 +1,44 @@
<h4>Změnit stav PDF:</h4>
<i>Aktuální: {{pdf.status}}</i>
<br>
<form method="post" id="PDFSTAV_FORM">
{% csrf_token %}
<input type="radio" name="state" value="{{ pdf.STATUS.PRIDAVANI }}" {% if pdf.status == pdf.STATUS.PRIDAVANI %} checked {% endif %}>Přidávání korektur
<br>
<input type="radio" name="state" value="{{ pdf.STATUS.ZANASENI }}" {% if pdf.status == pdf.STATUS.ZANASENI %} checked {% endif %}>Zanášení korektur
<br>
<input type="radio" name="state" value="{{ pdf.STATUS.ZASTARALE }}" {% if pdf.status == pdf.STATUS.ZASTARALE %} checked {% endif %}>Zastaralé, nekorigovat
<br>
<input type='submit' value='Změnit stav PDF'/>
</form>
<script>
const pdfstav_form = document.getElementById('PDFSTAV_FORM');
/**
*
* @param {RequestInit} data
* @param {Boolean} catchError
*/
function fetchStav(data, catchError=true) {
fetch("{% url 'korektury_api_pdf_stav' pdf.id %}", data
)
.then(response => {
if (!response.ok) { if (catchError) alert("Něco se nepovedlo:" + response.statusText);}
else response.json().then(data => document.body.dataset.status = data["status"]);
})
.catch(error => {if (catchError) alert("Něco se nepovedlo:" + error);});
}
pdfstav_form.addEventListener('submit', async event => {
event.preventDefault();
const data = new FormData(pdfstav_form);
fetchStav({method: "POST", body: data});
});
// FIXME není mi jasné, zda v {} nemá být `cache: "no-store"`, aby prohlížeč necachoval get.
setInterval(() => fetchStav({}, false), 30000); // Každou půl minutu fetchni stav
</script>

View file

@ -0,0 +1,44 @@
{% load static %}
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" title="opraf-css" type="text/css" media="screen, projection" href="{% static "korektury/opraf.css"%}" />
<link href="{% static 'css/rozliseni.css' %}?version=1" rel="stylesheet">
<script src="{% static "korektury/opraf.js"%}"></script>
<title>Korektury {{pdf.nazev}}</title>
</head>
<body class="{{ LOCAL_TEST_PROD }}web" data-status="{{ pdf.status }}" onload='place_comments()'>
<h1>Korektury {{pdf.nazev}}</h1>
<h2 class="textzanaseni"> Probíhá zanášení korektur, zvažte, zda chcete přidávat nové </h2>
<h2 class="textzastarale"> Toto PDF je již zastaralé, nepřidávejte nové korektury </h2>
<i>{{pdf.komentar}}</i>
<br>
<i>Klikni na chybu, napiš komentář</i> |
<a href="{{pdf.pdf.url}}">stáhnout PDF (bez korektur)</a> |
<a href="../">seznam souborů</a> |
<a href="/admin/korektury/korekturovanepdf/">Spravovat PDF</a> |
<a href="../help">nápověda</a> |
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|
<a href="/">hlavní stránka</a> |
<a href="https://mam.mff.cuni.cz/wiki">wiki</a> |
<hr />
{% include "korektury/korekturovatko/_schovani_korektur.html" %}
{% include "korektury/korekturovatko/_main.html" %}
{% include "korektury/korekturovatko/_zmena_stavu.html" %}
<hr/>
<p>
Děkujeme opravovatelům:
{% for z in zasluhy %}
{{z.autor}} ({{z.pocet}}){% if not forloop.last %},{% endif %}
{% endfor %}</p>
<hr>
</body>
</html>

View file

@ -1,244 +0,0 @@
{% load static %}
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" title="opraf-css" type="text/css" media="screen, projection" href="{% static "korektury/opraf.css"%}" />
<link href="{% static 'css/rozliseni.css' %}?version=1" rel="stylesheet">
<script src="{% static "korektury/opraf.js"%}"></script>
<title>Korektury {{pdf.nazev}}</title>
</head>
<body class="{{ LOCAL_TEST_PROD }}web{% if pdf.status == 'zanaseni'%} comitting{% elif pdf.status == 'zastarale' %} deprecated{% endif %}" onload='place_comments()'>
<h1>Korektury {{pdf.nazev}}</h1>
{% if pdf.status == 'zanaseni' %} <h2> Probíhá zanášení korektur, zvažte, zda chcete přidávat nové </h2> {% endif %}
{% if pdf.status == 'zastarale' %} <h2> Toto PDF je již zastaralé, nepřidávejte nové korektury </h2> {% endif %}
<i>{{pdf.komentar}}</i>
<br>
<i>Klikni na chybu, napiš komentář</i> |
<a href="{{pdf.pdf.url}}">stáhnout PDF (bez korektur)</a> |
<a href="../">seznam souborů</a> |
<a href="/admin/korektury/korekturovanepdf/">Spravovat PDF</a> |
<a href="../help">nápověda</a> |
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|
<a href="/">hlavní stránka</a> |
<a href="https://mam.mff.cuni.cz/wiki">wiki</a> |
<hr />
Zobrazit:
<input type="checkbox"
id="k_oprave_checkbox"
name="k_oprave_checkbox"
onchange="toggle_corrections('k_oprave')" checked>
<label for="k_oprave_checkbox">K opravě ({{k_oprave_cnt}})</label>
<input type="checkbox"
id="opraveno_checkbox"
name="opraveno_checkbox"
onchange="toggle_corrections('opraveno')" checked>
<label for="opraveno_checkbox">Opraveno ({{opraveno_cnt}})</label>
<input type="checkbox"
id="neni_chyba_checkbox"
name="neni_chyba_checkbox"
onchange="toggle_corrections('neni_chyba')" checked>
<label for="neni_chyba_checkbox">Není chyba ({{neni_chyba_cnt}})</label>
<input type="checkbox"
id="k_zaneseni_checkbox"
name="k_zaneseni_checkbox"
onchange="toggle_corrections('k_zaneseni')" checked>
<label for="k_zaneseni_checkbox">K zanesení ({{k_zaneseni_cnt}})</label>
<hr/>
<div id="commform-div">
<!-- Pridat korekturu / komentar !-->
<form action='' onsubmit='save_scroll(this)' id="commform" method="POST">
{% csrf_token %}
<input size="24" name="au" value="{{user.first_name}} {{user.last_name}}" readonly/>
<input type=submit value="Oprav!"/>
<button type="button" onclick="close_commform()">Zavřít</button>
<br/>
<textarea onkeypress="textarea_onkey(event);" id="commform-text" cols=40 rows=10 name="txt"></textarea>
<br/>
<input type="hidden" size="3" name="pdf" value='{{pdf.id}}'/>
<input type="hidden" size="3" id="commform-x" name="x"/>
<input type="hidden" size="3" id="commform-y" name="y"/>
<input type="hidden" size="3" id="commform-img-id" name="img-id"/>
<input type="hidden" size="3" id="commform-id" name="id"/>
<input type="hidden" size="3" id="commform-action" name="action"/>
<input type="hidden" size="3" id="commform-action" name="scroll"/>
</form>
<!-- /Pridat korekturu / komentar !-->
</div>
{% for i in img_indexes %}
<div class='imgdiv'>
<img width='1021' height='1448'
onclick='img_click(this,event)' id='img-{{i}}'
src='/media/korektury/img/{{img_prefix}}-{{i}}.png'/>
</div>
<hr/>
{% endfor %}
<h4>Změnit stav PDF:</h4>
<i>Aktuální: {{pdf.status}}</i>
<br>
<!-- Zmenit stav PDF !-->
<form method="post">
{% csrf_token %}
<input type='hidden' name='action' value='set-state'/>
<input type='hidden' name='pdf' value='{{pdf.id}}'/>
<input type="radio" name="state" value="adding" {% if pdf.status == 'pridavani' %} checked {% endif %}>Přidávání korektur
<br>
<input type="radio" name="state" value="comitting" {% if pdf.status == 'zanaseni' %} checked {% endif %}>Zanášení korektur
<br>
<input type="radio" name="state" value="deprecated" {% if pdf.status == 'zastarale' %} checked {% endif %}>Zastaralé, nekorigovat
<br>
<input type='submit' value='Změnit stav PDF'/>
</form>
<!-- /Zmenit stav PDF !-->
<hr/>
<p>
Děkujeme opravovatelům:
{% for z in zasluhy %}
{{z.autor}} ({{z.pocet}}){% if not forloop.last %},{% endif %}
{% endfor %}</p>
<hr>
{% for o in opravy %}
<div onclick='img_click(this,event)'
id='op{{o.id}}-pointer'
class='pointer {{o.status}}'>
</div>
<div name='op{{o.id}}' id='op{{o.id}}'
class='box {{o.status}}'
onmouseover='box_onmouseover(this)'
onmouseout='box_onmouseout(this)'>
<div class='corr-header'>
<span class='author' id='op{{o.id}}-autor'>{{o.autor}}</span>
<span class='float-right'>
<span id='op{{o.id}}-buttons'>
<!-- Existujici korektura !-->
<form action='' onsubmit='save_scroll(this)' method='POST'>
{% csrf_token %}
<input type='hidden' name="au" value="{{o.autor}}"/>
<input type='hidden' name='pdf' value='{{pdf.id}}'>
<input type='hidden' name='id' value='{{o.id}}'>
<input type='hidden' name='scroll'>
{% if o.komentare %}
<button name='action' value='del' type='button'
title="Opravu nelze smazat &ndash; už ji někdo okomentoval">
<img src="{% static "korektury/imgs/delete-gr.png"%}"/>
</button>
{% else %}
<button type='submit' name='action' value='del' title='Smaž opravu'>
<img src="{% static "korektury/imgs/delete.png"%}"/>
</button>
{% endif %}
{% if o.status != 'k_oprave' %}
<button type='submit' name='action' value='undone' title='Označ jako neopravené'>
<img src="{% static "korektury/imgs/undo.png"%}"/>
</button>
{% endif %}
{% if o.status != 'opraveno' %}
<button type='submit' name='action' value='done' title='Označ jako opravené'>
<img src="{% static "korektury/imgs/check.png"%}"/>
</button>
{% endif %}
{% if o.status != 'neni_chyba' %}
<button type='submit' name='action' value='wontfix' title='Označ, že se nebude měnit'>
<img src="{% static "korektury/imgs/cross.png" %}"/>
</button>
{% endif %}
{% if o.status != 'k_zaneseni' %}
<button type='submit' name='action' value='ready' title='Označ jako připraveno k zanesení'>
<img src="{% static "korektury/imgs/tex.png" %}"/>
</button>
{% endif %}
</form>
<!-- /Existujici korektura !-->
{% if o.komentare %}
<button type='button' title="Korekturu nelze upravit &ndash; už ji někdo okomentoval">
<img src="{% static "korektury/imgs/edit-gr.png" %}"/>
</button>
{% else %}
<button type='button' onclick='box_edit("op{{o.id}}","update");' title='Oprav opravu'>
<img src="{% static "korektury/imgs/edit.png" %}"/>
</button>
{% endif %}
{% if o.status == 'opraveno' or o.status == 'neni_chyba' %}
<button type='button' title='Korekturu nelze komentovat, protože už je uzavřená'>
<img src="{% static "korektury/imgs/comment-gr.png" %}"/>
</button>
{% else %}
<button type='button' onclick='box_edit("op{{o.id}}", "comment");' title='Komentovat'>
<img src="{% static "korektury/imgs/comment.png" %}"/>
</button>
{% endif %}
</span>
<button type='button' onclick='toggle_visibility("op{{o.id}}");' title='Skrýt/Zobrazit'>
<img src="{% static "korektury/imgs/hide.png" %}"/>
</button>
</span>
</div>
<div class='corr-body' id='op{{o.id}}-body'>
<div id='op{{o.id}}-text'>{{o.text|linebreaks}}</div>
{% for k in o.komentare %}
<hr>
<div class='comment' id='k{{k.id}}'>
<div class='corr-header'>
<div class='author'>{{k.autor}}</div>
<div class="float-right">
<!-- Komentar !-->
<form action='' onsubmit='save_scroll(this)' method='POST'>
{% csrf_token %}
<input type='hidden' name='pdf' value='{{pdf.id}}'>
<input type='hidden' name='id' value='{{k.id}}'>
<input type='hidden' name='scroll'>
{% if forloop.last %}
<button type='submit' name='action' value='del-comment' title='Smaž komentář'
onclick='return confirm("Opravdu smazat komentář?")'>
<img src="{% static "korektury/imgs/delete.png" %}"/>
</button>
{% else %}
<button name='action' value='del-comment' type='button'
title="Komentář nelze smazat &ndash; existuje novější">
<img src="{% static "korektury/imgs/delete-gr.png"%}"/>
</button>
{% endif %}
</form>
<!-- /Komentar !-->
{% if forloop.last %}
<button type='button' onclick="update_comment('op{{o.id}}','kt{{k.id}}');" title='Uprav komentář'>
<img src="{% static "korektury/imgs/edit.png"%}"/>
</button>
{% else %}
<button type='button' title="Komentář nelze upravit &ndash; existuje novější">
<img src="{% static "korektury/imgs/edit-gr.png" %}"/>
</button>
{% endif %}
</div>
</div>
<div id='kt{{k.id}}'>{{k.text|linebreaks}}</div>
</div>
{% endfor %}
</div>
</div>
{% endfor %}
<script>
var comments = [
{% for s in opravy_strany %}
["img-{{s.strana}}", [{% for o in s.op_id %}["op{{o.id}}",{{o.x}},{{o.y}}],{% endfor %}[]]],
{% endfor %}
[]]
{% if scroll %}
window.scrollTo(0,{{scroll}});
{% endif %}
</script>
</body>
</html>

View file

@ -1,4 +1,6 @@
from django.urls import path
from django.urls import include
from personalni.utils import org_required
from . import views
@ -7,4 +9,6 @@ urlpatterns = [
path('korektury/neseskupene/', org_required(views.KorekturyAktualniListView.as_view()), name='korektury_neseskupene_list'),
path('korektury/zastarale/', org_required(views.KorekturyZastaraleListView.as_view()), name='korektury_stare_list'),
path('korektury/<int:pdf>/', org_required(views.KorekturyView.as_view()), name='korektury'),
path('korektury/api/', include('korektury.api.urls')),
]

53
korektury/utils.py Normal file
View file

@ -0,0 +1,53 @@
from django.core.mail import EmailMessage
from django.http import HttpRequest
from django.urls import reverse
from korektury.models import Komentar, Oprava
from personalni.models import Organizator
def send_email_notification_komentar(oprava: Oprava, autor: Organizator, request: HttpRequest):
''' Rozesle e-mail pri pridani komentare / opravy,
ktery obsahuje text vlakna opravy.
'''
# parametry e-mailu
#odkaz = "https://mam.mff.cuni.cz/korektury/{}/".format(oprava.pdf.pk)
odkaz = request.build_absolute_uri(reverse('korektury', kwargs={'pdf': oprava.pdf.pk}))
odkaz = f"{odkaz}#op{oprava.id}-pointer"
from_email = 'korekturovatko@mam.mff.cuni.cz'
subject = 'Nová korektura od {} v {}'.format(autor, oprava.pdf.nazev)
texty = []
for kom in Komentar.objects.filter(oprava=oprava):
texty.append((kom.autor.osoba.plne_jmeno(),kom.text))
optext = "\n\n\n".join([": ".join(t) for t in texty])
text = u"Text komentáře:\n\n{}\n\n=== Konec textu komentáře ===\n\
\nodkaz do korekturovátka: {}\n\
\nVaše korekturovátko\n".format(optext, odkaz)
# Prijemci e-mailu
emails = set()
# nalezeni e-mailu na autory komentaru
for komentar in oprava.komentar_set.all():
email_komentujiciho = komentar.autor.osoba.email
if email_komentujiciho:
emails.add(email_komentujiciho)
# zodpovedni orgove
for org in oprava.pdf.orgove.all():
email_zobpovedny = org.osoba.email
if email_zobpovedny:
emails.add(email_zobpovedny)
# odstran e-mail autora opravy
email = autor.osoba.email
if email:
emails.discard(email)
EmailMessage(
subject=subject,
body=text,
from_email=from_email,
to=list(emails),
).send()

View file

@ -2,24 +2,18 @@ from django.shortcuts import get_object_or_404, render
from django.views import generic
from django.conf import settings
from django.http import HttpResponseForbidden
from django.core.mail import EmailMessage
from django.db.models import Count,Q
from .utils import send_email_notification_komentar
from .models import Oprava,Komentar,KorekturovanePDF, Organizator
from .forms import OpravaForm
import subprocess
import shutil
import os
class KorekturyListView(generic.ListView):
model = KorekturovanePDF
# Nefunguje, filtry se vubec nepouziji
queryset = KorekturovanePDF.objects.annotate(
k_oprave_cnt=Count('oprava',distinct=True,filter=Q(oprava__status='k_oprave')),
opraveno_cnt=Count('oprava',distinct=True,filter=Q(oprava__status='opraveno')),
neni_chyba_cnt=Count('oprava',distinct=True,filter=Q(oprava__status='neni_chyba')),
k_zaneseni_cnt=Count('oprava',distinct=True,filter=Q(oprava__status='k_zaneseni')),
k_oprave_cnt=Count('oprava',distinct=True,filter=Q(oprava__status=Oprava.STATUS.K_OPRAVE)),
opraveno_cnt=Count('oprava',distinct=True,filter=Q(oprava__status=Oprava.STATUS.OPRAVENO)),
neni_chyba_cnt=Count('oprava',distinct=True,filter=Q(oprava__status=Oprava.STATUS.NENI_CHYBA)),
k_zaneseni_cnt=Count('oprava',distinct=True,filter=Q(oprava__status=Oprava.STATUS.K_ZANESENI)),
)
template_name = 'korektury/seznam.html'
@ -58,13 +52,15 @@ class KorekturySeskupeneListView(KorekturyAktualniListView):
### Korektury
class KorekturyView(generic.TemplateView):
model = Oprava
template_name = 'korektury/opraf.html'
form_class = OpravaForm
template_name = 'korektury/korekturovatko/htmlstrana.html'
def setup(self, request, *args, **kwargs):
super().setup(request, *args, **kwargs)
self.pdf_id = self.kwargs["pdf"]
self.pdf = get_object_or_404(KorekturovanePDF, id=self.pdf_id)
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
q = request.POST
scroll = q.get('scroll')
# prirazeni autora podle prihlaseni
autor_user = request.user
@ -73,50 +69,27 @@ class KorekturyView(generic.TemplateView):
if not autor:
return HttpResponseForbidden()
if not scroll:
scroll = 0
action = q.get('action')
if (action == ''): # Přidej
x = int(q.get('x'))
y = int(q.get('y'))
text = q.get('txt')
strana = int(q.get('img-id')[4:])
pdf = KorekturovanePDF.objects.get(id=q.get('pdf'))
op = Oprava(x=x,y=y, autor=autor, text=text, strana=strana,pdf = pdf)
op = Oprava(x=x,y=y, strana=strana, pdf=self.pdf)
op.save()
self.send_email_notification_komentar(op,autor)
kom = Komentar(oprava=op,autor=autor,text=text)
kom.save()
send_email_notification_komentar(op, autor, request)
elif (action == 'del'):
id = int(q.get('id'))
op = Oprava.objects.get(id=id)
for k in Komentar.objects.filter(oprava=op):
k.delete()
op.delete()
elif (action == 'update'):
elif action in Oprava.STATUS.values:
id = int(q.get('id'))
op = Oprava.objects.get(id=id)
text = q.get('txt')
op.autor = autor
op.text = text
op.save()
elif (action == 'undone'):
id = int(q.get('id'))
op = Oprava.objects.get(id=id)
op.status = op.STATUS_K_OPRAVE
op.save()
elif (action == 'done'):
id = int(q.get('id'))
op = Oprava.objects.get(id=id)
op.status = op.STATUS_OPRAVENO
op.save()
elif (action == 'ready'):
id = int(q.get('id'))
op = Oprava.objects.get(id=id)
op.status = op.STATUS_K_ZANESENI
op.save()
elif (action == 'wontfix'):
id = int(q.get('id'))
op = Oprava.objects.get(id=id)
op.status = op.STATUS_NENI_CHYBA
op.status = action
op.save()
elif (action == 'comment'):
id = int(q.get('id'))
@ -124,7 +97,7 @@ class KorekturyView(generic.TemplateView):
text = q.get('txt')
kom = Komentar(oprava=op,autor=autor,text=text)
kom.save()
self.send_email_notification_komentar(op,autor)
send_email_notification_komentar(op, autor, request)
elif (action == 'update-comment'):
id = int(q.get('id'))
kom = Komentar.objects.get(id=id)
@ -137,85 +110,23 @@ class KorekturyView(generic.TemplateView):
kom = Komentar.objects.get(id=id)
kom.delete()
elif (action == 'set-state'):
pdf = KorekturovanePDF.objects.get(id=q.get('pdf'))
if (q.get('state') == 'adding'):
pdf.status = pdf.STATUS_PRIDAVANI
elif (q.get('state') == 'comitting'):
pdf.status = pdf.STATUS_ZANASENI
elif (q.get('state') == 'deprecated'):
pdf.status = pdf.STATUS_ZASTARALE
pdf.save()
status = q.get('state')
assert status in KorekturovanePDF.STATUS.values
self.pdf.status = status
self.pdf.save()
context = self.get_context_data()
context['scroll'] = scroll
context['autor'] = autor
return render(request, 'korektury/opraf.html',context)
def send_email_notification_komentar(self, oprava, autor):
''' Rozesle e-mail pri pridani komentare / opravy,
ktery obsahuje text vlakna opravy.
'''
# parametry e-mailu
#odkaz = "https://mam.mff.cuni.cz/korektury/{}/".format(oprava.pdf.pk)
from django.urls import reverse
odkaz = self.request.build_absolute_uri(reverse('korektury', kwargs={'pdf': oprava.pdf.pk}))
odkaz = f"{odkaz}#op{oprava.id}-pointer"
from_email = 'korekturovatko@mam.mff.cuni.cz'
subject = 'Nová korektura od {} v {}'.format(autor, oprava.pdf.nazev)
texty = [(oprava.autor.osoba.plne_jmeno(),oprava.text)]
for kom in Komentar.objects.filter(oprava=oprava):
texty.append((kom.autor.osoba.plne_jmeno(),kom.text))
optext = "\n\n\n".join([": ".join(t) for t in texty])
text = u"Text komentáře:\n\n{}\n\n=== Konec textu komentáře ===\n\
\nodkaz do korekturovátka: {}\n\
\nVaše korekturovátko\n".format(optext, odkaz)
# Prijemci e-mailu
emails = set()
# e-mail autora korektury
email = oprava.autor.osoba.email
if email:
emails.add(email)
# nalezeni e-mailu na autory komentaru
for komentar in oprava.komentar_set.all():
email_komentujiciho = komentar.autor.osoba.email
if email_komentujiciho:
emails.add(email_komentujiciho)
# zodpovedni orgove
for org in oprava.pdf.orgove.all():
email_zobpovedny = org.osoba.email
if email_zobpovedny:
emails.add(email_zobpovedny)
# odstran e-mail autora opravy
email = autor.osoba.email
if email:
emails.discard(email)
EmailMessage(
subject=subject,
body=text,
from_email=from_email,
to=list(emails),
).send()
return render(request, 'korektury/korekturovatko/htmlstrana.html', context)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
pdf = get_object_or_404(KorekturovanePDF, id=self.kwargs['pdf'])
context['pdf'] = pdf
context['img_prefix'] = pdf.get_prefix()
context['pdf'] = self.pdf
context['img_prefix'] = self.pdf.get_prefix()
context['img_path'] = settings.KOREKTURY_IMG_DIR
context['img_indexes'] = range(pdf.stran)
context['form_oprava'] = OpravaForm()
opravy = Oprava.objects.filter(pdf=self.kwargs['pdf'])
context['img_indexes'] = range(self.pdf.stran)
opravy = Oprava.objects.filter(pdf=self.pdf_id)
zasluhy = {}
for o in opravy:
if o.autor in zasluhy:
zasluhy[o.autor]+=1
else:
zasluhy[o.autor]=1
o.komentare = o.komentar_set.all()
for k in o.komentare:
if k.autor in zasluhy:
@ -241,6 +152,3 @@ class KorekturyView(generic.TemplateView):
context['zasluhy'] = zasluhy
return context
def form_valid(self,form):
return super().form_valid(form)

View file

@ -134,6 +134,7 @@ INSTALLED_APPS = (
'tvorba',
'galerie',
'korektury',
'korektury.api',
'prednasky',
'header_fotky',
'various',