]> git.openstreetmap.org Git - osqa.git/blob - forum/migrations/0023_flaten_node_inheritance_create_actions.py
Make RequestHolder thread-safe
[osqa.git] / forum / migrations / 0023_flaten_node_inheritance_create_actions.py
1 # encoding: utf-8
2 import datetime
3 from south.db import db
4 from south.v2 import DataMigration
5 from django.db import models
6 from forum.migrations import ProgressBar
7
8 GAIN_BY_UPVOTED = 1
9 GAIN_BY_ANSWER_ACCEPTED = 2
10 GAIN_BY_ACCEPTING_ANSWER = 3
11 GAIN_BY_DOWNVOTE_CANCELED = 4
12 GAIN_BY_CANCELING_DOWNVOTE = 5
13 LOST_BY_CANCELLING_ACCEPTED_ANSWER = -1
14 LOST_BY_ACCEPTED_ANSWER_CANCELED = -2
15 LOST_BY_DOWNVOTED = -3
16 LOST_BY_FLAGGED = -4
17 LOST_BY_DOWNVOTING = -5
18 LOST_BY_FLAGGED_3_TIMES = -6
19 LOST_BY_FLAGGED_5_TIMES = -7
20 LOST_BY_UPVOTE_CANCELED = -8
21
22 class Migration(DataMigration):
23     
24     def forwards(self, orm):
25         rephist = dict([(t, []) for t in range(-8, 6) if t != 0])
26
27         r_count = orm.Repute.objects.count()
28         print "\nCalculating rep gain/losses history through %d records:" % r_count
29         progress = ProgressBar(r_count)
30
31         for r in orm.Repute.objects.all():
32             l = rephist.get(r.reputation_type, None)
33             if l is None: continue
34
35             if (len(l) == 0) or (l[-1][1] != r.value):
36                 l.append((r.reputed_at, r.value))
37
38             progress.update()
39
40         print "\n...done\n"
41
42
43         def repval_at(reptype, repdate, default):
44             l = rephist.get(reptype, None)
45
46             if l is None: return 0
47             if len(l) == 0: return default
48
49             for r in l:
50                 if r[0] <= repdate:
51                     return r[1] or default
52
53
54         q_count = orm.Question.objects.count()
55         print "\nConverting %d questions:" % q_count
56         progress = ProgressBar(q_count)
57
58         for q in orm.Question.objects.all():
59             n = q.node_ptr
60             n.last_activity_at = q.last_activity_at
61             n.last_activity_by = q.last_activity_by
62
63             if q.accepted_answer:
64                 n.extra_ref = q.accepted_answer.node_ptr
65                 
66             n.extra_count = q.view_count
67
68             n.marked = q.closed
69             n.wiki = q.wiki
70
71             n.save()
72
73             ask = orm.Action(
74                 user = n.author,
75                 action_date = n.added_at,
76                 node = n,
77                 action_type = "ask",
78                 extra = ''
79             )
80
81             ask.save()
82
83             if n.deleted:
84                 action = orm.Action(
85                     user = n.deleted_by,
86                     node = n,
87                     action_type = "delete",
88                     action_date = n.deleted_at or datetime.datetime.now(),
89                     extra = ''
90                 )
91
92                 action.save()
93
94
95             if n.marked:
96                 action = orm.Action(
97                     user = q.closed_by,
98                     node = n,
99                     action_type = "close",
100                     extra = q.close_reason,
101                     action_date = q.closed_at or datetime.datetime.now(),
102                 )
103
104                 action.save()
105
106             if n.wiki:
107                 action = orm.Action(
108                     user = n.author,
109                     node = n,
110                     action_type = "wikify",
111                     action_date = q.wikified_at or datetime.datetime.now(),
112                     extra = ''
113                 )
114
115                 action.save()
116
117             progress.update()
118
119         print "\n...done\n"
120
121         a_count = orm.Answer.objects.count()
122         print "\nConverting %d answers:" % a_count
123         progress = ProgressBar(a_count)
124
125         for a in orm.Answer.objects.all():
126             n = a.node_ptr
127
128             n.marked = a.accepted
129             n.wiki = a.wiki
130
131             n.save()
132
133             ans = orm.Action(
134                 user = n.author,
135                 action_date = n.added_at,
136                 node = n,
137                 action_type = "answer",
138                 extra = ''
139             )
140
141             ans.save()
142
143             if n.deleted:
144                 action = orm.Action(
145                     user = n.deleted_by,
146                     node = n,
147                     action_type = "delete",
148                     action_date = n.deleted_at or datetime.datetime.now(),
149                     extra = ''
150                 )
151
152                 action.save()
153
154             if a.accepted:
155                 action = orm.Action(
156                     user = a.accepted_by,
157                     node = n,
158                     action_type = "acceptanswer",
159                     action_date = a.accepted_at or datetime.datetime.now(),
160                     extra = ''
161                 )
162
163                 action.save()
164
165                 if not a.wiki or a.wikified_at > action.action_date:
166                     if action.user == n.author:
167                         rep = orm.ActionRepute(
168                             action = action,
169                             user = action.user,
170                             value = repval_at(GAIN_BY_ACCEPTING_ANSWER, action.action_date, 2)
171                         )
172                         rep.save()
173
174                     if n.author != n.parent.author:
175                         rep = orm.ActionRepute(
176                             action = action,
177                             user = n.author,
178                             value = repval_at(GAIN_BY_ANSWER_ACCEPTED, action.action_date, 15)
179                         )
180                         rep.save()
181
182             if n.wiki:
183                 action = orm.Action(
184                     user = n.author,
185                     node = n,
186                     action_type = "wikify",
187                     action_date = a.wikified_at or datetime.datetime.now(),
188                     extra = ''
189                 )
190
191                 action.save()
192
193             progress.update()
194
195         print "\n...done\n"
196
197         v_count = orm.Vote.objects.count()
198         print "\nConverting %d votes:" % v_count
199         progress = ProgressBar(v_count)
200
201         for v in orm.Vote.objects.exclude(canceled=True):
202             a = orm.Action(
203                 action_type = (v.vote == 1) and ((v.node.node_type == "comment") and "voteupcomment" or "voteup") or "votedown",
204                 user = v.user,
205                 node = v.node,
206                 action_date = v.voted_at,
207                 canceled = v.canceled,
208                 extra = ''
209             )
210
211             a.save()
212
213             def impl(node):
214                 if node.node_type == "question":
215                     return orm.Question.objects.get(node_ptr=node)
216                 else:
217                     return orm.Answer.objects.get(node_ptr=node)
218
219             if a.node.node_type in ("question", "answer") and (not a.node.wiki or impl(a.node).wikified_at > a.action_date):
220                 reptype, default = (v.vote == 1) and (GAIN_BY_UPVOTED, 10) or (LOST_BY_DOWNVOTED, 2)
221                 rep = orm.ActionRepute(
222                     action = a,
223                     user = a.node.author,
224                     value = repval_at(reptype, a.action_date, default) or default
225                 )
226                 rep.save()
227
228                 if v.vote == -1:
229                     rep = orm.ActionRepute(
230                         action = a,
231                         user = a.node.author,
232                         value = repval_at(LOST_BY_DOWNVOTING, a.action_date, 1) or default
233                     )
234                     rep.save()
235
236             progress.update()
237
238         print "\n...done\n"
239
240         f_count = orm.FlaggedItem.objects.count()
241         print "\nConverting %d flags:" % f_count
242         progress = ProgressBar(f_count)
243
244         for f in orm.FlaggedItem.objects.all():
245             a = orm.Action(
246                 action_type = "flag",
247                 user = f.user,
248                 node = f.node,
249                 action_date = f.flagged_at,
250                 extra = f.reason or ''
251             )
252
253             a.save()
254
255             rep = orm.ActionRepute(
256                 action = a,
257                 user = a.node.author,
258                 value = repval_at(LOST_BY_FLAGGED, a.action_date, 2) or 2
259             )
260             rep.save()
261
262             progress.update()
263
264         print "\n...done\n"
265
266         n_count = orm.Node.objects.all().count()
267         print "\nChecking flag count of %d nodes:" % n_count
268         progress = ProgressBar(n_count)
269
270         for n in orm.Node.objects.all():
271             flags = list(orm.Action.objects.filter(action_type="flag", node=n, canceled=False).order_by('-action_date'))
272
273             if len(flags) >= 3:
274                 a = flags[2]
275                 rep = orm.ActionRepute(
276                     action = a,
277                     user = n.author,
278                     value = repval_at(LOST_BY_FLAGGED_3_TIMES, a.action_date, 30)
279                 )
280                 rep.save()
281
282
283             if len(flags) >= 5:
284                 a = flags[4]
285                 rep = orm.ActionRepute(
286                     action = a,
287                     user = n.author,
288                     value = repval_at(LOST_BY_FLAGGED_5_TIMES, a.action_date, 100)
289                 )
290                 rep.save()
291
292             progress.update()
293
294         print "\n...done\n"
295
296         c_count = orm.Node.objects.filter(node_type="comment").count()
297         print "\nCreating %d comment actions:" % c_count
298         progress = ProgressBar(c_count)
299
300         for c in orm.Node.objects.filter(node_type="comment").all():
301             a = orm.Action(
302                 action_type = "comment",
303                 user = c.author,
304                 node = c,
305                 action_date = c.added_at,
306                 extra = ''
307             )
308
309             a.save()
310
311             if c.deleted:
312                 action = orm.Action(
313                     user = c.deleted_by,
314                     node = c,
315                     action_type = "delete",
316                     action_date = c.deleted_at or datetime.datetime.now(),
317                     extra = ''
318                 )
319
320                 action.save()
321
322             progress.update()
323
324         print "\n...done\n"
325
326
327         r_count = orm.NodeRevision.objects.exclude(revision=1).count()
328         print "\nCreating %d edit actions:" % r_count
329         progress = ProgressBar(r_count)
330
331         for r in orm.NodeRevision.objects.exclude(revision=1):
332             a = orm.Action(
333                 action_type = "revise",
334                 user = r.author,
335                 node = r.node,
336                 action_date = r.revised_at,
337                 extra = r.revision
338             )
339
340             a.save()
341             progress.update()
342
343         print "\n...done\n"
344
345         u_count = orm.User.objects.all().count()
346         print "\nCreating %d user join actions and reputation recalculation:" % u_count
347         progress = ProgressBar(u_count)
348
349         for u in orm.User.objects.all():
350             a = orm.Action(
351                 user = u,
352                 action_date = u.date_joined,
353                 action_type = "userjoins",
354             )
355
356             a.save()
357
358             rep = orm.ActionRepute(
359                 action = a,
360                 user = u,
361                 value = 1
362             )
363             rep.save()
364
365             new_rep = orm.ActionRepute.objects.filter(user=u).aggregate(reputation=models.Sum('value'))['reputation']
366
367             if new_rep < 0:
368                 new_rep = 1
369
370             u.reputation = new_rep
371             u.save()
372
373             progress.update()
374
375         print "\n...done\n"
376
377     
378     
379     def backwards(self, orm):
380         "Write your backwards methods here."
381     
382     models = {
383         'auth.group': {
384             'Meta': {'object_name': 'Group'},
385             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
386             'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
387             'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'})
388         },
389         'auth.permission': {
390             'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
391             'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
392             'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
393             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
394             'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
395         },
396         'auth.user': {
397             'Meta': {'object_name': 'User'},
398             'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
399             'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
400             'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
401             'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}),
402             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
403             'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
404             'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
405             'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
406             'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
407             'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
408             'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
409             'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}),
410             'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
411         },
412         'contenttypes.contenttype': {
413             'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
414             'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
415             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
416             'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
417             'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
418         },
419         'forum.action': {
420             'Meta': {'object_name': 'Action'},
421             'action_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
422             'action_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
423             'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
424             'canceled_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
425             'canceled_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'canceled_actions'", 'null': 'True', 'to': "orm['forum.User']"}),
426             'extra': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
427             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
428             'ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
429             'node': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}),
430             'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'to': "orm['forum.User']"})
431         },
432         'forum.actionrepute': {
433             'Meta': {'object_name': 'ActionRepute'},
434             'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.Action']"}),
435             'by_canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
436             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
437             'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}),
438             'value': ('django.db.models.fields.IntegerField', [], {'default': '0'})
439         },
440         'forum.activity': {
441             'Meta': {'object_name': 'Activity', 'db_table': "u'activity'"},
442             'active_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
443             'activity_type': ('django.db.models.fields.SmallIntegerField', [], {}),
444             'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
445             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
446             'is_auditted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
447             'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
448             'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"})
449         },
450         'forum.anonymousnode': {
451             'Meta': {'object_name': 'AnonymousNode', '_ormbases': ['forum.Node']},
452             'convertible_to': ('django.db.models.fields.CharField', [], {'default': "'node'", 'max_length': '16'}),
453             'node_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['forum.Node']", 'unique': 'True', 'primary_key': 'True'}),
454             'validation_hash': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'anonymous_content'", 'to': "orm['forum.Node']"})
455         },
456         'forum.answer': {
457             'Meta': {'object_name': 'Answer', 'db_table': "u'answer'"},
458             'accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
459             'accepted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
460             'accepted_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']", 'null': 'True'}),
461             'node_ptr': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True', 'primary_key': 'True'}),
462             'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
463             'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
464         },
465         'forum.authkeyuserassociation': {
466             'Meta': {'object_name': 'AuthKeyUserAssociation'},
467             'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
468             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
469             'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
470             'provider': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
471             'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_keys'", 'to': "orm['forum.User']"})
472         },
473         'forum.award': {
474             'Meta': {'object_name': 'Award', 'db_table': "u'award'"},
475             'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
476             'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['forum.Badge']"}),
477             'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
478             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
479             'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
480             'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
481             'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'awards'", 'to': "orm['forum.User']"})
482         },
483         'forum.badge': {
484             'Meta': {'unique_together': "(('name', 'type'),)", 'object_name': 'Badge', 'db_table': "u'badge'"},
485             'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
486             'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'through': "'Award'", 'to': "orm['forum.User']"}),
487             'description': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
488             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
489             'multiple': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
490             'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
491             'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}),
492             'type': ('django.db.models.fields.SmallIntegerField', [], {})
493         },
494         'forum.favoritequestion': {
495             'Meta': {'unique_together': "(('question', 'user'),)", 'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"},
496             'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
497             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
498             'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'favourites'", 'to': "orm['forum.Question']"}),
499             'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['forum.User']"})
500         },
501         'forum.flaggeditem': {
502             'Meta': {'object_name': 'FlaggedItem', 'db_table': "u'flagged_item'"},
503             'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
504             'flagged_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
505             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
506             'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'flaggeditems'", 'null': 'True', 'to': "orm['forum.Node']"}),
507             'reason': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'}),
508             'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'flaggeditems'", 'to': "orm['forum.User']"})
509         },
510         'forum.keyvalue': {
511             'Meta': {'object_name': 'KeyValue'},
512             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
513             'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
514             'value': ('forum.models.utils.PickledObjectField', [], {})
515         },
516         'forum.markedtag': {
517             'Meta': {'object_name': 'MarkedTag'},
518             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
519             'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
520             'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['forum.Tag']"}),
521             'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['forum.User']"})
522         },
523         'forum.node': {
524             'Meta': {'object_name': 'Node'},
525             'abs_parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'all_children'", 'null': 'True', 'to': "orm['forum.Node']"}),
526             'active_revision': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'active'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.NodeRevision']"}),
527             'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
528             'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nodes'", 'to': "orm['forum.User']"}),
529             'body': ('django.db.models.fields.TextField', [], {}),
530             'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
531             'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
532             'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_nodes'", 'null': 'True', 'to': "orm['forum.User']"}),
533             'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
534             'extra_ref': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}),
535             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
536             'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
537             'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']", 'null': 'True'}),
538             'last_edited_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
539             'last_edited_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'last_edited_nodes'", 'null': 'True', 'to': "orm['forum.User']"}),
540             'marked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
541             'node_type': ('django.db.models.fields.CharField', [], {'default': "'node'", 'max_length': '16'}),
542             'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'children'", 'null': 'True', 'to': "orm['forum.Node']"}),
543             'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
544             'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
545             'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'nodes'", 'to': "orm['forum.Tag']"}),
546             'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
547             'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'})
548         },
549         'forum.noderevision': {
550             'Meta': {'unique_together': "(('node', 'revision'),)", 'object_name': 'NodeRevision'},
551             'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'noderevisions'", 'to': "orm['forum.User']"}),
552             'body': ('django.db.models.fields.TextField', [], {}),
553             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
554             'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['forum.Node']"}),
555             'revised_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
556             'revision': ('django.db.models.fields.PositiveIntegerField', [], {}),
557             'summary': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
558             'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
559             'title': ('django.db.models.fields.CharField', [], {'max_length': '300'})
560         },
561         'forum.openidassociation': {
562             'Meta': {'object_name': 'OpenIdAssociation'},
563             'assoc_type': ('django.db.models.fields.TextField', [], {'max_length': '64'}),
564             'handle': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
565             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
566             'issued': ('django.db.models.fields.IntegerField', [], {}),
567             'lifetime': ('django.db.models.fields.IntegerField', [], {}),
568             'secret': ('django.db.models.fields.TextField', [], {'max_length': '255'}),
569             'server_url': ('django.db.models.fields.TextField', [], {'max_length': '2047'})
570         },
571         'forum.openidnonce': {
572             'Meta': {'object_name': 'OpenIdNonce'},
573             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
574             'salt': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
575             'server_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
576             'timestamp': ('django.db.models.fields.IntegerField', [], {})
577         },
578         'forum.question': {
579             'Meta': {'object_name': 'Question', 'db_table': "u'question'"},
580             'accepted_answer': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'question_accepting'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Answer']"}),
581             'close_reason': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
582             'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
583             'closed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
584             'closed_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'closed_questions'", 'null': 'True', 'to': "orm['forum.User']"}),
585             'favorited_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'favorite_questions'", 'through': "'FavoriteQuestion'", 'to': "orm['forum.User']"}),
586             'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
587             'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'last_active_in_questions'", 'null': 'True', 'to': "orm['forum.User']"}),
588             'node_ptr': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True', 'primary_key': 'True'}),
589             'view_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
590             'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
591             'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
592         },
593         'forum.questionsubscription': {
594             'Meta': {'object_name': 'QuestionSubscription'},
595             'auto_subscription': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
596             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
597             'last_view': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 4, 27, 11, 40, 32, 68000)'}),
598             'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}),
599             'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"})
600         },
601         'forum.repute': {
602             'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"},
603             'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
604             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
605             'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'null': 'True', 'to': "orm['forum.Node']"}),
606             'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Question']"}),
607             'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}),
608             'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
609             'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.User']"}),
610             'user_previous_rep': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
611             'value': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'})
612         },
613         'forum.subscriptionsettings': {
614             'Meta': {'object_name': 'SubscriptionSettings'},
615             'all_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
616             'all_questions_watched_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
617             'enable_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
618             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
619             'member_joins': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}),
620             'new_question': ('django.db.models.fields.CharField', [], {'default': "'d'", 'max_length': '1'}),
621             'new_question_watched_tags': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}),
622             'notify_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
623             'notify_answers': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
624             'notify_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
625             'notify_comments_own_post': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
626             'notify_reply_to_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
627             'questions_answered': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
628             'questions_asked': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
629             'questions_commented': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
630             'questions_viewed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
631             'subscribed_questions': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}),
632             'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'subscription_settings'", 'unique': 'True', 'to': "orm['forum.User']"})
633         },
634         'forum.tag': {
635             'Meta': {'object_name': 'Tag', 'db_table': "u'tag'"},
636             'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['forum.User']"}),
637             'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
638             'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
639             'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['forum.User']"}),
640             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
641             'marked_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'marked_tags'", 'through': "'MarkedTag'", 'to': "orm['forum.User']"}),
642             'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
643             'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
644         },
645         'forum.user': {
646             'Meta': {'object_name': 'User', '_ormbases': ['auth.User']},
647             'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
648             'bronze': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
649             'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
650             'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
651             'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
652             'gold': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
653             'hide_ignored_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
654             'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
655             'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
656             'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
657             'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
658             'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
659             'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
660             'silver': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
661             'subscriptions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribers'", 'through': "'QuestionSubscription'", 'to': "orm['forum.Node']"}),
662             'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}),
663             'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
664         },
665         'forum.validationhash': {
666             'Meta': {'unique_together': "(('user', 'type'),)", 'object_name': 'ValidationHash'},
667             'expiration': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 4, 28, 11, 40, 32, 153000)'}),
668             'hash_code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
669             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
670             'seed': ('django.db.models.fields.CharField', [], {'max_length': '12'}),
671             'type': ('django.db.models.fields.CharField', [], {'max_length': '12'}),
672             'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"})
673         },
674         'forum.vote': {
675             'Meta': {'object_name': 'Vote', 'db_table': "u'vote'"},
676             'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
677             'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
678             'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'null': 'True', 'to': "orm['forum.Node']"}),
679             'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['forum.User']"}),
680             'vote': ('django.db.models.fields.SmallIntegerField', [], {}),
681             'voted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'})
682         }
683     }
684     
685     complete_apps = ['forum']