# -*- 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 multiselectfield import MultiSelectField from coin.members.models import count_active_members from coin.offers.models import count_active_subscriptions from coin import utils from coin.validation import chatroom_url_validator # API version, see http://db.ffdn.org/format API_VERSION = 0.1 TECHNOLOGIES = (('ftth', 'FTTH'), ('dsl', '*DSL'), ('wifi', 'WiFi'), ('vpn', 'VPN'), ('cube', 'Brique Internet')) 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...) """ # These two properties can be overriden with static counters, see below. @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() name = models.CharField(max_length=512, verbose_name="Nom", help_text="Nom du FAI") # Length required by the spec shortname = models.CharField(max_length=15, blank=True, verbose_name="Abréviation", help_text="Nom plus court") description = models.TextField(blank=True, verbose_name="Description", help_text="Description courte du projet") logoURL = models.URLField(blank=True, verbose_name="URL du logo", help_text="Adresse HTTP(S) du logo du FAI") website = models.URLField(blank=True, verbose_name="URL du site Internet", help_text='Adresse URL du site Internet') email = models.EmailField(verbose_name="Courriel", help_text="Adresse courriel de contact") mainMailingList = models.EmailField(blank=True, verbose_name="Liste de discussion principale", help_text="Principale liste de discussion publique") phone_number = models.CharField(max_length=25, blank=True, verbose_name="Numéro de téléphone", help_text='Numéro de téléphone de contact principal') creationDate = models.DateField(blank=True, null=True, verbose_name="Date de création", help_text="Date de création de la structure légale") ffdnMemberSince = models.DateField(blank=True, null=True, verbose_name="Membre de FFDN depuis", help_text="Date à laquelle le FAI a rejoint la Fédération FDN") # TODO: choice field progressStatus = models.PositiveSmallIntegerField( validators=[MaxValueValidator(7)], blank=True, null=True, verbose_name="État d'avancement", help_text="État d'avancement du FAI") # TODO: better model for coordinates latitude = models.FloatField(blank=True, null=True, verbose_name="Latitude", help_text="Coordonnées latitudinales du siège") longitude = models.FloatField(blank=True, null=True, verbose_name="Longitude", help_text="Coordonnées longitudinales du siège") # Uncomment this (and handle the necessary migrations) if you want to # manage one of the counters by hand. Otherwise, they are computed # automatically, which is probably what you want. #memberCount = models.PositiveIntegerField(help_text="Nombre de membres", # default=0) #subscriberCount = models.PositiveIntegerField( # help_text="Nombre d'abonnés à un accès Internet", # default=0) # field outside of db-ffdn format: administrative_email = models.EmailField( blank=True, verbose_name="contact administratif", help_text='Adresse email pour les contacts administratifs (ex: bureau)') support_email = models.EmailField( 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") class Meta: verbose_name = "Information du FAI" verbose_name_plural = "Informations du FAI" @property def version(self): """Version de l'API""" return API_VERSION @property def main_chat_verbose(self): first_chatroom = self.chatroom_set.first() if first_chatroom: m = utils.re_chat_url.match(first_chatroom.url) if m: return '{channel} sur {server}'.format(**(m.groupdict())) return None 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, verbose_name="Nom") url = models.URLField(verbose_name="URL") isp = models.ForeignKey(ISPInfo) class Meta: verbose_name = "Autre site Internet" verbose_name_plural = "Autres sites Internet" class RegisteredOffice(models.Model): """ http://json-schema.org/address """ post_office_box = models.CharField(max_length=512, blank=True, verbose_name="Boîte postale") extended_address = models.CharField(max_length=512, blank=True, verbose_name="Adresse complémentaire") street_address = models.CharField(max_length=512, blank=True, verbose_name="Adresse") locality = models.CharField(max_length=512, verbose_name="Ville") region = models.CharField(max_length=512, verbose_name="Région") postal_code = models.CharField(max_length=512, blank=True, verbose_name="Code postal") country_name = models.CharField(max_length=512, verbose_name="Pays") isp = models.OneToOneField(ISPInfo) # not in db.ffdn.org spec siret = FRSIRETField('SIRET') class Meta: verbose_name = "Siège social" verbose_name_plural = "Sièges sociaux" 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, validators=[chatroom_url_validator]) isp = models.ForeignKey(ISPInfo) class Meta: verbose_name = "Salon de discussions" verbose_name_plural = "Salons de discussions" class CoveredArea(models.Model): name = models.CharField(max_length=512, verbose_name="Nom") technologies = MultiSelectField(choices=TECHNOLOGIES, max_length=42, verbose_name="Technologie") # TODO: find a geojson library #area = isp = models.ForeignKey(ISPInfo) def to_dict(self): return {"name": self.name, "technologies": self.technologies} class Meta: verbose_name = "Zone couverte" verbose_name_plural = "Zones couvertes" 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