123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- # -*- 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
|