Browse Source

Prototyp korekturovatka v Djangu.

remotes/origin/opraf
parent
commit
7de6a7e614
  1. 3
      .gitignore
  2. 13
      korektury/forms.py
  3. 63
      korektury/migrations/0001_initial.py
  4. 122
      korektury/models.py
  5. BIN
      korektury/static/korektury/imgs/check.png
  6. BIN
      korektury/static/korektury/imgs/delete.png
  7. BIN
      korektury/static/korektury/imgs/edit.png
  8. BIN
      korektury/static/korektury/imgs/link.png
  9. BIN
      korektury/static/korektury/imgs/next-gr.png
  10. BIN
      korektury/static/korektury/imgs/next.png
  11. BIN
      korektury/static/korektury/imgs/undo.png
  12. 105
      korektury/static/korektury/opraf.css
  13. 213
      korektury/static/korektury/opraf.js
  14. 1
      korektury/static/korektury/png/.gitignore
  15. 1
      korektury/static/korektury/tmp/.gitignore
  16. 97
      korektury/templates/korektury/opraf.html
  17. 7
      korektury/urls.py
  18. 55
      korektury/views.py
  19. 1
      mamweb/settings_common.py
  20. 3
      mamweb/urls.py

3
.gitignore

@ -20,3 +20,6 @@
# secrets
/django.secret
# vim tmp files
*~

13
korektury/forms.py

@ -0,0 +1,13 @@
from django import forms
class OpravaForm(forms.Form):
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)

63
korektury/migrations/0001_initial.py

@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import korektury.models
class Migration(migrations.Migration):
dependencies = [
]
operations = [
migrations.CreateModel(
name='KorekturovanePDF',
fields=[
('id', models.AutoField(serialize=False, primary_key=True)),
('pdf', models.FileField(upload_to=korektury.models.generate_filename, verbose_name='pdf')),
],
options={
'db_table': 'korekturovane_cislo',
'verbose_name': 'PDF k oprav\xe1m',
'verbose_name_plural': 'PDF k oprav\xe1m',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Oprava',
fields=[
('id', models.AutoField(serialize=False, primary_key=True)),
('strana', models.IntegerField(help_text=b'Strana s opravou (od 0)', verbose_name='strana s opravou')),
('x', models.IntegerField(verbose_name='x-ov\xe1 sou\u0159adnice bugu')),
('y', models.IntegerField(verbose_name='y-ov\xe1 sou\u0159adnice bugu')),
('status', models.CharField(default=b'k_oprave', max_length=16, verbose_name='stav opravy', choices=[(b'k_oprave', 'K oprav\u011b'), (b'opraveno', 'Opraveno'), (b'smazano', 'Smaz\xe1no')])),
('autor', models.TextField(help_text=b'Autor opravy', verbose_name='autor opravy', blank=True)),
('text', models.TextField(help_text=b'Text opravy', verbose_name='text opravy', blank=True)),
],
options={
'ordering': ['y', 'x'],
'db_table': 'opravy',
'verbose_name': 'Oprava',
'verbose_name_plural': 'Opravy',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='OpravaKomentar',
fields=[
('id', models.AutoField(serialize=False, primary_key=True)),
('cas', models.DateTimeField(help_text=b'\xc4\x8cas zad\xc3\xa1n\xc3\xad koment\xc3\xa1\xc5\x99e', verbose_name='\u010das koment\xe1\u0159e')),
('autor', models.TextField(help_text=b'Autor koment\xc3\xa1\xc5\x99e', verbose_name='autor koment\xe1\u0159e', blank=True)),
('text', models.TextField(help_text=b'Text koment\xc3\xa1\xc5\x99e', verbose_name='text koment\xe1\u0159e', blank=True)),
('oprava', models.ForeignKey(to='korektury.Oprava')),
],
options={
'ordering': ['cas'],
'db_table': 'opravy_komentare',
'verbose_name': 'Koment\xe1\u0159 k oprav\u011b',
'verbose_name_plural': 'Koment\xe1\u0159e k oprav\u011b',
},
bases=(models.Model,),
),
]

122
korektury/models.py

@ -1,3 +1,123 @@
# -*- coding: utf-8 -*-
import os
import random
from django.db import models
from django.contrib import auth
from django.utils import timezone
from django.conf import settings
from django.utils.encoding import python_2_unicode_compatible
from django.utils.encoding import force_unicode
from django.utils.text import slugify
from django.core.urlresolvers import reverse
from django.core.cache import cache
from imagekit.models import ImageSpecField, ProcessedImageField
from imagekit.processors import ResizeToFit, Transpose
from PIL import Image
import os
from cStringIO import StringIO
from django.core.files.base import ContentFile
from django_countries.fields import CountryField
from solo.models import SingletonModel
from taggit.managers import TaggableManager
import reversion
# PrilohaReseni method
def generate_filename(self, filename):
clean = filename.replace('/','-').replace('\0', '')
datedir = timezone.now().strftime('%Y-%m')
fname = "%s_%s" % (
timezone.now().strftime('%Y-%m-%d-%H:%M'),
clean)
return os.path.join(settings.SEMINAR_RESENI_DIR, datedir, fname)
#@reversion.register(ignore_duplicate_revision=True)
#@python_2_unicode_compatible
class KorekturovanePDF(models.Model):
class Meta:
db_table = 'korekturovane_cislo'
verbose_name = u'PDF k opravám'
verbose_name_plural = u'PDF k opravám'
#Interní ID
id = models.AutoField(primary_key = True)
pdf = models.FileField(u'pdf', upload_to = generate_filename)
#TODO Nepovinný foreign key k číslu
@reversion.register(ignore_duplicate_revision=True)
@python_2_unicode_compatible
class Oprava(models.Model):
class Meta:
db_table = 'opravy'
verbose_name = u'Oprava'
verbose_name_plural = u'Opravy'
ordering = ['y','x']
#Interní ID
id = models.AutoField(primary_key = True)
#pdf = models.ForeignKey(KorekturovanePDF)
strana = models.IntegerField(u'strana s opravou', help_text='Strana s opravou (od 0)')
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_SMAZANO = 'smazano'
STATUS_CHOICES = (
(STATUS_K_OPRAVE, u'K opravě'),
(STATUS_OPRAVENO, u'Opraveno'),
(STATUS_SMAZANO, u'Smazáno'),
)
status = models.CharField(u'stav opravy',max_length=16, choices=STATUS_CHOICES, blank=False,
default = STATUS_K_OPRAVE)
# TODO: Změnit na cizí klíč do orgů
autor = models.TextField(u'autor opravy',blank = True, help_text='Autor opravy')
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 force_unicode(u'%s od %s: %s',self.status,self.autor,self.text)
@reversion.register(ignore_duplicate_revision=True)
@python_2_unicode_compatible
class OpravaKomentar(models.Model):
class Meta:
db_table = 'opravy_komentare'
verbose_name = u'Komentář k opravě'
verbose_name_plural = u'Komentáře k opravě'
ordering = ['cas']
#Interní ID
id = models.AutoField(primary_key = True)
cas = models.DateTimeField(u'čas komentáře',help_text = 'Čas zadání komentáře')
oprava = models.ForeignKey(Oprava)
# TODO: Změnit na cizí klíč do orgů
autor = models.TextField(u'autor komentáře',blank = True, help_text='Autor komentáře')
text = models.TextField(u'text komentáře',blank = True, help_text='Text komentáře')
def __str__(self):
return force_unicode(u'%s od %s: %s',self.cas,self.autor,self.text)
# Create your models here.

BIN
korektury/static/korektury/imgs/check.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 697 B

BIN
korektury/static/korektury/imgs/delete.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

BIN
korektury/static/korektury/imgs/edit.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
korektury/static/korektury/imgs/link.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
korektury/static/korektury/imgs/next-gr.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 881 B

BIN
korektury/static/korektury/imgs/next.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 B

BIN
korektury/static/korektury/imgs/undo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 617 B

105
korektury/static/korektury/opraf.css

@ -0,0 +1,105 @@
.pointer-hi,
.pointer,
.pointer-done,
.pointer-done-hi {
position:absolute;
/*border-bottom-left-radius: 10px; */
border-left: 2px solid yellow;
border-bottom: 2px solid yellow;
}
.pointer-done-hi,
.pointer-hi {
border-width: 3px;
}
.pointer {
border-color: #F00; /*IE*/
border-color: rgba(255, 0, 0, 0.35);
}
.pointer-hi {
border-color: #F00; /*IE*/
border-color: rgba(255, 0, 0, 1);
}
.pointer-done {
border-color: #00F; /*IE*/
border-color: rgba(0, 0, 255, 0.2);
}
.pointer-done-hi {
border-color: #00F; /*IE*/
border-color: rgba(0, 0, 255, 1);
}
.box:hover, .box-done:hover {
border-width:3px;
margin: 0px;
}
.box, .box-done {
margin: 1px;
background-color: white;
width:300px;
/*position:absolute;*/
padding: 3px;
border: 2px solid black;
border-radius: 10px;
}
.box {
border-color: red;
}
.box-done {
border-color: blue;
}
form {
display:inline;
}
.float-right{
float:right;
}
.imgdiv {
position:relative;
left:0px;
top:0px;
}
#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;
}
.close-button{
background-color: yellow;
}
body {
background-color: #b0b0ff;
}
.box button,
.box img,
.box-done button,
.box-done 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 {
border: 1px solid black;
}

213
korektury/static/korektury/opraf.js

@ -0,0 +1,213 @@
function place_comments_one_div(img_id, comments)
{
var img = document.getElementById(img_id);
if( img == null ) {
return;
}
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;
}
}
// 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 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, '', '', '', '');
}
// show comment form, when 'edit' button pressed
function box_edit(button)
{
var divbox = button.parentNode.parentNode.parentNode;
var id = divbox.id;
//alert("id: " + id);
var divpointer = document.getElementById(divbox.id + '-pointer');
var text_el = document.getElementById(divbox.id + '-text');
var text = text_el.innerHTML.unescapeHTML();
var dx = parseInt(divpointer.style.left);
var dy = parseInt(divpointer.style.top);
//alert('not yet 2:' + text + text_el); // + divpointer.style.top "x" + divpo );
id = id.substring(2);
return show_form(divbox.img_id, dx, dy, id, text, 'update');
}
//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, done)
{
var id = box.id;
var pointer = document.getElementById(box.id + '-pointer');
pointer.className = done ? 'pointer-done-hi' : 'pointer-hi';
//console.log('mouseout');
}
function box_onmouseout(box, done)
{
var id = box.id;
var pointer = document.getElementById(box.id + '-pointer');
pointer.className = done ? 'pointer-done' : 'pointer';
//console.log('mousein');
}
function save_scroll(form)
{
//alert('save_scroll:' + document.body.scrollTop);
form.scroll.value = document.body.scrollTop;
//alert('save_scroll:' + form.scroll.value);
return true;
}
String.prototype.unescapeHTML = function () {
return(
this.replace(/&amp;/g,'&').
replace(/&gt;/g,'>').
replace(/&lt;/g,'<').
replace(/&quot;/g,'"')
);
};

1
korektury/static/korektury/png/.gitignore

@ -0,0 +1 @@
*

1
korektury/static/korektury/tmp/.gitignore

@ -0,0 +1 @@
*

97
korektury/templates/korektury/opraf.html

@ -0,0 +1,97 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" media="screen, projection" href="/static/korektury/opraf.css" />
<script src="/static/korektury/opraf.js"></script>
<title>Korektury 22_3_verze5.pdf</title>
</head>
<body>
<h1>Korektury 22_3_verze5.pdf</h1>
<i>Klikni na chybu, napi¹ komentáø</i> |
<a href="?action=ls">ls</a> |
<a href="?action=doc">help</a> |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|
<a href="https://mam.mff.cuni.cz/">hlavní stránka</a> |
<a href="https://mam.mff.cuni.cz/wiki">wiki</a> |
<hr/>
<div id="commform-div">
<form action='' onsubmit='save_scroll(this)' id="commform" method="POST">
{% csrf_token %}
<input size="8" name="au" value="anonym"/>
<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='22_3_verze5.pdf'/>
<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>
</div>
{% for i in img_indexes %}
<div class='imgdiv'><img width='1021' height='1448' onclick='img_click(this,event)' id='img-{{i}}' src='/static/korektury/png/{{img_name}}-{{i}}.png'/></div><hr/>
{% endfor %}
<form method="post">
<input type='hidden' name='action' value='delall'/>
<input type='submit' value='Smazat v¹echny komentáøe'/>
<input type='hidden' name='pdf' value='22_3_verze5.pdf'/>
<input type='checkbox' name='yes'/> Souhlasím se smazáním v¹ech kometáøù
</form>
<hr/>
Dìkujeme opravovatelùm: Marble(4), zuzka(4), Matìj(2), O(N)dra(2), Va¹ek(1), Kristý(1), Tonda(1), Jethro(1), Pe»a(1), ML(1), Lucka(1) <hr>
{% for o in opravy %}
<div onclick='img_click(this,event)' id='op{{o.id}}-pointer' class='pointer-done'></div>
<div name='op{{o.id}}' id='op{{o.id}}' class='box-done' onmouseover='box_onmouseover(this,1)' onmouseout='box_onmouseout(this,1)' >
<b>{{o.autor}}</b>
<div class='float-right'>
<form action='' onsubmit='save_scroll(this)' method='POST'>
<input type='hidden' name='pdf' value='22_3_verze5.pdf'>
<input type='hidden' name='id' value='op{{o.id}}'>
<input type='hidden' name='scroll'>
<button type='submit' name='action' value='del' title='Sma¾ opravu'><img src="/static/korektury/imgs/delete.png"/></button>
<button type='submit' name='action' value='undone' title='Oznaè jako neopravené'><img src="/static/korektury/imgs/undo.png"/></button>
<button type='button' onclick='box_edit(this);' title='Oprav opravu'><img src="/static/korektury/imgs/edit.png"/></button>
<a href='#op578'><button type='button' title='Link na opravu'><img src="/static/korektury/imgs/link.png"/></button></a>
<a href='#op579'><img title='Dal¹í oprava' src="/static/korektury/imgs/next.png"/></button></a>
</form>
</div> <div id='op{{o.id}}-text'>{{o.text}}</div>
</div> <div onclick='img_click(this,event)' id='op579-pointer' class='pointer'></div>
{% endfor %}
<script>
{% for o in opravy %}
place_comments_one_div("img-{{o.strana}}", [["op{{o.id}}",{{o.x}},{{o.y}}]]);
{% endfor %}
</script>
<form action='' onsubmit='save_scroll(this)' method='POST'>
{% csrf_token %}
{{ form_oprava.as_p }}
<input type='submit' value='Odeslat'/>
</form>
</body> </html>

7
korektury/urls.py

@ -0,0 +1,7 @@
from django.conf.urls import * # NOQA
from django.conf.urls import patterns, url
from . import views
urlpatterns = patterns('',
url(r'^korektury$',views.KorekturyView.as_view(),name='korektury'),
)

55
korektury/views.py

@ -1,3 +1,56 @@
# -*- coding: utf-8 -*-
from django.shortcuts import render
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect
from django.http import HttpResponse
from django.core.urlresolvers import reverse
from django.views import generic
from django.utils.translation import ugettext as _
from django.http import Http404
from django.http import HttpResponseRedirect
from .models import Oprava
from .forms import OpravaForm
from datetime import timedelta, date, datetime
from itertools import groupby
import tempfile
import subprocess
import shutil
import os
from django.conf import settings
import unicodedata
### Korektury
class KorekturyView(generic.TemplateView):
model = Oprava
template_name = 'korektury/opraf.html'
form_class = OpravaForm
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
q = request.POST
x = int(q.get('x'))
y = int(q.get('y'))
autor = q.get('au')
text = q.get('txt')
strana = int(q.get('img-id')[4:])
scroll = q.get('scroll')
oprava = Oprava(x=x,y=y, autor=autor, text=text, strana=strana)
oprava.save()
return HttpResponseRedirect(reverse('korektury')+"?scroll=%s"%(scroll))
# return render(request, 'seminar/opraf.html',self.get_context_data())
# return HttpResponse(u'Oprav: %d,x: %d y: %d, autor: %s, text: %s, strana: %d'%(
# len(Oprava.objects.all()),x,y,autor,text,strana))
def get_context_data(self, **kwargs):
context = super(KorekturyView,self).get_context_data(**kwargs)
context['img_name'] = "22_3_verze5"
context['img_indexes'] = range(27)
context['form_oprava'] = OpravaForm()
context['opravy'] = Oprava.objects.all()
return context
def form_valid(self,form):
return super(KorekturyView,self).form_valid(form)
# Create your views here.

1
mamweb/settings_common.py

@ -117,6 +117,7 @@ INSTALLED_APPS = (
'mamweb',
'seminar',
'galerie',
'korektury',
# Admin upravy:

3
mamweb/urls.py

@ -14,6 +14,9 @@ urlpatterns = patterns('',
# Seminarova aplikace (ma vlastni podadresare)
url(r'^', include('seminar.urls')),
# Korekturovaci aplikace (ma vlastni podadresare)
url(r'^', include('korektury.urls')),
# Comments (interni i verejne)
url(r'^comments_dj/', include('django_comments.urls')),

Loading…
Cancel
Save