class CellAreaKeyMixin(HashKeyMixin): _hashkey_cls = CellAreaKey radio = Column(TinyIntEnum(Radio), autoincrement=False) mcc = Column(SmallInteger, autoincrement=False) mnc = Column(SmallInteger, autoincrement=False) lac = Column(SmallInteger(unsigned=True), autoincrement=False)
class CellAreaKeyMixin(HashKeyMixin): _hashkey_cls = CellAreaKey # mapped via RADIO_TYPE radio = Column(TinyInteger, autoincrement=False) mcc = Column(SmallInteger, autoincrement=False) mnc = Column(SmallInteger, autoincrement=False) lac = Column(SmallInteger(unsigned=True), autoincrement=False)
class Cell(_Model): __tablename__ = 'cell' __table_args__ = ( Index('cell_created_idx', 'created'), Index('cell_modified_idx', 'modified'), Index('cell_new_measures_idx', 'new_measures'), Index('cell_total_measures_idx', 'total_measures'), { 'mysql_engine': 'InnoDB', 'mysql_charset': 'utf8', } ) created = Column(DateTime) modified = Column(DateTime) # lat/lon lat = Column(Double(asdecimal=False)) max_lat = Column(Double(asdecimal=False)) min_lat = Column(Double(asdecimal=False)) lon = Column(Double(asdecimal=False)) max_lon = Column(Double(asdecimal=False)) min_lon = Column(Double(asdecimal=False)) # mapped via RADIO_TYPE radio = Column(TinyInteger, autoincrement=False, primary_key=True) # int in the range 0-1000 mcc = Column(SmallInteger, autoincrement=False, primary_key=True) # int in the range 0-1000 for gsm # int in the range 0-32767 for cdma (system id) mnc = Column(SmallInteger, autoincrement=False, primary_key=True) lac = Column( SmallInteger(unsigned=True), autoincrement=False, primary_key=True) cid = Column(Integer(unsigned=True), autoincrement=False, primary_key=True) psc = Column(SmallInteger) range = Column(Integer) new_measures = Column(Integer(unsigned=True)) total_measures = Column(Integer(unsigned=True)) def __init__(self, *args, **kw): if 'created' not in kw: kw['created'] = util.utcnow() if 'modified' not in kw: kw['modified'] = util.utcnow() if 'lac' not in kw or not kw['lac']: kw['lac'] = 0 if 'cid' not in kw or not kw['cid']: kw['cid'] = 0 if 'range' not in kw: kw['range'] = 0 if 'new_measures' not in kw: kw['new_measures'] = 0 if 'total_measures' not in kw: kw['total_measures'] = 0 super(Cell, self).__init__(*args, **kw)
class CellAreaMixin(PositionMixin, TimeTrackingMixin, CreationMixin): _valid_schema = ValidCellAreaSchema() areaid = Column(CellAreaColumn(7)) radio = Column(TinyIntEnum(Radio), nullable=False) mcc = Column(SmallInteger, nullable=False) mnc = Column(SmallInteger, nullable=False) lac = Column(SmallInteger(unsigned=True), nullable=False) radius = Column(Integer) region = Column(String(2)) avg_cell_radius = Column(Integer(unsigned=True)) num_cells = Column(Integer(unsigned=True)) last_seen = Column(Date) @declared_attr def __table_args__(cls): # NOQA prefix = cls.__tablename__ _indices = ( PrimaryKeyConstraint('areaid'), UniqueConstraint('radio', 'mcc', 'mnc', 'lac', name='%s_areaid_unique' % prefix), Index('%s_region_radio_idx' % prefix, 'region', 'radio'), Index('%s_created_idx' % prefix, 'created'), Index('%s_modified_idx' % prefix, 'modified'), Index('%s_latlon_idx' % prefix, 'lat', 'lon'), ) return _indices + (cls._settings, ) @classmethod def validate(cls, entry, _raise_invalid=False, **kw): validated = super(CellAreaMixin, cls).validate(entry, _raise_invalid=_raise_invalid, **kw) if validated is not None and 'areaid' not in validated: validated['areaid'] = ( validated['radio'], validated['mcc'], validated['mnc'], validated['lac'], ) if (('region' not in validated or not validated['region']) and validated['lat'] is not None and validated['lon'] is not None): validated['region'] = GEOCODER.region_for_cell( validated['lat'], validated['lon'], validated['mcc']) return validated
class CellAreaMixin(PositionMixin, TimeTrackingMixin, CreationMixin): _valid_schema = ValidCellAreaSchema() areaid = Column(CellAreaColumn(7)) radio = Column(TinyIntEnum(Radio), nullable=False) mcc = Column(SmallInteger, nullable=False) mnc = Column(SmallInteger, nullable=False) lac = Column(SmallInteger(unsigned=True), nullable=False) radius = Column(Integer) region = Column(String(2)) avg_cell_radius = Column(Integer(unsigned=True)) num_cells = Column(Integer(unsigned=True)) last_seen = Column(Date) @declared_attr def __table_args__(cls): prefix = cls.__tablename__ _indices = ( PrimaryKeyConstraint("areaid"), UniqueConstraint("radio", "mcc", "mnc", "lac", name="%s_areaid_unique" % prefix), Index("%s_region_radio_idx" % prefix, "region", "radio"), Index("%s_created_idx" % prefix, "created"), Index("%s_modified_idx" % prefix, "modified"), Index("%s_latlon_idx" % prefix, "lat", "lon"), ) return _indices + (cls._settings, ) @classmethod def validate(cls, entry, _raise_invalid=False, **kw): validated = super(CellAreaMixin, cls).validate(entry, _raise_invalid=_raise_invalid, **kw) if validated is not None and "areaid" not in validated: validated["areaid"] = ( validated["radio"], validated["mcc"], validated["mnc"], validated["lac"], ) if (("region" not in validated or not validated["region"]) and validated["lat"] is not None and validated["lon"] is not None): validated["region"] = GEOCODER.region_for_cell( validated["lat"], validated["lon"], validated["mcc"]) return validated
class CellBlocklist(_Model): __tablename__ = 'cell_blacklist' _indices = (PrimaryKeyConstraint('radio', 'mcc', 'mnc', 'lac', 'cid'), ) radio = Column(TinyIntEnum(Radio), autoincrement=False, default=None) mcc = Column(SmallInteger, autoincrement=False, default=None) mnc = Column(SmallInteger, autoincrement=False, default=None) lac = Column(SmallInteger(unsigned=True), autoincrement=False, default=None) cid = Column(Integer(unsigned=True), autoincrement=False, default=None) time = Column(DateTime) count = Column(Integer)
class BaseCell(StationMixin): _valid_schema = ValidCellShardSchema() cellid = Column(CellIdColumn(11)) radio = Column(TinyIntEnum(Radio), autoincrement=False, nullable=False) mcc = Column(SmallInteger, autoincrement=False, nullable=False) mnc = Column(SmallInteger, autoincrement=False, nullable=False) lac = Column(SmallInteger(unsigned=True), autoincrement=False, nullable=False) cid = Column(Integer(unsigned=True), autoincrement=False, nullable=False) psc = Column(SmallInteger, autoincrement=False) @classmethod def validate(cls, entry, _raise_invalid=False, **kw): validated = super(BaseCell, cls).validate(entry, _raise_invalid=_raise_invalid, **kw) if validated is not None: if 'cellid' not in validated: validated['cellid'] = ( validated['radio'], validated['mcc'], validated['mnc'], validated['lac'], validated['cid'], ) if (('region' not in validated or not validated['region']) and validated['lat'] is not None and validated['lon'] is not None): validated['region'] = GEOCODER.region_for_cell( validated['lat'], validated['lon'], validated['mcc']) return validated @property def areaid(self): return encode_cellarea(self.radio, self.mcc, self.mnc, self.lac) @property def unique_key(self): return encode_cellid(*self.cellid)
class OCIDCell(_Model): __tablename__ = 'ocid_cell' __table_args__ = (Index('ocid_cell_created_idx', 'created'), { 'mysql_engine': 'InnoDB', 'mysql_charset': 'utf8', }) created = Column(DateTime) modified = Column(DateTime) # lat/lon lat = Column(Double(asdecimal=False)) lon = Column(Double(asdecimal=False)) # radio mapped via RADIO_TYPE radio = Column(TinyInteger, autoincrement=False, primary_key=True) mcc = Column(SmallInteger, autoincrement=False, primary_key=True) mnc = Column(SmallInteger, autoincrement=False, primary_key=True) lac = Column(SmallInteger(unsigned=True), autoincrement=False, primary_key=True) cid = Column(Integer(unsigned=True), autoincrement=False, primary_key=True) psc = Column(SmallInteger) range = Column(Integer) total_measures = Column(Integer(unsigned=True)) changeable = Column(Boolean) def __init__(self, *args, **kw): if 'created' not in kw: kw['created'] = util.utcnow() if 'modified' not in kw: kw['modified'] = util.utcnow() if 'lac' not in kw: kw['lac'] = -1 if 'cid' not in kw: kw['cid'] = -1 if 'range' not in kw: kw['range'] = 0 if 'total_measures' not in kw: kw['total_measures'] = 0 if 'changeable' not in kw: kw['changeable'] = True super(OCIDCell, self).__init__(*args, **kw)
class CellMeasure(_Model): __tablename__ = 'cell_measure' __table_args__ = ( Index('cell_measure_created_idx', 'created'), Index('cell_measure_key_idx', 'radio', 'mcc', 'mnc', 'lac', 'cid'), { 'mysql_engine': 'InnoDB', 'mysql_charset': 'utf8', } ) id = Column(BigInteger(unsigned=True), primary_key=True, autoincrement=True) report_id = Column(BINARY(length=16)) created = Column(DateTime) # the insert time of the record into the DB # lat/lon lat = Column(Double(asdecimal=False)) lon = Column(Double(asdecimal=False)) time = Column(DateTime) # the time of observation of this data accuracy = Column(Integer) altitude = Column(Integer) altitude_accuracy = Column(Integer) # http://dev.w3.org/geo/api/spec-source.html#heading heading = Column(Float) # http://dev.w3.org/geo/api/spec-source.html#speed speed = Column(Float) # mapped via RADIO_TYPE radio = Column(TinyInteger) mcc = Column(SmallInteger) mnc = Column(SmallInteger) lac = Column(SmallInteger(unsigned=True)) cid = Column(Integer(unsigned=True)) psc = Column(SmallInteger) asu = Column(SmallInteger) signal = Column(SmallInteger) ta = Column(TinyInteger) def __init__(self, *args, **kw): if 'created' not in kw: kw['created'] = util.utcnow() super(CellMeasure, self).__init__(*args, **kw)
class Cell(BboxMixin, PositionMixin, TimeTrackingMixin, _Model): # BBB __tablename__ = 'cell' _indices = ( PrimaryKeyConstraint('radio', 'mcc', 'mnc', 'lac', 'cid'), Index('cell_created_idx', 'created'), Index('cell_modified_idx', 'modified'), ) radio = Column(TinyIntEnum(Radio), autoincrement=False, default=None) mcc = Column(SmallInteger, autoincrement=False, default=None) mnc = Column(SmallInteger, autoincrement=False, default=None) lac = Column(SmallInteger(unsigned=True), autoincrement=False, default=None) cid = Column(Integer(unsigned=True), autoincrement=False, default=None) psc = Column(SmallInteger, autoincrement=False) radius = Column(Integer) samples = Column(Integer(unsigned=True)) new_measures = Column(Integer(unsigned=True))
class CellBlacklist(_Model): __tablename__ = 'cell_blacklist' __table_args__ = ({ 'mysql_engine': 'InnoDB', 'mysql_charset': 'utf8', }) time = Column(DateTime) radio = Column(TinyInteger, autoincrement=False, primary_key=True) mcc = Column(SmallInteger, autoincrement=False, primary_key=True) mnc = Column(SmallInteger, autoincrement=False, primary_key=True) lac = Column( SmallInteger(unsigned=True), autoincrement=False, primary_key=True) cid = Column(Integer(unsigned=True), autoincrement=False, primary_key=True) count = Column(Integer) def __init__(self, *args, **kw): if 'time' not in kw: kw['time'] = util.utcnow() if 'count' not in kw: kw['count'] = 1 super(CellBlacklist, self).__init__(*args, **kw)
class CellArea(_Model): __tablename__ = 'cell_area' __table_args__ = { 'mysql_engine': 'InnoDB', 'mysql_charset': 'utf8', } created = Column(DateTime) modified = Column(DateTime) # lat/lon lat = Column(Double(asdecimal=False)) lon = Column(Double(asdecimal=False)) # radio mapped via RADIO_TYPE radio = Column(TinyInteger, autoincrement=False, primary_key=True) mcc = Column(SmallInteger, autoincrement=False, primary_key=True) mnc = Column(SmallInteger, autoincrement=False, primary_key=True) lac = Column(SmallInteger(unsigned=True), autoincrement=False, primary_key=True) range = Column(Integer) avg_cell_range = Column(Integer) num_cells = Column(Integer(unsigned=True)) def __init__(self, *args, **kw): if 'created' not in kw: kw['created'] = util.utcnow() if 'modified' not in kw: kw['modified'] = util.utcnow() if 'range' not in kw: kw['range'] = 0 if 'avg_cell_range' not in kw: kw['avg_cell_range'] = 0 if 'num_cells' not in kw: kw['num_cells'] = 0 super(CellArea, self).__init__(*args, **kw)
class CellShard(StationMixin): """Cell shard.""" _shards = CELL_SHARDS _valid_schema = ValidCellShardSchema() cellid = Column(CellIdColumn(11)) radio = Column(TinyIntEnum(Radio), nullable=False) mcc = Column(SmallInteger, nullable=False) mnc = Column(SmallInteger, nullable=False) lac = Column(SmallInteger(unsigned=True), nullable=False) cid = Column(Integer(unsigned=True), nullable=False) psc = Column(SmallInteger) @declared_attr def __table_args__(cls): # NOQA _indices = ( PrimaryKeyConstraint('cellid'), UniqueConstraint('radio', 'mcc', 'mnc', 'lac', 'cid', name='%s_cellid_unique' % cls.__tablename__), Index('%s_region_idx' % cls.__tablename__, 'region'), Index('%s_created_idx' % cls.__tablename__, 'created'), Index('%s_modified_idx' % cls.__tablename__, 'modified'), Index('%s_latlon_idx' % cls.__tablename__, 'lat', 'lon'), ) return _indices + (cls._settings, ) @property def unique_key(self): return encode_cellid(*self.cellid) @classmethod def validate(cls, entry, _raise_invalid=False, **kw): validated = super(CellShard, cls).validate(entry, _raise_invalid=_raise_invalid, **kw) if validated is not None: if 'cellid' not in validated: validated['cellid'] = ( validated['radio'], validated['mcc'], validated['mnc'], validated['lac'], validated['cid'], ) if (('region' not in validated or not validated['region']) and validated['lat'] is not None and validated['lon'] is not None): validated['region'] = GEOCODER.region_for_cell( validated['lat'], validated['lon'], validated['mcc']) return validated @classmethod def create(cls, _raise_invalid=False, **kw): """ Returns an instance of the correct shard model class, if the passed in keyword arguments pass schema validation, otherwise returns None. """ validated = cls.validate(kw, _raise_invalid=_raise_invalid) if validated is None: # pragma: no cover return None shard = cls.shard_model(validated['radio']) return shard(**validated) @classmethod def shard_id(cls, radio): """ Given a radio type return the correct shard id. """ if type(radio) == bytes and len(radio) == 11: # extract radio from cellid radio = decode_cellid(radio)[0] if type(radio) == Radio: return radio.name if isinstance(radio, tuple) and len(radio) == 5: return radio[0].name try: return Radio[radio].name except KeyError: pass return None @classmethod def shard_model(cls, radio): """ Given a radio type return the correct DB model class. """ return cls._shards.get(cls.shard_id(radio), None) @classmethod def shards(cls): """Return a dict of shard id to model classes.""" return cls._shards @classmethod def export_header(cls): return ('radio,mcc,mnc,lac,cid,psc,' 'lat,lon,max_lat,min_lat,max_lon,min_lon,' 'radius,region,samples,source,weight,' 'created,modified,last_seen,' 'block_first,block_last,block_count') @classmethod def export_stmt(cls): stmt = '''SELECT `cellid` AS `export_key`, CONCAT_WS(",", CASE radio WHEN 0 THEN "GSM" WHEN 2 THEN "WCDMA" WHEN 3 THEN "LTE" ELSE "" END, `mcc`, `mnc`, `lac`, `cid`, COALESCE(`psc`, ""), COALESCE(ROUND(`lat`, 7), ""), COALESCE(ROUND(`lon`, 7), ""), COALESCE(ROUND(`max_lat`, 7), ""), COALESCE(ROUND(`min_lat`, 7), ""), COALESCE(ROUND(`max_lon`, 7), ""), COALESCE(ROUND(`min_lon`, 7), ""), COALESCE(`radius`, "0"), COALESCE(`region`, ""), COALESCE(`samples`, "0"), COALESCE(`source`, ""), COALESCE(`weight`, "0"), COALESCE(UNIX_TIMESTAMP(`created`), ""), COALESCE(UNIX_TIMESTAMP(`modified`), ""), COALESCE(UNIX_TIMESTAMP(`last_seen`), ""), COALESCE(UNIX_TIMESTAMP(`block_first`), ""), COALESCE(UNIX_TIMESTAMP(`block_last`), ""), COALESCE(`block_count`, "0") ) AS `export_value` FROM %s WHERE `cellid` > :export_key ORDER BY `cellid` LIMIT :limit ''' % cls.__tablename__ return stmt.replace('\n', ' ')
class Account(Model, BaseModelMixin): __tablename__ = 'accounts' account_id = Column(Integer(unsigned=True), primary_key=True, autoincrement=True) account_name = Column(String(256), nullable=False, index=True, unique=True) account_type_id = Column(Integer(unsigned=True), ForeignKey(AccountType.account_type_id, name='fk_account_account_type_id', ondelete='CASCADE'), nullable=False, index=True) contacts = Column(JSON, nullable=False) enabled = Column(SmallInteger(unsigned=True), nullable=False, default=1) required_roles = Column(JSON, nullable=True) properties = relationship('AccountProperty', lazy='select', uselist=True, primaryjoin=account_id == foreign( AccountProperty.account_id), cascade='all, delete-orphan') @staticmethod def get(account_id, account_type_id=None): """Return account by ID and type Args: account_id (`int`, `str`): Unique Account identifier account_type_id (str): Type of account to get Returns: :obj:`Account`: Returns an Account object if found, else None """ if type(account_id) == str: args = {'account_name': account_id} else: args = {'account_id': account_id} if account_type_id: args['account_type_id'] = account_type_id return db.Account.find_one(**args) def user_has_access(self, user): """Check if a user has access to view information for the account Args: user (:obj:`User`): User object to check Returns: True if user has access to the account, else false """ if ROLE_ADMIN in user.roles: return True # Non-admin users should only see active accounts if self.enabled: if not self.required_roles: return True for role in self.required_roles: if role in user.roles: return True return False
class CellAreaMixin(PositionMixin, TimeTrackingMixin, CreationMixin, ScoreMixin): _valid_schema = ValidCellAreaSchema() areaid = Column(CellAreaColumn(7)) radio = Column(TinyIntEnum(Radio), autoincrement=False, nullable=False) mcc = Column(SmallInteger, autoincrement=False, nullable=False) mnc = Column(SmallInteger, autoincrement=False, nullable=False) lac = Column(SmallInteger(unsigned=True), autoincrement=False, nullable=False) radius = Column(Integer) region = Column(String(2)) avg_cell_radius = Column(Integer(unsigned=True)) num_cells = Column(Integer(unsigned=True)) def score_sample_weight(self): # treat areas for which we get the exact same # cells multiple times as if we only got 1 cell samples = self.num_cells if samples > 1 and not self.radius: samples = 1 # sample_weight is a number between: # 1.0 for 1 sample # 1.41 for 2 samples # 10 for 100 samples # we use a sqrt scale instead of log2 here, as this represents # the number of cells in an area and not the sum of samples # from all cells in the area return min(math.sqrt(max(samples, 1)), 10.0) @declared_attr def __table_args__(cls): # NOQA prefix = cls.__tablename__ _indices = ( PrimaryKeyConstraint('areaid'), UniqueConstraint('radio', 'mcc', 'mnc', 'lac', name='%s_areaid_unique' % prefix), Index('%s_region_radio_idx' % prefix, 'region', 'radio'), Index('%s_created_idx' % prefix, 'created'), Index('%s_modified_idx' % prefix, 'modified'), Index('%s_latlon_idx' % prefix, 'lat', 'lon'), ) return _indices + (cls._settings, ) @classmethod def validate(cls, entry, _raise_invalid=False, **kw): validated = super(CellAreaMixin, cls).validate(entry, _raise_invalid=_raise_invalid, **kw) if validated is not None and 'areaid' not in validated: validated['areaid'] = ( validated['radio'], validated['mcc'], validated['mnc'], validated['lac'], ) if (('region' not in validated or not validated['region']) and validated['lat'] is not None and validated['lon'] is not None): validated['region'] = GEOCODER.region_for_cell( validated['lat'], validated['lon'], validated['mcc']) return validated
class CellShard(StationMixin): """Cell shard.""" _shards = CELL_SHARDS _valid_schema = ValidCellShardSchema() cellid = Column(CellIdColumn(11)) radio = Column(TinyIntEnum(Radio), autoincrement=False, nullable=False) mcc = Column(SmallInteger, autoincrement=False, nullable=False) mnc = Column(SmallInteger, autoincrement=False, nullable=False) lac = Column(SmallInteger(unsigned=True), autoincrement=False, nullable=False) cid = Column(Integer(unsigned=True), autoincrement=False, nullable=False) psc = Column(SmallInteger, autoincrement=False) @declared_attr def __table_args__(cls): # NOQA _indices = ( PrimaryKeyConstraint('cellid'), UniqueConstraint('radio', 'mcc', 'mnc', 'lac', 'cid', name='%s_cellid_unique' % cls.__tablename__), Index('%s_region_idx' % cls.__tablename__, 'region'), Index('%s_created_idx' % cls.__tablename__, 'created'), Index('%s_modified_idx' % cls.__tablename__, 'modified'), Index('%s_latlon_idx' % cls.__tablename__, 'lat', 'lon'), ) return _indices + (cls._settings, ) @property def areaid(self): return encode_cellarea(self.radio, self.mcc, self.mnc, self.lac) @property def unique_key(self): return encode_cellid(*self.cellid) @classmethod def validate(cls, entry, _raise_invalid=False, **kw): validated = super(CellShard, cls).validate(entry, _raise_invalid=_raise_invalid, **kw) if validated is not None: if 'cellid' not in validated: validated['cellid'] = ( validated['radio'], validated['mcc'], validated['mnc'], validated['lac'], validated['cid'], ) if (('region' not in validated or not validated['region']) and validated['lat'] is not None and validated['lon'] is not None): validated['region'] = GEOCODER.region_for_cell( validated['lat'], validated['lon'], validated['mcc']) return validated @classmethod def create(cls, _raise_invalid=False, **kw): """ Returns an instance of the correct shard model class, if the passed in keyword arguments pass schema validation, otherwise returns None. """ validated = cls.validate(kw, _raise_invalid=_raise_invalid) if validated is None: # pragma: no cover return None shard = cls.shard_model(validated['radio']) return shard(**validated) @classmethod def shard_id(cls, radio): """ Given a radio type return the correct shard id. """ if type(radio) == bytes and len(radio) == 11: # extract radio from cellid radio = decode_cellid(radio)[0] if type(radio) == Radio: return radio.name if isinstance(radio, tuple) and len(radio) == 5: return radio[0].name try: return Radio[radio].name except KeyError: pass return None @classmethod def shard_model(cls, radio): """ Given a radio type return the correct DB model class. """ return cls._shards.get(cls.shard_id(radio), None) @classmethod def shards(cls): """Return a dict of shard id to model classes.""" return cls._shards