]> git.openstreetmap.org Git - osqa.git/blob - forum/models/question.py
initial import
[osqa.git] / forum / models / question.py
1 from base import *
2 from tag import Tag
3
4 class QuestionManager(CachedManager):
5     def create_new(self, title=None,author=None,added_at=None, wiki=False,tagnames=None,summary=None, text=None):
6
7         question = Question(
8             title            = title,
9             author           = author,
10             added_at         = added_at,
11             last_activity_at = added_at,
12             last_activity_by = author,
13             wiki             = wiki,
14             tagnames         = tagnames,
15             html             = text,
16             summary          = summary
17         )
18         if question.wiki:
19             question.last_edited_by = question.author
20             question.last_edited_at = added_at
21             question.wikified_at = added_at
22
23         question.save()
24
25         # create the first revision
26         QuestionRevision.objects.create(
27             question   = question,
28             revision   = 1,
29             title      = question.title,
30             author     = author,
31             revised_at = added_at,
32             tagnames   = question.tagnames,
33             summary    = CONST['default_version'],
34             text       = text
35         )
36         return question
37
38 question_view = django.dispatch.Signal(providing_args=['instance', 'user'])
39
40 class Question(Content):
41     title    = models.CharField(max_length=300)
42     tags     = models.ManyToManyField(Tag, related_name='questions')
43     answer_accepted = models.BooleanField(default=False)
44     closed          = models.BooleanField(default=False)
45     closed_by       = models.ForeignKey(User, null=True, blank=True, related_name='closed_questions')
46     closed_at       = models.DateTimeField(null=True, blank=True)
47     close_reason    = models.SmallIntegerField(choices=CLOSE_REASONS, null=True, blank=True)
48     followed_by     = models.ManyToManyField(User, related_name='followed_questions')
49     subscribers     = models.ManyToManyField(User, related_name='subscriptions', through='QuestionSubscription')
50
51     # Denormalised data
52     answer_count         = models.PositiveIntegerField(default=0)
53     view_count           = models.IntegerField(default=0)
54     favourite_count      = models.IntegerField(default=0)
55     last_activity_at     = models.DateTimeField(default=datetime.datetime.now)
56     last_activity_by     = models.ForeignKey(User, related_name='last_active_in_questions')
57     tagnames             = models.CharField(max_length=125)
58     summary              = models.CharField(max_length=180)
59
60     favorited_by         = models.ManyToManyField(User, through='FavoriteQuestion', related_name='favorite_questions') 
61
62     objects = QuestionManager()
63
64     class Meta(Content.Meta):
65         db_table = u'question'
66
67     def delete(self):
68         super(Question, self).delete()
69         try:
70             ping_google()
71         except Exception:
72             logging.debug('problem pinging google did you register you sitemap with google?')
73
74     def get_tag_list_if_changed(self):
75         dirty = self.get_dirty_fields()
76
77         if 'tagnames' in dirty:
78             new_tags = self.tagname_list()
79
80             old_tags = dirty['tagnames']
81             if old_tags is None:
82                 old_tags = []
83             else:
84                 old_tags = [name for name in dirty['tagnames'].split(u' ')]
85
86             tag_list = []
87
88             for name in new_tags:
89                 try:
90                     tag = Tag.objects.get(name=name)
91                 except:
92                     tag = Tag.objects.create(name=name, created_by=self.last_edited_by or self.author)
93
94                 tag_list.append(tag)
95
96                 if not name in old_tags:
97                     tag.used_count = tag.used_count + 1
98                     if tag.deleted:
99                         tag.unmark_deleted()
100                     tag.save()
101
102             for name in [n for n in old_tags if not n in new_tags]:
103                 tag = Tag.objects.get(name=name)
104                 tag.used_count = tag.used_count - 1
105                 if tag.used_count == 0:
106                     tag.mark_deleted(self.last_edited_by or self.author)
107                 tag.save()
108
109             return tag_list
110
111         return None
112
113     def save(self, *args, **kwargs):
114         tags = self.get_tag_list_if_changed()
115         super(Question, self).save(*args, **kwargs)
116         if not tags is None: self.tags = tags
117
118     def tagname_list(self):
119         """Creates a list of Tag names from the ``tagnames`` attribute."""
120         return [name for name in self.tagnames.split(u' ')]
121
122     def tagname_meta_generator(self):
123         return u','.join([unicode(tag) for tag in self.tagname_list()])
124
125     @models.permalink    
126     def get_absolute_url(self):
127         return ('question', (), {'id': self.id, 'slug': django_urlquote(slugify(self.title))})
128
129     def get_answer_count_by_user(self, user_id):
130         from answer import Answer
131         query_set = Answer.objects.filter(author__id=user_id)
132         return query_set.filter(question=self).count()
133
134     def get_question_title(self):
135         if self.closed:
136             attr = CONST['closed']
137         elif self.deleted:
138             attr = CONST['deleted']
139         else:
140             attr = None
141         if attr is not None:
142             return u'%s %s' % (self.title, attr)
143         else:
144             return self.title
145
146     def get_revision_url(self):
147         return reverse('question_revisions', args=[self.id])
148
149     def get_latest_revision(self):
150         return self.revisions.all()[0]
151
152     def get_last_update_info(self):
153         when, who = self.post_get_last_update_info()
154
155         answers = self.answers.all()
156         if len(answers) > 0:
157             for a in answers:
158                 a_when, a_who = a.post_get_last_update_info()
159                 if a_when > when:
160                     when = a_when
161                     who = a_who
162
163         return when, who
164
165     def get_related_questions(self, count=10):
166         cache_key = '%s.related_questions:%d:%d' % (settings.APP_URL, count, self.id)
167         related_list = cache.get(cache_key)
168
169         if related_list is None:
170             related_list = Question.objects.values('id').filter(tags__id__in=[t.id for t in self.tags.all()]
171             ).exclude(id=self.id).exclude(deleted=True).annotate(frequency=models.Count('id')).order_by('-frequency')[:count]
172             cache.set(cache_key, related_list, 60 * 60)
173
174         return [Question.objects.get(id=r['id']) for r in related_list]
175
176     def __unicode__(self):
177         return self.title
178
179 def question_viewed(instance, **kwargs):
180     instance.view_count += 1
181     instance.save()
182
183 question_view.connect(question_viewed)
184
185 class FavoriteQuestion(models.Model):
186     """A favorite Question of a User."""
187     question      = models.ForeignKey('Question')
188     user          = models.ForeignKey(User, related_name='user_favorite_questions')
189     added_at      = models.DateTimeField(default=datetime.datetime.now)
190
191     class Meta:
192         unique_together = ('question', 'user')
193         app_label = 'forum'
194         db_table = u'favorite_question'
195
196     def __unicode__(self):
197         return '[%s] favorited at %s' %(self.user, self.added_at)
198
199     def _update_question_fav_count(self, diff):
200         self.question.favourite_count = self.question.favourite_count + diff
201         self.question.save()
202
203     def save(self, *args, **kwargs):
204         super(FavoriteQuestion, self).save(*args, **kwargs)
205         if self._is_new:
206             self._update_question_fav_count(1)
207
208     def delete(self):
209         self._update_question_fav_count(-1)
210         super(FavoriteQuestion, self).delete()
211
212 class QuestionSubscription(models.Model):
213     user = models.ForeignKey(User)
214     question = models.ForeignKey(Question)
215     auto_subscription = models.BooleanField(default=True)
216     last_view = models.DateTimeField(default=datetime.datetime.now())
217
218     class Meta:
219         app_label = 'forum'
220
221 class QuestionRevision(ContentRevision):
222     """A revision of a Question."""
223     question   = models.ForeignKey(Question, related_name='revisions')
224     title      = models.CharField(max_length=300)
225     tagnames   = models.CharField(max_length=125)
226
227     class Meta(ContentRevision.Meta):
228         db_table = u'question_revision'
229         ordering = ('-revision',)
230
231     def get_question_title(self):
232         return self.question.title
233
234     def get_absolute_url(self):
235         #print 'in QuestionRevision.get_absolute_url()'
236         return reverse('question_revisions', args=[self.question.id])
237
238     def save(self, *args, **kwargs):
239         """Looks up the next available revision number."""
240         if not self.revision:
241             self.revision = QuestionRevision.objects.filter(
242                 question=self.question).values_list('revision',
243                                                     flat=True)[0] + 1
244         super(QuestionRevision, self).save(*args, **kwargs)
245
246     def __unicode__(self):
247         return u'revision %s of %s' % (self.revision, self.title)
248
249 class AnonymousQuestion(AnonymousContent):
250     title = models.CharField(max_length=300)
251     tagnames = models.CharField(max_length=125)
252
253     def publish(self,user):
254         added_at = datetime.datetime.now()
255         Question.objects.create_new(title=self.title, author=user, added_at=added_at,
256                                 wiki=self.wiki, tagnames=self.tagnames,
257                                 summary=self.summary, text=self.text)
258         self.delete()
259
260 from answer import Answer, AnswerManager