Commit 699a3d939086747dc9b0a4ec300a19aa44b85a75
1 parent
2fd3e43d
Add splitting chunks feature skeleton
Showing
6 changed files
with
189 additions
and
49 deletions
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">×</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' |
... | ... |