0001-applications-sort-elements-by-category-in-add-popup-.patch
hobo/applications/templates/hobo/applications/add-element.html | ||
---|---|---|
9 | 9 |
<form method="post"> |
10 | 10 |
{% csrf_token %} |
11 | 11 |
<div class="application-elements"> |
12 |
{% for element in elements %} |
|
13 |
<label data-slugged-text="{{ element.text|slugify }}"><input type="checkbox" name="elements" value="{{ element.id }}">{{ element.text }}</label> |
|
12 |
{% for category in categories %} |
|
13 |
<div> |
|
14 |
<h4>{{ category.name }}</h4> |
|
15 |
{% for element in category.elements %} |
|
16 |
<label data-slugged-text="{{ element.text|slugify }}"><input type="checkbox" name="elements" value="{{ element.id }}">{{ element.text }}</label> |
|
17 |
{% endfor %} |
|
18 |
</div> |
|
14 | 19 |
{% endfor %} |
15 | 20 |
</div> |
16 | 21 |
<div style="text-align: right"> |
... | ... | |
27 | 32 |
$('.application-elements').css('height', $('.application-elements').height()); |
28 | 33 |
$('.application-elements').css('width', $('.application-elements').width()); |
29 | 34 |
if (!val) { |
35 |
$('.application-elements div').show(); |
|
30 | 36 |
$('.application-elements label').show(); |
31 | 37 |
} else { |
32 | 38 |
$('.application-elements label').each(function(idx, elem) { |
39 |
var container = $(elem).parent(); |
|
33 | 40 |
var slugged_text = $(elem).attr('data-slugged-text'); |
34 | 41 |
if (slugged_text.indexOf(val) > -1) { |
35 | 42 |
$(elem).show(); |
43 |
container.show(); |
|
36 | 44 |
} else { |
37 | 45 |
$(elem).hide(); |
46 |
if (!$('label:visible', container).lenght) { |
|
47 |
container.hide(); |
|
48 |
} |
|
38 | 49 |
} |
39 | 50 |
}); |
40 | 51 |
} |
hobo/applications/views.py | ||
---|---|---|
14 | 14 |
# You should have received a copy of the GNU Affero General Public License |
15 | 15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 |
import dataclasses |
|
17 | 18 |
import io |
18 | 19 |
import json |
20 |
import re |
|
19 | 21 |
import tarfile |
22 |
import unicodedata |
|
20 | 23 |
import urllib.parse |
21 | 24 | |
22 | 25 |
from django.conf import settings |
... | ... | |
25 | 28 |
from django.http import HttpResponse, HttpResponseRedirect |
26 | 29 |
from django.shortcuts import get_object_or_404 |
27 | 30 |
from django.urls import reverse, reverse_lazy |
31 |
from django.utils.encoding import force_text |
|
28 | 32 |
from django.utils.translation import ugettext_lazy as _ |
29 | 33 |
from django.views.generic import DetailView, FormView, ListView, TemplateView |
30 | 34 |
from django.views.generic.edit import CreateView, DeleteView, UpdateView |
... | ... | |
146 | 150 |
metadata = MetadataView.as_view() |
147 | 151 | |
148 | 152 | |
153 |
@dataclasses.dataclass |
|
154 |
class Category: |
|
155 |
name: str |
|
156 |
elements: list |
|
157 | ||
158 | ||
159 |
def simplify(s, space='-'): |
|
160 |
if s is None: |
|
161 |
return '' |
|
162 |
s = force_text(unicodedata.normalize('NFKD', s).encode('ascii', 'ignore')) |
|
163 |
s = re.sub(r'[^\w\s\'%s]' % space, '', s).strip().lower() |
|
164 |
s = re.sub(r'[\s\'%s]+' % space, space, s) |
|
165 |
return s |
|
166 | ||
167 | ||
149 | 168 |
class AppAddElementView(TemplateView): |
150 | 169 |
template_name = 'hobo/applications/add-element.html' |
151 | 170 | |
... | ... | |
157 | 176 |
context['type'] = object_type |
158 | 177 |
url = object_type['urls']['list'] |
159 | 178 |
response = requests.get(url) |
160 |
context['elements'] = response.json()['data'] |
|
179 |
elements = response.json()['data'] |
|
180 |
category_names = {el.get('category') or '' for el in elements} |
|
181 |
categories = [ |
|
182 |
Category( |
|
183 |
name=c, |
|
184 |
elements=sorted( |
|
185 |
[el for el in elements if el.get('category') == c], |
|
186 |
key=lambda a: simplify(a['text']), |
|
187 |
), |
|
188 |
) |
|
189 |
for c in sorted(list(category_names)) |
|
190 |
if c |
|
191 |
] |
|
192 |
categories.append( |
|
193 |
Category( |
|
194 |
name=_('Uncategorized'), |
|
195 |
elements=sorted( |
|
196 |
[el for el in elements if not el.get('category')], |
|
197 |
key=lambda a: simplify(a['text']), |
|
198 |
), |
|
199 |
) |
|
200 |
) |
|
201 |
context['categories'] = categories |
|
161 | 202 |
break |
162 | 203 |
return context |
163 | 204 | |
164 | 205 |
def post(self, request, app_slug, type): |
165 | 206 |
context = self.get_context_data() |
166 | 207 |
app = context['app'] |
167 |
element_infos = {x['id']: x for x in context['elements']}
|
|
208 |
element_infos = {x['id']: x for c in context['categories'] for x in c.elements}
|
|
168 | 209 |
for element_slug in request.POST.getlist('elements'): |
169 | 210 |
element, created = Element.objects.get_or_create( |
170 | 211 |
type=type, slug=element_slug, defaults={'name': element_infos[element_slug]['text']} |
tests/test_application.py | ||
---|---|---|
7 | 7 |
import httmock |
8 | 8 |
import pytest |
9 | 9 |
from httmock import HTTMock |
10 |
from pyquery import PyQuery |
|
10 | 11 |
from test_manager import login |
11 | 12 |
from webtest import Upload |
12 | 13 | |
... | ... | |
102 | 103 |
"dependencies": "https://wcs.example.invalid/api/export-import/forms/test2-form/dependencies/", |
103 | 104 |
}, |
104 | 105 |
}, |
106 |
{ |
|
107 |
"id": "foo2-form", |
|
108 |
"text": "Foo2 Test Form", |
|
109 |
"type": "forms", |
|
110 |
"category": "Foo", |
|
111 |
"urls": { |
|
112 |
"export": "https://wcs.example.invalid/api/export-import/forms/foo2-form/", |
|
113 |
"dependencies": "https://wcs.example.invalid/api/export-import/forms/foo2-form/dependencies/", |
|
114 |
}, |
|
115 |
}, |
|
116 |
{ |
|
117 |
"id": "foo-form", |
|
118 |
"text": "Foo Test Form", |
|
119 |
"type": "forms", |
|
120 |
"category": "Foo", |
|
121 |
"urls": { |
|
122 |
"export": "https://wcs.example.invalid/api/export-import/forms/foo-form/", |
|
123 |
"dependencies": "https://wcs.example.invalid/api/export-import/forms/foo-form/dependencies/", |
|
124 |
}, |
|
125 |
}, |
|
126 |
{ |
|
127 |
"id": "bar-form", |
|
128 |
"text": "Bar Test Form", |
|
129 |
"type": "forms", |
|
130 |
"category": "Bar", |
|
131 |
"urls": { |
|
132 |
"export": "https://wcs.example.invalid/api/export-import/forms/bar-form/", |
|
133 |
"dependencies": "https://wcs.example.invalid/api/export-import/forms/bar-form/dependencies/", |
|
134 |
}, |
|
135 |
}, |
|
105 | 136 |
] |
106 | 137 |
} |
107 | 138 | |
... | ... | |
203 | 234 |
# add forms |
204 | 235 |
assert '/add/forms/' in resp |
205 | 236 |
resp = resp.click('Forms') |
206 |
assert resp.form.fields['elements'][0]._value == 'test-form' |
|
207 |
assert resp.form.fields['elements'][1]._value == 'test2-form' |
|
208 |
resp.form.fields['elements'][0].checked = True |
|
237 |
assert len(resp.pyquery('.application-elements div')) == 3 |
|
238 |
assert resp.pyquery('.application-elements div:nth-child(1) h4').text() == 'Bar' |
|
239 |
assert PyQuery(resp.pyquery('.application-elements div:nth-child(1) input')[0]).val() == 'bar-form' |
|
240 |
assert resp.pyquery('.application-elements div:nth-child(2) h4').text() == 'Foo' |
|
241 |
assert PyQuery(resp.pyquery('.application-elements div:nth-child(2) input')[0]).val() == 'foo-form' |
|
242 |
assert PyQuery(resp.pyquery('.application-elements div:nth-child(2) input')[1]).val() == 'foo2-form' |
|
243 |
assert resp.pyquery('.application-elements div:nth-child(3) h4').text() == 'Uncategorized' |
|
244 |
assert PyQuery(resp.pyquery('.application-elements div:nth-child(3) input')[0]).val() == 'test2-form' |
|
245 |
assert PyQuery(resp.pyquery('.application-elements div:nth-child(3) input')[1]).val() == 'test-form' |
|
246 |
resp.form.fields['elements'][4].checked = True |
|
209 | 247 |
resp = resp.form.submit().follow() |
210 | 248 |
assert application.elements.count() == 1 |
211 | 249 |
element = application.elements.all()[0] |
tox.ini | ||
---|---|---|
42 | 42 |
pytest-cov |
43 | 43 |
pytest-django |
44 | 44 |
pytest-mock |
45 |
pyquery |
|
45 | 46 |
coverage |
46 | 47 |
cssselect |
47 | 48 |
WebTest |
48 |
- |