# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models from django.core.validators import MaxValueValidator from django.core.exceptions import ValidationError from localflavor.generic.models import IBANField, BICField from localflavor.fr.models import FRSIRETField from coin.members.models import count_active_members from coin.offers.models import count_active_subscriptions from coin import utils # API version, see http://db.ffdn.org/format API_VERSION = 0.1 TECHNOLOGIES = (('ftth', 'FTTH'), ('dsl', '*DSL'), ('wifi', 'WiFi')) class SingleInstanceMixin(object): """Makes sure that no more than one instance of a given model is created.""" def clean(self): model = self.__class__ if (model.objects.count() > 0 and self.id != model.objects.get().id): raise ValidationError("Can only create 1 instance of %s" % model.__name__) super(SingleInstanceMixin, self).clean() class ISPInfo(SingleInstanceMixin, models.Model): """http://db.ffdn.org/format The naming convention is different from Python/django so that it matches exactly the format (which uses CamelCase...) """ name = models.CharField(max_length=512, help_text="The ISP's name") # Length required by the spec shortname = models.CharField(max_length=15, blank=True, help_text="Shorter name") description = models.TextField(blank=True, help_text="Short text describing the project") logoURL = models.URLField(blank=True, verbose_name="logo URL", help_text="HTTP(S) URL of the ISP's logo") website = models.URLField(blank=True, help_text='URL to the official website') email = models.EmailField(max_length=254, help_text="Contact email address") mainMailingList = models.EmailField(max_length=254, blank=True, verbose_name="main mailing list", help_text="Main public mailing-list") creationDate = models.DateField(blank=True, null=True, verbose_name="creation date", help_text="Date of creation for legal structure") ffdnMemberSince = models.DateField(blank=True, null=True, verbose_name="FFDN member since", help_text="Date at wich the ISP joined the Federation") # TODO: choice field progressStatus = models.PositiveSmallIntegerField( validators=[MaxValueValidator(7)], blank=True, null=True, verbose_name='progress status', help_text="Progression status of the ISP") # TODO: better model for coordinates latitude = models.FloatField(blank=True, null=True, help_text="Coordinates of the registered office (latitude)") longitude = models.FloatField(blank=True, null=True, help_text="Coordinates of the registered office (longitude)") # Uncomment this if you want to manage these counters by hand. #member_count = models.PositiveIntegerField(help_text="Number of members") #subscriber_count = models.PositiveIntegerField( # help_text="Number of subscribers to an internet access") # field outside of db-ffdn format: administrative_email = models.EmailField( max_length=254, blank=True, verbose_name="contact administratif", help_text='Adresse email pour les contacts administratifs (ex: bureau)') support_email = models.EmailField( max_length=254, blank=True, verbose_name="contact de support", help_text="Adresse email pour les demandes de support technique") lists_url = models.URLField( verbose_name="serveur de listes", blank=True, help_text="URL du serveur de listes de discussions/diffusion") @property def memberCount(self): """Number of members""" return count_active_members() @property def subscriberCount(self): """Number of subscribers to an internet access""" return count_active_subscriptions() @property def version(self): """Version of the API""" return API_VERSION @property def main_chat_verbose(self): m = utils.re_chat_url.match(self.chatroom_set.first().url) return '{channel} sur {server}'.format(**(m.groupdict())) def get_absolute_url(self): return '/isp.json' def to_dict(self): data = dict() # These are required for f in ('version', 'name', 'email', 'memberCount', 'subscriberCount'): data[f] = getattr(self, f) # These are optional for f in ('shortname', 'description', 'logoURL', 'website', 'mainMailingList', 'progressStatus'): if getattr(self, f): data[f] = getattr(self, f) # Dates for d in ('creationDate', 'ffdnMemberSince'): if getattr(self, d): data[d] = getattr(self, d).isoformat() # Hackish for now if self.latitude or self.longitude: data['coordinates'] = { "latitude": self.latitude, "longitude": self.longitude } # Related objects data['coveredAreas'] = [c.to_dict() for c in self.coveredarea_set.all()] otherwebsites = self.otherwebsite_set.all() if otherwebsites: data['otherWebsites'] = { site.name: site.url for site in otherwebsites } chatrooms = self.chatroom_set.all() if chatrooms: data['chatrooms'] = [chatroom.url for chatroom in chatrooms] if hasattr(self, 'registeredoffice'): data['registeredOffice'] = self.registeredoffice.to_dict() return data def __unicode__(self): return self.name class OtherWebsite(models.Model): name = models.CharField(max_length=512) url = models.URLField(verbose_name="URL") isp = models.ForeignKey(ISPInfo) class RegisteredOffice(models.Model): """ http://json-schema.org/address """ post_office_box = models.CharField(max_length=512, blank=True) extended_address = models.CharField(max_length=512, blank=True) street_address = models.CharField(max_length=512, blank=True) locality = models.CharField(max_length=512) region = models.CharField(max_length=512) postal_code = models.CharField(max_length=512, blank=True) country_name = models.CharField(max_length=512) isp = models.OneToOneField(ISPInfo) # not in db.ffdn.org spec siret = FRSIRETField('SIRET') def to_dict(self): d = dict() for field in ('post_office_box', 'extended_address', 'street_address', 'locality', 'region', 'postal_code', 'country_name'): if getattr(self, field): key = field.replace('_', '-') d[key] = getattr(self, field) return d class ChatRoom(models.Model): url = models.CharField(verbose_name="URL", max_length=256) isp = models.ForeignKey(ISPInfo) class CoveredArea(models.Model): name = models.CharField(max_length=512) # TODO: we must allow multiple values technologies = models.CharField(choices=TECHNOLOGIES, max_length=16) # TODO: find a geojson library #area = isp = models.ForeignKey(ISPInfo) def to_dict(self): return {"name": self.name, "technologies": [self.technologies]} class BankInfo(models.Model): """Information about bank account and the bank itself This is out of the scope of db.ffdn.org spec. """ isp = models.OneToOneField(ISPInfo) iban = IBANField('IBAN') bic = BICField('BIC', blank=True, null=True) bank_name = models.CharField('établissement bancaire', max_length=100, blank=True, null=True) check_order = models.CharField('ordre', max_length=100, blank=False, null=False, help_text='Ordre devant figurer sur un \ chèque bancaire à destination de\ l\'association') class Meta: verbose_name = 'coordonnées bancaires' verbose_name_plural = verbose_name