1
0

models.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. from django.db import models
  4. from django.core.validators import MaxValueValidator
  5. from django.core.exceptions import ValidationError
  6. from localflavor.generic.models import IBANField, BICField
  7. from localflavor.fr.models import FRSIRETField
  8. from multiselectfield import MultiSelectField
  9. from coin.members.models import count_active_members
  10. from coin.offers.models import count_active_subscriptions
  11. from coin import utils
  12. from coin.validation import chatroom_url_validator
  13. # API version, see http://db.ffdn.org/format
  14. API_VERSION = 0.1
  15. TECHNOLOGIES = (('ftth', 'FTTH'),
  16. ('dsl', '*DSL'),
  17. ('wifi', 'WiFi'),
  18. ('vpn', 'VPN'),
  19. ('cube', 'Brique Internet'))
  20. class SingleInstanceMixin(object):
  21. """Makes sure that no more than one instance of a given model is created."""
  22. def clean(self):
  23. model = self.__class__
  24. if (model.objects.count() > 0 and self.id != model.objects.get().id):
  25. raise ValidationError("Can only create 1 instance of %s" % model.__name__)
  26. super(SingleInstanceMixin, self).clean()
  27. class ISPInfo(SingleInstanceMixin, models.Model):
  28. """http://db.ffdn.org/format
  29. The naming convention is different from Python/django so that it
  30. matches exactly the format (which uses CamelCase...)
  31. """
  32. # These two properties can be overriden with static counters, see below.
  33. @property
  34. def memberCount(self):
  35. """Number of members"""
  36. return count_active_members()
  37. @property
  38. def subscriberCount(self):
  39. """Number of subscribers to an internet access"""
  40. return count_active_subscriptions()
  41. name = models.CharField(max_length=512,
  42. help_text="The ISP's name")
  43. # Length required by the spec
  44. shortname = models.CharField(max_length=15, blank=True,
  45. help_text="Shorter name")
  46. description = models.TextField(blank=True,
  47. help_text="Short text describing the project")
  48. logoURL = models.URLField(blank=True,
  49. verbose_name="logo URL",
  50. help_text="HTTP(S) URL of the ISP's logo")
  51. website = models.URLField(blank=True,
  52. help_text='URL to the official website')
  53. email = models.EmailField(help_text="Contact email address")
  54. mainMailingList = models.EmailField(blank=True,
  55. verbose_name="main mailing list",
  56. help_text="Main public mailing-list")
  57. phone_number = models.CharField(max_length=25, blank=True,
  58. verbose_name="phone number",
  59. help_text='Main contact phone number')
  60. creationDate = models.DateField(blank=True, null=True,
  61. verbose_name="creation date",
  62. help_text="Date of creation for legal structure")
  63. ffdnMemberSince = models.DateField(blank=True, null=True,
  64. verbose_name="FFDN member since",
  65. help_text="Date at wich the ISP joined the Federation")
  66. # TODO: choice field
  67. progressStatus = models.PositiveSmallIntegerField(
  68. validators=[MaxValueValidator(7)],
  69. blank=True, null=True, verbose_name='progress status',
  70. help_text="Progression status of the ISP")
  71. # TODO: better model for coordinates
  72. latitude = models.FloatField(blank=True, null=True,
  73. help_text="Coordinates of the registered office (latitude)")
  74. longitude = models.FloatField(blank=True, null=True,
  75. help_text="Coordinates of the registered office (longitude)")
  76. # Uncomment this (and handle the necessary migrations) if you want to
  77. # manage one of the counters by hand. Otherwise, they are computed
  78. # automatically, which is probably what you want.
  79. #memberCount = models.PositiveIntegerField(help_text="Number of members",
  80. # default=0)
  81. #subscriberCount = models.PositiveIntegerField(
  82. # help_text="Number of subscribers to an internet access",
  83. # default=0)
  84. # field outside of db-ffdn format:
  85. administrative_email = models.EmailField(
  86. blank=True, verbose_name="contact administratif",
  87. help_text='Adresse email pour les contacts administratifs (ex: bureau)')
  88. support_email = models.EmailField(
  89. blank=True, verbose_name="contact de support",
  90. help_text="Adresse email pour les demandes de support technique")
  91. lists_url = models.URLField(
  92. verbose_name="serveur de listes", blank=True,
  93. help_text="URL du serveur de listes de discussions/diffusion")
  94. @property
  95. def version(self):
  96. """Version of the API"""
  97. return API_VERSION
  98. @property
  99. def main_chat_verbose(self):
  100. first_chatroom = self.chatroom_set.first()
  101. if first_chatroom:
  102. m = utils.re_chat_url.match(first_chatroom.url)
  103. if m:
  104. return '{channel} sur {server}'.format(**(m.groupdict()))
  105. return None
  106. def get_absolute_url(self):
  107. return '/isp.json'
  108. def to_dict(self):
  109. data = dict()
  110. # These are required
  111. for f in ('version', 'name', 'email', 'memberCount', 'subscriberCount'):
  112. data[f] = getattr(self, f)
  113. # These are optional
  114. for f in ('shortname', 'description', 'logoURL', 'website',
  115. 'mainMailingList', 'progressStatus'):
  116. if getattr(self, f):
  117. data[f] = getattr(self, f)
  118. # Dates
  119. for d in ('creationDate', 'ffdnMemberSince'):
  120. if getattr(self, d):
  121. data[d] = getattr(self, d).isoformat()
  122. # Hackish for now
  123. if self.latitude or self.longitude:
  124. data['coordinates'] = { "latitude": self.latitude,
  125. "longitude": self.longitude }
  126. # Related objects
  127. data['coveredAreas'] = [c.to_dict() for c in self.coveredarea_set.all()]
  128. otherwebsites = self.otherwebsite_set.all()
  129. if otherwebsites:
  130. data['otherWebsites'] = { site.name: site.url for site in otherwebsites }
  131. chatrooms = self.chatroom_set.all()
  132. if chatrooms:
  133. data['chatrooms'] = [chatroom.url for chatroom in chatrooms]
  134. if hasattr(self, 'registeredoffice'):
  135. data['registeredOffice'] = self.registeredoffice.to_dict()
  136. return data
  137. def __unicode__(self):
  138. return self.name
  139. class OtherWebsite(models.Model):
  140. name = models.CharField(max_length=512)
  141. url = models.URLField(verbose_name="URL")
  142. isp = models.ForeignKey(ISPInfo)
  143. class RegisteredOffice(models.Model):
  144. """ http://json-schema.org/address """
  145. post_office_box = models.CharField(max_length=512, blank=True)
  146. extended_address = models.CharField(max_length=512, blank=True)
  147. street_address = models.CharField(max_length=512, blank=True)
  148. locality = models.CharField(max_length=512)
  149. region = models.CharField(max_length=512)
  150. postal_code = models.CharField(max_length=512, blank=True)
  151. country_name = models.CharField(max_length=512)
  152. isp = models.OneToOneField(ISPInfo)
  153. # not in db.ffdn.org spec
  154. siret = FRSIRETField('SIRET')
  155. def to_dict(self):
  156. d = dict()
  157. for field in ('post_office_box', 'extended_address', 'street_address',
  158. 'locality', 'region', 'postal_code', 'country_name'):
  159. if getattr(self, field):
  160. key = field.replace('_', '-')
  161. d[key] = getattr(self, field)
  162. return d
  163. class ChatRoom(models.Model):
  164. url = models.CharField(
  165. verbose_name="URL", max_length=256, validators=[chatroom_url_validator])
  166. isp = models.ForeignKey(ISPInfo)
  167. class CoveredArea(models.Model):
  168. name = models.CharField(max_length=512)
  169. technologies = MultiSelectField(choices=TECHNOLOGIES, max_length=42)
  170. # TODO: find a geojson library
  171. #area =
  172. isp = models.ForeignKey(ISPInfo)
  173. def to_dict(self):
  174. return {"name": self.name,
  175. "technologies": self.technologies}
  176. class BankInfo(models.Model):
  177. """Information about bank account and the bank itself
  178. This is out of the scope of db.ffdn.org spec.
  179. """
  180. isp = models.OneToOneField(ISPInfo)
  181. iban = IBANField('IBAN')
  182. bic = BICField('BIC', blank=True, null=True)
  183. bank_name = models.CharField('établissement bancaire',
  184. max_length=100, blank=True, null=True)
  185. check_order = models.CharField('ordre',
  186. max_length=100, blank=False, null=False,
  187. help_text='Ordre devant figurer sur un \
  188. chèque bancaire à destination de\
  189. l\'association')
  190. class Meta:
  191. verbose_name = 'coordonnées bancaires'
  192. verbose_name_plural = verbose_name