class Users(db.Model): id = db.Column(db.Integer, primary_key=True) email = db.Column(db.String(255), nullable=False, unique=True) name = db.Column(db.String(50), nullable=False) profile_pic = db.Column(db.String(), nullable=False) owned_recipes = db.relationship("Recipe", backref="user", lazy=True) favorite_recipes = db.Column(db.PickleType) saved_recipes = db.Column(db.PickleType) shopping_list = db.Column(db.PickleType) ratings = db.relationship("Rating", backref="user", lazy=True)
class Picture(db.Model): id = db.Column(db.Integer, primary_key=True) orga = db.Column(db.Integer) name = db.Column(db.String(80)) filename = db.Column(db.String(255)) radiotext = db.Column(db.String(255)) radiolink = db.Column(db.String(255)) image_url_prefix = db.Column(db.String(255)) channels = db.relationship('Channel', backref='default_picture', lazy='dynamic') def __init__(self, orga): self.orga = orga def __repr__(self): return '<Picture %r[%s]>' % (self.name, self.orga) @property def clean_filename(self): if not self.filename: return '' return self.filename.split('/')[-1] @property def public_url(self): return "%s%s" % (self.image_url_prefix, self.filename) @property def json(self): return to_json(self, self.__class__, ['clean_filename', 'public_url'])
class Recipe(db.Model): id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String(150), nullable=False) description = db.Column(db.String()) number_of_forks = db.Column(db.Integer) forked_from_recipe = db.Column(db.Integer) images = db.Column(db.PickleType, nullable=False) user_submitted_images = db.Column(db.PickleType) videos = db.Column(db.PickleType) user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False) difficulty = db.Column(db.String(50), db.ForeignKey("levels.difficulty"), nullable=False) ingredients = db.Column(db.PickleType, nullable=False) tags = db.relationship("Tag", secondary=tags, back_populates="recipes") instructions = db.Column(db.PickleType, nullable=False) ready_in_minutes = db.Column(db.Integer, nullable=False) servings = db.Column(db.Integer, nullable=False) ratings = db.relationship("Rating", backref="recipe", lazy=True)
class Recipe(db.Model): id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String(150), nullable=False) description = db.Column(db.String()) images = db.Column(db.PickleType, nullable=False) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) difficulty = db.Column(db.String(50), db.ForeignKey('levels.difficulty'), nullable=False) ingredients = db.Column(db.PickleType, nullable=False) tags = db.relationship('Tag', secondary=tags, back_populates='recipes') instructions = db.Column(db.PickleType, nullable=False) ready_in_minutes = db.Column(db.Integer, nullable=False) servings = db.Column(db.Integer, nullable=False)
class Show(db.Model): id = db.Column(db.Integer, primary_key=True) orga = db.Column(db.Integer) medium_name = db.Column(db.String(255)) long_name = db.Column(db.String(255)) description = db.Column(db.String(255)) color = db.Column(db.String(7)) station_id = db.Column(db.Integer, db.ForeignKey('station.id')) schedules = db.relationship('Schedule', backref='show', lazy='dynamic') def __init__(self, orga): self.orga = orga def __repr__(self): return '<Show %r[%s]>' % (self.medium_name, self.orga) @property def json(self): return to_json(self, self.__class__, [])
class Tag(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(255), unique=True) recipes = db.relationship("Recipe", secondary=tags, back_populates="tags")
class Levels(db.Model): id = db.Column(db.Integer, primary_key=True) difficulty = db.Column(db.String(50), unique=True) recipes = db.relationship("Recipe", backref="levels", lazy=True)
class LogoImage(db.Model): id = db.Column(db.Integer, primary_key=True) orga = db.Column(db.Integer) codops = db.Column(db.String(255)) name = db.Column(db.String(255)) filename = db.Column(db.String(255)) url32x32 = db.Column(db.String(255)) url112x32 = db.Column(db.String(255)) url128x128 = db.Column(db.String(255)) url320x240 = db.Column(db.String(255)) url600x600 = db.Column(db.String(255)) service_provider_id = db.Column(db.Integer, db.ForeignKey('service_provider.id')) service_provider = db.relationship("ServiceProvider", backref='logo_images', uselist=False, foreign_keys=[service_provider_id]) stations = db.relationship('Station', backref='epg_picture', lazy='dynamic') def __init__(self, orga): self.orga = orga def __repr__(self): return '<LogoImage %r[%s]>' % (self.filename, self.orga) @property def clean_filename(self): if not self.filename: return '' return self.filename.split('/')[-1] @property def public_url(self): if self.service_provider: return "%s%s" % (self.service_provider.image_url_prefix, self.filename) else: return None @property def public_32x32_url(self): if self.url32x32: return "%s%s" % (self.service_provider.image_url_prefix, self.url32x32) else: return self.public_url @property def public_112x32_url(self): if self.url112x32: return "%s%s" % (self.service_provider.image_url_prefix, self.url112x32) else: return self.public_url @property def public_128x128_url(self): if self.url128x128: return "%s%s" % (self.service_provider.image_url_prefix, self.url128x128) else: return self.public_url @property def public_320x240_url(self): if self.url320x240: return "%s%s" % (self.service_provider.image_url_prefix, self.url320x240) else: return self.public_url @property def public_600x600_url(self): if self.url600x600: return "%s%s" % (self.service_provider.image_url_prefix, self.url600x600) else: return self.public_url @property def json(self): return to_json(self, self.__class__, [ 'clean_filename', 'public_url', 'public_32x32_url', 'public_112x32_url', 'public_128x128_url', 'public_320x240_url', 'public_600x600_url' ])
class Channel(db.Model): id = db.Column(db.Integer, primary_key=True) station_id = db.Column(db.Integer, db.ForeignKey('station.id')) name = db.Column(db.String(255)) TYPE_ID_CHOICES = [ ('fm', 'VHF/FM', ['ecc_id', 'pi', 'frequency']), ('dab', 'DAB', ['ecc_id', 'eid', 'sid', 'scids', 'appty_uatype', 'pa', 'mime_type']), ('drm', 'DRM', ['sid']), ('amss', 'AMSS', ['sid']), ('hd', 'HD Radio', ['cc', 'tx']), ('id', 'IP', ['fqdn', 'serviceIdentifier', 'stream_url', 'mime_type', 'bitrate']) ] TO_IGNORE_IN_DNS = ['stream_url', 'mime_type', 'bitrate'] type_id = db.Column(db.String(5), index=True) ecc_id = db.Column(db.Integer, db.ForeignKey('ecc.id')) pi = db.Column(db.String(4)) frequency = db.Column(db.String(5)) eid = db.Column(db.String(4)) sid = db.Column(db.String(8)) scids = db.Column(db.String(3)) appty_uatype = db.Column(db.String(6)) pa = db.Column(db.Integer) stream_url = db.Column(db.String(255)) bitrate = db.Column(db.Integer) mime_type = db.Column(db.String(64)) tx = db.Column(db.String(5)) cc = db.Column(db.String(3)) fqdn = db.Column(db.String(255)) serviceIdentifier = db.Column(db.String(16)) fk_client = db.Column( db.Integer, db.ForeignKey('clients.id', use_alter=True, name='channel_clients_id_fk')) client = db.relationship("Clients", foreign_keys=[fk_client]) default_picture_id = db.Column(db.Integer, db.ForeignKey('picture.id')) servicefollowingentries = db.relationship('GenericServiceFollowingEntry', backref='channel', lazy='dynamic') def __repr__(self): return '<Channel %r[%s]>' % (self.name, self.station.__repr__) def updateservicefollowingentry(self): """Updates the existing service following entry linked to the channel if one""" entries = self.servicefollowingentries.all() for entry in entries: if self.type_id == 'dab' and not self.mime_type: entry.mime_type = 'audio/mpeg' else: entry.mime_type = self.mime_type if self.type_id == 'id' and not self.mime_type: entry.mime_type = 'audio/mpeg' else: entry.mime_type = self.mime_type if self.type_id == 'id' and not self.bitrate: entry.bitrate = 128 else: entry.bitrate = self.bitrate db.session.commit() @property def servicefollowingentry(self): """Return (or create) the associated service following entry""" # Find in exisiting objects entries = self.servicefollowingentries.all() if len(entries) > 0: return entries[0] # Create a new one object = GenericServiceFollowingEntry() object.channel_id = self.id object.active = True object.cost = 100 object.offset = 0 # Mime Type and default values if self.type_id == 'id': object.cost = 100 object.offset = 2000 if not self.mime_type: object.mime_type = 'audio/mpeg' else: object.mime_type = self.mime_type if not self.bitrate: object.bitrate = 128 else: object.bitrate = self.bitrate if self.type_id == 'fm': object.cost = 50 if self.type_id == 'dab': object.cost = 20 if not self.mime_type: object.mime_type = 'audio/mpeg' else: object.mime_type = self.mime_type db.session.add(object) db.session.commit() return object @property def service_identifier(self): ecc = Ecc.query.filter_by(id=self.ecc_id).first() gcc = ecc.pi + ecc.ecc if self.type_id == "fm": return "fm/{}/{}/{}".format(gcc, self.pi, self.frequency) elif self.type_id == "dab": return "dab/{}/{}/{}/{}".format(gcc, self.eid, self.sid, self.scids) @property def topic(self): return self.topic_no_slash + '/' @property def topic_no_slash(self): return '/topic/' + '/'.join(self.dns_entry.split('.')[::-1]) def generate_dns_entry(self, return_iso): val = self.type_id for (t, _, props) in Channel.TYPE_ID_CHOICES: if t == self.type_id: for v in props: if getattr(self, v) is not None: value = str(getattr(self, v)).lower() if v == 'ecc_id': # Special case cc_obj = Ecc.query.filter_by(id=value).first() if return_iso: value = (cc_obj.iso).lower() else: value = (cc_obj.pi + cc_obj.ecc).lower() if v not in Channel.TO_IGNORE_IN_DNS: # Ignore certain values val = value + '.' + val return val @property def dns_entry(self): return self.generate_dns_entry(False) @property def dns_entry_iso(self): return self.generate_dns_entry(True) @property def radiodns_entry(self): return self.dns_entry + '.radiodns.org.' @property def station_name(self): if self.station: return self.station.name else: return '' @property def station_ascii_name(self): if self.station: return self.station.ascii_name else: return '' @property def default_picture_data(self): if self.default_picture: return self.default_picture.json else: return None @property def json(self): return to_json(self, self.__class__, [ 'topic', 'station_json', 'radiodns_entry', 'station_name', 'station_ascii_name', 'default_picture_data', 'topic_no_slash', 'client_json' ]) @property def client_json(self): return self.client.json if self.client else {'name': 'default'} @property def station_json(self): if self.station: return self.station.json else: return None @property def dns_values(self): fqdn = '' vis = '' epg = '' tag = '' dns_entry = self.radiodns_entry # Special case with * if dns_entry[0] == '*': dns_entry = '10800' + dns_entry[1:] # Find radiodns servers ns = str(dns.resolver.query('radiodns.org', 'NS')[0]) ip = str(dns.resolver.query(ns, 'A')[0]) # Build a resolver using radiodns.org nameserver, timeout of 2, to be sure to have the latested FQDN resolver = dns.resolver.Resolver() resolver.lifetime = 2 # Timeout of 2 resolver.nameservers = [ip] # Use radiodns.org servers try: fqdn = str(resolver.query(dns_entry, 'CNAME')[0]) except: pass # Build resolver for others queries using local nameserver resolver = dns.resolver.Resolver() resolver.lifetime = 2 # Timeout of 2 if fqdn: try: vis = str(resolver.query('_radiovis._tcp.' + fqdn, 'SRV')[0]) except: pass try: epg = str(resolver.query('_radioepg._tcp.' + fqdn, 'SRV')[0]) except: pass try: tag = str(resolver.query('_radiotag._tcp.' + fqdn, 'SRV')[0]) except: pass return (fqdn, vis, epg, tag) @property def epg_uri(self): # Special Case Urls / Streaming / Ip / Ids if self.type_id == 'id' and self.stream_url: return self.stream_url splited = self.dns_entry.split('.') return splited[-1] + ':' + '.'.join(splited[::-1][1:])
class ServiceProvider(db.Model): id = db.Column(db.Integer, primary_key=True) codops = db.Column(db.String(255), index=True) short_name = db.Column(db.String(8)) medium_name = db.Column(db.String(16)) long_name = db.Column(db.String(128)) short_description = db.Column(db.String(180)) long_description = db.Column(db.String(1200)) url_default = db.Column(db.String(255)) postal_name = db.Column(db.String(255)) street = db.Column(db.String(255)) city = db.Column(db.String(255)) zipcode = db.Column(db.String(25)) phone_number = db.Column(db.String(128)) keywords = db.Column(db.String(255)) default_language = db.Column(db.String(5)) location_country = db.Column(db.String(5)) default_logo_image_id = db.Column( db.Integer, db.ForeignKey('logo_image.id', use_alter=True, name='fk_default_logo_id')) default_logo_image = db.relationship("LogoImage", foreign_keys=[default_logo_image_id]) stations = db.relationship('Station', backref='service_provider', lazy='dynamic') def __init__(self, codops): self.codops = codops def __eq__(self, other): return other is not None and self.id == other.id def check_aws(self): return awsutils.check_serviceprovider(self) def escape_slash_rfc3986(self, value): if value: return value.replace('/', '%2F') return '' @property def default_logo_image_data(self): if self.default_logo_image: return self.default_logo_image.json else: return None @property def epg_country(self): if self.location_country: ecc = Ecc.query.filter_by(iso=self.location_country).first() if ecc: return ecc.name return None @property def epg_postal(self): if self.postal_name: return "postal:%s/%s/%s/%s/%s" % (self.escape_slash_rfc3986( self.postal_name), self.escape_slash_rfc3986( self.street), self.escape_slash_rfc3986( self.city), self.escape_slash_rfc3986(self.zipcode), self.escape_slash_rfc3986( self.epg_country)) return None @property def epg_phone_number(self): if self.phone_number: return "tel:%s" % (self.phone_number) return None @property def fqdn(self): if self.codops: return "%s.%s" % (self.codops.lower(), config.DOMAIN) return None @property def vis_fqdn(self): if self.codops: return "%s.%s" % (self.codops.lower(), config.RADIOVIS_DNS) return None @property def epg_fqdn(self): if self.codops: return "%s.%s" % (self.codops.lower(), config.RADIOEPG_DNS) return None @property def spi_fqdn(self): if self.codops: return "%s.%s" % (self.codops.lower(), config.RADIOSPI_DNS) return None @property def tag_fqdn(self): if self.codops: return "%s.%s" % (self.codops.lower(), config.RADIOTAG_DNS) return None @property def vis_service(self): if self.codops: return "%s.%s" % (self.codops.lower(), config.RADIOVIS_SERVICE_DEFAULT) return None @property def epg_service(self): if self.codops: return "%s.%s" % (self.codops.lower(), config.RADIOEPG_SERVICE_DEFAULT) return None @property def tag_service(self): if self.codops: return "%s.%s" % (self.codops.lower(), config.RADIOTAG_SERVICE_DEFAULT) return None @property def spi_service(self): if self.codops: return "%s.%s" % (self.codops.lower(), config.RADIOSPI_SERVICE_DEFAULT) return None @property def image_url_prefix(self): if config.STANDALONE: return config.LOGO_PUBLIC_URL + "/" else: return awsutils.get_public_urlprefix(self) @property def json(self): return to_json(self, self.__class__, [ 'default_logo_image_data', 'image_url_prefix', 'epg_postal', 'epg_phone_number', 'epg_country', 'fqdn', 'vis_fqdn', 'epg_fqdn', 'tag_fqdn', 'vis_service', 'epg_service', 'tag_service' ])
class Station(db.Model): id = db.Column(db.Integer, primary_key=True) orga = db.Column(db.Integer, index=True) parent = db.Column(db.Integer) name = db.Column(db.String(80)) short_name = db.Column(db.String(8)) medium_name = db.Column(db.String(16)) long_name = db.Column(db.String(128)) short_description = db.Column(db.String(180)) long_description = db.Column(db.String(1200)) url_default = db.Column(db.String(255)) random_password = db.Column(db.String(32)) postal_name = db.Column(db.String(255)) street = db.Column(db.String(255)) city = db.Column(db.String(255)) zipcode = db.Column(db.String(25)) phone_number = db.Column(db.String(128)) sms_number = db.Column(db.String(128)) sms_body = db.Column(db.String(255)) sms_description = db.Column(db.String(255)) email_address = db.Column(db.String(255)) email_description = db.Column(db.String(255)) keywords = db.Column(db.String(255)) default_language = db.Column(db.String(5)) location_country = db.Column(db.String(5)) # Services # fqdn_station_prefix = db.Column(db.String(255)) maybe to add due to filtering issue in Alchemy radiovis_enabled = db.Column(db.Boolean, index=True) radiovis_service = db.Column(db.String(255)) radioepg_enabled = db.Column(db.Boolean, index=True) radioepgpi_enabled = db.Column(db.Boolean, default=False, index=True) radioepg_service = db.Column(db.String(255)) radiotag_enabled = db.Column(db.Boolean, index=True) radiotag_service = db.Column(db.String(255)) radiospi_enabled = db.Column(db.Boolean, index=True) radiospi_service = db.Column(db.String(255)) service_provider_id = db.Column(db.Integer, db.ForeignKey('service_provider.id')) ip_allowed = db.Column( db.String(256)) # A list of ip/subnet, with , between genres = db.Column(db.Text()) channels = db.relationship('Channel', backref='station', lazy='dynamic') shows = db.relationship('Show', backref='station', lazy='dynamic') schedules = db.relationship('Schedule', backref='station', lazy='dynamic') servicefollowingentries = db.relationship('GenericServiceFollowingEntry', backref='station', lazy='dynamic') # epg_picture_id = db.Column(db.Integer, db.ForeignKey('picture_forEPG.id')) default_logo_image_id = db.Column( db.Integer, db.ForeignKey('logo_image.id', use_alter=True, name='fk_epg_default_logo_id')) default_logo_image = db.relationship("LogoImage", foreign_keys=[default_logo_image_id]) fk_client = db.Column( db.Integer, db.ForeignKey('clients.id', use_alter=True, name='station_clients_id_fk')) client = db.relationship("Clients", foreign_keys=[fk_client]) __table_args__ = (db.Index('ix_station_spid_radioepg_enabled', "service_provider_id", "radioepg_enabled"), ) def __getitem__(self, item): return getattr(self, item) def __eq__(self, other): return other is not None and self.id == other.id def escape_slash_rfc3986(self, value): if value: return value.replace('/', '%2F') return None @property def service_provider_data(self): if self.service_provider: return self.service_provider.json else: return None @property def epg_country(self): if self.location_country: ecc = Ecc.query.filter_by(iso=self.location_country).first() if ecc: return ecc.name return None @property def epg_postal(self): if self.postal_name: return "postal:%s/%s/%s/%s/%s" % (self.escape_slash_rfc3986( self.postal_name), self.escape_slash_rfc3986( self.street), self.escape_slash_rfc3986( self.city), self.escape_slash_rfc3986(self.zipcode), self.escape_slash_rfc3986( self.epg_country)) return None @property def epg_phone_number(self): if self.phone_number: return "tel:%s" % (self.phone_number) return None @property def epg_email(self): if self.email_address: return "mailto:%s" % (self.email_address) return None @property def epg_sms(self): if self.sms_body: if self.sms_body: return "sms:%s?%s" % ( self.sms_number, urllib.urlencode({'body': self.sms_body})) else: return "sms:%s" % (self.sms_number) return None @property def genres_list(self): try: return json.loads(self.genres) except: return [] @property def default_logo_image_data(self): if self.default_logo_image: return self.default_logo_image.json else: return None @property def fqdn(self): if self.service_provider: if self.service_provider.codops: return "%s.%s.%s" % ( filter(lambda x: x in string.ascii_letters + string.digits, self.ascii_name.lower()), self.service_provider.codops.lower(), config.DOMAIN) return None @property def service_identifier(self): if self.service_provider: if self.service_provider.codops: return "ebu%s%s" % (self.id, self.service_provider.codops.lower()) return None def __init__(self, orga, name=u''): self.orga = orga self.name = name @property def ascii_name(self): return unicodedata.normalize('NFKD', self.name if self.name else u'').encode( 'ascii', 'ignore') def gen_random_password(self): self.random_password = ''.join( random.choice(string.ascii_letters + string.digits) for x in range(32)) def check_aws(self): return awsutils.check_station(self) @property def short_name_to_use(self): """Return the shortname, based on the name or the short one""" return (self.short_name or self.name)[:8] if self.short_name or self.name else u'' @property def fqdn_prefix(self): return filter(lambda x: x in string.ascii_letters + string.digits, self.ascii_name.lower()) @property def fqdn(self): if self.service_provider: return "%s.%s" % (self.fqdn_prefix, self.service_provider.fqdn) return None @property def stomp_username(self): return str(self.id) + '.' + filter( lambda x: x in string.ascii_letters + string.digits, self.ascii_name.lower()) @property def json(self): return to_json(self, self.__class__, [ 'stomp_username', 'short_name_to_use', 'service_provider_data', 'default_logo_image_data', 'epg_country', 'epg_postal', 'epg_phone_number', 'epg_sms', 'epg_email', 'genres_list', 'ascii_name', 'fqdn', 'fqdn_prefix', 'service_identifier' ])