/TheChurchofHorrors/apps/blog/models.py
Python | 543 lines | 497 code | 36 blank | 10 comment | 12 complexity | 4aa3db2c687809284279884970b5f047 MD5 | raw file
1# coding=utf-8 2 3from django.db import models 4from django.utils.translation import ugettext as _ 5from django.contrib.auth.models import User 6from tinymce import models as tinymce_models 7from django.template.defaultfilters import slugify 8from django.conf import settings 9from filebrowser.fields import FileBrowseField 10from taggit.managers import TaggableManager 11from django.core.urlresolvers import reverse 12from django.core.cache import cache 13from dateutil.relativedelta import relativedelta 14import datetime,sys,random 15 16from south.modelsinspector import add_introspection_rules 17add_introspection_rules([], ["^tinymce\.models\.HTMLField"]) 18 19def in_sync(): 20 return False 21 return "syncdb" in sys.argv or "migrate" in sys.argv 22 23"""class Section(models.Model): 24 name = models.CharField(_(u"Nombre"),max_length=255,unique=True,db_index=True,blank=False) 25 slug = models.SlugField(max_length=255,unique=True,blank=True,help_text=u"Será generada automaticamente a partir del nombre") 26 27 def __unicode__(self): 28 return unicode(self.name) 29 30 @models.permalink 31 def get_absolute_url(self): 32 return ('common', (), { 33 'slug': self.slug}) 34 35 class Meta: 36 verbose_name = _(u"sección") 37 verbose_name_plural = _(u"secciones") 38 ordering = ('name',) 39 40 def save(self,*args,**kwargs): 41 self.slug = slugify(self.name) 42 super(type(self),self).save(*args,**kwargs)""" 43 44MONTH_CHOICES = ( (1,'Enero',), 45 (2,'Febrero'), 46 (3,'Marzo'), 47 (4,'Abril'), 48 (5,'Mayo'), 49 (6,'Junio'), 50 (7,'Julio'), 51 (8,'Agosto'), 52 (9,'Septiembre'), 53 (10,'Octubre'), 54 (11,'Noviembre'), 55 (12,'Diciembre'), 56 ) 57 58class Number(models.Model): 59 number = models.PositiveIntegerField(u"Número",unique=True,db_index=True) 60 month = models.PositiveIntegerField(u"Mes",choices=MONTH_CHOICES) 61 year = models.PositiveIntegerField(u"Año") 62 imagen = FileBrowseField(blank=False,format='image',extensions=[".jpg",".png",".jpeg",".gif"],default='') 63 published = models.BooleanField("Publicado",default=False,) 64 65 @property 66 def modified(self): 67 return self.entries.all().order_by("-modified").values('modified')[0]['modified'] 68 69 @property 70 def editorial(self): 71 try: 72 return self.entries.get(is_editorial=True) 73 except Entry.DoesNotExist: 74 return None 75 76 @property 77 def cartoon(self): 78 try: 79 return self.entries.get(is_cartoon=True) 80 except Entry.DoesNotExist: 81 return None 82 83 @models.permalink 84 def get_absolute_url(self): 85 return ('number', (), {'number': self.number,'month':self.month,'year':self.year}) 86 87 @property 88 def other_entries(self): 89 return self.entries.filter(is_editorial=False,is_cartoon=False,published=True) 90 91 @property 92 def other_entries_random(self): 93 return self.other_entries.order_by("?") 94 95 @property 96 def all_entries(self): 97 return self.entries.filter(published=True) 98 99 @classmethod 100 def get_current(cls): 101 return cls.objects.filter(published=True).order_by("-number")[0] 102 103 def is_current(self): 104 return Number.objects.filter(published=True).values("id").order_by("-number")[0]['id'] == self.id 105 106 @classmethod 107 def get_anteriores(cls): 108 if settings.DEBUG: 109 return [cls.get_current(),cls.get_current(),cls.get_current(),cls.get_current(),cls.get_current()] 110 else: 111 return cls.objects.filter(published=True).order_by("-number")[1:] 112 113 def get_more_comment(self): 114 return self.other_entries.filter(published=True).order_by("-ncomments") 115 116 def get_more_view(self): 117 return self.other_entries.filter(published=True).order_by("-views") 118 119 def get_more_shared(self): 120 return self.other_entries.filter(published=True).order_by("-shared") 121 122 def __unicode__(self): 123 return u"Número %d"%self.number 124 125 def get_sitios(self): 126 qs = UserProfile.objects.filter(rols__rol__id=2,user__entries__id__in=self.entries.filter(published=True,is_cartoon=False,).values_list("id",flat=True)) 127 qs = list(set(qs)) 128 random.shuffle(qs) 129 130 return qs 131 132 def get_sitios(self): 133 qs = UserProfile.objects.filter(rols__rol__id=2,user__entries__id__in=Entry.objects.filter(published=True,number__published=True,is_cartoon=False,).values_list("id",flat=True)) 134 qs = list(set(qs)) 135 random.shuffle(qs) 136 137 return qs 138 139 @classmethod 140 def get_published(cls): 141 return cls.objects.filter(published=True) 142 143 def get_subsections(self): 144 145 return Subsection.objects.filter(id__in=self.other_entries.values_list("subsection",flat=True)) 146 147 class Meta: 148 ordering = ("-number",) 149 verbose_name = "Número" 150 151 152class Subsection(models.Model): 153 name = models.CharField(_(u"Nombre"),max_length=255,unique=True,db_index=True,blank=False) 154 slug = models.SlugField(max_length=255,unique=True,blank=True,help_text=u"Será generada automaticamente a partir del nombre") 155 sort = models.PositiveIntegerField(_(u"Orden"),default=0,blank=False,null=False,help_text=_(u"En el que se mostrará en el menú")) 156 color = models.CharField("Color Css",max_length="7",help_text="#3f3f3f",default="") 157 hidden = models.BooleanField("Oculta",default=False) 158 159 def __unicode__(self): 160 return unicode(self.name) 161 162 @property 163 def other_entries(self): 164 return self.entries.filter(published=True,number__published=True) 165 166 @property 167 def other_entries_random(self): 168 return self.other_entries.order_by("?") 169 170 class Meta: 171 verbose_name = _(u"categoría") 172 verbose_name_plural = _(u"categorías") 173 ordering = ('sort',) 174 175 @models.permalink 176 def get_absolute_url(self): 177 return ('subsection', (), { 178 'slug': self.slug}) 179 180 @classmethod 181 def get_css_colors(cls): 182 css = """ 183 .home .short-entry.%(slug)s { border-bottom: 3px solid %(color)s } 184 .entry .content-entry.%(slug)s .link { color: %(color)s } 185 #header #subsections.%(slug)s {border-top:3px solid %(color)s} 186 .cartoon.%(slug)s .cartoon-title {border-bottom:3px solid %(color)s} 187 """ 188 ret = "" 189 190 for ss in cls.objects.exclude(color=""): 191 ret+=css%dict(color=ss.color,slug=ss.slug) 192 193 return ret 194 195 196 def save(self,*args,**kwargs): 197 self.slug = slugify(self.name) 198 super(type(self),self).save(*args,**kwargs) 199 200class SubsectionTag(models.Model): 201 tag = models.ForeignKey("taggit.tag",verbose_name=_(u"Etiqueta")) 202 subsection = models.ForeignKey(Subsection,verbose_name=_(u"Categoría"),related_name="tags") 203 sort = models.PositiveIntegerField(_(u"Orden"),default=0,blank=False,null=False,help_text=_(u"En el que se mostrará en el menú")) 204 205 def __unicode__(self): 206 return unicode(self.tag) 207 208 class Meta: 209 verbose_name = _(u"etiqueta de categoría") 210 verbose_name_plural = _(u"etiquetas de categoría") 211 ordering = ('sort',) 212 unique_together = ( ('subsection','tag',) ,('sort','subsection',)) 213 214 @models.permalink 215 def get_absolute_url(self): 216 217 return ('subsection_tag', (), { 218 'subsection': self.subsection.slug, 219 'tag': self.tag.slug}) 220 221class Entry(models.Model): 222 title = models.CharField(_(u"Título"),max_length=255,blank=False,unique=True) 223 content = tinymce_models.HTMLField(_("Contenido"),blank=False,) 224 brief = models.CharField(_(u'Resumen'),max_length=450, help_text = _(u'Un breve resumen representativo de la entrada. Si queda vacío se cogerá el primer párrafo.'),blank=True) 225 author = models.ForeignKey(User,verbose_name=_(u"Autor"),related_name="entries",blank=False,null=True) 226 ilustrator = models.ForeignKey(User,verbose_name=_(u"Ilustrador"),related_name="ilustrations",blank=True,null=True,default=None) 227 number = models.ForeignKey(Number,verbose_name=_(u"Número"),null=True,default=None,blank=True,related_name='entries') 228 #section = models.ForeignKey(Section,verbose_name=_(u"Sección")) 229 subsection = models.ForeignKey(Subsection,verbose_name=_(u"Categoría"),related_name="entries") 230 created = models.DateTimeField(_(u'Creado'),help_text=_("La hora no tiene importancia")) 231 modified = models.DateTimeField(_(u'Modificado'),auto_now=True) 232 published = models.BooleanField(_(u'Publicado'),default=False,blank=False) 233 slug = models.SlugField(max_length=255,unique=True,blank=True,help_text=_(u"Será generada automaticamente a partir del título")) 234 views = models.PositiveIntegerField("V",db_index=True,default=0,blank=True) 235 ncomments = models.PositiveIntegerField("C",db_index=True,default=0,blank=True) 236 shared = models.PositiveIntegerField("S",db_index=True,default=0,blank=True) 237 #gallery = models.BooleanField(_(u'Mostrar en galería de HOME'),help_text=_(u'Se mostrará sólo la imagen marcada cómo principal'),default=False,blank=True) 238 #show_gallery = models.BooleanField(_(u'Mostrar galería en entrada'),help_text=_(u'Se mostrará en la propia entrada una galería con las imágenes en el orden establecido. Si hay sólo una imagen se mostrará la imagen estática'),default=False,blank=True) 239 tags = TaggableManager() 240 imagen = FileBrowseField(blank=False,format='image',extensions=[".jpg",".png",".jpeg",".gif"],default='') 241 is_editorial = models.BooleanField(_(u'Es editorial'),default=False,blank=True) 242 is_cartoon = models.BooleanField(_(u'Es viñeta'),default=False,blank=True) 243 244 def __unicode__(self): 245 return unicode(self.title) 246 247 def add_view_mark(self,ip,user): 248 if not self.published: 249 return 250 251 key = "thechurch-view-%s-%s"%(ip,user.id,) 252 if not cache.get(key) and not self.author==user: 253 self.views+=1 254 self.save() 255 256 cache.set(key,"ok",3600) 257 258 259 @classmethod 260 def get_archive(self,year,month): 261 current = datetime.date(int(year),int(month),1) 262 return self.objects.filter(created__gte=current,created__lt=current+relativedelta(months=1)) 263 264 """@classmethod 265 def search(self,q): 266 return self.objects.raw("SELECT * FROM blog_entry WHERE MATCH (title) AGAINST ('%s');" % q)""" 267 268 @classmethod 269 def search(self,q): 270 from whoosh.filedb.filestore import FileStorage 271 from whoosh.qparser import MultifieldParser 272 storage = FileStorage(settings.WHOOSH_INDEX) 273 ix = storage.open_index() 274 q = q.replace('+', ' AND ').replace(' -', ' NOT ') 275 parser = MultifieldParser(["content","title","tags","author"], schema=ix.schema) 276 qry = parser.parse(q) 277 searcher = ix.searcher() 278 hits = searcher.search(qry) 279 280 281 282 return self.objects.filter(id__in=[h.fields()['id'] for h in hits]).filter(published=True) 283 284 @classmethod 285 def get_home_gallery(self): 286 return Entry.objects.filter(published=True,gallery=True).order_by('-created') 287 288 def save(self,*args,**kwargs): 289 self.slug = slugify(self.title) 290 super(type(self),self).save(*args,**kwargs) 291 292 class Meta: 293 verbose_name = _(u"entrada") 294 ordering = ('-created',) 295 296 @models.permalink 297 def get_absolute_url(self): 298 return ('entry', (), { 299 'number':self.number.number, 300 'month':self.number.month, 301 'year':self.number.year, 302 'subsection': self.subsection.slug, 303 'slug': self.slug}) 304 305 306 """@models.permalink 307 def get_absolute_url(self): 308 return ('common', (), { 309 'slug': self.slug})""" 310 311 def get_admin_url(self): 312 return reverse("admin:blog_entry_change", args=[self.id]) 313 314 def get_approved_comments(self): 315 return self.comments.filter(approved=True) 316 317 def get_comments(self): 318 return self.comments.all() 319 320 def get_related(self): 321 return Entry.objects.filter(subsection__hidden=False,number__published=True,tags__name__in=self.tags.values_list("name",flat=True).all()[:]).exclude(id=self.id).exclude(published=False).distinct()[:4] 322 323 def n_comments(self): 324 return self.comments.all().count() 325 326 @classmethod 327 def get_last(self,**kwargs): 328 329 if kwargs: 330 return Entry.objects.order_by('-created').filter(**kwargs).filter(published=True) 331 else: 332 return Entry.objects.order_by('-created').filter(published=True) 333 334 @classmethod 335 def get_last_by_author(self,author,entry=None,max=settings.BLOG_OTHER_LAST_ENTRIES): 336 337 if author.get_profile().is_ilustrator: 338 entries = Entry.objects.filter(published=True,ilustrator__id=author.id,number__published=True).order_by('-created') 339 else: 340 entries = Entry.objects.filter(published=True,author__id=author.id,is_cartoon=False,number__published=True).order_by('-created') 341 342 343 if entry: 344 return entries.exclude(id=entry.id) 345 346 return entries 347 348 @classmethod 349 def get_last_by_section(self,section=None,max=settings.BLOG_RIGHT_LAST_ENTRIES): 350 351 entries = {} 352 353 q = models.Q(published=True) 354 355 for s in Section.objects.all()[:]: 356 if not section is None: 357 if s.id <> section.id: 358 query = Entry.objects.filter( q ).filter(section__id=s.id) 359 if query.count()>0: 360 entries[unicode(s)] = query[:max] 361 else: 362 query = Entry.objects.filter( q ).filter(section__id=s.id) 363 if query.count()>0: 364 entries[unicode(s)] = query[:max] 365 366 return entries 367 368 def get_main_image(self): 369 if self.imagen: 370 return self.imagen 371 return self.images.get(main=True).file 372 373 def get_brief(self): 374 import re 375 from templatetags.blog import unescape 376 377 if self.brief: 378 return unescape(re.sub(r'<[^>]+>','',self.brief)) 379 380 content = unescape(re.sub(r'<[^>]+>','',self.content)) 381 382 if len(content) < 300: 383 return content 384 385 idx = 0 386 for i in range(300,450): 387 if content[i] == ".": 388 idx = i 389 break 390 else: 391 return content[:450]+"..." 392 393 return content[:idx+1] 394 395class ImageGallery(models.Model): 396 entry = models.ForeignKey(Entry,verbose_name=_(u"Entrada"),related_name="images") 397 file = FileBrowseField(blank=False,format='image',extensions=[".jpg",".png",".jpeg",".gif"]) 398 #file = models.ImageField(blank=False,upload_to='images/%Y/%m') 399 order = models.PositiveIntegerField(_(u'Orden en la galería'),default=1) 400 main = models.BooleanField(_(u'Usar como principal')) 401 adjust = models.BooleanField(_(u'Auto-ajustar'),default=False) 402 403 def __unicode__(self): 404 return unicode(self.file) 405 406 class Meta: 407 verbose_name = _(u"Imagen de galería") 408 verbose_name_plural = _(u"Imagenes de galería") 409 ordering = ('order',) 410 #unique_together = ('entry','order',) 411 412 413class Comment(models.Model): 414 entry = models.ForeignKey(Entry,verbose_name=_(u"Entrada"),related_name="comments",blank=True) 415 author = models.CharField(_(u'Nombre'),max_length=256) 416 email = models.EmailField(_(u'Email'),max_length=256) 417 time = models.DateTimeField(_(u'Enviado'),auto_now_add=False) 418 website = models.URLField(_(u'Web'),blank=True,default="") 419 approved = models.BooleanField(_(u'Aprobado'),blank=True,default=True) 420 content = models.TextField() 421 422 def save(self,*args,**kwargs): 423 424 if not getattr(self,"pk",None): 425 e = self.entry 426 e.ncomments+=1 427 e.save() 428 429 super(Comment,self).save(self,*args,**kwargs) 430 431 432 def notify(self): 433 to = set(UserProfile.get_by_rol(settings.BLOG_EDITOR_ROL_ID).values_list("user__email",flat=True)) 434 to.add(self.entry.author.email) 435 [ to.add(e[1]) for e in settings.ADMINS ] 436 [ to.add(e) for e in Comment.objects.filter(entry=self.entry).values_list("email",flat=True) ] 437 438 439 msg = "Se ha añadido un nuevo comentario a la entrada '%s'.\n\nPuedes verlo en http://thechurchofhorrors.com%s#comments" % (self.entry,self.entry.get_absolute_url()) 440 441 for e in to: 442 if e: 443 send_mail('[TheChurchofHorrors] Nuevo comentario', msg, settings.BLOG_DEFAULT_SENDER, [e], fail_silently=False) 444 445 def __unicode__(self): 446 return self.content 447 448 class Meta: 449 verbose_name = _(u"comentario") 450 ordering = ('time',) 451 452 453# notify to editors signal 454from django.db.models.signals import post_save 455from django.dispatch import receiver 456from userprofile.models import UserProfile 457from django.core.mail import send_mail 458 459@receiver(post_save, sender=Entry) 460def notify_editors(sender, instance, created, **kwargs): 461 462 if created: 463 editors = list(UserProfile.get_by_rol(settings.BLOG_EDITOR_ROL_ID).values_list("user__email",flat=True)) 464 465 msg = "Se ha creado una nueva entrada.\nPuedes verla en http://thechurchofhorrors.com%s" % instance.get_admin_url() 466 467 send_mail('[TheChurchofHorrors] Nueva entrada', msg, settings.BLOG_DEFAULT_SENDER, editors, fail_silently=False) 468 469 470""" WHOOSH """ 471 472from django.db.models import signals 473import os 474from whoosh import fields 475from whoosh.filedb.filestore import FileStorage 476 477WHOOSH_SCHEMA = fields.Schema(title=fields.TEXT(stored=True), 478 content=fields.TEXT, 479 tags=fields.TEXT, 480 author=fields.TEXT, 481 id=fields.ID(stored=True, unique=True)) 482 483def create_index(sender=None, **kwargs): 484 if not os.path.exists(settings.WHOOSH_INDEX): 485 os.mkdir(settings.WHOOSH_INDEX) 486 storage = FileStorage(settings.WHOOSH_INDEX) 487 ix = storage.create_index(schema=WHOOSH_SCHEMA) 488 489signals.post_syncdb.connect(create_index) 490 491 492def update_index(sender, instance, created, **kwargs): 493 storage = FileStorage(settings.WHOOSH_INDEX) 494 ix = storage.open_index() 495 496 try: 497 writer = ix.writer() 498 except: 499 return 500 501 tags = [] 502 for t in instance.tags.all(): 503 try: 504 tags.append(unicode(t.name)) 505 except: 506 pass 507 508 tags = u','.join(tags) 509 510 try: 511 512 if created: 513 writer.add_document(title=instance.title, content=instance.content,tags=tags,author=instance.author.get_profile().name+u"\n"+instance.author.username, 514 id=unicode(instance.pk)) 515 writer.commit() 516 else: 517 writer.update_document(title=instance.title, content=instance.content,tags=tags,author=instance.author.get_profile().name+u"\n"+instance.author.username, 518 id=unicode(instance.pk)) 519 writer.commit() 520 except: 521 pass 522 523signals.post_save.connect(update_index, sender=Entry) 524 525""" END WHOOSH """ 526 527""" 528@receiver(post_save, sender=Comment) 529def notify_newcomment(sender, instance, created, **kwargs): 530 531 if created: 532 to = set(UserProfile.get_by_rol(settings.BLOG_EDITOR_ROL_ID).values_list("user__email",flat=True)) 533 to.add(instance.entry.author.email) 534 for e in settings.BLOG_CONTACT_EMAILS: 535 to.add(e) 536 537 538 msg = "Se ha añadido un nuevo comentario.\nPuedes verlo en http://thechurchofhorrors.com%s#comments" % instance.entry.get_absolute_url() 539 540 send_mail('[TheChurchofHorrors] Nueva comentario', msg, settings.BLOG_DEFAULT_SENDER, to, fail_silently=False)""" 541 542 543