Merge lp:~elachuni/ubuntu-webcatalog/departments into lp:ubuntu-webcatalog

Proposed by Anthony Lenton
Status: Merged
Approved by: Michael Nelson
Approved revision: 9
Merged at revision: 8
Proposed branch: lp:~elachuni/ubuntu-webcatalog/departments
Merge into: lp:ubuntu-webcatalog
Diff against target: 764 lines (+663/-8)
8 files modified
src/webcatalog/admin.py (+6/-2)
src/webcatalog/department_filters.py (+116/-0)
src/webcatalog/fixtures/initial_data.json (+370/-0)
src/webcatalog/management/commands/import_app_install_data.py (+3/-2)
src/webcatalog/models.py (+37/-4)
src/webcatalog/tests/__init__.py (+2/-0)
src/webcatalog/tests/test_department_filters.py (+69/-0)
src/webcatalog/tests/test_models.py (+60/-0)
To merge this branch: bzr merge lp:~elachuni/ubuntu-webcatalog/departments
Reviewer Review Type Date Requested Status
Michael Nelson (community) Approve
Review via email: mp+57424@code.launchpad.net

Description of the change

Overview
========
Add a Department model to loosely group applications.

Details
=======
This is needed to allow simple browsing of the site, to have apps grouped into departments like the software-center does.
A few tiny bugs were fixed while I was there, to fix failures I was getting when importing app data locally:
 - Encoded unicode strings when writing to stdout in import_app_install_data.py to avoid UnicodeEncodeErrors
 - Changed Application.package_name from a SlugField to a CharField, as there are package names with periods in them
 - Extended max_length for Application.mimetype
 - Allowed blank Application.app_type and Application.categories

To post a comment you must log in.
Revision history for this message
Michael Nelson (michael.nelson) wrote :

Excellent Anthony! I'll assume that you're also working on the ui aspect of this and grab something else this morning.

Thanks for the small import fixes too.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/webcatalog/admin.py'
--- src/webcatalog/admin.py 2011-04-08 12:25:25 +0000
+++ src/webcatalog/admin.py 2011-04-13 02:40:59 +0000
@@ -22,7 +22,10 @@
22 with_statement,22 with_statement,
23 )23 )
24from django.contrib import admin24from django.contrib import admin
25from webcatalog.models import Application25from webcatalog.models import (
26 Application,
27 Department,
28 )
2629
27__metaclass__ = type30__metaclass__ = type
28__all__ = [31__all__ = [
@@ -33,6 +36,7 @@
33class ApplicationAdmin(admin.ModelAdmin):36class ApplicationAdmin(admin.ModelAdmin):
34 list_display = ('package_name', 'name', 'comment')37 list_display = ('package_name', 'name', 'comment')
35 search_fields = ('package_name', 'name', 'comment')38 search_fields = ('package_name', 'name', 'comment')
3639 list_filter = ('departments',)
3740
38admin.site.register(Application, ApplicationAdmin)41admin.site.register(Application, ApplicationAdmin)
42admin.site.register(Department)
3943
=== added file 'src/webcatalog/department_filters.py'
--- src/webcatalog/department_filters.py 1970-01-01 00:00:00 +0000
+++ src/webcatalog/department_filters.py 2011-04-13 02:40:59 +0000
@@ -0,0 +1,116 @@
1# -*- coding: utf-8 -*-
2# This file is part of the Ubuntu Web Catalog
3# Copyright (C) 2011 Canonical Ltd.
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Affero General Public License as
7# published by the Free Software Foundation, either version 3 of the
8# License, or (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU Affero General Public License for more details.
14#
15# You should have received a copy of the GNU Affero General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18"""Department filters."""
19
20from __future__ import (
21 absolute_import,
22 with_statement,
23 )
24
25import re
26
27from django.db import models
28
29__metaclass__ = type
30__all__ = [
31 'department_filters',
32 ]
33
34
35def category_filter(categories_set):
36 """Returns a filter func that checks an app for certain categories."""
37 def filter_func(app):
38 return bool(app.categories_set.intersection(categories_set))
39 return filter_func
40
41def package_name_filter(name_regex):
42 """Returns a filter func that checks if an app's name matches a regex"""
43 def filter_func(app):
44 return re.match(name_regex, app.package_name) is not None
45 return filter_func
46
47def section_filter(sections):
48 """Returns a filter that checks if an app is in certain sections."""
49 def filter_func(app):
50 return app.section in sections
51 return filter_func
52
53# Taken from https://wiki.ubuntu.com/SoftwareCenter#line-793
54
55department_filters = {
56 'Accessories': [category_filter(set(['Utility', 'System']))],
57 'Education': [category_filter(set(['Education']))],
58 'Fonts': [package_name_filter(r'[to]tf-.*')],
59 'Games': [category_filter(set(['Game', 'Sports']))],
60 'Board Games': [category_filter(set(['BoardGame']))],
61 'Card Games': [category_filter(set(['CardGame']))],
62 'Puzzles': [category_filter(set(['LogicGame']))],
63 'Role-Playing': [category_filter(set(['RolePlaying']))],
64 'Sports': [category_filter(set(['SportsGame']))],
65 'Graphics': [category_filter(set(['Graphics']))],
66 '3D': [category_filter(set(['3DGraphics']))],
67 'Drawing': [category_filter(set(['VectorGraphics']))],
68 'Painting': [category_filter(set(['RasterGraphics']))],
69 'Photography': [category_filter(set(['Photography']))],
70 'Publishing': [category_filter(set(['Publishing']))],
71 'Scanning & OCR': [category_filter(set(['Scanning', 'OCR']))],
72 'Viewers': [category_filter(set(['Viewer']))],
73 'Internet': [category_filter(set(['Network']))],
74 'Chat': [category_filter(set(['InstantMessaging', 'IRCClient']))],
75 'File Sharing': [category_filter(set(['FileTransfer']))],
76 'Mail': [category_filter(set(['Email']))],
77 'Web Browsers': [category_filter(set(['WebBrowser']))],
78 'Office': [category_filter(set(['Office']))],
79 'Science & Engineering': [category_filter(set(['Science'])),
80 section_filter(['science'])],
81 'Astronomy': [category_filter(set(['Astronomy']))],
82 'Biology': [category_filter(set(['Biology']))],
83 'Chemistry': [category_filter(set(['Chemistry']))],
84 'Computing & Robotics': [category_filter(set(['ArtificialIntelligence',
85 'ComputerScience', 'Robotics']))],
86 'Electronics': [category_filter(set(['Electronics']))],
87 'Engineering': [category_filter(set(['Engineering']))],
88 'Geography': [category_filter(set(['Geography']))],
89 'Geology': [category_filter(set(['Geology', 'Geoscience']))],
90 'Mathematics': [category_filter(set(['DataVisualization', 'Math',
91 'NumericalAnalysis'])), section_filter(['math', 'gnu-r'])],
92 'Medicine': [category_filter(set(['MedicalSoftware']))],
93 'Physics': [category_filter(set(['Electricity', 'Physics']))],
94 'Sound & Video': [category_filter(set(['AudioVideo', 'Audio', 'Video']))],
95 'Themes & Tweaks': [category_filter(set(['Settings']))],
96 'Universal Access': [category_filter(set(['Accessibility']))],
97 'Developer Tools': [category_filter(set(['Development'])),
98 section_filter(['devel'])],
99 'Debugging': [category_filter(set(['Debugger']))],
100 'Graphic Interface Design': [category_filter(set(['GUIDesigner']))],
101 'Haskell': [section_filter(['haskell'])],
102 'IDEs': [category_filter(set(['IDE']))],
103 'Java': [section_filter(['java'])],
104 'Libraries': [section_filter(['libdevel'])],
105 'Lisp': [section_filter(['lisp'])],
106 'Localization': [category_filter(set(['Translation']))],
107 'Mono/CLI': [section_filter(['cli-mono'])],
108 'OCaml': [section_filter(['ocaml'])],
109 'Perl': [section_filter(['perl'])],
110 'Profiling': [category_filter(set(['Profiling']))],
111 'Python': [section_filter(['python'])],
112 'Ruby': [section_filter(['ruby'])],
113 'Version Control': [category_filter(set(['RevisionControl'])),
114 section_filter(['vcs'])],
115 'Web Development': [category_filter(set(['WebDevelopment']))],
116}
0117
=== added directory 'src/webcatalog/fixtures'
=== added file 'src/webcatalog/fixtures/initial_data.json'
--- src/webcatalog/fixtures/initial_data.json 1970-01-01 00:00:00 +0000
+++ src/webcatalog/fixtures/initial_data.json 2011-04-13 02:40:59 +0000
@@ -0,0 +1,370 @@
1[
2 {
3 "pk": 1,
4 "model": "webcatalog.department",
5 "fields": {
6 "name": "Games",
7 "parent": null
8 }
9 },
10 {
11 "pk": 2,
12 "model": "webcatalog.department",
13 "fields": {
14 "name": "Office",
15 "parent": null
16 }
17 },
18 {
19 "pk": 3,
20 "model": "webcatalog.department",
21 "fields": {
22 "name": "Sound & Video",
23 "parent": null
24 }
25 },
26 {
27 "pk": 4,
28 "model": "webcatalog.department",
29 "fields": {
30 "name": "Developer Tools",
31 "parent": null
32 }
33 },
34 {
35 "pk": 5,
36 "model": "webcatalog.department",
37 "fields": {
38 "name": "Science & Engineering",
39 "parent": null
40 }
41 },
42 {
43 "pk": 6,
44 "model": "webcatalog.department",
45 "fields": {
46 "name": "Education",
47 "parent": null
48 }
49 },
50 {
51 "pk": 7,
52 "model": "webcatalog.department",
53 "fields": {
54 "name": "Biology",
55 "parent": 5
56 }
57 },
58 {
59 "pk": 8,
60 "model": "webcatalog.department",
61 "fields": {
62 "name": "Accessories",
63 "parent": null
64 }
65 },
66 {
67 "pk": 9,
68 "model": "webcatalog.department",
69 "fields": {
70 "name": "Role-Playing",
71 "parent": 1
72 }
73 },
74 {
75 "pk": 10,
76 "model": "webcatalog.department",
77 "fields": {
78 "name": "Geography",
79 "parent": 5
80 }
81 },
82 {
83 "pk": 11,
84 "model": "webcatalog.department",
85 "fields": {
86 "name": "Medicine",
87 "parent": 5
88 }
89 },
90 {
91 "pk": 12,
92 "model": "webcatalog.department",
93 "fields": {
94 "name": "Viewers",
95 "parent": 13
96 }
97 },
98 {
99 "pk": 13,
100 "model": "webcatalog.department",
101 "fields": {
102 "name": "Graphics",
103 "parent": null
104 }
105 },
106 {
107 "pk": 14,
108 "model": "webcatalog.department",
109 "fields": {
110 "name": "Themes & Tweaks",
111 "parent": null
112 }
113 },
114 {
115 "pk": 15,
116 "model": "webcatalog.department",
117 "fields": {
118 "name": "Internet",
119 "parent": null
120 }
121 },
122 {
123 "pk": 16,
124 "model": "webcatalog.department",
125 "fields": {
126 "name": "Debugging",
127 "parent": 4
128 }
129 },
130 {
131 "pk": 17,
132 "model": "webcatalog.department",
133 "fields": {
134 "name": "Profiling",
135 "parent": 4
136 }
137 },
138 {
139 "pk": 18,
140 "model": "webcatalog.department",
141 "fields": {
142 "name": "Chat",
143 "parent": 15
144 }
145 },
146 {
147 "pk": 19,
148 "model": "webcatalog.department",
149 "fields": {
150 "name": "IDEs",
151 "parent": 4
152 }
153 },
154 {
155 "pk": 20,
156 "model": "webcatalog.department",
157 "fields": {
158 "name": "3D",
159 "parent": 13
160 }
161 },
162 {
163 "pk": 21,
164 "model": "webcatalog.department",
165 "fields": {
166 "name": "Engineering",
167 "parent": 5
168 }
169 },
170 {
171 "pk": 22,
172 "model": "webcatalog.department",
173 "fields": {
174 "name": "Electronics",
175 "parent": 5
176 }
177 },
178 {
179 "pk": 23,
180 "model": "webcatalog.department",
181 "fields": {
182 "name": "Web Browsers",
183 "parent": 15
184 }
185 },
186 {
187 "pk": 24,
188 "model": "webcatalog.department",
189 "fields": {
190 "name": "Mathematics",
191 "parent": 5
192 }
193 },
194 {
195 "pk": 25,
196 "model": "webcatalog.department",
197 "fields": {
198 "name": "Chemistry",
199 "parent": 5
200 }
201 },
202 {
203 "pk": 26,
204 "model": "webcatalog.department",
205 "fields": {
206 "name": "Physics",
207 "parent": 5
208 }
209 },
210 {
211 "pk": 27,
212 "model": "webcatalog.department",
213 "fields": {
214 "name": "FileSharing",
215 "parent": null
216 }
217 },
218 {
219 "pk": 28,
220 "model": "webcatalog.department",
221 "fields": {
222 "name": "Mail",
223 "parent": 15
224 }
225 },
226 {
227 "pk": 29,
228 "model": "webcatalog.department",
229 "fields": {
230 "name": "Computing & Robotics",
231 "parent": 5
232 }
233 },
234 {
235 "pk": 30,
236 "model": "webcatalog.department",
237 "fields": {
238 "name": "Web Development",
239 "parent": 4
240 }
241 },
242 {
243 "pk": 31,
244 "model": "webcatalog.department",
245 "fields": {
246 "name": "Graphic Interface Design",
247 "parent": 4
248 }
249 },
250 {
251 "pk": 32,
252 "model": "webcatalog.department",
253 "fields": {
254 "name": "Version Control",
255 "parent": 4
256 }
257 },
258 {
259 "pk": 33,
260 "model": "webcatalog.department",
261 "fields": {
262 "name": "Photography",
263 "parent": 13
264 }
265 },
266 {
267 "pk": 34,
268 "model": "webcatalog.department",
269 "fields": {
270 "name": "Astronomy",
271 "parent": 5
272 }
273 },
274 {
275 "pk": 35,
276 "model": "webcatalog.department",
277 "fields": {
278 "name": "Universal Access",
279 "parent": null
280 }
281 },
282 {
283 "pk": 36,
284 "model": "webcatalog.department",
285 "fields": {
286 "name": "Drawing",
287 "parent": 13
288 }
289 },
290 {
291 "pk": 37,
292 "model": "webcatalog.department",
293 "fields": {
294 "name": "Painting",
295 "parent": 13
296 }
297 },
298 {
299 "pk": 38,
300 "model": "webcatalog.department",
301 "fields": {
302 "name": "Publishing",
303 "parent": 13
304 }
305 },
306 {
307 "pk": 39,
308 "model": "webcatalog.department",
309 "fields": {
310 "name": "Localization",
311 "parent": 4
312 }
313 },
314 {
315 "pk": 40,
316 "model": "webcatalog.department",
317 "fields": {
318 "name": "Scanning & OCR",
319 "parent": 13
320 }
321 },
322 {
323 "pk": 41,
324 "model": "webcatalog.department",
325 "fields": {
326 "name": "Geology",
327 "parent": 5
328 }
329 },
330 {
331 "pk": 42,
332 "model": "webcatalog.department",
333 "fields": {
334 "name": "Board Games",
335 "parent": 1
336 }
337 },
338 {
339 "pk": 43,
340 "model": "webcatalog.department",
341 "fields": {
342 "name": "Puzzles",
343 "parent": 1
344 }
345 },
346 {
347 "pk": 44,
348 "model": "webcatalog.department",
349 "fields": {
350 "name": "File Sharing",
351 "parent": 15
352 }
353 },
354 {
355 "pk": 45,
356 "model": "webcatalog.department",
357 "fields": {
358 "name": "Sports",
359 "parent": 1
360 }
361 },
362 {
363 "pk": 46,
364 "model": "webcatalog.department",
365 "fields": {
366 "name": "Card Games",
367 "parent": 1
368 }
369 }
370]
0\ No newline at end of file371\ No newline at end of file
1372
=== modified file 'src/webcatalog/management/commands/import_app_install_data.py'
--- src/webcatalog/management/commands/import_app_install_data.py 2011-04-12 15:55:30 +0000
+++ src/webcatalog/management/commands/import_app_install_data.py 2011-04-13 02:40:59 +0000
@@ -86,11 +86,12 @@
8686
87 if form.is_valid():87 if form.is_valid():
88 app = form.save()88 app = form.save()
89 app.update_departments()
89 if self.verbosity > 0:90 if self.verbosity > 0:
90 self.stdout.write(91 self.stdout.write(
91 u"{0} created.\n".format(app.name))92 u"{0} created.\n".format(app.name).encode('utf-8'))
92 else:93 else:
93 if self.verbosity > 0:94 if self.verbosity > 0:
94 self.stdout.write(95 self.stdout.write(
95 u"Skipping {0} as input failed validation: {1}.\n".format(96 u"Skipping {0} as input failed validation: {1}.\n".format(
96 member.name, form.errors))97 member.name, form.errors).encode('utf-8'))
9798
=== modified file 'src/webcatalog/models.py'
--- src/webcatalog/models.py 2011-04-12 15:22:54 +0000
+++ src/webcatalog/models.py 2011-04-13 02:40:59 +0000
@@ -21,8 +21,13 @@
21 absolute_import,21 absolute_import,
22 with_statement,22 with_statement,
23 )23 )
24
25import logging
26
24from django.db import models27from django.db import models
2528
29from webcatalog.department_filters import department_filters
30
26__metaclass__ = type31__metaclass__ = type
27__all__ = [32__all__ = [
28 'Application',33 'Application',
@@ -37,19 +42,20 @@
37 # at runtime instead.42 # at runtime instead.
3843
39 # The following fields are extracted from app-install-data.44 # The following fields are extracted from app-install-data.
40 package_name = models.SlugField(max_length=100)45 package_name = models.CharField(max_length=100)
41 name = models.CharField(max_length=255)46 name = models.CharField(max_length=255)
42 comment = models.CharField(max_length=255, blank=True)47 comment = models.CharField(max_length=255, blank=True)
43 popcon = models.IntegerField()48 popcon = models.IntegerField()
44 channel = models.CharField(max_length=255, blank=True)49 channel = models.CharField(max_length=255, blank=True)
45 screenshot_url = models.URLField(blank=True,50 screenshot_url = models.URLField(blank=True,
46 help_text="Only use this if it is other than the normal screenshot url.")51 help_text="Only use this if it is other than the normal screenshot url.")
47 mimetype = models.CharField(max_length=255, blank=True)52 mimetype = models.CharField(max_length=2048, blank=True)
48 architectures = models.CharField(max_length=255, blank=True)53 architectures = models.CharField(max_length=255, blank=True)
49 keywords = models.CharField(max_length=255, blank=True)54 keywords = models.CharField(max_length=255, blank=True)
50 app_type = models.CharField(max_length=32)55 app_type = models.CharField(max_length=32, blank=True)
51 section = models.CharField(max_length=32)56 section = models.CharField(max_length=32)
52 categories = models.CharField(max_length=255)57 categories = models.CharField(max_length=255, blank=True)
58 departments = models.ManyToManyField('Department', blank=True)
5359
54 # Other desktop fields used by s-c60 # Other desktop fields used by s-c
55 gnome_full_name = models.CharField(max_length=255, blank=True)61 gnome_full_name = models.CharField(max_length=255, blank=True)
@@ -64,3 +70,30 @@
6470
65 def __unicode__(self):71 def __unicode__(self):
66 return u"{0} ({1})".format(self.name, self.package_name)72 return u"{0} ({1})".format(self.name, self.package_name)
73
74 @property
75 def categories_set(self):
76 """Return the set of categories for this app"""
77 stripped = [x.strip() for x in self.categories.split(';')]
78 return set(x for x in stripped if x)
79
80 def update_departments(self):
81 """Update the list of departments for this app"""
82 self.departments.clear()
83 for dept_name, dept_filters in department_filters.items():
84 for dept_filter in dept_filters:
85 if dept_filter(self):
86 dept, created = Department.objects.get_or_create(
87 name=dept_name)
88 if created:
89 logging.warn("Department %s automatically created!" %
90 dept_name)
91 self.departments.add(dept)
92 break
93
94class Department(models.Model):
95 parent = models.ForeignKey('self', blank=True, null=True)
96 name = models.CharField(max_length=64)
97
98 def __unicode__(self):
99 return self.name
67100
=== modified file 'src/webcatalog/tests/__init__.py'
--- src/webcatalog/tests/__init__.py 2011-04-12 12:54:38 +0000
+++ src/webcatalog/tests/__init__.py 2011-04-13 02:40:59 +0000
@@ -19,3 +19,5 @@
19from .test_forms import *19from .test_forms import *
20from .test_commands import *20from .test_commands import *
21from .test_views import *21from .test_views import *
22from .test_department_filters import *
23from .test_models import *
2224
=== added file 'src/webcatalog/tests/test_department_filters.py'
--- src/webcatalog/tests/test_department_filters.py 1970-01-01 00:00:00 +0000
+++ src/webcatalog/tests/test_department_filters.py 2011-04-13 02:40:59 +0000
@@ -0,0 +1,69 @@
1# -*- coding: utf-8 -*-
2# This file is part of the Ubuntu Web Catalog
3# Copyright (C) 2011 Canonical Ltd.
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Affero General Public License as
7# published by the Free Software Foundation, either version 3 of the
8# License, or (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU Affero General Public License for more details.
14#
15# You should have received a copy of the GNU Affero General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18"""Test cases for department filters."""
19
20from __future__ import (
21 absolute_import,
22 with_statement,
23 )
24
25
26from webcatalog.tests.factory import TestCaseWithFactory
27from webcatalog.department_filters import (
28 category_filter,
29 package_name_filter,
30 section_filter
31 )
32
33__metaclass__ = type
34__all__ = [
35 'DepartmentFilterTestCase',
36 ]
37
38
39class DepartmentFilterTestCase(TestCaseWithFactory):
40 def test_package_name_filter(self):
41 dept_filter = package_name_filter('a*$')
42 app1 = self.factory.make_application(package_name='aaaaa')
43 self.assertTrue(dept_filter(app1))
44
45 app2 = self.factory.make_application(package_name='aaabaa')
46 self.assertFalse(dept_filter(app2))
47
48 def test_category_filter(self):
49 dept_filter = category_filter(['foo', 'bar'])
50 app = self.factory.make_application()
51 self.assertFalse(dept_filter(app))
52 app.categories = 'foo;bin'
53 self.assertTrue(dept_filter(app))
54 app.categories = 'foobin'
55 self.assertFalse(dept_filter(app))
56 app.categories = ''
57 self.assertFalse(dept_filter(app))
58
59 def test_section_filter(self):
60 dept_filter = section_filter(['foo', 'bar'])
61 app = self.factory.make_application()
62
63 self.assertFalse(dept_filter(app))
64 app.section = 'foo'
65 self.assertTrue(dept_filter(app))
66 app.section = 'foobin'
67 self.assertFalse(dept_filter(app))
68 app.section = ''
69 self.assertFalse(dept_filter(app))
070
=== added file 'src/webcatalog/tests/test_models.py'
--- src/webcatalog/tests/test_models.py 1970-01-01 00:00:00 +0000
+++ src/webcatalog/tests/test_models.py 2011-04-13 02:40:59 +0000
@@ -0,0 +1,60 @@
1# -*- coding: utf-8 -*-
2# This file is part of the Ubuntu Web Catalog
3# Copyright (C) 2011 Canonical Ltd.
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Affero General Public License as
7# published by the Free Software Foundation, either version 3 of the
8# License, or (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU Affero General Public License for more details.
14#
15# You should have received a copy of the GNU Affero General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18"""Test cases for models."""
19
20from __future__ import (
21 absolute_import,
22 with_statement,
23 )
24
25
26from webcatalog.tests.factory import TestCaseWithFactory
27from webcatalog.models import Application
28
29__metaclass__ = type
30__all__ = [
31 'ApplicationTestCase',
32 ]
33
34
35class ApplicationTestCase(TestCaseWithFactory):
36 def test_categories_set(self):
37 app = self.factory.make_application()
38 app.categories = 'foo;;;; bar '
39 self.assertEqual(set(['foo', 'bar']), app.categories_set)
40
41 def test_empty_category_set(self):
42 app = self.factory.make_application()
43 app.categories = ''
44 self.assertEqual(set(), app.categories_set)
45
46 def test_update_empty_departments(self):
47 app = self.factory.make_application()
48
49 app.update_departments()
50
51 self.assertEqual(0, app.departments.count())
52
53 def test_update_departments(self):
54 app = self.factory.make_application()
55 app.categories = 'Game;'
56
57 app.update_departments()
58
59 self.assertEqual(1, app.departments.count())
60 self.assertEqual('Games', app.departments.get().name)

Subscribers

People subscribed via source and target branches