Commit 139e39eefdc9258719b07f906d88136e2e4c02f8
1 parent
699a3d93
Correct splitting chunks feature
Showing
5 changed files
with
87 additions
and
55 deletions
collector/storage/forms.py
... | ... | @@ -378,13 +378,12 @@ class ChunkMergeForm(ModelForm): |
378 | 378 | fields = ['target_chunk'] |
379 | 379 | |
380 | 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 | - } | |
381 | +class ChunkSplitForm(Form): | |
382 | + text = CharField(required=True) | |
383 | + | |
384 | + def __init__(self, initial_text, *args, **kwargs): | |
385 | + super(ChunkSplitForm, self).__init__(*args, **kwargs) | |
386 | + self.fields['text'].initial = initial_text | |
387 | + self.fields['text'].widget = Textarea(attrs={'class': 'formset-field', | |
388 | + 'placeholder': 'Tekst sekcji', | |
389 | + 'rows': 5}) | |
... | ... |
collector/storage/static/storage/css/document.css
... | ... | @@ -168,6 +168,15 @@ a.nav-button:hover { |
168 | 168 | color: #ff6600; |
169 | 169 | } |
170 | 170 | |
171 | +i.remove-form-row { | |
172 | + color: red; | |
173 | + cursor: pointer; | |
174 | +} | |
175 | + | |
176 | +i.remove-form-row:hover { | |
177 | + color: #870000; | |
178 | +} | |
179 | + | |
171 | 180 | .col-actions { |
172 | 181 | width: 5%; |
173 | 182 | } |
... | ... | @@ -212,6 +221,14 @@ a.nav-button:hover { |
212 | 221 | width: 10%; |
213 | 222 | } |
214 | 223 | |
224 | +.col-remove { | |
225 | + width: 3%; | |
226 | +} | |
227 | + | |
228 | +tr.item textarea { | |
229 | + width: 100%; | |
230 | +} | |
231 | + | |
215 | 232 | #doc-list-table { |
216 | 233 | table-layout: fixed; |
217 | 234 | } |
... | ... |
collector/storage/static/storage/js/formset.js
1 | 1 | function updateElementIndex(el, prefix, ndx) { |
2 | 2 | var id_regex = new RegExp('(' + prefix + '-\\d+-)'); |
3 | 3 | var replacement = prefix + '-' + ndx + '-'; |
4 | - if ($(el).attr("for")) $(el).attr("for", $(el).attr("for").replace(id_regex, | |
5 | - replacement)); | |
4 | + if ($(el).attr("for")) $(el).attr("for", $(el).attr("for").replace(id_regex, replacement)); | |
6 | 5 | if (el.id) el.id = el.id.replace(id_regex, replacement); |
7 | 6 | if (el.name) el.name = el.name.replace(id_regex, replacement); |
8 | 7 | } |
9 | 8 | |
10 | 9 | function addForm(btn, prefix) { |
11 | 10 | var formCount = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val()); |
12 | - if (formCount < 1000) { | |
13 | - // Clone a form (without event handlers) from the first form | |
11 | + if (formCount < 3) { | |
14 | 12 | var row = $(".item:last").clone(false).get(0); |
15 | - | |
16 | - // Insert it after the last form | |
17 | 13 | $(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 | 14 | $(".errorlist", row).remove(); |
22 | 15 | $(row).children().removeClass("error"); |
23 | - | |
24 | - // Relabel or rename all the relevant bits | |
25 | 16 | $(row).find('.formset-field').each(function () { |
26 | 17 | updateElementIndex(this, prefix, formCount); |
27 | 18 | $(this).val(''); |
28 | 19 | $(this).removeAttr('value'); |
29 | 20 | $(this).prop('checked', false); |
30 | 21 | }); |
31 | - | |
32 | - // Add an event handler for the delete item/form link | |
33 | 22 | $(row).find(".delete").click(function () { |
34 | 23 | return deleteForm(this, prefix); |
35 | 24 | }); |
36 | - // Update the total form count | |
37 | 25 | $("#id_" + prefix + "-TOTAL_FORMS").val(formCount + 1); |
38 | - | |
39 | - } // End if | |
26 | + } | |
40 | 27 | |
41 | 28 | return false; |
42 | 29 | } |
... | ... | @@ -45,11 +32,11 @@ function addForm(btn, prefix) { |
45 | 32 | function deleteForm(btn, prefix) { |
46 | 33 | var formCount = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val()); |
47 | 34 | if (formCount > 1) { |
48 | - // Delete the item/form | |
49 | 35 | var goto_id = $(btn).find('input').val(); |
50 | 36 | if( goto_id ){ |
51 | 37 | $.ajax({ |
52 | - url: "/" + window.location.pathname.split("/")[1] + "/formset-data-delete/"+ goto_id +"/?next="+ window.location.pathname, | |
38 | + url: "/" + window.location.pathname.split("/")[1] + "/formset-data-delete/" + goto_id + | |
39 | + "/?next=" + window.location.pathname, | |
53 | 40 | error: function () { |
54 | 41 | console.log("error"); |
55 | 42 | }, |
... | ... | @@ -62,17 +49,15 @@ function deleteForm(btn, prefix) { |
62 | 49 | $(btn).parents('.item').remove(); |
63 | 50 | } |
64 | 51 | |
65 | - var forms = $('.item'); // Get all the forms | |
66 | - // Update the total number of forms (1 less than before) | |
52 | + var forms = $('.item'); | |
67 | 53 | $('#id_' + prefix + '-TOTAL_FORMS').val(forms.length); |
68 | 54 | var i = 0; |
69 | - // Go through the forms and set their indices, names and IDs | |
70 | 55 | for (formCount = forms.length; i < formCount; i++) { |
71 | 56 | $(forms.get(i)).find('.formset-field').each(function () { |
72 | 57 | updateElementIndex(this, prefix, i); |
73 | 58 | }); |
74 | 59 | } |
75 | - } // End if | |
60 | + } | |
76 | 61 | |
77 | 62 | return false; |
78 | 63 | } |
... | ... |
collector/storage/templates/storage/chunk-split.html
... | ... | @@ -24,29 +24,39 @@ |
24 | 24 | </div> |
25 | 25 | |
26 | 26 | <div class="modal-body"> |
27 | + | |
28 | + <div class="alert alert-warning text-center" role="alert"><strong>Uwaga!</strong> Zamknięcie formularza nie spodowuje zapisania | |
29 | + zmian. W razie pomyłki lub nieumyślnego usunięcia sekcji zamknij to okno.</div> | |
30 | + | |
27 | 31 | <form method="POST"> |
28 | 32 | {% csrf_token %} |
29 | 33 | {{ form.as_p}} |
34 | + | |
35 | + <table class="table"> | |
30 | 36 | |
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> | |
37 | + <tbody> | |
38 | + {% for form in formset %} | |
39 | + <tr class="item"> | |
40 | + {% for field in form %} | |
41 | + <td class="col-field"> | |
42 | + {{ field }} | |
43 | + </td> | |
44 | + {% endfor %} | |
45 | + <td class="col-remove"> | |
46 | + <i class="remove-form-row material-icons" id="{{ formset.prefix }}" title="Usuń sekcję">clear</i> | |
47 | + </td> | |
48 | + </tr> | |
35 | 49 | {% 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> | |
50 | + </tbody> | |
51 | + | |
52 | + </table> | |
46 | 53 | |
47 | 54 | {{ formset.management_form }} |
48 | 55 | |
49 | 56 | <div class="modal-footer"> |
57 | + <button type="button" class="btn btn-sm btn-success add-form-row mr-auto" id="{{ formset.prefix }}"> | |
58 | + Dodaj sekcję <i class="material-icons def-col-button">add</i> | |
59 | + </button> | |
50 | 60 | <button type="submit" class="submit-btn btn btn-primary">{{ submit_btn_text }}</button> |
51 | 61 | </div> |
52 | 62 | |
... | ... |
collector/storage/views.py
... | ... | @@ -14,10 +14,12 @@ 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 | +from django.forms import modelformset_factory, formset_factory | |
18 | +from django.db import transaction, IntegrityError | |
18 | 19 | |
19 | 20 | from keras import backend |
20 | 21 | from sys import maxsize |
22 | +from functools import partial, wraps | |
21 | 23 | |
22 | 24 | from .forms import ChunkForm, ParticipantForm, SubchunkForm, MetadataForm, DocDetailsForm, KeywordForm, DocSplitForm, \ |
23 | 25 | ChunkMoveForm, SubDocDetailsForm, AuthorForm, ChunkMergeForm, ChunkSplitForm |
... | ... | @@ -78,18 +80,37 @@ def swap_subdocs(direction, subdoc, subdoc_seq, temp_seq, max_seq, parent_doc): |
78 | 80 | |
79 | 81 | def split_chunk(request, pk): |
80 | 82 | context = {} |
81 | - ChunksFormset = modelformset_factory(Chunk, form=ChunkSplitForm) | |
82 | - formset = ChunksFormset(request.POST or None, queryset=Chunk.objects.none(), prefix='chunks') | |
83 | + chunk = Chunk.objects.get(pk=pk) | |
84 | + ChunksFormset = formset_factory(wraps(ChunkSplitForm)(partial(ChunkSplitForm, initial_text=chunk.text))) | |
85 | + formset = ChunksFormset(request.POST or None, prefix='chunks') | |
83 | 86 | 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') | |
87 | + chunk_document_id = chunk.document.id | |
88 | + formset_len = len(formset) | |
89 | + if formset_len > 1 and formset.is_valid() and not request.is_ajax(): | |
90 | + next_chunks = Chunk.objects.filter(document=chunk.document, | |
91 | + sequence__gt=chunk.sequence).order_by('-sequence') | |
92 | + for ch in next_chunks: | |
93 | + ch.sequence += formset_len - 1 | |
94 | + ch.save() | |
95 | + old_chunk = Chunk.objects.create(text=formset[0].data['chunks-0-text'], | |
96 | + document=chunk.document, | |
97 | + sequence=chunk.sequence) | |
98 | + old_chunk.save() | |
99 | + for num, f in enumerate(formset[1:]): | |
100 | + ch = Chunk.objects.create(text=f.cleaned_data['text'], | |
101 | + document=chunk.document, | |
102 | + sequence=chunk.sequence + num + 1) | |
103 | + ch.save() | |
104 | + chunk.delete() | |
105 | + return HttpResponseRedirect('%s#chunk-%d' % ( | |
106 | + reverse_lazy('annotation', kwargs={'doc_id': chunk_document_id}), old_chunk.pk)) | |
107 | + else: | |
108 | + messages.error(request, 'Błąd: Nieprawidłowy podział sekcji') | |
109 | + return HttpResponseRedirect(reverse('annotation', kwargs={'doc_id': chunk_document_id})) | |
90 | 110 | context['formset'] = formset |
91 | 111 | context['title'] = 'Dzielenie sekcji' |
92 | 112 | context['submit_btn_text'] = 'Podziel' |
113 | + formset.data['chunks-TOTAL_FORMS'] = 0 | |
93 | 114 | return render(request, 'storage/chunk-split.html', context) |
94 | 115 | |
95 | 116 | |
... | ... |