Commit 699a3d939086747dc9b0a4ec300a19aa44b85a75

Authored by Marcel Kawski
1 parent 2fd3e43d

Add splitting chunks feature skeleton

collector/storage/forms.py
1 1 import re
2 2  
3   -from django.forms import ChoiceField, ModelChoiceField, ModelForm, Textarea, CharField, Form, formset_factory
  3 +from django.forms import ChoiceField, ModelChoiceField, ModelForm, Textarea, CharField, Form
4 4 from django import forms
5 5  
6 6 from .models import Chunk, Document, Participant, Metadata, Keyword
... ... @@ -42,14 +42,14 @@ class ChunkForm(ModelForm):
42 42 doc.chunks.get(sequence=new_chunk_seq) # if the new sequence is the same as other chunk's sequence
43 43 if self.old_sequence > new_chunk_seq:
44 44 chunks_to_move = doc.chunks.filter(sequence__gte=new_chunk_seq,
45   - sequence__lt=self.old_sequence)
  45 + sequence__lt=self.old_sequence)
46 46 if chunks_to_move is not None:
47 47 for chunk in chunks_to_move:
48 48 chunk.sequence += 1
49 49 chunk.save()
50 50 elif self.old_sequence < new_chunk_seq:
51 51 chunks_to_move = doc.chunks.filter(sequence__gt=self.old_sequence,
52   - sequence__lte=new_chunk_seq)
  52 + sequence__lte=new_chunk_seq)
53 53 if chunks_to_move is not None:
54 54 for chunk in chunks_to_move:
55 55 chunk.sequence -= 1
... ... @@ -336,20 +336,6 @@ class DocSplitForm(ModelForm):
336 336 fields = ['chunk_beg', 'chunk_end']
337 337  
338 338  
339   -class ChunkSplitForm(ModelForm):
340   - new_chunk_text = CharField(widget=Textarea(attrs={'rows': 10}), label='Tekst nowej sekcji')
341   -
342   - def __init__(self, *args, **kwargs):
343   - super(ChunkSplitForm, self).__init__(*args, **kwargs)
344   -
345   - class Meta:
346   - model = Chunk
347   - fields = ['text', 'new_chunk_text']
348   - labels = {
349   - 'text': 'Tekst',
350   - }
351   -
352   -
353 339 class ChunkMoveForm(ModelForm):
354 340  
355 341 def __init__(self, *args, **kwargs):
... ... @@ -390,3 +376,15 @@ class ChunkMergeForm(ModelForm):
390 376 class Meta:
391 377 model = Chunk
392 378 fields = ['target_chunk']
  379 +
  380 +
  381 +class ChunkSplitForm(ModelForm):
  382 +
  383 + class Meta:
  384 + model = Chunk
  385 +
  386 + fields = ['text']
  387 +
  388 + labels = {
  389 + 'text': 'Tekst nowej sekcji'
  390 + }
... ...
collector/storage/static/storage/js/formset.js 0 → 100755
  1 +function updateElementIndex(el, prefix, ndx) {
  2 + var id_regex = new RegExp('(' + prefix + '-\\d+-)');
  3 + var replacement = prefix + '-' + ndx + '-';
  4 + if ($(el).attr("for")) $(el).attr("for", $(el).attr("for").replace(id_regex,
  5 + replacement));
  6 + if (el.id) el.id = el.id.replace(id_regex, replacement);
  7 + if (el.name) el.name = el.name.replace(id_regex, replacement);
  8 +}
  9 +
  10 +function addForm(btn, prefix) {
  11 + var formCount = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val());
  12 + if (formCount < 1000) {
  13 + // Clone a form (without event handlers) from the first form
  14 + var row = $(".item:last").clone(false).get(0);
  15 +
  16 + // Insert it after the last form
  17 + $(row).removeAttr('id').hide().insertAfter(".item:last").slideDown(300);
  18 +
  19 + // Remove the bits we don't want in the new row/form
  20 + // e.g. error messages
  21 + $(".errorlist", row).remove();
  22 + $(row).children().removeClass("error");
  23 +
  24 + // Relabel or rename all the relevant bits
  25 + $(row).find('.formset-field').each(function () {
  26 + updateElementIndex(this, prefix, formCount);
  27 + $(this).val('');
  28 + $(this).removeAttr('value');
  29 + $(this).prop('checked', false);
  30 + });
  31 +
  32 + // Add an event handler for the delete item/form link
  33 + $(row).find(".delete").click(function () {
  34 + return deleteForm(this, prefix);
  35 + });
  36 + // Update the total form count
  37 + $("#id_" + prefix + "-TOTAL_FORMS").val(formCount + 1);
  38 +
  39 + } // End if
  40 +
  41 + return false;
  42 +}
  43 +
  44 +
  45 +function deleteForm(btn, prefix) {
  46 + var formCount = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val());
  47 + if (formCount > 1) {
  48 + // Delete the item/form
  49 + var goto_id = $(btn).find('input').val();
  50 + if( goto_id ){
  51 + $.ajax({
  52 + url: "/" + window.location.pathname.split("/")[1] + "/formset-data-delete/"+ goto_id +"/?next="+ window.location.pathname,
  53 + error: function () {
  54 + console.log("error");
  55 + },
  56 + success: function (data) {
  57 + $(btn).parents('.item').remove();
  58 + },
  59 + type: 'GET'
  60 + });
  61 + }else{
  62 + $(btn).parents('.item').remove();
  63 + }
  64 +
  65 + var forms = $('.item'); // Get all the forms
  66 + // Update the total number of forms (1 less than before)
  67 + $('#id_' + prefix + '-TOTAL_FORMS').val(forms.length);
  68 + var i = 0;
  69 + // Go through the forms and set their indices, names and IDs
  70 + for (formCount = forms.length; i < formCount; i++) {
  71 + $(forms.get(i)).find('.formset-field').each(function () {
  72 + updateElementIndex(this, prefix, i);
  73 + });
  74 + }
  75 + } // End if
  76 +
  77 + return false;
  78 + }
  79 +
  80 + $("body").on('click', '.remove-form-row',function () {
  81 + deleteForm($(this), String($('.add-form-row').attr('id')));
  82 + });
  83 +
  84 + $("body").on('click', '.add-form-row',function () {
  85 + return addForm($(this), String($(this).attr('id')));
  86 + });
0 87 \ No newline at end of file
... ...
collector/storage/templates/storage/chunk-split.html 0 → 100755
  1 +<!doctype html>
  2 +<html lang="en">
  3 +{% load widget_tweaks %}
  4 +{% load static %}
  5 +<head>
  6 + <meta charset="utf-8">
  7 + <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
  8 + <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
  9 + <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
  10 + <link rel="stylesheet" href="{% static 'storage/css/document.css' %}">
  11 +</head>
  12 +
  13 +<body>
  14 +
  15 +<div class="ui-widget">
  16 + <form method="post" action="">
  17 + {% csrf_token %}
  18 +
  19 + <div class="modal-header">
  20 + <h3 class="modal-title">{{ title }}</h3>
  21 + <button type="button" class="close" data-dismiss="modal" aria-label="Close">
  22 + <span aria-hidden="true">&times;</span>
  23 + </button>
  24 + </div>
  25 +
  26 + <div class="modal-body">
  27 + <form method="POST">
  28 + {% csrf_token %}
  29 + {{ form.as_p}}
  30 +
  31 + {% for form in formset %}
  32 + <div class="item">
  33 + {% for field in form %}
  34 + {% render_field field class="form-control" placeholder=field.label %}</td>
  35 + {% endfor %}
  36 + <button type="button" class="btn btn-danger btn-sm remove-form-row"
  37 + id="{{ formset.prefix }}">
  38 + Usuń
  39 + </button>
  40 + <br><br>
  41 + </div>
  42 + {% endfor %}
  43 +
  44 + <button type="button" class="btn btn-sm btn-success add-form-row" id="{{ formset.prefix }}">
  45 + Dodaj</button>
  46 +
  47 + {{ formset.management_form }}
  48 +
  49 + <div class="modal-footer">
  50 + <button type="submit" class="submit-btn btn btn-primary">{{ submit_btn_text }}</button>
  51 + </div>
  52 +
  53 + </form>
  54 + </div>
  55 +
  56 + <script type="text/javascript" src="{% static 'storage/js/formset.js' %}"></script>
  57 +
  58 +</body>
  59 +</html>
  60 +
  61 +
  62 +
... ...
collector/storage/templates/storage/edit.html
... ... @@ -42,6 +42,7 @@
42 42 {% for field in form %}
43 43 <div class="form-group">
44 44 <label for="{{ field.id_for_label }}">{{ field.label }}</label>
  45 +
45 46 {% if field.id_for_label == "id_name" and all_md_names %}
46 47 <div class="used-vals-info">
47 48 <p>Zdefiniowane wcześniej nazwy metadanych:</p>
... ... @@ -52,20 +53,26 @@
52 53 </ul>
53 54 </div>
54 55 {% endif %}
  56 +
55 57 {% if form.is_bound %}
56 58 {% if field.errors %}
  59 +
57 60 {% render_field field class="form-control is-invalid" placeholder=field.label %}
58 61 <div class="invalid invalid-feedback">
59 62 {% for error in field.errors %}
60 63 <p>{{ error }}</p>
61 64 {% endfor %}
62 65 </div>
  66 +
63 67 {% else %}
64 68 {% render_field field class="form-control is-valid" placeholder=field.label %}
  69 +
65 70 {% endif %}
  71 +
66 72 {% else %}
67 73 {% render_field field class="form-control" placeholder=field.label %}
68 74 {% endif %}
  75 +
69 76 </div>
70 77 {% endfor %}
71 78 </div>
... ...
collector/storage/urls.py
... ... @@ -18,7 +18,7 @@ urlpatterns = [
18 18 path('chunk/edit/<int:pk>', login_required(views.ChunkEditView.as_view()), name='edit_chunk'),
19 19 path('chunk/delete/<int:pk>', login_required(views.ChunkDeleteView.as_view()), name='delete_chunk'),
20 20 path('chunk/move/<int:pk>', login_required(views.ChunkMoveView.as_view()), name='move_chunk'),
21   - path('chunk/split/<int:pk>', login_required(views.ChunkSplitView.as_view()), name='split_chunk'),
  21 + path('chunk/split/<int:pk>', login_required(views.split_chunk), name='split_chunk'),
22 22 path('chunk/merge/<int:pk>', login_required(views.ChunkMergeView.as_view()), name='merge_chunk'),
23 23 path('subchunk/add/<int:chunk_pk>', login_required(views.SubchunkAddView.as_view()), name='add_subchunk'),
24 24 path('subchunk/edit/<int:pk>', login_required(views.SubchunkEditView.as_view()), name='edit_subchunk'),
... ...
collector/storage/views.py
... ... @@ -14,6 +14,7 @@ from django.contrib.auth.decorators import login_required
14 14 from django.db.models.query import QuerySet
15 15 from django.db.models import Q
16 16 from django.template.loader import render_to_string
  17 +from django.forms import modelformset_factory
17 18  
18 19 from keras import backend
19 20 from sys import maxsize
... ... @@ -75,6 +76,23 @@ def swap_subdocs(direction, subdoc, subdoc_seq, temp_seq, max_seq, parent_doc):
75 76 neighbour.save()
76 77  
77 78  
  79 +def split_chunk(request, pk):
  80 + context = {}
  81 + ChunksFormset = modelformset_factory(Chunk, form=ChunkSplitForm)
  82 + formset = ChunksFormset(request.POST or None, queryset=Chunk.objects.none(), prefix='chunks')
  83 + if request.method == "POST":
  84 + if formset.is_valid():
  85 + for f in formset:
  86 + # TODO
  87 + chunk = f.save(commit=False)
  88 + chunk.save()
  89 + return redirect('annotation')
  90 + context['formset'] = formset
  91 + context['title'] = 'Dzielenie sekcji'
  92 + context['submit_btn_text'] = 'Podziel'
  93 + return render(request, 'storage/chunk-split.html', context)
  94 +
  95 +
78 96 class DocumentView(View):
79 97 template_name = 'storage/document.html'
80 98  
... ... @@ -345,37 +363,6 @@ class ChunkMoveView(UpdateView):
345 363 return '%s#chunk-%d' % (reverse_lazy('annotation', kwargs={'doc_id': self.object.document.id}), self.object.pk)
346 364  
347 365  
348   -class ChunkSplitView(UpdateView):
349   - model = Chunk
350   - template_name = 'storage/edit.html'
351   - form_class = ChunkSplitForm
352   -
353   - def form_valid(self, form):
354   - if not self.request.is_ajax():
355   - self.object.text = form.cleaned_data['text']
356   - self.object.save()
357   - next_chunks = Chunk.objects.filter(document=self.object.document,
358   - sequence__gt=self.object.sequence).order_by('sequence')
359   - if next_chunks.first().sequence == self.object.sequence + 1:
360   - for chunk in next_chunks:
361   - chunk.sequence += 1
362   - chunk.save()
363   - Chunk.objects.create(document=self.object.document,
364   - text=form.cleaned_data['new_chunk_text'],
365   - sequence=self.object.sequence + 1)
366   - return HttpResponseRedirect(self.get_success_url())
367   - return render(self.request, self.template_name, {'form': form})
368   -
369   - def get_context_data(self, **kwargs):
370   - context = super().get_context_data(**kwargs)
371   - context['title'] = 'Podział sekcji'
372   - context['submit_btn_text'] = 'Podziel'
373   - return context
374   -
375   - def get_success_url(self):
376   - return reverse_lazy('annotation', kwargs={'doc_id': self.object.document.id})
377   -
378   -
379 366 class ChunkMergeView(UpdateView):
380 367 model = Chunk
381 368 template_name = 'storage/edit.html'
... ...