1
1
from django.db import models
2
from django.db.models import Count
3
from django.contrib.auth.models import User as DjangoUser
4
from django.contrib.auth.models import UserManager
2
from django.db.models import Count, Q
3
from django.db.models.signals import post_save
4
from django.contrib.auth.models import User
5
from django.contrib.comments.models import Comment
6
from django.dispatch import receiver
6
8
from datetime import datetime
7
import random, string, re
10
20
''' Helper methods '''
12
23
def randomstring(length=16):
13
24
return ''.join(random.choice(string.letters) for i in xrange(length))
27
def get_query_terms(query):
35
return re.split(r' +', query)
38
@receiver(post_save, sender=User)
39
def create_user_profile(sender, instance, created, **kwargs):
42
HackerProfile.objects.get_or_create(user=instance)
46
post_save.connect(create_user_profile, sender=User)
16
48
''' Core models '''
18
class Hacker(DjangoUser):
19
twitter = models.SlugField(max_length=100, blank=True, null=True)
24
# Use UserManager to get the create_user method, etc.
25
objects = UserManager()
51
class HackerProfile(models.Model):
52
user = models.OneToOneField(User)
54
display_name = models.SlugField(max_length=50, blank=True, null=True, unique=True)
55
twitter_name = models.SlugField(max_length=50, blank=True, null=True)
56
blog_url = models.URLField('Blog URL', blank=True, null=True)
57
homepage_url = models.URLField('Homepage URL', blank=True, null=True)
59
def twitter_url(self):
60
return 'http://twitter.com/%s/' % self.twitter_name
62
def get_username(self):
63
return self.user.username
66
return self.user.email
68
def get_date_joined(self):
69
return self.user.date_joined
71
def get_display_name(self):
72
return self.display_name or self.user.username
74
def __unicode__(self):
75
return ', '.join([x for x in (self.user.username, self.user.email) if x])
28
81
class OneLiner(models.Model):
29
hacker = models.ForeignKey(Hacker)
82
user = models.ForeignKey(User)
84
summary = models.CharField(max_length=200)
31
85
line = models.TextField()
32
summary = models.TextField()
33
explanation = models.TextField(blank=True)
34
caveats = models.TextField(blank=True)
86
explanation = models.TextField()
87
limitations = models.TextField(blank=True)
36
is_published = models.BooleanField(default=False)
89
is_published = models.BooleanField(default=True)
90
was_tweeted = models.BooleanField(default=False)
38
92
created_dt = models.DateTimeField(default=datetime.now, blank=True)
41
return [x for x in self.line.split('\n') if x.strip() != '']
43
def vote_up(self, hacker):
44
Vote.vote_up(hacker, self)
46
def vote_down(self, hacker):
47
Vote.vote_down(hacker, self)
94
def vote_up(self, user):
95
Vote.vote_up(user, self)
97
def vote_down(self, user):
98
Vote.vote_down(user, self)
49
100
def get_votes(self):
50
return (self.get_votes_up(), self.get_votes_down())
101
return (self.get_votes_up(), self.get_votes_down())
52
103
def get_votes_up(self):
53
return self.vote_set.filter(up=True).count()
104
return self.vote_set.filter(up=True).count()
55
106
def get_votes_down(self):
56
return self.vote_set.filter(up=False).count()
60
return OneLiner.objects.filter(vote__up=True).annotate(votes=Count('vote')).order_by('-votes')[:limit]
62
def __unicode__(self):
107
return self.vote_set.filter(up=False).count()
110
return self.answer_set.filter(question__is_published=True)
112
def alternatives(self):
113
return self.alternativeoneliner_set.filter(alternative__is_published=True)
115
def add_alternative(self, alternative):
116
AlternativeOneLiner(alternative=alternative, oneliner=self).save()
119
return self.related_set.filter(oneliner__is_published=True)
123
return OneLiner.objects.get(pk=pk)
126
def feed(limit=FEED_LIMIT):
127
return OneLiner.recent(limit)
130
def recent(limit=RECENT_LIMIT):
131
return OneLiner.objects.filter(is_published=True)[:limit]
134
def recent_by_tag(text, limit=RECENT_LIMIT):
135
return OneLiner.objects.filter(is_published=True, onelinertag__tag__text=text)[:limit]
138
def top(limit=SEARCH_LIMIT):
139
return OneLiner.objects.filter(vote__up=True).annotate(votes=Count('vote')).order_by('-votes')[:limit]
142
def simplesearch(query=None, limit=SEARCH_LIMIT):
144
for term in get_query_terms(query):
146
sub_qq |= Q(summary__icontains=term)
147
sub_qq |= Q(line__icontains=term)
148
sub_qq |= Q(explanation__icontains=term)
149
sub_qq |= Q(limitations__icontains=term)
151
return OneLiner.objects.filter(is_published=True).filter(qq)[:limit]
154
def search(form, limit=SEARCH_LIMIT):
155
query = form.cleaned_data.get('query')
156
match_summary = form.cleaned_data.get('match_summary')
157
match_line = form.cleaned_data.get('match_line')
158
match_explanation = form.cleaned_data.get('match_explanation')
159
match_limitations = form.cleaned_data.get('match_limitations')
160
match_whole_words = form.cleaned_data.get('match_whole_words')
162
terms = get_query_terms(query)
167
sub_qq |= Q(summary__icontains=term)
169
sub_qq |= Q(line__icontains=term)
170
if match_explanation:
171
sub_qq |= Q(explanation__icontains=term)
172
if match_limitations:
173
sub_qq |= Q(limitations__icontains=term)
174
if len(sub_qq.children) > 0:
177
if len(qq.children) > 0:
178
results = OneLiner.objects.filter(is_published=True).filter(qq)
180
if match_whole_words:
181
results = [x for x in results if x.matches_words(terms, match_summary, match_line, match_explanation, match_limitations)]
183
return results[:limit]
187
def matches_words(self, terms, match_summary, match_line, match_explanation, match_limitations):
189
if match_summary and re.search(r'\b%s\b' % term, self.summary):
191
if match_line and re.search(r'\b%s\b' % term, self.line):
193
if match_explanation and re.search(r'\b%s\b' % term, self.explanation):
195
if match_limitations and re.search(r'\b%s\b' % term, self.limitations):
198
def update_tags(self):
199
self.onelinertag_set.all().delete()
200
if self.is_published:
201
words = re.split(r'[ ;|]+', self.line)
202
tagwords = set([word for word in words if re.match(r'^[a-z_]{2,}$', word)])
203
for tagword in tagwords:
204
tag = Tag.create_or_get(tagword)
205
OneLinerTag(oneliner=self, tag=tag).save()
208
return [rel.tag.text for rel in self.onelinertag_set.all()]
210
def save(self, *args, **kwargs):
211
ret = super(OneLiner, self).save(*args, **kwargs)
215
def get_absolute_url(self):
216
return "/main/oneliner/%i/" % self.pk
218
def __unicode__(self):
226
class AlternativeOneLiner(models.Model):
227
alternative = models.ForeignKey(OneLiner, related_name='related_set')
228
oneliner = models.ForeignKey(OneLiner)
231
unique_together = (('alternative', 'oneliner',),)
234
class Tag(models.Model):
235
text = models.SlugField(max_length=50)
237
def __unicode__(self):
241
def create_or_get(text):
243
return Tag.objects.get(text=text)
251
return Tag.objects.annotate(count=Count('onelinertag')).filter(count__gt=1).order_by('-count').values('text', 'count')
252
#return Tag.objects.annotate(count=Count('onelinertag')).order_by('-count').values_list('text', 'count')
255
class OneLinerTag(models.Model):
256
oneliner = models.ForeignKey(OneLiner)
257
tag = models.ForeignKey(Tag)
260
class Question(models.Model):
261
user = models.ForeignKey(User)
262
summary = models.CharField(max_length=200)
263
explanation = models.TextField(blank=True, null=True)
264
is_published = models.BooleanField(default=True)
265
is_answered = models.BooleanField(default=False)
266
created_dt = models.DateTimeField(default=datetime.now, blank=True)
268
def __unicode__(self):
271
def add_answer(self, oneliner):
272
Answer(question=self, oneliner=oneliner).save()
275
return self.answer_set.filter(oneliner__is_published=True)
277
def accept_answer(self, oneliner):
278
self.is_answered = True
281
AcceptedAnswer(question=self, oneliner=oneliner).save()
285
def clear_all_answers(self):
286
self.is_answered = False
289
def save(self, *args, **kwargs):
290
if not self.is_answered:
291
AcceptedAnswer.objects.filter(question=self).delete()
292
return super(Question, self).save(*args, **kwargs)
296
return Question.objects.get(pk=pk)
299
def feed(limit=FEED_LIMIT):
300
return Question.objects.filter(is_published=True)[:limit]
303
def recent(limit=RECENT_LIMIT):
304
return Question.objects.filter(is_published=True).exclude(is_answered=True)[:limit]
309
return Question.recent(1)[0]
313
def get_absolute_url(self):
314
return "/main/question/%i/" % self.pk
321
class Answer(models.Model):
322
question = models.ForeignKey(Question)
323
oneliner = models.ForeignKey(OneLiner)
326
class AcceptedAnswer(models.Model):
327
question = models.ForeignKey(Question)
328
oneliner = models.ForeignKey(OneLiner)
331
unique_together = (('question', 'oneliner',),)
69
334
class Vote(models.Model):
70
hacker = models.ForeignKey(Hacker)
335
user = models.ForeignKey(User)
71
336
oneliner = models.ForeignKey(OneLiner)
72
337
up = models.BooleanField(default=True)
74
339
created_dt = models.DateTimeField(default=datetime.now)
77
def vote(hacker, oneliner, updown):
78
if oneliner.hacker == hacker:
82
oneliner.vote_set.get(hacker=hacker, up=updown)
87
oneliner.vote_set.filter(hacker=hacker).delete()
88
Vote(hacker=hacker, oneliner=oneliner, up=updown).save()
91
def vote_up(hacker, oneliner):
92
Vote.vote(hacker, oneliner, True)
95
def vote_down(hacker, oneliner):
96
Vote.vote(hacker, oneliner, False)
342
def vote(user, oneliner, updown):
343
if oneliner.user == user:
347
oneliner.vote_set.get(user=user, up=updown)
352
oneliner.vote_set.filter(user=user).delete()
353
Vote(user=user, oneliner=oneliner, up=updown).save()
356
def vote_up(user, oneliner):
357
Vote.vote(user, oneliner, True)
360
def vote_down(user, oneliner):
361
Vote.vote(user, oneliner, False)
98
363
def __unicode__(self):
99
return '%s %s %s' % (self.hacker.full_name, ('--', '++')[self.up], self.oneliner.summary)
364
return '%s %s %s' % (self.user.full_name, ('--', '++')[self.up], self.oneliner.summary)
102
unique_together = (('hacker', 'oneliner',),)
367
unique_together = (('user', 'oneliner',),)
370
def Comment_recent(limit=RECENT_LIMIT):
371
return Comment.objects.filter(is_public=True).exclude(is_removed=True).order_by('-submit_date')[:limit]
374
def Comment_feed(limit=FEED_LIMIT):
375
return Comment_recent(limit)