Merge lp:~elachuni/ubuntu-webcatalog/department-slug into lp:ubuntu-webcatalog
- department-slug
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Michael Nelson | ||||
Approved revision: | 80 | ||||
Merged at revision: | 75 | ||||
Proposed branch: | lp:~elachuni/ubuntu-webcatalog/department-slug | ||||
Merge into: | lp:ubuntu-webcatalog | ||||
Diff against target: |
1453 lines (+667/-117) 15 files modified
src/webcatalog/admin.py (+5/-1) src/webcatalog/fixtures/initial_data.json (+93/-48) src/webcatalog/migrations/0010_add_department_slug.py (+154/-0) src/webcatalog/migrations/0011_populate_department_slug.py (+153/-0) src/webcatalog/migrations/0012_unique_department_slug.py (+160/-0) src/webcatalog/models/applications.py (+6/-9) src/webcatalog/static/css/carousel.css (+5/-0) src/webcatalog/templates/webcatalog/department_overview.html (+1/-1) src/webcatalog/templates/webcatalog/department_overview_snippet.html (+3/-3) src/webcatalog/templates/webcatalog/featured_apps_widget.html (+4/-4) src/webcatalog/tests/factory.py (+4/-2) src/webcatalog/tests/test_models.py (+15/-22) src/webcatalog/tests/test_views.py (+50/-20) src/webcatalog/urls.py (+2/-2) src/webcatalog/views.py (+12/-5) |
||||
To merge this branch: | bzr merge lp:~elachuni/ubuntu-webcatalog/department-slug | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Nelson (community) | Approve | ||
Review via email: mp+96792@code.launchpad.net |
Commit message
Added a 'slug' field to the Department models, and made it possible to specify department overview requests by the department's slug.
Description of the change
Overview
========
This branch adds a 'slug' field to the Department models, and makes it possible to specify department overview requests by the department's slug.
Details
=======
First, apologies for the long MP. Most of it is migrations and fixture fix, I thought this was going to be a simple and small branch.
Three migrations were needed: One to add the 'slug' field (null, non-unique), a data migration to populate it, and one to add a non-null and unique restriction.
The branch also makes all the links on the site use the sluggified version of the department overview URL, although specifying department by ID still works, to not break links already out there.
As I thought this branch was going to be small, I sorted out a couple of minor drive-through fixes before starting :-/ Related to the featured_apps widget, it now never displays blank slides (it did before if you happened to have a muliple of 4 featured apps), and a css fix to remove the right-side border on the last featured app.
Anthony Lenton (elachuni) wrote : | # |
Michael Nelson (michael.nelson) wrote : | # |
On Fri, Mar 9, 2012 at 6:33 PM, Anthony Lenton
<email address hidden> wrote:
> Anthony Lenton has proposed merging lp:~elachuni/ubuntu-webcatalog/department-slug into lp:ubuntu-webcatalog.
>
> Overview
> ========
> This branch adds a 'slug' field to the Department models, and makes it possible to specify department overview requests by the department's slug.
>
> Details
> =======
> First, apologies for the long MP. Most of it is migrations and fixture fix, I thought this was going to be a simple and small branch.
> Three migrations were needed: One to add the 'slug' field (null, non-unique), a data migration to populate it, and one to add a non-null and unique restriction.
No worries - I don't need to go through much of that code :-)
>
> The branch also makes all the links on the site use the sluggified version of the department overview URL, although specifying department by ID still works, to not break links already out there.
Excellent.
>
> As I thought this branch was going to be small, I sorted out a couple of minor drive-through fixes before starting :-/ Related to the featured_apps widget, it now never displays blank slides (it did before if you happened to have a muliple of 4 featured apps), and a css fix to remove the right-side border on the last featured app.
k.
>
>
> --
> https:/
> You are subscribed to branch lp:ubuntu-webcatalog.
>
> === modified file 'src/webcatalog
> --- src/webcatalog/
> +++ src/webcatalog/
> === modified file 'src/webcatalog
> --- src/webcatalog/
> +++ src/webcatalog/
> === added file 'src/webcatalog
> --- src/webcatalog/
> +++ src/webcatalog/
> === added file 'src/webcatalog
> --- src/webcatalog/
> +++ src/webcatalog/
> @@ -0,0 +1,162 @@
> +# encoding: utf-8
> +import datetime
> +from south.db import db
> +from south.v2 import DataMigration
> +from django.db import models
> +
> +class Migration(
> +
> + # A static version of sluggify name, for if the one on the
> + # Department model changes
> + def sluggify_name(self, name):
> + slug = name.lower()
> + for char in ' _&,.':
> + slug = slug.replace(char, '-')
> + while '--' in slug:
> + slug = slug.replace('--', '-')
> + return slug
You can use the slugify method built in to django if you want? less code :-)
> +
> + def forwards(self, orm):
> + for dept in orm.Department.
> + dept.slug = self.sluggify_
> + dept.save()
> +
> === added file 'src/webcatalog
- 78. By Anthony Lenton
-
Replaced custom sluggify_name for Django's out-of-the-box slugify filter.
Michael Nelson (michael.nelson) : | # |
- 79. By Anthony Lenton
-
Replaced dept_slug and dept_id args in the department overview view for a single dept_slug_or_id arg.
- 80. By Anthony Lenton
-
Merged in changes from trunk.
Preview Diff
1 | === modified file 'src/webcatalog/admin.py' |
2 | --- src/webcatalog/admin.py 2012-03-01 21:38:31 +0000 |
3 | +++ src/webcatalog/admin.py 2012-03-12 15:48:24 +0000 |
4 | @@ -52,8 +52,12 @@ |
5 | class ExhibitAdmin(admin.ModelAdmin): |
6 | list_display = ('package_names', 'published', 'display') |
7 | |
8 | + |
9 | +class DepartmentAdmin(admin.ModelAdmin): |
10 | + prepopulated_fields = {"slug": ("name",)} |
11 | + |
12 | admin.site.register(Application, ApplicationAdmin) |
13 | -admin.site.register(Department) |
14 | +admin.site.register(Department, DepartmentAdmin) |
15 | admin.site.register(DistroSeries) |
16 | admin.site.register(Exhibit, ExhibitAdmin) |
17 | admin.site.register(Machine, MachineAdmin) |
18 | |
19 | === modified file 'src/webcatalog/fixtures/initial_data.json' |
20 | --- src/webcatalog/fixtures/initial_data.json 2011-04-19 18:46:21 +0000 |
21 | +++ src/webcatalog/fixtures/initial_data.json 2012-03-12 15:48:24 +0000 |
22 | @@ -4,7 +4,8 @@ |
23 | "model": "webcatalog.department", |
24 | "fields": { |
25 | "name": "Games", |
26 | - "parent": null |
27 | + "parent": null, |
28 | + "slug": "games" |
29 | } |
30 | }, |
31 | { |
32 | @@ -12,7 +13,8 @@ |
33 | "model": "webcatalog.department", |
34 | "fields": { |
35 | "name": "Office", |
36 | - "parent": null |
37 | + "parent": null, |
38 | + "slug": "office" |
39 | } |
40 | }, |
41 | { |
42 | @@ -20,7 +22,8 @@ |
43 | "model": "webcatalog.department", |
44 | "fields": { |
45 | "name": "Sound & Video", |
46 | - "parent": null |
47 | + "parent": null, |
48 | + "slug": "sound-video" |
49 | } |
50 | }, |
51 | { |
52 | @@ -28,7 +31,8 @@ |
53 | "model": "webcatalog.department", |
54 | "fields": { |
55 | "name": "Developer Tools", |
56 | - "parent": null |
57 | + "parent": null, |
58 | + "slug": "developer-tools" |
59 | } |
60 | }, |
61 | { |
62 | @@ -36,7 +40,8 @@ |
63 | "model": "webcatalog.department", |
64 | "fields": { |
65 | "name": "Science & Engineering", |
66 | - "parent": null |
67 | + "parent": null, |
68 | + "slug": "science-engineering" |
69 | } |
70 | }, |
71 | { |
72 | @@ -44,7 +49,8 @@ |
73 | "model": "webcatalog.department", |
74 | "fields": { |
75 | "name": "Education", |
76 | - "parent": null |
77 | + "parent": null, |
78 | + "slug": "education" |
79 | } |
80 | }, |
81 | { |
82 | @@ -52,7 +58,8 @@ |
83 | "model": "webcatalog.department", |
84 | "fields": { |
85 | "name": "Biology", |
86 | - "parent": 5 |
87 | + "parent": 5, |
88 | + "slug": "biology" |
89 | } |
90 | }, |
91 | { |
92 | @@ -60,7 +67,8 @@ |
93 | "model": "webcatalog.department", |
94 | "fields": { |
95 | "name": "Accessories", |
96 | - "parent": null |
97 | + "parent": null, |
98 | + "slug": "accessories" |
99 | } |
100 | }, |
101 | { |
102 | @@ -68,7 +76,8 @@ |
103 | "model": "webcatalog.department", |
104 | "fields": { |
105 | "name": "Role-Playing", |
106 | - "parent": 1 |
107 | + "parent": 1, |
108 | + "slug": "role-playing" |
109 | } |
110 | }, |
111 | { |
112 | @@ -76,7 +85,8 @@ |
113 | "model": "webcatalog.department", |
114 | "fields": { |
115 | "name": "Geography", |
116 | - "parent": 5 |
117 | + "parent": 5, |
118 | + "slug": "geography" |
119 | } |
120 | }, |
121 | { |
122 | @@ -84,7 +94,8 @@ |
123 | "model": "webcatalog.department", |
124 | "fields": { |
125 | "name": "Viewers", |
126 | - "parent": 13 |
127 | + "parent": 13, |
128 | + "slug": "viewers" |
129 | } |
130 | }, |
131 | { |
132 | @@ -92,7 +103,8 @@ |
133 | "model": "webcatalog.department", |
134 | "fields": { |
135 | "name": "Graphics", |
136 | - "parent": null |
137 | + "parent": null, |
138 | + "slug": "graphics" |
139 | } |
140 | }, |
141 | { |
142 | @@ -100,7 +112,8 @@ |
143 | "model": "webcatalog.department", |
144 | "fields": { |
145 | "name": "Themes & Tweaks", |
146 | - "parent": null |
147 | + "parent": null, |
148 | + "slug": "themes-tweaks" |
149 | } |
150 | }, |
151 | { |
152 | @@ -108,7 +121,8 @@ |
153 | "model": "webcatalog.department", |
154 | "fields": { |
155 | "name": "Internet", |
156 | - "parent": null |
157 | + "parent": null, |
158 | + "slug": "internet" |
159 | } |
160 | }, |
161 | { |
162 | @@ -116,7 +130,8 @@ |
163 | "model": "webcatalog.department", |
164 | "fields": { |
165 | "name": "Debugging", |
166 | - "parent": 4 |
167 | + "parent": 4, |
168 | + "slug": "debugging" |
169 | } |
170 | }, |
171 | { |
172 | @@ -124,7 +139,8 @@ |
173 | "model": "webcatalog.department", |
174 | "fields": { |
175 | "name": "Profiling", |
176 | - "parent": 4 |
177 | + "parent": 4, |
178 | + "slug": "profiling" |
179 | } |
180 | }, |
181 | { |
182 | @@ -132,7 +148,8 @@ |
183 | "model": "webcatalog.department", |
184 | "fields": { |
185 | "name": "Chat", |
186 | - "parent": 15 |
187 | + "parent": 15, |
188 | + "slug": "chat" |
189 | } |
190 | }, |
191 | { |
192 | @@ -140,7 +157,8 @@ |
193 | "model": "webcatalog.department", |
194 | "fields": { |
195 | "name": "IDEs", |
196 | - "parent": 4 |
197 | + "parent": 4, |
198 | + "slug": "ides" |
199 | } |
200 | }, |
201 | { |
202 | @@ -148,7 +166,8 @@ |
203 | "model": "webcatalog.department", |
204 | "fields": { |
205 | "name": "3D", |
206 | - "parent": 13 |
207 | + "parent": 13, |
208 | + "slug": "3d" |
209 | } |
210 | }, |
211 | { |
212 | @@ -156,7 +175,8 @@ |
213 | "model": "webcatalog.department", |
214 | "fields": { |
215 | "name": "Engineering", |
216 | - "parent": 5 |
217 | + "parent": 5, |
218 | + "slug": "engineering" |
219 | } |
220 | }, |
221 | { |
222 | @@ -164,7 +184,8 @@ |
223 | "model": "webcatalog.department", |
224 | "fields": { |
225 | "name": "Electronics", |
226 | - "parent": 5 |
227 | + "parent": 5, |
228 | + "slug": "electronics" |
229 | } |
230 | }, |
231 | { |
232 | @@ -172,7 +193,8 @@ |
233 | "model": "webcatalog.department", |
234 | "fields": { |
235 | "name": "Web Browsers", |
236 | - "parent": 15 |
237 | + "parent": 15, |
238 | + "slug": "web-browsers" |
239 | } |
240 | }, |
241 | { |
242 | @@ -180,7 +202,8 @@ |
243 | "model": "webcatalog.department", |
244 | "fields": { |
245 | "name": "Mathematics", |
246 | - "parent": 5 |
247 | + "parent": 5, |
248 | + "slug": "mathematics" |
249 | } |
250 | }, |
251 | { |
252 | @@ -188,7 +211,8 @@ |
253 | "model": "webcatalog.department", |
254 | "fields": { |
255 | "name": "Chemistry", |
256 | - "parent": 5 |
257 | + "parent": 5, |
258 | + "slug": "chemistry" |
259 | } |
260 | }, |
261 | { |
262 | @@ -196,7 +220,8 @@ |
263 | "model": "webcatalog.department", |
264 | "fields": { |
265 | "name": "Physics", |
266 | - "parent": 5 |
267 | + "parent": 5, |
268 | + "slug": "physics" |
269 | } |
270 | }, |
271 | { |
272 | @@ -204,7 +229,8 @@ |
273 | "model": "webcatalog.department", |
274 | "fields": { |
275 | "name": "Mail", |
276 | - "parent": 15 |
277 | + "parent": 15, |
278 | + "slug": "mail" |
279 | } |
280 | }, |
281 | { |
282 | @@ -212,7 +238,8 @@ |
283 | "model": "webcatalog.department", |
284 | "fields": { |
285 | "name": "Computing & Robotics", |
286 | - "parent": 5 |
287 | + "parent": 5, |
288 | + "slug": "computing-robotics" |
289 | } |
290 | }, |
291 | { |
292 | @@ -220,7 +247,8 @@ |
293 | "model": "webcatalog.department", |
294 | "fields": { |
295 | "name": "Web Development", |
296 | - "parent": 4 |
297 | + "parent": 4, |
298 | + "slug": "web-development" |
299 | } |
300 | }, |
301 | { |
302 | @@ -228,7 +256,8 @@ |
303 | "model": "webcatalog.department", |
304 | "fields": { |
305 | "name": "Graphic Interface Design", |
306 | - "parent": 4 |
307 | + "parent": 4, |
308 | + "slug": "graphic-interface-design" |
309 | } |
310 | }, |
311 | { |
312 | @@ -236,7 +265,8 @@ |
313 | "model": "webcatalog.department", |
314 | "fields": { |
315 | "name": "Version Control", |
316 | - "parent": 4 |
317 | + "parent": 4, |
318 | + "slug": "version-control" |
319 | } |
320 | }, |
321 | { |
322 | @@ -244,7 +274,8 @@ |
323 | "model": "webcatalog.department", |
324 | "fields": { |
325 | "name": "Photography", |
326 | - "parent": 13 |
327 | + "parent": 13, |
328 | + "slug": "photography" |
329 | } |
330 | }, |
331 | { |
332 | @@ -252,7 +283,8 @@ |
333 | "model": "webcatalog.department", |
334 | "fields": { |
335 | "name": "Astronomy", |
336 | - "parent": 5 |
337 | + "parent": 5, |
338 | + "slug": "astronomy" |
339 | } |
340 | }, |
341 | { |
342 | @@ -260,7 +292,8 @@ |
343 | "model": "webcatalog.department", |
344 | "fields": { |
345 | "name": "Universal Access", |
346 | - "parent": null |
347 | + "parent": null, |
348 | + "slug": "universal-access" |
349 | } |
350 | }, |
351 | { |
352 | @@ -268,7 +301,8 @@ |
353 | "model": "webcatalog.department", |
354 | "fields": { |
355 | "name": "Drawing", |
356 | - "parent": 13 |
357 | + "parent": 13, |
358 | + "slug": "drawing" |
359 | } |
360 | }, |
361 | { |
362 | @@ -276,7 +310,8 @@ |
363 | "model": "webcatalog.department", |
364 | "fields": { |
365 | "name": "Painting", |
366 | - "parent": 13 |
367 | + "parent": 13, |
368 | + "slug": "painting" |
369 | } |
370 | }, |
371 | { |
372 | @@ -284,7 +319,8 @@ |
373 | "model": "webcatalog.department", |
374 | "fields": { |
375 | "name": "Publishing", |
376 | - "parent": 13 |
377 | + "parent": 13, |
378 | + "slug": "publishing" |
379 | } |
380 | }, |
381 | { |
382 | @@ -292,7 +328,8 @@ |
383 | "model": "webcatalog.department", |
384 | "fields": { |
385 | "name": "Localization", |
386 | - "parent": 4 |
387 | + "parent": 4, |
388 | + "slug": "localization" |
389 | } |
390 | }, |
391 | { |
392 | @@ -300,7 +337,8 @@ |
393 | "model": "webcatalog.department", |
394 | "fields": { |
395 | "name": "Scanning & OCR", |
396 | - "parent": 13 |
397 | + "parent": 13, |
398 | + "slug": "scanning-ocr" |
399 | } |
400 | }, |
401 | { |
402 | @@ -308,7 +346,8 @@ |
403 | "model": "webcatalog.department", |
404 | "fields": { |
405 | "name": "Geology", |
406 | - "parent": 5 |
407 | + "parent": 5, |
408 | + "slug": "geology" |
409 | } |
410 | }, |
411 | { |
412 | @@ -316,7 +355,8 @@ |
413 | "model": "webcatalog.department", |
414 | "fields": { |
415 | "name": "Board Games", |
416 | - "parent": 1 |
417 | + "parent": 1, |
418 | + "slug": "board-games" |
419 | } |
420 | }, |
421 | { |
422 | @@ -324,7 +364,8 @@ |
423 | "model": "webcatalog.department", |
424 | "fields": { |
425 | "name": "Puzzles", |
426 | - "parent": 1 |
427 | + "parent": 1, |
428 | + "slug": "puzzles" |
429 | } |
430 | }, |
431 | { |
432 | @@ -332,7 +373,8 @@ |
433 | "model": "webcatalog.department", |
434 | "fields": { |
435 | "name": "File Sharing", |
436 | - "parent": 15 |
437 | + "parent": 15, |
438 | + "slug": "file-sharing" |
439 | } |
440 | }, |
441 | { |
442 | @@ -340,7 +382,8 @@ |
443 | "model": "webcatalog.department", |
444 | "fields": { |
445 | "name": "Sports", |
446 | - "parent": 1 |
447 | + "parent": 1, |
448 | + "slug": "sports" |
449 | } |
450 | }, |
451 | { |
452 | @@ -348,15 +391,17 @@ |
453 | "model": "webcatalog.department", |
454 | "fields": { |
455 | "name": "Card Games", |
456 | - "parent": 1 |
457 | + "parent": 1, |
458 | + "slug": "card-games" |
459 | } |
460 | - }, |
461 | + }, |
462 | { |
463 | "pk": 47, |
464 | "model": "webcatalog.department", |
465 | "fields": { |
466 | "name": "Fonts", |
467 | - "parent": null |
468 | + "parent": null, |
469 | + "slug": "fonts" |
470 | } |
471 | - } |
472 | -] |
473 | \ No newline at end of file |
474 | + } |
475 | +] |
476 | |
477 | === added file 'src/webcatalog/migrations/0010_add_department_slug.py' |
478 | --- src/webcatalog/migrations/0010_add_department_slug.py 1970-01-01 00:00:00 +0000 |
479 | +++ src/webcatalog/migrations/0010_add_department_slug.py 2012-03-12 15:48:24 +0000 |
480 | @@ -0,0 +1,154 @@ |
481 | +# encoding: utf-8 |
482 | +import datetime |
483 | +from south.db import db |
484 | +from south.v2 import SchemaMigration |
485 | +from django.db import models |
486 | + |
487 | +class Migration(SchemaMigration): |
488 | + |
489 | + def forwards(self, orm): |
490 | + |
491 | + # Adding field 'Department.slug' |
492 | + db.add_column('webcatalog_department', 'slug', self.gf('django.db.models.fields.SlugField')(max_length=50, null=True, db_index=True), keep_default=False) |
493 | + |
494 | + |
495 | + def backwards(self, orm): |
496 | + |
497 | + # Deleting field 'Department.slug' |
498 | + db.delete_column('webcatalog_department', 'slug') |
499 | + |
500 | + |
501 | + models = { |
502 | + 'auth.group': { |
503 | + 'Meta': {'object_name': 'Group'}, |
504 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
505 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
506 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
507 | + }, |
508 | + 'auth.permission': { |
509 | + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, |
510 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
511 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), |
512 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
513 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
514 | + }, |
515 | + 'auth.user': { |
516 | + 'Meta': {'object_name': 'User'}, |
517 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
518 | + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), |
519 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
520 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), |
521 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
522 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
523 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
524 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
525 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
526 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
527 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
528 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), |
529 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) |
530 | + }, |
531 | + 'contenttypes.contenttype': { |
532 | + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, |
533 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
534 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
535 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
536 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
537 | + }, |
538 | + 'webcatalog.application': { |
539 | + 'Meta': {'unique_together': "(('distroseries', 'archive_id'),)", 'object_name': 'Application'}, |
540 | + 'app_type': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), |
541 | + 'architectures': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
542 | + 'archive_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '64', 'null': 'True', 'blank': 'True'}), |
543 | + 'categories': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
544 | + 'channel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
545 | + 'comment': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
546 | + 'departments': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['webcatalog.Department']", 'symmetrical': 'False', 'blank': 'True'}), |
547 | + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
548 | + 'distroseries': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.DistroSeries']"}), |
549 | + 'for_purchase': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
550 | + 'icon': ('django.db.models.fields.files.ImageField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), |
551 | + 'icon_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
552 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
553 | + 'keywords': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
554 | + 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'blank': 'True'}), |
555 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
556 | + 'package_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
557 | + 'popcon': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
558 | + 'price': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '7', 'decimal_places': '2', 'blank': 'True'}), |
559 | + 'ratings_average': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '3', 'decimal_places': '2', 'blank': 'True'}), |
560 | + 'ratings_histogram': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), |
561 | + 'ratings_total': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
562 | + 'screenshot_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), |
563 | + 'section': ('django.db.models.fields.CharField', [], {'max_length': '32'}) |
564 | + }, |
565 | + 'webcatalog.consumer': { |
566 | + 'Meta': {'object_name': 'Consumer'}, |
567 | + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
568 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
569 | + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}), |
570 | + 'secret': ('django.db.models.fields.CharField', [], {'default': "'VXPcqxReXucklUvCpbyRIxgjjAOLNM'", 'max_length': '255', 'blank': 'True'}), |
571 | + 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), |
572 | + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'oauth_consumer'", 'unique': 'True', 'to': "orm['auth.User']"}) |
573 | + }, |
574 | + 'webcatalog.department': { |
575 | + 'Meta': {'object_name': 'Department'}, |
576 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
577 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), |
578 | + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Department']", 'null': 'True', 'blank': 'True'}), |
579 | + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'null': 'True', 'db_index': 'True'}) |
580 | + }, |
581 | + 'webcatalog.distroseries': { |
582 | + 'Meta': {'object_name': 'DistroSeries'}, |
583 | + 'code_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20', 'db_index': 'True'}), |
584 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
585 | + 'version': ('django.db.models.fields.CharField', [], {'max_length': '10', 'blank': 'True'}) |
586 | + }, |
587 | + 'webcatalog.exhibit': { |
588 | + 'Meta': {'object_name': 'Exhibit'}, |
589 | + 'banner_url': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), |
590 | + 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
591 | + 'display': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), |
592 | + 'distroseries': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['webcatalog.DistroSeries']", 'symmetrical': 'False'}), |
593 | + 'html': ('django.db.models.fields.TextField', [], {}), |
594 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
595 | + 'package_names': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), |
596 | + 'published': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
597 | + 'sca_id': ('django.db.models.fields.IntegerField', [], {}) |
598 | + }, |
599 | + 'webcatalog.machine': { |
600 | + 'Meta': {'unique_together': "(('owner', 'uuid'),)", 'object_name': 'Machine'}, |
601 | + 'hostname': ('django.db.models.fields.CharField', [], {'max_length': '64'}), |
602 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
603 | + 'logo_checksum': ('django.db.models.fields.CharField', [], {'max_length': '56', 'blank': 'True'}), |
604 | + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), |
605 | + 'package_list': ('django.db.models.fields.TextField', [], {}), |
606 | + 'packages_checksum': ('django.db.models.fields.CharField', [], {'max_length': '56'}), |
607 | + 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}) |
608 | + }, |
609 | + 'webcatalog.nonce': { |
610 | + 'Meta': {'object_name': 'Nonce'}, |
611 | + 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Consumer']"}), |
612 | + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
613 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
614 | + 'nonce': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), |
615 | + 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Token']"}) |
616 | + }, |
617 | + 'webcatalog.reviewstatsimport': { |
618 | + 'Meta': {'object_name': 'ReviewStatsImport'}, |
619 | + 'distroseries': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.DistroSeries']", 'unique': 'True'}), |
620 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
621 | + 'last_import': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'}) |
622 | + }, |
623 | + 'webcatalog.token': { |
624 | + 'Meta': {'object_name': 'Token'}, |
625 | + 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Consumer']"}), |
626 | + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
627 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
628 | + 'token': ('django.db.models.fields.CharField', [], {'default': "'lNTrEuIeWzyjJuFkpRDfxMjrRYTctAsxDmaYOnwasgDncZmAHa'", 'max_length': '50', 'primary_key': 'True'}), |
629 | + 'token_secret': ('django.db.models.fields.CharField', [], {'default': "'OWMLurVHqVwKOGfcvWTwWBLBUtQNfaTfIiHOhVdDFYPMdWorhu'", 'max_length': '50'}), |
630 | + 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) |
631 | + } |
632 | + } |
633 | + |
634 | + complete_apps = ['webcatalog'] |
635 | |
636 | === added file 'src/webcatalog/migrations/0011_populate_department_slug.py' |
637 | --- src/webcatalog/migrations/0011_populate_department_slug.py 1970-01-01 00:00:00 +0000 |
638 | +++ src/webcatalog/migrations/0011_populate_department_slug.py 2012-03-12 15:48:24 +0000 |
639 | @@ -0,0 +1,153 @@ |
640 | +# encoding: utf-8 |
641 | +import datetime |
642 | +from south.db import db |
643 | +from south.v2 import DataMigration |
644 | +from django.db import models |
645 | +from django.template.defaultfilters import slugify |
646 | + |
647 | +class Migration(DataMigration): |
648 | + |
649 | + def forwards(self, orm): |
650 | + for dept in orm.Department.objects.all(): |
651 | + dept.slug = slugify(dept.name) |
652 | + dept.save() |
653 | + |
654 | + |
655 | + def backwards(self, orm): |
656 | + pass |
657 | + |
658 | + |
659 | + models = { |
660 | + 'auth.group': { |
661 | + 'Meta': {'object_name': 'Group'}, |
662 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
663 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
664 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
665 | + }, |
666 | + 'auth.permission': { |
667 | + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, |
668 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
669 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), |
670 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
671 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
672 | + }, |
673 | + 'auth.user': { |
674 | + 'Meta': {'object_name': 'User'}, |
675 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
676 | + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), |
677 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
678 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), |
679 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
680 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
681 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
682 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
683 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
684 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
685 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
686 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), |
687 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) |
688 | + }, |
689 | + 'contenttypes.contenttype': { |
690 | + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, |
691 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
692 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
693 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
694 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
695 | + }, |
696 | + 'webcatalog.application': { |
697 | + 'Meta': {'unique_together': "(('distroseries', 'archive_id'),)", 'object_name': 'Application'}, |
698 | + 'app_type': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), |
699 | + 'architectures': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
700 | + 'archive_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '64', 'null': 'True', 'blank': 'True'}), |
701 | + 'categories': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
702 | + 'channel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
703 | + 'comment': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
704 | + 'departments': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['webcatalog.Department']", 'symmetrical': 'False', 'blank': 'True'}), |
705 | + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
706 | + 'distroseries': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.DistroSeries']"}), |
707 | + 'for_purchase': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
708 | + 'icon': ('django.db.models.fields.files.ImageField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), |
709 | + 'icon_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
710 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
711 | + 'keywords': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
712 | + 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'blank': 'True'}), |
713 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
714 | + 'package_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
715 | + 'popcon': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
716 | + 'price': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '7', 'decimal_places': '2', 'blank': 'True'}), |
717 | + 'ratings_average': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '3', 'decimal_places': '2', 'blank': 'True'}), |
718 | + 'ratings_histogram': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), |
719 | + 'ratings_total': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
720 | + 'screenshot_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), |
721 | + 'section': ('django.db.models.fields.CharField', [], {'max_length': '32'}) |
722 | + }, |
723 | + 'webcatalog.consumer': { |
724 | + 'Meta': {'object_name': 'Consumer'}, |
725 | + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
726 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
727 | + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}), |
728 | + 'secret': ('django.db.models.fields.CharField', [], {'default': "'eMxgHKWQFIAYuEMIIToVaDrRnKNzQb'", 'max_length': '255', 'blank': 'True'}), |
729 | + 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), |
730 | + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'oauth_consumer'", 'unique': 'True', 'to': "orm['auth.User']"}) |
731 | + }, |
732 | + 'webcatalog.department': { |
733 | + 'Meta': {'object_name': 'Department'}, |
734 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
735 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), |
736 | + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Department']", 'null': 'True', 'blank': 'True'}), |
737 | + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'null': 'True', 'db_index': 'True'}) |
738 | + }, |
739 | + 'webcatalog.distroseries': { |
740 | + 'Meta': {'object_name': 'DistroSeries'}, |
741 | + 'code_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20', 'db_index': 'True'}), |
742 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
743 | + 'version': ('django.db.models.fields.CharField', [], {'max_length': '10', 'blank': 'True'}) |
744 | + }, |
745 | + 'webcatalog.exhibit': { |
746 | + 'Meta': {'object_name': 'Exhibit'}, |
747 | + 'banner_url': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), |
748 | + 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
749 | + 'display': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), |
750 | + 'distroseries': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['webcatalog.DistroSeries']", 'symmetrical': 'False'}), |
751 | + 'html': ('django.db.models.fields.TextField', [], {}), |
752 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
753 | + 'package_names': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), |
754 | + 'published': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
755 | + 'sca_id': ('django.db.models.fields.IntegerField', [], {}) |
756 | + }, |
757 | + 'webcatalog.machine': { |
758 | + 'Meta': {'unique_together': "(('owner', 'uuid'),)", 'object_name': 'Machine'}, |
759 | + 'hostname': ('django.db.models.fields.CharField', [], {'max_length': '64'}), |
760 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
761 | + 'logo_checksum': ('django.db.models.fields.CharField', [], {'max_length': '56', 'blank': 'True'}), |
762 | + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), |
763 | + 'package_list': ('django.db.models.fields.TextField', [], {}), |
764 | + 'packages_checksum': ('django.db.models.fields.CharField', [], {'max_length': '56'}), |
765 | + 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}) |
766 | + }, |
767 | + 'webcatalog.nonce': { |
768 | + 'Meta': {'object_name': 'Nonce'}, |
769 | + 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Consumer']"}), |
770 | + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
771 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
772 | + 'nonce': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), |
773 | + 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Token']"}) |
774 | + }, |
775 | + 'webcatalog.reviewstatsimport': { |
776 | + 'Meta': {'object_name': 'ReviewStatsImport'}, |
777 | + 'distroseries': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.DistroSeries']", 'unique': 'True'}), |
778 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
779 | + 'last_import': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'}) |
780 | + }, |
781 | + 'webcatalog.token': { |
782 | + 'Meta': {'object_name': 'Token'}, |
783 | + 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Consumer']"}), |
784 | + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
785 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
786 | + 'token': ('django.db.models.fields.CharField', [], {'default': "'wIDOKFDyfTtXPceLzIWJZixhGXlKFwjSushOLHzrXEYlMtidGA'", 'max_length': '50', 'primary_key': 'True'}), |
787 | + 'token_secret': ('django.db.models.fields.CharField', [], {'default': "'aRrkCDLnsQJCJYgreczXsyLUQujPikUDOydAaQGLQpiqOsVeRp'", 'max_length': '50'}), |
788 | + 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) |
789 | + } |
790 | + } |
791 | + |
792 | + complete_apps = ['webcatalog'] |
793 | |
794 | === added file 'src/webcatalog/migrations/0012_unique_department_slug.py' |
795 | --- src/webcatalog/migrations/0012_unique_department_slug.py 1970-01-01 00:00:00 +0000 |
796 | +++ src/webcatalog/migrations/0012_unique_department_slug.py 2012-03-12 15:48:24 +0000 |
797 | @@ -0,0 +1,160 @@ |
798 | +# encoding: utf-8 |
799 | +import datetime |
800 | +from south.db import db |
801 | +from south.v2 import SchemaMigration |
802 | +from django.db import models |
803 | + |
804 | +class Migration(SchemaMigration): |
805 | + |
806 | + def forwards(self, orm): |
807 | + |
808 | + # Changing field 'Department.slug' |
809 | + db.alter_column('webcatalog_department', 'slug', self.gf('django.db.models.fields.SlugField')(default='', unique=True, max_length=50)) |
810 | + |
811 | + # Adding unique constraint on 'Department', fields ['slug'] |
812 | + db.create_unique('webcatalog_department', ['slug']) |
813 | + |
814 | + |
815 | + def backwards(self, orm): |
816 | + |
817 | + # Removing unique constraint on 'Department', fields ['slug'] |
818 | + db.delete_unique('webcatalog_department', ['slug']) |
819 | + |
820 | + # Changing field 'Department.slug' |
821 | + db.alter_column('webcatalog_department', 'slug', self.gf('django.db.models.fields.SlugField')(max_length=50, null=True)) |
822 | + |
823 | + |
824 | + models = { |
825 | + 'auth.group': { |
826 | + 'Meta': {'object_name': 'Group'}, |
827 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
828 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
829 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
830 | + }, |
831 | + 'auth.permission': { |
832 | + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, |
833 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
834 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), |
835 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
836 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
837 | + }, |
838 | + 'auth.user': { |
839 | + 'Meta': {'object_name': 'User'}, |
840 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
841 | + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), |
842 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
843 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), |
844 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
845 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
846 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
847 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
848 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
849 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
850 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
851 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), |
852 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) |
853 | + }, |
854 | + 'contenttypes.contenttype': { |
855 | + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, |
856 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
857 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
858 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
859 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
860 | + }, |
861 | + 'webcatalog.application': { |
862 | + 'Meta': {'unique_together': "(('distroseries', 'archive_id'),)", 'object_name': 'Application'}, |
863 | + 'app_type': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), |
864 | + 'architectures': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
865 | + 'archive_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '64', 'null': 'True', 'blank': 'True'}), |
866 | + 'categories': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
867 | + 'channel': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
868 | + 'comment': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
869 | + 'departments': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['webcatalog.Department']", 'symmetrical': 'False', 'blank': 'True'}), |
870 | + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
871 | + 'distroseries': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.DistroSeries']"}), |
872 | + 'for_purchase': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
873 | + 'icon': ('django.db.models.fields.files.ImageField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), |
874 | + 'icon_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
875 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
876 | + 'keywords': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
877 | + 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'blank': 'True'}), |
878 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
879 | + 'package_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
880 | + 'popcon': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
881 | + 'price': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '7', 'decimal_places': '2', 'blank': 'True'}), |
882 | + 'ratings_average': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '3', 'decimal_places': '2', 'blank': 'True'}), |
883 | + 'ratings_histogram': ('django.db.models.fields.CharField', [], {'max_length': '128', 'blank': 'True'}), |
884 | + 'ratings_total': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
885 | + 'screenshot_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), |
886 | + 'section': ('django.db.models.fields.CharField', [], {'max_length': '32'}) |
887 | + }, |
888 | + 'webcatalog.consumer': { |
889 | + 'Meta': {'object_name': 'Consumer'}, |
890 | + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
891 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
892 | + 'key': ('django.db.models.fields.CharField', [], {'max_length': '64'}), |
893 | + 'secret': ('django.db.models.fields.CharField', [], {'default': "'vSCKgfkvLgUIVdNWrMGYJQlttWOpbo'", 'max_length': '255', 'blank': 'True'}), |
894 | + 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), |
895 | + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'oauth_consumer'", 'unique': 'True', 'to': "orm['auth.User']"}) |
896 | + }, |
897 | + 'webcatalog.department': { |
898 | + 'Meta': {'object_name': 'Department'}, |
899 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
900 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), |
901 | + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Department']", 'null': 'True', 'blank': 'True'}), |
902 | + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'}) |
903 | + }, |
904 | + 'webcatalog.distroseries': { |
905 | + 'Meta': {'object_name': 'DistroSeries'}, |
906 | + 'code_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20', 'db_index': 'True'}), |
907 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
908 | + 'version': ('django.db.models.fields.CharField', [], {'max_length': '10', 'blank': 'True'}) |
909 | + }, |
910 | + 'webcatalog.exhibit': { |
911 | + 'Meta': {'object_name': 'Exhibit'}, |
912 | + 'banner_url': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), |
913 | + 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
914 | + 'display': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), |
915 | + 'distroseries': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['webcatalog.DistroSeries']", 'symmetrical': 'False'}), |
916 | + 'html': ('django.db.models.fields.TextField', [], {}), |
917 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
918 | + 'package_names': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), |
919 | + 'published': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
920 | + 'sca_id': ('django.db.models.fields.IntegerField', [], {}) |
921 | + }, |
922 | + 'webcatalog.machine': { |
923 | + 'Meta': {'unique_together': "(('owner', 'uuid'),)", 'object_name': 'Machine'}, |
924 | + 'hostname': ('django.db.models.fields.CharField', [], {'max_length': '64'}), |
925 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
926 | + 'logo_checksum': ('django.db.models.fields.CharField', [], {'max_length': '56', 'blank': 'True'}), |
927 | + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), |
928 | + 'package_list': ('django.db.models.fields.TextField', [], {}), |
929 | + 'packages_checksum': ('django.db.models.fields.CharField', [], {'max_length': '56'}), |
930 | + 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}) |
931 | + }, |
932 | + 'webcatalog.nonce': { |
933 | + 'Meta': {'object_name': 'Nonce'}, |
934 | + 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Consumer']"}), |
935 | + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
936 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
937 | + 'nonce': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), |
938 | + 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Token']"}) |
939 | + }, |
940 | + 'webcatalog.reviewstatsimport': { |
941 | + 'Meta': {'object_name': 'ReviewStatsImport'}, |
942 | + 'distroseries': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.DistroSeries']", 'unique': 'True'}), |
943 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
944 | + 'last_import': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'}) |
945 | + }, |
946 | + 'webcatalog.token': { |
947 | + 'Meta': {'object_name': 'Token'}, |
948 | + 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['webcatalog.Consumer']"}), |
949 | + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
950 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
951 | + 'token': ('django.db.models.fields.CharField', [], {'default': "'POzAvRZABjUIXjSjeDWxcsWSfBIGbCtkcVkVaQmMELqOqUitPR'", 'max_length': '50', 'primary_key': 'True'}), |
952 | + 'token_secret': ('django.db.models.fields.CharField', [], {'default': "'smrslTHrGlbKfWTzEPNyEIJNPKYmyZKzIValymhyldsXRqdeez'", 'max_length': '50'}), |
953 | + 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) |
954 | + } |
955 | + } |
956 | + |
957 | + complete_apps = ['webcatalog'] |
958 | |
959 | === modified file 'src/webcatalog/models/applications.py' |
960 | --- src/webcatalog/models/applications.py 2012-03-08 18:00:09 +0000 |
961 | +++ src/webcatalog/models/applications.py 2012-03-12 15:48:24 +0000 |
962 | @@ -29,6 +29,7 @@ |
963 | from django.contrib.auth.models import User |
964 | from django.core.urlresolvers import reverse |
965 | from django.db import models |
966 | +from django.template.defaultfilters import slugify |
967 | |
968 | from webcatalog.department_filters import department_filters |
969 | from webcatalog.managers import ApplicationManager |
970 | @@ -127,7 +128,7 @@ |
971 | for dept_filter in dept_filters: |
972 | if dept_filter(self): |
973 | dept, created = Department.objects.get_or_create( |
974 | - name=dept_name) |
975 | + name=dept_name, defaults={'slug': slugify(dept_name)}) |
976 | if created: |
977 | logging.warn("Department %s automatically created!" % |
978 | dept_name) |
979 | @@ -162,24 +163,20 @@ |
980 | class Department(models.Model): |
981 | parent = models.ForeignKey('self', blank=True, null=True) |
982 | name = models.CharField(max_length=64) |
983 | + slug = models.SlugField(unique=True) |
984 | |
985 | def __unicode__(self): |
986 | return self.name |
987 | |
988 | - @property |
989 | - def normalized_name(self): |
990 | - return re.sub(r'[-&.,;" \']', '', self.name) |
991 | - |
992 | def crumbs(self, distro=None): |
993 | if self.parent: |
994 | crumbs = self.parent.crumbs(distro=distro) |
995 | else: |
996 | crumbs = [{'name': 'Get Software', 'url': reverse('wc-index')}] |
997 | - args = [] |
998 | + kwargs = {'dept_slug_or_id': self.slug} |
999 | if distro is not None: |
1000 | - args = [distro] |
1001 | - args.append(self.id) |
1002 | - url = reverse('wc-department', args=args) |
1003 | + kwargs['distro'] = distro |
1004 | + url = reverse('wc-department', kwargs=kwargs) |
1005 | crumbs.append({'name': self.name, 'url': url}) |
1006 | return crumbs |
1007 | |
1008 | |
1009 | === modified file 'src/webcatalog/static/css/carousel.css' |
1010 | --- src/webcatalog/static/css/carousel.css 2012-03-08 18:00:09 +0000 |
1011 | +++ src/webcatalog/static/css/carousel.css 2012-03-12 15:48:24 +0000 |
1012 | @@ -133,6 +133,11 @@ |
1013 | padding: 0 10px; |
1014 | width: 202px; |
1015 | } |
1016 | + |
1017 | +#content .featured-widget table td.lastrow { |
1018 | + border-right: 0; |
1019 | +} |
1020 | + |
1021 | #featured-carousel .pagination li a.active { |
1022 | background-color: #333; |
1023 | } |
1024 | |
1025 | === renamed file 'src/webcatalog/static/images/dept_icons/3D.png' => 'src/webcatalog/static/images/dept_icons/3d.png' |
1026 | === renamed file 'src/webcatalog/static/images/dept_icons/Accessories.png' => 'src/webcatalog/static/images/dept_icons/accessories.png' |
1027 | === renamed file 'src/webcatalog/static/images/dept_icons/Astronomy.png' => 'src/webcatalog/static/images/dept_icons/astronomy.png' |
1028 | === renamed file 'src/webcatalog/static/images/dept_icons/Biology.png' => 'src/webcatalog/static/images/dept_icons/biology.png' |
1029 | === renamed file 'src/webcatalog/static/images/dept_icons/BoardGames.png' => 'src/webcatalog/static/images/dept_icons/board-games.png' |
1030 | === renamed file 'src/webcatalog/static/images/dept_icons/CardGames.png' => 'src/webcatalog/static/images/dept_icons/card-games.png' |
1031 | === renamed file 'src/webcatalog/static/images/dept_icons/Chat.png' => 'src/webcatalog/static/images/dept_icons/chat.png' |
1032 | === renamed file 'src/webcatalog/static/images/dept_icons/Chemistry.png' => 'src/webcatalog/static/images/dept_icons/chemistry.png' |
1033 | === renamed file 'src/webcatalog/static/images/dept_icons/ComputingRobotics.png' => 'src/webcatalog/static/images/dept_icons/computing-robotics.png' |
1034 | === renamed file 'src/webcatalog/static/images/dept_icons/Debugging.png' => 'src/webcatalog/static/images/dept_icons/debugging.png' |
1035 | === renamed file 'src/webcatalog/static/images/dept_icons/DeveloperTools.png' => 'src/webcatalog/static/images/dept_icons/developer-tools.png' |
1036 | === renamed file 'src/webcatalog/static/images/dept_icons/Drawing.png' => 'src/webcatalog/static/images/dept_icons/drawing.png' |
1037 | === renamed file 'src/webcatalog/static/images/dept_icons/Education.png' => 'src/webcatalog/static/images/dept_icons/education.png' |
1038 | === renamed file 'src/webcatalog/static/images/dept_icons/Electronics.png' => 'src/webcatalog/static/images/dept_icons/electronics.png' |
1039 | === renamed file 'src/webcatalog/static/images/dept_icons/Engineering.png' => 'src/webcatalog/static/images/dept_icons/engineering.png' |
1040 | === renamed file 'src/webcatalog/static/images/dept_icons/FileSharing.png' => 'src/webcatalog/static/images/dept_icons/file-sharing.png' |
1041 | === renamed file 'src/webcatalog/static/images/dept_icons/Fonts.png' => 'src/webcatalog/static/images/dept_icons/fonts.png' |
1042 | === renamed file 'src/webcatalog/static/images/dept_icons/Games.png' => 'src/webcatalog/static/images/dept_icons/games.png' |
1043 | === renamed file 'src/webcatalog/static/images/dept_icons/Geography.png' => 'src/webcatalog/static/images/dept_icons/geography.png' |
1044 | === renamed file 'src/webcatalog/static/images/dept_icons/Geology.png' => 'src/webcatalog/static/images/dept_icons/geology.png' |
1045 | === renamed file 'src/webcatalog/static/images/dept_icons/GraphicInterfaceDesign.png' => 'src/webcatalog/static/images/dept_icons/graphic-interface-design.png' |
1046 | === renamed file 'src/webcatalog/static/images/dept_icons/Graphics.png' => 'src/webcatalog/static/images/dept_icons/graphics.png' |
1047 | === renamed file 'src/webcatalog/static/images/dept_icons/IDEs.png' => 'src/webcatalog/static/images/dept_icons/ides.png' |
1048 | === renamed file 'src/webcatalog/static/images/dept_icons/Internet.png' => 'src/webcatalog/static/images/dept_icons/internet.png' |
1049 | === renamed file 'src/webcatalog/static/images/dept_icons/Localization.png' => 'src/webcatalog/static/images/dept_icons/localization.png' |
1050 | === renamed file 'src/webcatalog/static/images/dept_icons/Mail.png' => 'src/webcatalog/static/images/dept_icons/mail.png' |
1051 | === renamed file 'src/webcatalog/static/images/dept_icons/Mathematics.png' => 'src/webcatalog/static/images/dept_icons/mathematics.png' |
1052 | === renamed file 'src/webcatalog/static/images/dept_icons/Office.png' => 'src/webcatalog/static/images/dept_icons/office.png' |
1053 | === renamed file 'src/webcatalog/static/images/dept_icons/Painting.png' => 'src/webcatalog/static/images/dept_icons/painting.png' |
1054 | === renamed file 'src/webcatalog/static/images/dept_icons/Photography.png' => 'src/webcatalog/static/images/dept_icons/photography.png' |
1055 | === renamed file 'src/webcatalog/static/images/dept_icons/Physics.png' => 'src/webcatalog/static/images/dept_icons/physics.png' |
1056 | === renamed file 'src/webcatalog/static/images/dept_icons/Profiling.png' => 'src/webcatalog/static/images/dept_icons/profiling.png' |
1057 | === renamed file 'src/webcatalog/static/images/dept_icons/Publishing.png' => 'src/webcatalog/static/images/dept_icons/publishing.png' |
1058 | === renamed file 'src/webcatalog/static/images/dept_icons/Puzzles.png' => 'src/webcatalog/static/images/dept_icons/puzzles.png' |
1059 | === renamed file 'src/webcatalog/static/images/dept_icons/RolePlaying.png' => 'src/webcatalog/static/images/dept_icons/role-playing.png' |
1060 | === renamed file 'src/webcatalog/static/images/dept_icons/ScanningOCR.png' => 'src/webcatalog/static/images/dept_icons/scanning-ocr.png' |
1061 | === renamed file 'src/webcatalog/static/images/dept_icons/ScienceEngineering.png' => 'src/webcatalog/static/images/dept_icons/science-engineering.png' |
1062 | === renamed file 'src/webcatalog/static/images/dept_icons/SoundVideo.png' => 'src/webcatalog/static/images/dept_icons/sound-video.png' |
1063 | === renamed file 'src/webcatalog/static/images/dept_icons/Sports.png' => 'src/webcatalog/static/images/dept_icons/sports.png' |
1064 | === renamed file 'src/webcatalog/static/images/dept_icons/ThemesTweaks.png' => 'src/webcatalog/static/images/dept_icons/themes-tweaks.png' |
1065 | === renamed file 'src/webcatalog/static/images/dept_icons/UniversalAccess.png' => 'src/webcatalog/static/images/dept_icons/universal-access.png' |
1066 | === renamed file 'src/webcatalog/static/images/dept_icons/VersionControl.png' => 'src/webcatalog/static/images/dept_icons/version-control.png' |
1067 | === renamed file 'src/webcatalog/static/images/dept_icons/Viewers.png' => 'src/webcatalog/static/images/dept_icons/viewers.png' |
1068 | === renamed file 'src/webcatalog/static/images/dept_icons/WebBrowsers.png' => 'src/webcatalog/static/images/dept_icons/web-browsers.png' |
1069 | === renamed file 'src/webcatalog/static/images/dept_icons/WebDevelopment.png' => 'src/webcatalog/static/images/dept_icons/web-development.png' |
1070 | === modified file 'src/webcatalog/templates/webcatalog/department_overview.html' |
1071 | --- src/webcatalog/templates/webcatalog/department_overview.html 2011-09-12 18:37:16 +0000 |
1072 | +++ src/webcatalog/templates/webcatalog/department_overview.html 2012-03-12 15:48:24 +0000 |
1073 | @@ -16,7 +16,7 @@ |
1074 | {% ifequal ds.code_name distroseries %} |
1075 | <span>Ubuntu {{ ds.version }} ({{ ds.code_name }})</span> |
1076 | {% else %} |
1077 | - <a href="{% url wc-department ds.code_name dept.id %}">Ubuntu {{ ds.version }} ({{ ds.code_name }})</a> |
1078 | + <a href="{% url wc-department distro=ds.code_name dept_slug_or_id=dept.slug %}">Ubuntu {{ ds.version }} ({{ ds.code_name }})</a> |
1079 | {% endifequal %}</li> |
1080 | {% endfor %} |
1081 | </ul> |
1082 | |
1083 | === modified file 'src/webcatalog/templates/webcatalog/department_overview_snippet.html' |
1084 | --- src/webcatalog/templates/webcatalog/department_overview_snippet.html 2011-04-20 19:41:44 +0000 |
1085 | +++ src/webcatalog/templates/webcatalog/department_overview_snippet.html 2012-03-12 15:48:24 +0000 |
1086 | @@ -1,8 +1,8 @@ |
1087 | <div class="department"> |
1088 | <div class="dept-icon"> |
1089 | - <a href="{% url wc-department dept.id %}"> |
1090 | + <a href="{% url wc-department dept_slug_or_id=dept.slug %}"> |
1091 | <img width="48" height="48" |
1092 | - src="{{ STATIC_URL }}images/dept_icons/{{ dept.normalized_name }}.png"/> |
1093 | + src="{{ STATIC_URL }}images/dept_icons/{{ dept.slug }}.png"/> |
1094 | </a></div> |
1095 | - <h3><a href="{% url wc-department dept.id %}">{{ dept.name }}</a></h3> |
1096 | + <h3><a href="{% url wc-department dept_slug_or_id=dept.slug %}">{{ dept.name }}</a></h3> |
1097 | </div> |
1098 | |
1099 | === modified file 'src/webcatalog/templates/webcatalog/featured_apps_widget.html' |
1100 | --- src/webcatalog/templates/webcatalog/featured_apps_widget.html 2012-03-08 18:21:07 +0000 |
1101 | +++ src/webcatalog/templates/webcatalog/featured_apps_widget.html 2012-03-12 15:48:24 +0000 |
1102 | @@ -12,9 +12,9 @@ |
1103 | Javascript. |
1104 | {% endcomment %} |
1105 | <li class="slide{% if forloop.counter > 1%} disabled{% endif %}"> |
1106 | - <table><tr> |
1107 | + <table class="apps"><tr> |
1108 | {% for app in featured_apps %} |
1109 | - <td> |
1110 | + <td{% if forloop.counter|divisibleby:4 %} class="lastrow"{% endif %}> |
1111 | {% if app.icon %} |
1112 | <img class="icon64" src="{{ app.icon.url }}"/> |
1113 | {% else %} |
1114 | @@ -24,11 +24,11 @@ |
1115 | <p>{{ app.departments.all.0.name }} | <b>{% if app.price %}${{ app.price }}{% else %}FREE{% endif %}</b></p> |
1116 | <p>{{ app.comment }}</p> |
1117 | </td> |
1118 | - {% if forloop.counter|divisibleby:4 %} |
1119 | + {% if forloop.counter|divisibleby:4 and not forloop.last %} |
1120 | </tr></table> |
1121 | </li> |
1122 | <li class="slide{% if forloop.counter > 1%} disabled{% endif %}"> |
1123 | - <table><tr> |
1124 | + <table class="apps"><tr> |
1125 | {% endif %} |
1126 | {% endfor %} |
1127 | </tr></table> |
1128 | |
1129 | === modified file 'src/webcatalog/tests/factory.py' |
1130 | --- src/webcatalog/tests/factory.py 2012-03-01 21:38:31 +0000 |
1131 | +++ src/webcatalog/tests/factory.py 2012-03-12 15:48:24 +0000 |
1132 | @@ -112,8 +112,10 @@ |
1133 | ratings_average=ratings_average, ratings_total=ratings_total, |
1134 | ratings_histogram=ratings_histogram, screenshot_url=screenshot_url) |
1135 | |
1136 | - def make_department(self, name, parent=None): |
1137 | - return Department.objects.create(name=name, parent=parent) |
1138 | + def make_department(self, name, parent=None, slug=None): |
1139 | + if slug is None: |
1140 | + slug = self.get_unique_string(prefix='slug-') |
1141 | + return Department.objects.create(name=name, parent=parent, slug=slug) |
1142 | |
1143 | def make_exhibit(self, package_names=None, published=True, display=None, |
1144 | distroseries=None): |
1145 | |
1146 | === modified file 'src/webcatalog/tests/test_models.py' |
1147 | --- src/webcatalog/tests/test_models.py 2012-03-01 21:38:31 +0000 |
1148 | +++ src/webcatalog/tests/test_models.py 2012-03-12 15:48:24 +0000 |
1149 | @@ -25,7 +25,7 @@ |
1150 | from django.core.urlresolvers import reverse |
1151 | |
1152 | from webcatalog.tests.factory import TestCaseWithFactory |
1153 | -from webcatalog.models import Application |
1154 | +from webcatalog.models import Department |
1155 | |
1156 | __metaclass__ = type |
1157 | __all__ = [ |
1158 | @@ -78,7 +78,8 @@ |
1159 | dept = app.departments.get() |
1160 | expected = [{'name': 'Get Software', 'url': reverse('wc-index')}, |
1161 | {'name': dept.name, 'url': reverse('wc-department', |
1162 | - args=[app.distroseries.code_name, dept.id])}, |
1163 | + kwargs={'distro': app.distroseries.code_name, |
1164 | + 'dept_slug_or_id': dept.slug})}, |
1165 | {'name': app.name, 'url': reverse('wc-package-detail', |
1166 | args=[app.distroseries.code_name, app.package_name])}] |
1167 | |
1168 | @@ -91,33 +92,23 @@ |
1169 | dept = app.departments.get() |
1170 | expected = [{'name': 'Get Software', 'url': reverse('wc-index')}, |
1171 | {'name': dept.parent.name, 'url': reverse('wc-department', |
1172 | - args=[app.distroseries.code_name, dept.parent.id])}, |
1173 | + kwargs={'distro': app.distroseries.code_name, |
1174 | + 'dept_slug_or_id': dept.parent.slug})}, |
1175 | {'name': dept.name, 'url': reverse('wc-department', |
1176 | - args=[app.distroseries.code_name, dept.id])}, |
1177 | + kwargs={'distro': app.distroseries.code_name, |
1178 | + 'dept_slug_or_id': dept.slug})}, |
1179 | {'name': app.name, 'url': reverse('wc-package-detail', |
1180 | args=[app.distroseries.code_name, app.package_name])}] |
1181 | |
1182 | self.assertEquals(expected, app.crumbs()) |
1183 | |
1184 | |
1185 | -def DepartmentTestCase(TestCaseWithFactory): |
1186 | - def test_normalized_name(self): |
1187 | - cases = { |
1188 | - 'Foo': 'Foo', |
1189 | - 'Foo & Bar': 'FooBar', |
1190 | - ' Foo, Bar ': 'FooBar', |
1191 | - ' && , ': '', |
1192 | - '': '' |
1193 | - } |
1194 | - for case, expected in cases.items(): |
1195 | - dept = self.factory.make_department(case) |
1196 | - self.assertEquals(dept.normalized_name, expected) |
1197 | - |
1198 | +class DepartmentTestCase(TestCaseWithFactory): |
1199 | def test_crumbs_no_parent(self): |
1200 | dept = self.factory.make_department('Foo') |
1201 | expected = [{'name': 'Get Software', 'url': reverse('wc-index')}, |
1202 | {'name': dept.name, 'url': reverse('wc-department', |
1203 | - args=[dept.id])}] |
1204 | + kwargs={'dept_slug_or_id': dept.slug})}] |
1205 | |
1206 | self.assertEquals(expected, dept.crumbs()) |
1207 | |
1208 | @@ -126,9 +117,9 @@ |
1209 | dept = self.factory.make_department('Bar', parent=parent) |
1210 | expected = [{'name': 'Get Software', 'url': reverse('wc-index')}, |
1211 | {'name': dept.parent.name, 'url': reverse('wc-department', |
1212 | - args=[dept.parent.id])}, |
1213 | + kwargs={'dept_slug_or_id': dept.parent.slug})}, |
1214 | {'name': dept.name, 'url': reverse('wc-department', |
1215 | - args=[dept.id])}] |
1216 | + kwargs={'dept_slug_or_id': dept.slug})}] |
1217 | |
1218 | self.assertEquals(expected, dept.crumbs()) |
1219 | |
1220 | @@ -137,9 +128,11 @@ |
1221 | dept = self.factory.make_department('Bar', parent=parent) |
1222 | expected = [{'name': 'Get Software', 'url': reverse('wc-index')}, |
1223 | {'name': dept.parent.name, 'url': reverse('wc-department', |
1224 | - args=['frobbly', dept.parent.id])}, |
1225 | + kwargs={'distro': 'frobbly', |
1226 | + 'dept_slug_or_id': dept.parent.slug})}, |
1227 | {'name': dept.name, 'url': reverse('wc-department', |
1228 | - args=['frobbly', dept.id])}] |
1229 | + kwargs={'distro': 'frobbly', |
1230 | + 'dept_slug_or_id': dept.slug})}] |
1231 | |
1232 | self.assertEquals(expected, dept.crumbs(distro='frobbly')) |
1233 | |
1234 | |
1235 | === modified file 'src/webcatalog/tests/test_views.py' |
1236 | --- src/webcatalog/tests/test_views.py 2012-03-12 09:12:56 +0000 |
1237 | +++ src/webcatalog/tests/test_views.py 2012-03-12 15:48:24 +0000 |
1238 | @@ -489,7 +489,7 @@ |
1239 | response = self.client.get(reverse('wc-index')) |
1240 | |
1241 | self.assertContains(response, reverse('wc-department', |
1242 | - args=[dept.id])) |
1243 | + kwargs={'dept_slug_or_id': dept.slug})) |
1244 | |
1245 | def test_exhibits_widget_doesnt_display_if_no_exhibits_published(self): |
1246 | self.factory.make_exhibit(published=False) |
1247 | @@ -524,6 +524,15 @@ |
1248 | |
1249 | self.assertEqual([app], response.context[0]['featured_apps']) |
1250 | |
1251 | + def test_featured_apps_doesnt_show_blank_slides(self): |
1252 | + # Make 4 apps to fill exactly one slide |
1253 | + apps = [self.factory.make_application() for x in range(4)] |
1254 | + |
1255 | + with patch_settings(FEATURED_APPS=[app.package_name for app in apps]): |
1256 | + response = self.client.get(reverse('wc-index')) |
1257 | + |
1258 | + self.assertContains(response, '<table class="apps">', 1) |
1259 | + |
1260 | def test_link_to_dev_site(self): |
1261 | response = self.client.get(reverse('wc-index')) |
1262 | |
1263 | @@ -539,19 +548,19 @@ |
1264 | subdept = self.factory.make_department('bar', parent=dept) |
1265 | distro = self.factory.make_distroseries(code_name='lucid') |
1266 | |
1267 | - response = self.client.get(reverse('wc-department', args=[ |
1268 | - 'lucid', dept.id])) |
1269 | + response = self.client.get(reverse('wc-department', kwargs={ |
1270 | + 'distro': 'lucid', 'dept_slug_or_id': dept.id})) |
1271 | |
1272 | self.assertContains(response, reverse('wc-department', |
1273 | - args=[subdept.id])) |
1274 | + kwargs={'dept_slug_or_id': subdept.slug})) |
1275 | |
1276 | def test_department_contains_links_to_apps(self): |
1277 | app = self.factory.make_application(package_name='foo') |
1278 | dept = self.factory.make_department('bar') |
1279 | app.departments.add(dept) |
1280 | |
1281 | - response = self.client.get(reverse('wc-department', args=[ |
1282 | - app.distroseries.code_name, dept.id])) |
1283 | + response = self.client.get(reverse('wc-department', kwargs={ |
1284 | + 'distro': app.distroseries.code_name, 'dept_slug_or_id': dept.id})) |
1285 | |
1286 | self.assertContains(response, reverse('wc-package-detail', |
1287 | args=[app.distroseries.code_name, app.package_name]), count=2) |
1288 | @@ -560,8 +569,8 @@ |
1289 | dept = self.factory.make_department('bar') |
1290 | distro = self.factory.make_distroseries(code_name='lucid') |
1291 | |
1292 | - response = self.client.get(reverse('wc-department', args=[ |
1293 | - 'lucid', dept.id])) |
1294 | + response = self.client.get(reverse('wc-department', kwargs={ |
1295 | + 'distro': 'lucid', 'dept_slug_or_id': dept.id})) |
1296 | |
1297 | self.assertNotContains(response, 'Subsections') |
1298 | |
1299 | @@ -572,7 +581,8 @@ |
1300 | app = self.factory.make_application(distroseries=lucid) |
1301 | app.departments.add(dept) |
1302 | |
1303 | - url = reverse('wc-department', args=['lucid', dept.id]) |
1304 | + url = reverse('wc-department', |
1305 | + kwargs={'distro': 'lucid', 'dept_slug_or_id': dept.id}) |
1306 | with patch_settings(PAGE_BATCH_SIZE=2): |
1307 | response = self.client.get(url) |
1308 | |
1309 | @@ -589,7 +599,8 @@ |
1310 | def test_invalid_page_doesnt_error(self): |
1311 | dept = self.factory.make_department('bar') |
1312 | distro = self.factory.make_distroseries(code_name='lucid') |
1313 | - url = reverse('wc-department', args=['lucid', dept.id]) |
1314 | + url = reverse('wc-department', |
1315 | + kwargs={'distro': 'lucid', 'dept_slug_or_id': dept.id}) |
1316 | response = self.client.get(url, data={'page': 'aef8'}) |
1317 | page = response.context['page'] |
1318 | self.assertEqual(1, page.number) |
1319 | @@ -611,11 +622,15 @@ |
1320 | maverick = self.factory.make_distroseries(code_name='maverick', |
1321 | version='10.10') |
1322 | |
1323 | - response = self.client.get(reverse('wc-department', args=[ |
1324 | - settings.DEFAULT_DISTRO, dept.id])) |
1325 | + response = self.client.get(reverse('wc-department', |
1326 | + kwargs={ |
1327 | + 'distro': settings.DEFAULT_DISTRO, |
1328 | + 'dept_slug_or_id': dept.id |
1329 | + })) |
1330 | |
1331 | for ds in ['lucid', 'maverick']: |
1332 | - url = reverse('wc-department', args=[ds, dept.id]) |
1333 | + url = reverse('wc-department', |
1334 | + kwargs={'distro': ds, 'dept_slug_or_id': dept.slug}) |
1335 | self.assertContains(response, '<a href="{0}">Ubuntu'.format(url)) |
1336 | |
1337 | def test_department_includes_rating_summary(self): |
1338 | @@ -625,7 +640,8 @@ |
1339 | ratings_average=Decimal('3.5'), ratings_total=23) |
1340 | app.departments.add(dept) |
1341 | |
1342 | - url = reverse('wc-department', args=['lucid', dept.id]) |
1343 | + url = reverse('wc-department', |
1344 | + kwargs={'distro': 'lucid', 'dept_slug_or_id': dept.id}) |
1345 | response = self.client.get(url) |
1346 | |
1347 | self.assertContains(response, 'images/star-small-1.png', 3) |
1348 | @@ -634,8 +650,8 @@ |
1349 | def test_invalid_distroseries_returns_404(self): |
1350 | dept = self.factory.make_department('bar') |
1351 | |
1352 | - response = self.client.get(reverse('wc-department', args=[ |
1353 | - 'amnesiac', dept.id])) |
1354 | + response = self.client.get(reverse('wc-department', |
1355 | + kwargs={'distro': 'amnesiac', 'dept_slug_or_id': dept.id})) |
1356 | |
1357 | self.assertEqual(404, response.status_code) |
1358 | |
1359 | @@ -648,15 +664,29 @@ |
1360 | maverick = self.factory.make_distroseries(code_name='maverick', |
1361 | version='10.10') |
1362 | |
1363 | - response = self.client.get(reverse('wc-department', args=[ |
1364 | - settings.DEFAULT_DISTRO, dept.id])) |
1365 | + response = self.client.get(reverse('wc-department', |
1366 | + kwargs={ |
1367 | + 'distro': settings.DEFAULT_DISTRO, |
1368 | + 'dept_slug_or_id': dept.id |
1369 | + })) |
1370 | |
1371 | - url = reverse('wc-department', args=['lucid', dept.id]) |
1372 | + url = reverse('wc-department', |
1373 | + kwargs={'distro': 'lucid', 'dept_slug_or_id': dept.slug}) |
1374 | lucid_pos = response.content.find('<a href="{0}">Ubuntu'.format(url)) |
1375 | - url = reverse('wc-department', args=['maverick', dept.id]) |
1376 | + url = reverse('wc-department', |
1377 | + kwargs={'distro': 'maverick', 'dept_slug_or_id': dept.slug}) |
1378 | maver_pos = response.content.find('<a href="{0}">Ubuntu'.format(url)) |
1379 | self.assertTrue(lucid_pos > maver_pos) |
1380 | |
1381 | + def test_get_department_by_slug(self): |
1382 | + dept = self.factory.make_department('bar') |
1383 | + self.factory.make_distroseries(code_name='lucid') |
1384 | + |
1385 | + response = self.client.get(reverse('wc-department', kwargs={ |
1386 | + 'distro': 'lucid', 'dept_slug_or_id': dept.slug})) |
1387 | + |
1388 | + self.assertEqual(200, response.status_code) |
1389 | + |
1390 | |
1391 | class ApplicationReviewsTestCase(TestCaseWithFactory): |
1392 | |
1393 | |
1394 | === modified file 'src/webcatalog/urls.py' |
1395 | --- src/webcatalog/urls.py 2012-03-07 21:23:46 +0000 |
1396 | +++ src/webcatalog/urls.py 2012-03-12 15:48:24 +0000 |
1397 | @@ -31,9 +31,9 @@ |
1398 | |
1399 | urlpatterns = patterns('webcatalog.views', |
1400 | url(r'^$', 'index', name='wc-index'), |
1401 | - url(r'^department/(?P<distro>[-.+\w]+)/(?P<dept_id>\d+)/$', |
1402 | + url(r'^department/(?P<distro>[-.+\w]+)/(?P<dept_slug_or_id>[\w\d-]+)/$', |
1403 | 'department_overview', name='wc-department'), |
1404 | - url(r'^department/(?P<dept_id>\d+)/$', 'department_overview', |
1405 | + url(r'^department/(?P<dept_slug_or_id>[\w\d-]+)/$', 'department_overview', |
1406 | name='wc-department'), |
1407 | url(r'^applications/(?P<distro>[-.+\w]+)/(?P<package_name>[-.+\w]+)/$', |
1408 | 'application_detail', name="wc-package-detail"), |
1409 | |
1410 | === modified file 'src/webcatalog/views.py' |
1411 | --- src/webcatalog/views.py 2012-03-08 22:20:32 +0000 |
1412 | +++ src/webcatalog/views.py 2012-03-12 15:48:24 +0000 |
1413 | @@ -34,7 +34,10 @@ |
1414 | from django.core.paginator import Paginator |
1415 | from django.core.urlresolvers import reverse |
1416 | from django.db.models import Q |
1417 | -from django.http import HttpResponseRedirect, HttpResponse |
1418 | +from django.http import ( |
1419 | + HttpResponseRedirect, |
1420 | + HttpResponse, |
1421 | + ) |
1422 | from django.shortcuts import ( |
1423 | get_object_or_404, |
1424 | render_to_response, |
1425 | @@ -135,7 +138,7 @@ |
1426 | context_instance=context) |
1427 | |
1428 | |
1429 | -def department_overview(request, dept_id, distro=None): |
1430 | +def department_overview(request, dept_slug_or_id=None, distro=None): |
1431 | if distro is None: |
1432 | useragent = UserAgentString(request.META.get('HTTP_USER_AGENT', '')) |
1433 | # Check for the distroseries in the useragent, if we have it, |
1434 | @@ -144,13 +147,17 @@ |
1435 | distro = useragent.distroseries |
1436 | else: |
1437 | distro = settings.DEFAULT_DISTRO |
1438 | - return HttpResponseRedirect( |
1439 | - reverse('wc-department', args=[distro, dept_id])) |
1440 | + url = reverse('wc-department', |
1441 | + kwargs={'distro': distro, 'dept_slug_or_id': dept_slug_or_id}) |
1442 | + return HttpResponseRedirect(url) |
1443 | |
1444 | # Attempt to retrieve the DistroSeries to ensure that it actually exists |
1445 | get_object_or_404(DistroSeries, code_name=distro) |
1446 | |
1447 | - dept = get_object_or_404(Department, pk=dept_id) |
1448 | + if dept_slug_or_id.isdigit(): |
1449 | + dept = get_object_or_404(Department, pk=dept_slug_or_id) |
1450 | + else: |
1451 | + dept = get_object_or_404(Department, slug=dept_slug_or_id) |
1452 | subdepts = Department.objects.filter(parent=dept) |
1453 | subdepts = subdepts.order_by('name') |
1454 |
Ok, I also removed Department. normalized_ name because it was kind of working as a slug, except you couldn't use it for this use case (as you can't retrieve departments by their normalized_name).
So I removed all the uses of normalized_name and replaced them for calls to slug instead.