PageRenderTime 71ms CodeModel.GetById 43ms app.highlight 24ms RepoModel.GetById 0ms app.codeStats 0ms

/TheChurchofHorrors/apps/blog/models.py

https://github.com/dugo/The-Church-of-Horrors
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