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