PageRenderTime 27ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/campaign/models.py

https://github.com/mci/mpatlas
Python | 250 lines | 161 code | 42 blank | 47 comment | 22 complexity | 800acf36b78dcf55f83b9c69532d949f MD5 | raw file
  1. # -*- coding: utf-8 -*-
  2. # Do not remove, we're using some special characters
  3. # in string literals below
  4. from django.contrib.gis.db import models
  5. from django.contrib.gis import geos, gdal
  6. from django.db.models.signals import post_save, post_delete
  7. from django.dispatch import receiver
  8. from django.db import connection, transaction
  9. from django.utils.translation import ugettext_lazy as _
  10. from django.urls import reverse
  11. # from tinymce.models import HTMLField
  12. from ckeditor.fields import RichTextField
  13. from bs4 import BeautifulSoup
  14. from uuslug import uuslug, slugify
  15. import datetime
  16. # import reversion
  17. from reversion import revisions as reversion
  18. from reversion.models import Revision
  19. from mpa.models import Mpa
  20. from filer.fields.image import FilerImageField
  21. from taggit.managers import TaggableManager
  22. from category.models import TaggedItem
  23. # fields variable is overwritten at end of module, listing all fields needed to pull from mpatlas
  24. # via a .values(*fields) call. Update this for new columns.
  25. campaign_export_fields = []
  26. YEAR_CHOICES = [(None, None)] + [(r,r) for r in range(1990, datetime.date.today().year+1)]
  27. class Campaign(models.Model):
  28. # ID / Name
  29. id = models.AutoField('Campaign id', primary_key=True, editable=False)
  30. name = models.CharField('Name', max_length=254)
  31. slug = models.SlugField(max_length=254, unique=True, blank=True, editable=True)
  32. # Taggit TaggableManger used to define categories
  33. categories = TaggableManager(through=TaggedItem, verbose_name='Categories', help_text='You can assign this area to one or more categories by providing a comma-separated list of tags (e.g., [ Shark Sanctuary, World Heritage Site ]', blank=True)
  34. # Set up foreign key to ISO Countries and Sub Locations
  35. country = models.CharField('Country / Territory', max_length=20)
  36. sub_location = models.CharField('Sub Location', max_length=100, null=True, blank=True)
  37. # Summary Info
  38. logo = FilerImageField(verbose_name='Campaign Logo', related_name='campaign_logos', blank=True, null=True, on_delete=models.SET_NULL)
  39. summary = RichTextField('Campaign Description', null=True, blank=True)
  40. # Associated Organizations
  41. organizations = models.ManyToManyField('Organization')
  42. # Associated MPAs
  43. mpas = models.ManyToManyField(Mpa, blank=True)
  44. # Point location, used when we don't have polygon boundaries
  45. is_point = models.BooleanField(default=False)
  46. point_geom = models.PointField(srid=4326, null=True, blank=True, editable=False)
  47. point_geom_smerc = models.PointField(srid=3857, null=True, blank=True, editable=False)
  48. # Full-res polygon features
  49. # If is_point is true, this will be a box or circle based on the
  50. # area estimate (calculated from local UTM crs or a global equal area crs)
  51. geom = models.MultiPolygonField(srid=4326, null=True, blank=True, editable=False)
  52. geom_smerc = models.MultiPolygonField(srid=3857, null=True, blank=True, editable=False)
  53. # Campaign Status
  54. start_year = models.IntegerField(_('Start Year'), choices=YEAR_CHOICES, null=True, blank=True)
  55. active = models.BooleanField(default=True)
  56. # Associated MPAs
  57. mpas = models.ManyToManyField(Mpa, blank=True)
  58. def __str__(self):
  59. return self.name
  60. def get_absolute_url(self):
  61. return reverse('campaign-info', args=[self.slug])
  62. @classmethod
  63. def get_geom_fields(cls):
  64. return ('geom', 'geom_smerc', 'point_geom')
  65. @property
  66. def my_fields(self):
  67. d = {}
  68. for field in self._meta.fields:
  69. d[field.name] = { "verbose": field.verbose_name, "value": field.value_to_string(self) }
  70. return d
  71. @property
  72. def my_fields_list(self):
  73. return list(self.my_fields.items())
  74. @property
  75. def export_field_names(self):
  76. return campaign_export_fields
  77. @property
  78. def export_dict(self):
  79. export_fields = campaign_export_fields[:] # copy list for local manipulation
  80. if 'categories' in campaign_export_fields:
  81. export_fields.remove('categories')
  82. if 'mpas' in campaign_export_fields:
  83. export_fields.remove('mpas')
  84. if 'geom' in export_fields:
  85. export_fields.remove('geom')
  86. if 'point_geom' in export_fields:
  87. export_fields.remove('geom')
  88. export_dict = Campaign.objects.filter(pk=self.id).values(*export_fields).first()
  89. export_dict = OrderedDict([(f, export_dict[f]) for f in export_fields])
  90. if 'categories' in campaign_export_fields:
  91. export_dict['categories'] = list(self.categories.names())
  92. if 'mpas' in campaign_export_fields:
  93. export_dict['mpas'] = list(campaign.mpas.values_list('pk', flat=True))
  94. return export_dict
  95. @transaction.atomic
  96. def process_geom_fields(self):
  97. # Raw SQL update geometry fields, much faster than through django
  98. cursor = connection.cursor()
  99. cursor.execute("UPDATE campaign_campaign SET geom_smerc = ST_TRANSFORM(geom, 3857) WHERE id = %s" , [self.id])
  100. cursor.execute("UPDATE campaign_campaign SET point_geom_smerc = ST_TRANSFORM(point_geom, 3857) WHERE id = %s" , [self.id])
  101. self.is_point = False if self.geom else True
  102. self.save()
  103. def save(self, *args, **kwargs):
  104. # self.slug = uuslug(self.name, instance=self, separator="_") # optional non-dash separator
  105. if not self.slug:
  106. # set slug from name only if slug is empty
  107. self.slug = uuslug(self.name, instance=self)
  108. else:
  109. # set slug from what was given in slug field
  110. # if slug previously set and unique, just return the same slug
  111. # if slug is not unique, append integer (e.g. '-1')
  112. self.slug = uuslug(self.slug, instance=self)
  113. super(Campaign, self).save(*args, **kwargs)
  114. @receiver(post_save, sender=Campaign)
  115. def campaign_post_save(sender, instance, *args, **kwargs):
  116. if kwargs['raw']:
  117. return
  118. # Calculate things once an mpa object is created or updated
  119. # Disconnect post_save so we don't enter recursive loop
  120. post_save.disconnect(campaign_post_save, sender=Campaign)
  121. try:
  122. instance.process_geom_fields()
  123. except:
  124. pass # just move on and stop worrying so much
  125. finally:
  126. post_save.connect(campaign_post_save, sender=Campaign)
  127. try:
  128. from mpatlas.utils import cartompa
  129. cartompa.updateCampaign(instance.pk)
  130. except:
  131. pass # let this fail silently, maybe Carto is unreachable
  132. @receiver(post_delete, sender=Campaign)
  133. def campaign_post_delete(sender, instance, *args, **kwargs):
  134. # Calculate things once an mpa object is created or updated
  135. # Disconnect post_save so we don't enter recursive loop
  136. try:
  137. from mpatlas.utils import cartompa
  138. cartompa.purgeCartoCampaigns()
  139. except:
  140. pass # let this fail silently, maybe Carto is unreachable
  141. class Initiative(models.Model):
  142. # ID / Name
  143. id = models.AutoField('Initiative id', primary_key=True, editable=False)
  144. name = models.CharField('Name', max_length=254)
  145. slug = models.SlugField(max_length=254, unique=True, blank=True, editable=True)
  146. # Summary Info
  147. logo = FilerImageField(verbose_name='Initiative Logo', related_name='initiative_logos', blank=True, null=True, on_delete=models.SET_NULL)
  148. summary = RichTextField('Initiative Description', null=True, blank=True)
  149. # Associated Campaigns
  150. campaigns = models.ManyToManyField(Campaign)
  151. # Associated Organizations
  152. organizations = models.ManyToManyField('Organization')
  153. def __str__(self):
  154. return self.name
  155. def get_absolute_url(self):
  156. return reverse('initiative-info', args=[self.slug])
  157. def save(self, *args, **kwargs):
  158. # self.slug = uuslug(self.name, instance=self, separator="_") # optional non-dash separator
  159. if not self.slug:
  160. # set slug from name only if slug is empty
  161. self.slug = uuslug(self.name, instance=self)
  162. else:
  163. # set slug from what was given in slug field
  164. # if slug previously set and unique, just return the same slug
  165. # if slug is not unique, append integer (e.g. '-1')
  166. self.slug = uuslug(self.slug, instance=self)
  167. super(Initiative, self).save(*args, **kwargs)
  168. class Organization(models.Model):
  169. # ID / Name
  170. id = models.AutoField('Organization id', primary_key=True, editable=False)
  171. name = models.CharField('Name', max_length=254)
  172. nickname = models.CharField('Nickname or Acronym', max_length=254)
  173. slug = models.SlugField(max_length=254, unique=True, blank=True, editable=True)
  174. # Summary Info
  175. logo = FilerImageField(verbose_name='Organization Logo', related_name='organization_logos', blank=True, null=True, on_delete=models.SET_NULL)
  176. website = models.URLField('Website', max_length=254, blank=True)
  177. social_handles = models.CharField('Social Media Handles', max_length=254, blank=True)
  178. summary = RichTextField('Organization Description', null=True, blank=True)
  179. def __str__(self):
  180. return self.name
  181. def get_absolute_url(self):
  182. return reverse('org-info', args=[self.slug])
  183. def save(self, *args, **kwargs):
  184. # self.slug = uuslug(self.name, instance=self, separator="_") # optional non-dash separator
  185. if not self.slug:
  186. # set slug from name only if slug is empty
  187. self.slug = uuslug(self.name, instance=self)
  188. else:
  189. # set slug from what was given in slug field
  190. # if slug previously set and unique, just return the same slug
  191. # if slug is not unique, append integer (e.g. '-1')
  192. self.slug = uuslug(self.slug, instance=self)
  193. super(Organization, self).save(*args, **kwargs)
  194. campaign_export_fields = [
  195. 'id',
  196. 'name',
  197. 'slug',
  198. 'categories',
  199. 'country',
  200. 'sub_location',
  201. 'summary',
  202. 'is_point',
  203. 'start_year',
  204. 'active',
  205. 'mpas',
  206. #'point_geom',
  207. #'geom',
  208. ]