class Building(ModelBase):
    """
        Establishes a building in the database.
        lat, lng, place_id, state etc. are all pulled from Google maps
    """
    __tablename__ = 'building'

    lat = db.Column(db.Float())
    lng = db.Column(db.Float())

    formatted_address = db.Column(db.String())
    place_id = db.Column(db.String())
    state = db.Column(db.String())
    country = db.Column(db.String())
    locality = db.Column(db.String())
    postal_code = db.Column(db.String())
    route = db.Column(db.String())
    street_number = db.Column(db.String())

    url = db.Column(db.String())

    roof_area_estimation = db.relationship(
        'RoofAreaEstimation',
        backref='building',
        lazy='dynamic',
        foreign_keys='RoofAreaEstimation.building_id')
Beispiel #2
0
class Point(db.Model):
    __tablename__ = "points"
    id = db.Column(db.Integer, autoincrement=True, primary_key=True)
    date = db.Column(DateTime, default=datetime.datetime.utcnow)
    smell = db.Column(db.Boolean(), nullable=False)
    taste = db.Column(db.Boolean(), nullable=False)
    latitude = db.Column(db.Float(), nullable=False)
    longitude = db.Column(db.Float(), nullable=False)
class EstimationPoints(ModelBase):
    """
        We store the paths of a polygon submitted by the user here
        These make up the roof area estimation area
    """
    __tablename__ = 'estimation_points'

    latitude = db.Column(db.Float())
    longitude = db.Column(db.Float())

    roof_area_estimation_id = db.Column(
        db.Integer, db.ForeignKey('roof_area_estimation.id'))
class RoofAreaEstimation(ModelBase):
    """
        Roof area estimation tied to a specific building.
        Estimated lat & lng stored to 5 decimal points
    """
    __tablename__ = 'roof_area_estimation'

    area = db.Column(db.Float())
    center_latitude = db.Column(db.Float())
    center_longitude = db.Column(db.Float())

    building_id = db.Column(db.Integer, db.ForeignKey("building.id"))

    estimation_points = db.relationship(
        'EstimationPoints',
        backref='roof_area_estimation',
        lazy='dynamic',
        foreign_keys='EstimationPoints.roof_area_estimation_id')
Beispiel #5
0
class Run(db.Model):
    """Contains a set of screenshot records uploaded by a diff worker."""

    DATA_PENDING = 'data_pending'
    DIFF_APPROVED = 'diff_approved'
    DIFF_FOUND = 'diff_found'
    DIFF_NOT_FOUND = 'diff_not_found'
    FAILED = 'failed'
    NEEDS_DIFF = 'needs_diff'
    NO_DIFF_NEEDED = 'no_diff_needed'

    STATES = frozenset([
        DATA_PENDING, DIFF_APPROVED, DIFF_FOUND, DIFF_NOT_FOUND,
        FAILED, NEEDS_DIFF, NO_DIFF_NEEDED])

    DIFF_NEEDED_STATES = frozenset([DIFF_FOUND, DIFF_APPROVED])

    id = db.Column(db.Integer, primary_key=True)
    release_id = db.Column(db.Integer, db.ForeignKey('release.id'))
    release = db.relationship('Release',
                              backref=db.backref('runs', lazy='select'),
                              lazy='joined',
                              join_depth=1)

    name = db.Column(db.String(255), nullable=False)
    # TODO: Put rigid DB constraint on uniqueness of (release_id, name)
    created = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    modified = db.Column(db.DateTime, default=datetime.datetime.utcnow,
                         onupdate=datetime.datetime.utcnow)
    status = db.Column(db.Enum(*STATES), nullable=False)

    image = db.Column(db.String(100), db.ForeignKey('artifact.id'))
    log = db.Column(db.String(100), db.ForeignKey('artifact.id'))
    config = db.Column(db.String(100), db.ForeignKey('artifact.id'))
    url = db.Column(db.String(2048))

    ref_image = db.Column(db.String(100), db.ForeignKey('artifact.id'))
    ref_log = db.Column(db.String(100), db.ForeignKey('artifact.id'))
    ref_config = db.Column(db.String(100), db.ForeignKey('artifact.id'))
    ref_url = db.Column(db.String(2048))

    diff_image = db.Column(db.String(100), db.ForeignKey('artifact.id'))
    diff_log = db.Column(db.String(100), db.ForeignKey('artifact.id'))
    distortion = db.Column(db.Float())

    tasks = db.relationship('WorkQueue',
                            backref=db.backref('runs', lazy='select'),
                            lazy='joined',
                            join_depth=1,
                            order_by='WorkQueue.created')

    # For flask-cache memoize key.
    def __repr__(self):
        return 'Run(id=%r)' % self.id
Beispiel #6
0
class Game(db.Model):
    """
    This model is used to represent Company entries in our database.
    Attributes:
    Game_ID - ID of the game object. The ID will self-increment as we add more values into the Game table.
    Name - Name of the game. The IGDB API also pulls information like alternate names for the game, which may be integrated in a future project release.
    Image_URL - The image for the game.
    Rating - The rating the game received. This rating is pulled from IGDB, just like the other information regarding games.
    Release_Year - The year the game had came out.
    Associated_Companies - The companies who developed and published the game. This information will be populated using a combination of the association table and the IGDB API.
    Associated_Genres - The genres associated with the game. Like companies, the information  is populated using an associated table with the data retrieved from the IGDB API.
    Associated_Platforms - The platforms the game was released on. This information is populated using a combination of the association table for platforms and games and the IGDB API.
    """
    query_class = GameQuery
    __tablename__ = 'games'
    game_id = db.Column(db.Integer, primary_key=True, autoincrement=False)
    name = db.Column(db.String(50))
    image_url = db.Column(db.String(255))
    rating = db.Column(db.Float(4))
    release_year = db.Column(db.Integer, db.ForeignKey('years.year_id'))
    search_vector = db.Column(TSVectorType('name'))
    associated_companies = db.relationship(
        "Company",
        secondary=association_table_game_company,
        backref=db.backref("games"))
    associated_genres = db.relationship("Genre",
                                        secondary=association_table_game_genre,
                                        backref=db.backref("games"))
    associated_platforms = db.relationship(
        "Platform",
        secondary=association_table_game_platform,
        backref=db.backref("games"))

    def __init__(self,
                 id,
                 name=None,
                 image_url=None,
                 rating=0.0,
                 release_year=0,
                 search_vector=None):
        self.game_id = id
        self.name = name
        self.image_url = image_url
        self.rating = rating
        self.release_year = release_year
        self.search_vector = search_vector

    def __repr__(self):
        return '<Game: %r>' % (self.name)
Beispiel #7
0
class Report(db.Model):
  __tablename__ = 'reports'

  id = db.Column(db.Integer, primary_key=True)
  results = db.Column(db.Text())
  seo = db.Column(db.Float())
  accessibility = db.Column(db.Float())
  usability = db.Column(db.Float())
  scanid = db.Column(db.Integer, db.ForeignKey('scans.id'), nullable=False)

  def __init__(self, scanid, seo, accessibility, usability, results):
    self.scanid = scanid
    self.seo = seo
    self.accessibility = accessibility
    self.usability = usability
    self.results = results
    db.session.add(self)
    db.session.commit()
    return

  def get_json_results(self):
    if self.results:
      return json.loads(self.results.replace("'",'"'))
    return None
Beispiel #8
0
class Invoice(db.Model):
  __tablename__ = 'invoices'

  id = db.Column(db.Integer, primary_key=True)
  datetime = db.Column(db.DateTime(), default=datetime.datetime.now())
  ispaid = db.Column(db.Boolean(), default=False)
  paymentconfirmationid = db.Column(db.String(50))
  discount = db.Column(db.Float(), default=0)
  amountdue = db.Column(db.Float(), nullable=False)
  tax = db.Column(db.Float(), nullable=False)
  description = db.Column(db.Text(), nullable=False)
  userid = db.Column(db.Integer(), db.ForeignKey('users.id'), nullable=False)

  def __repr__(self):
    return f'[Invoice] #{self.id}: On: {self.datetime} Due: {self.amountdue} Paid: {self.ispaid}'

  def __init__(self, amountdue, tax, description, userid):
    self.amountdue = amountdue
    self.tax = tax
    self.description = description
    self.userid = userid
    db.session.add(self)
    db.session.commit()
    return
Beispiel #9
0
class Recipe(db.Model):
    __tablename__ = 'recipe'

    id = db.Column(UUID(as_uuid=True),
                   primary_key=True,
                   default=uuid.uuid4,
                   unique=True,
                   nullable=False)
    name = db.Column(db.String(40), nullable=False)
    preparation = db.Column(db.String())
    rating = db.Column(db.Float(), default=0)
    num_of_ratings = db.Column(db.Integer(), default=0)
    user_id = db.Column(UUID(as_uuid=True),
                        db.ForeignKey('user.id'),
                        nullable=False)
    num_of_ingredients = db.Column(db.Integer())
    ingredients = db.relationship('Ingredient',
                                  secondary=recipe_ing,
                                  back_populates='recipes',
                                  lazy='dynamic')

    def __repr__(self):
        return f'<Recipe {self.name}>'
Beispiel #10
0
class User(ManyOrgBase, ModelBase, SoftDelete):
    """Establishes the identity of a user for both making transactions and more general interactions.

        Admin users are created through the auth api by registering
        an account with an email that has been pre-approved on the whitelist.
        By default, admin users only have minimal access levels (view).
        Permissions must be elevated manually in the database.

        Transaction-capable users (vendors and beneficiaries) are
        created using the POST user API or the bulk upload function
    """
    __tablename__ = 'user'

    # override ModelBase deleted to add an index
    created = db.Column(db.DateTime,
                        default=datetime.datetime.utcnow,
                        index=True)

    first_name = db.Column(db.String())
    last_name = db.Column(db.String())
    preferred_language = db.Column(db.String())

    primary_blockchain_address = db.Column(db.String())

    _last_seen = db.Column(db.DateTime)

    email = db.Column(db.String())
    _phone = db.Column(db.String(), unique=True, index=True)
    _public_serial_number = db.Column(db.String())
    uuid = db.Column(db.String(), index=True)

    nfc_serial_number = db.Column(db.String())

    password_hash = db.Column(db.String(200))
    one_time_code = db.Column(db.String)
    secret = db.Column(db.String())
    _TFA_secret = db.Column(db.String(128))
    TFA_enabled = db.Column(db.Boolean, default=False)
    pin_hash = db.Column(db.String())
    seen_latest_terms = db.Column(db.Boolean, default=False)

    failed_pin_attempts = db.Column(db.Integer, default=0)

    default_currency = db.Column(db.String())

    _location = db.Column(db.String(), index=True)
    lat = db.Column(db.Float(), index=True)
    lng = db.Column(db.Float(), index=True)

    _held_roles = db.Column(JSONB)

    is_activated = db.Column(db.Boolean, default=False)
    is_disabled = db.Column(db.Boolean, default=False)
    is_phone_verified = db.Column(db.Boolean, default=False)
    is_self_sign_up = db.Column(db.Boolean, default=True)
    is_market_enabled = db.Column(db.Boolean, default=False)

    password_reset_tokens = db.Column(JSONB, default=[])
    pin_reset_tokens = db.Column(JSONB, default=[])

    terms_accepted = db.Column(db.Boolean, default=True)

    matched_profile_pictures = db.Column(JSON)

    business_usage_id = db.Column(db.Integer, db.ForeignKey(TransferUsage.id))

    transfer_accounts = db.relationship(
        "TransferAccount",
        secondary=user_transfer_account_association_table,
        back_populates="users")

    default_transfer_account_id = db.Column(
        db.Integer, db.ForeignKey('transfer_account.id'), index=True)

    default_transfer_account = db.relationship(
        'TransferAccount',
        primaryjoin='TransferAccount.id == User.default_transfer_account_id',
        lazy=True,
        uselist=False)

    default_organisation_id = db.Column(db.Integer,
                                        db.ForeignKey('organisation.id'),
                                        index=True)

    default_organisation = db.relationship(
        'Organisation',
        primaryjoin=Organisation.id == default_organisation_id,
        lazy=True,
        uselist=False)

    # roles = db.relationship('UserRole', backref='user', lazy=True,
    #                              foreign_keys='UserRole.user_id')

    ussd_sessions = db.relationship('UssdSession',
                                    backref='user',
                                    lazy=True,
                                    foreign_keys='UssdSession.user_id')

    uploaded_images = db.relationship('UploadedResource',
                                      backref='user',
                                      lazy=True,
                                      foreign_keys='UploadedResource.user_id')

    kyc_applications = db.relationship('KycApplication',
                                       backref='user',
                                       lazy=True,
                                       foreign_keys='KycApplication.user_id')

    devices = db.relationship('DeviceInfo', backref='user', lazy=True)

    referrals = db.relationship(
        'User',
        secondary=referrals,
        primaryjoin="User.id == referrals.c.referred_user_id",
        secondaryjoin="User.id == referrals.c.referrer_user_id",
        backref='referred_by')

    transfer_card = db.relationship('TransferCard',
                                    backref='user',
                                    lazy=True,
                                    uselist=False)

    credit_sends = db.relationship(
        'CreditTransfer',
        backref='sender_user',
        lazy='dynamic',
        foreign_keys='CreditTransfer.sender_user_id')

    credit_receives = db.relationship(
        'CreditTransfer',
        backref='recipient_user',
        lazy='dynamic',
        foreign_keys='CreditTransfer.recipient_user_id')

    ip_addresses = db.relationship('IpAddress', backref='user', lazy=True)

    feedback = db.relationship('Feedback',
                               backref='user',
                               lazy='dynamic',
                               foreign_keys='Feedback.user_id')

    custom_attributes = db.relationship(
        "CustomAttributeUserStorage",
        backref='user',
        lazy='joined',
        foreign_keys='CustomAttributeUserStorage.user_id')

    exchanges = db.relationship("Exchange", backref="user")

    @hybrid_property
    def coordinates(self):
        return str(self.lat) + ', ' + str(self.lng)

    @coordinates.expression
    def coordinates(cls):
        return cast(cls.lat, String) + ', ' + cast(cls.lng, String)

    def delete_user_and_transfer_account(self):
        """
        Soft deletes a User and default Transfer account if no other users associated to it.
        Removes User PII
        Disables transfer card
        """
        try:
            ta = self.default_transfer_account
            ta.delete_transfer_account_from_user(user=self)

            timenow = datetime.datetime.utcnow()
            self.deleted = timenow

            self.first_name = None
            self.last_name = None
            self.phone = None

            transfer_card = None

            try:
                transfer_card = TransferCard.get_transfer_card(
                    self.public_serial_number)
            except NoTransferCardError as e:
                pass

            if transfer_card and not transfer_card.is_disabled:
                transfer_card.disable()

        except (ResourceAlreadyDeletedError,
                TransferAccountDeletionError) as e:
            raise e

    @hybrid_property
    def cashout_authorised(self):
        # loop over all
        any_valid_token = [t.token for t in self.transfer_accounts]
        for token in any_valid_token:
            ct = server.models.credit_transfer
            example_transfer = ct.CreditTransfer(
                transfer_type=ct.TransferTypeEnum.PAYMENT,
                transfer_subtype=ct.TransferSubTypeEnum.AGENT_OUT,
                sender_user=self,
                recipient_user=self,
                token=token,
                amount=0)

            limits = example_transfer.get_transfer_limits()
            limit = limits[0]
            return limit.period_amount > 0
        else:
            # default to false
            return False

    @hybrid_property
    def phone(self):
        return self._phone

    @phone.setter
    def phone(self, phone):
        self._phone = proccess_phone_number(phone)

    @hybrid_property
    def public_serial_number(self):
        return self._public_serial_number

    @public_serial_number.setter
    def public_serial_number(self, public_serial_number):
        self._public_serial_number = public_serial_number

        try:
            transfer_card = TransferCard.get_transfer_card(
                public_serial_number)

            if transfer_card.user_id is None and transfer_card.nfc_serial_number is not None:
                # Card hasn't been used before, and has a nfc number attached
                self.nfc_serial_number = transfer_card.nfc_serial_number
                self.transfer_card = transfer_card

        except NoTransferCardError:
            pass

    @hybrid_property
    def tfa_url(self):

        if not self._TFA_secret:
            self.set_TFA_secret()
            db.session.flush()

        secret_key = self.get_TFA_secret()
        return pyotp.totp.TOTP(secret_key).provisioning_uri(
            self.email,
            issuer_name='Sempo: {}'.format(
                current_app.config.get('DEPLOYMENT_NAME')))

    @hybrid_property
    def location(self):
        return self._location

    @location.setter
    def location(self, location):

        self._location = location

    def attempt_update_gps_location(self):
        from server.utils.location import async_set_user_gps_from_location
        if self._location is not None and self._location is not '':
            # Delay execution until after request to avoid race condition with db
            # We still need to flush to get user id though
            db.session.flush()
            add_after_request_executor_job(async_set_user_gps_from_location,
                                           kwargs={
                                               'user_id': self.id,
                                               'location': self._location
                                           })
        add_after_request_executor_job(async_set_user_gps_from_location,
                                       kwargs={
                                           'user_id': self.id,
                                           'location': self._location
                                       })

    @hybrid_property
    def roles(self):
        if self._held_roles is None:
            return {}
        return self._held_roles

    def remove_all_held_roles(self):
        self._held_roles = {}

    def set_held_role(self, role: str, tier: Union[str, None]):
        if role not in ACCESS_ROLES:
            raise RoleNotFoundException("Role '{}' not valid".format(role))
        allowed_tiers = ACCESS_ROLES[role]
        if tier is not None and tier not in allowed_tiers:
            raise TierNotFoundException(
                "Tier {} not recognised for role {}".format(tier, role))

        if self._held_roles is None:
            self._held_roles = {}
        if tier is None:
            self._held_roles.pop(role, None)
        else:
            self._held_roles[role] = tier
            flag_modified(self, '_held_roles')

    @hybrid_property
    def has_admin_role(self):
        return AccessControl.has_any_tier(self.roles, 'ADMIN')

    @has_admin_role.expression
    def has_admin_role(cls):
        return cls._held_roles.has_key('ADMIN')

    @hybrid_property
    def has_vendor_role(self):
        return AccessControl.has_any_tier(self.roles, 'VENDOR')

    @has_vendor_role.expression
    def has_vendor_role(cls):
        return cls._held_roles.has_key('VENDOR')

    @hybrid_property
    def has_beneficiary_role(self):
        return AccessControl.has_any_tier(self.roles, 'BENEFICIARY')

    @has_beneficiary_role.expression
    def has_beneficiary_role(cls):
        return cls._held_roles.has_key('BENEFICIARY')

    @hybrid_property
    def has_token_agent_role(self):
        return AccessControl.has_any_tier(self.roles, 'TOKEN_AGENT')

    @has_token_agent_role.expression
    def has_token_agent_role(cls):
        return cls._held_roles.has_key('TOKEN_AGENT')

    @hybrid_property
    def has_group_account_role(self):
        return AccessControl.has_any_tier(self.roles, 'GROUP_ACCOUNT')

    @has_group_account_role.expression
    def has_group_account_role(cls):
        return cls._held_roles.has_key('GROUP_ACCOUNT')

    @hybrid_property
    def admin_tier(self):
        return self._held_roles.get('ADMIN', None)

    @hybrid_property
    def vendor_tier(self):
        return self._held_roles.get('VENDOR', None)

    # todo: Refactor into above roles
    # These two are here to interface with the mobile API
    @hybrid_property
    def is_vendor(self):
        return AccessControl.has_sufficient_tier(self.roles, 'VENDOR',
                                                 'vendor')

    @hybrid_property
    def is_supervendor(self):
        return AccessControl.has_sufficient_tier(self.roles, 'VENDOR',
                                                 'supervendor')

    @hybrid_property
    def organisation_ids(self):
        return [organisation.id for organisation in self.organisations]

    @property
    def transfer_account(self):
        active_organisation = getattr(
            g, "active_organisation",
            None) or self.fallback_active_organisation()

        # TODO: Review if this could have a better concept of a default?
        return self.get_transfer_account_for_organisation(active_organisation)

    @hybrid_method
    def great_circle_distance(self, lat, lng):
        """
        Tries to calculate the great circle distance between
        the two locations in km by using the Haversine formula.
        """
        return self._haversine(math, self, lat, lng)

    @great_circle_distance.expression
    def great_circle_distance(cls, lat, lng):
        return cls._haversine(func, cls, lat, lng)

    @staticmethod
    def _haversine(lib, selfref, lat, lng):
        return 6371 * lib.acos(
            lib.cos(lib.radians(selfref.lat)) * lib.cos(lib.radians(lat)) *
            lib.cos(lib.radians(selfref.lng) - lib.radians(lng)) +
            lib.sin(lib.radians(selfref.lat)) * lib.sin(lib.radians(lat)))

    def get_users_within_radius(self, radius):
        if not (self.lat or self.lng):
            raise Exception(
                'Cannot get users within radius-- User location undefined')

        return db.session.query(User).filter(
            self.users_within_radius_filter(radius)).all()

    def users_within_radius_filter(self, radius):
        return or_(
            and_(User.lat == None, User.lng == None),
            and_(User.lat == self.lat, User.lng == self.lng),
            User.great_circle_distance(self.lat, self.lng) < radius,
            and_(self._location is not None, User._location == self._location))

    def get_transfer_account_for_organisation(self, organisation):
        for ta in self.transfer_accounts:
            if ta.organisation.id == organisation.id:
                return ta

        raise Exception(
            f"No matching transfer account for user {self}, token {organisation.token} and organsation {organisation}"
        )

    def get_transfer_account_for_token(self, token):
        return find_transfer_accounts_with_matching_token(self, token)

    def fallback_active_organisation(self):
        if len(self.organisations) == 0:
            return None

        if len(self.organisations) > 1:
            return self.default_organisation

        return self.organisations[0]

    def update_last_seen_ts(self):
        pass
        # cur_time = datetime.datetime.utcnow()
        # if self._last_seen:
        #     # default to 1 minute intervals
        #     if cur_time - self._last_seen >= datetime.timedelta(minutes=1):
        #         self._last_seen = cur_time
        # else:
        #     self._last_seen = cur_time

    @staticmethod
    def salt_hash_secret(password):
        f = Fernet(config.PASSWORD_PEPPER)
        return f.encrypt(bcrypt.hashpw(password.encode(),
                                       bcrypt.gensalt())).decode()

    @staticmethod
    def check_salt_hashed_secret(password, hashed_password):
        if not hashed_password:
            return False
        f = Fernet(config.PASSWORD_PEPPER)
        hashed_password = f.decrypt(hashed_password.encode())
        return bcrypt.checkpw(password.encode(), hashed_password)

    def hash_password(self, password):
        self.password_hash = self.salt_hash_secret(password)

    def verify_password(self, password):
        return self.check_salt_hashed_secret(password, self.password_hash)

    def hash_pin(self, pin):
        self.pin_hash = self.salt_hash_secret(pin)

    def verify_pin(self, pin):
        return self.check_salt_hashed_secret(pin, self.pin_hash)

    def encode_TFA_token(self, valid_days=1):
        """
        Generates the Auth Token for TFA
        :return: string
        """
        try:

            payload = {
                'exp':
                datetime.datetime.utcnow() +
                datetime.timedelta(days=valid_days, seconds=30),
                'iat':
                datetime.datetime.utcnow(),
                'id':
                self.id
            }

            tfa = jwt.encode(payload,
                             current_app.config['SECRET_KEY'],
                             algorithm='HS256')
            return bytes(tfa, 'utf-8') if isinstance(tfa, str) else tfa
        except Exception as e:
            return e

    def encode_auth_token(self):
        """
        Generates the Auth Token
        :return: string
        """
        try:

            payload = {
                'exp':
                datetime.datetime.utcnow() + datetime.timedelta(
                    seconds=current_app.config['AUTH_TOKEN_EXPIRATION']),
                'iat':
                datetime.datetime.utcnow(),
                'id':
                self.id,
                'roles':
                self.roles
            }

            token = jwt.encode(payload,
                               current_app.config['SECRET_KEY'],
                               algorithm='HS256')
            return bytes(token, 'utf-8') if isinstance(token, str) else token
        except Exception as e:
            return e

    @staticmethod
    def decode_auth_token(auth_token, token_type='Auth'):
        """
        Validates the auth token
        :param auth_token:
        :return: integer|string
        """
        try:
            payload = jwt.decode(auth_token,
                                 current_app.config['SECRET_KEY'],
                                 algorithms='HS256',
                                 options={
                                     'verify_exp':
                                     current_app.config['VERIFY_JWT_EXPIRY']
                                 })

            is_blacklisted_token = BlacklistToken.check_blacklist(auth_token)
            if is_blacklisted_token:
                return 'Token blacklisted. Please log in again.'
            else:
                return bytes(payload, 'utf-8') if isinstance(payload,
                                                             str) else payload

        except jwt.ExpiredSignatureError:
            return '{} Token Signature expired.'.format(token_type)
        except jwt.InvalidTokenError:
            return 'Invalid {} Token.'.format(token_type)

    def encode_single_use_JWS(self, token_type):

        s = TimedJSONWebSignatureSerializer(
            current_app.config['SECRET_KEY'],
            expires_in=current_app.config['SINGLE_USE_TOKEN_EXPIRATION'])

        return s.dumps({'id': self.id, 'type': token_type}).decode("utf-8")

    @classmethod
    def decode_single_use_JWS(cls, token, required_type):

        try:
            s = TimedJSONWebSignatureSerializer(
                current_app.config['SECRET_KEY'])

            data = s.loads(token.encode("utf-8"))

            user_id = data.get('id')

            token_type = data.get('type')

            if token_type != required_type:
                return {
                    'success': False,
                    'message': 'Wrong token type (needed %s)' % required_type
                }

            if not user_id:
                return {'success': False, 'message': 'No User ID provided'}

            user = cls.query.filter_by(id=user_id).execution_options(
                show_all=True).first()

            if not user:
                return {'success': False, 'message': 'User not found'}

            return {'success': True, 'user': user}

        except BadSignature:

            return {'success': False, 'message': 'Token signature not valid'}

        except SignatureExpired:

            return {'success': False, 'message': 'Token has expired'}

        except Exception as e:

            return {'success': False, 'message': e}

    def save_password_reset_token(self, password_reset_token):
        # make a "clone" of the existing token list
        self.clear_expired_password_reset_tokens()
        current_password_reset_tokens = self.password_reset_tokens[:]
        current_password_reset_tokens.append(password_reset_token)
        # set db value
        self.password_reset_tokens = current_password_reset_tokens

    def save_pin_reset_token(self, pin_reset_token):
        self.clear_expired_pin_reset_tokens()

        current_pin_reset_tokens = self.pin_reset_tokens[:]
        current_pin_reset_tokens.append(pin_reset_token)

        self.pin_reset_tokens = current_pin_reset_tokens

    def check_reset_token_already_used(self, password_reset_token):
        self.clear_expired_password_reset_tokens()
        is_valid = password_reset_token in self.password_reset_tokens
        return is_valid

    def delete_password_reset_tokens(self):
        self.password_reset_tokens = []

    def delete_pin_reset_tokens(self):
        self.pin_reset_tokens = []

    def clear_expired_reset_tokens(self, token_list):
        if token_list is None:
            token_list = []

        valid_tokens = []
        for token in token_list:
            validity_check = self.decode_single_use_JWS(token, 'R')
            if validity_check['success']:
                valid_tokens.append(token)
        return valid_tokens

    def clear_expired_password_reset_tokens(self):
        tokens = self.clear_expired_reset_tokens(self.password_reset_tokens)
        self.password_reset_tokens = tokens

    def clear_expired_pin_reset_tokens(self):
        tokens = self.clear_expired_reset_tokens(self.pin_reset_tokens)
        self.pin_reset_tokens = tokens

    def create_admin_auth(self,
                          email,
                          password,
                          tier='view',
                          organisation=None):
        self.email = email
        self.hash_password(password)
        self.set_held_role('ADMIN', tier)

        if organisation:
            self.add_user_to_organisation(organisation, is_admin=True)

    def add_user_to_organisation(self,
                                 organisation: Organisation,
                                 is_admin=False):
        if not self.default_organisation:
            self.default_organisation = organisation

        self.organisations.append(organisation)

        if is_admin and organisation.org_level_transfer_account_id:
            if organisation.org_level_transfer_account is None:
                organisation.org_level_transfer_account = (db.session.query(
                    server.models.transfer_account.TransferAccount
                ).execution_options(show_all=True).get(
                    organisation.org_level_transfer_account_id))

            self.transfer_accounts.append(
                organisation.org_level_transfer_account)

    def is_TFA_required(self):
        for tier in current_app.config['TFA_REQUIRED_ROLES']:
            if AccessControl.has_exact_role(self.roles, 'ADMIN', tier):
                return True
        else:
            return False

    def is_TFA_secret_set(self):
        return bool(self._TFA_secret)

    def set_TFA_secret(self):
        secret = pyotp.random_base32()
        self._TFA_secret = encrypt_string(secret)

    def get_TFA_secret(self):
        return decrypt_string(self._TFA_secret)

    def validate_OTP(self, input_otp):
        secret = self.get_TFA_secret()
        server_otp = pyotp.TOTP(secret)
        ret = server_otp.verify(input_otp, valid_window=2)
        return ret

    def set_one_time_code(self, supplied_one_time_code):
        if supplied_one_time_code is None:
            self.one_time_code = str(random.randint(0, 9999)).zfill(4)
        else:
            self.one_time_code = supplied_one_time_code

    # pin as used in mobile. is set to password. we should probably change this to be same as ussd pin
    def set_pin(self, supplied_pin=None, is_activated=False):
        self.is_activated = is_activated

        if not is_activated:
            # Use a one time code, either generated or supplied. PIN will be set to random number for now
            self.set_one_time_code(supplied_one_time_code=supplied_pin)

            pin = str(random.randint(0, 9999)).zfill(4)
        else:
            pin = supplied_pin

        self.hash_pin(pin)

    def has_valid_pin(self):
        # not in the process of resetting pin and has a pin
        self.clear_expired_pin_reset_tokens()
        not_resetting = len(self.pin_reset_tokens) == 0

        return self.pin_hash is not None and not_resetting and self.failed_pin_attempts < 3

    def user_details(self):
        # should drop the country code from phone number?
        return "{} {} {}".format(self.first_name, self.last_name, self.phone)

    def get_most_relevant_transfer_usages(self):
        '''Finds the transfer usage/business categories there are most relevant for the user
        based on the last number of send and completed credit transfers supplemented with the
        defaul business categories
        :return: list of most relevant transfer usage objects for the usage
        """
        '''

        sql = text('''
            SELECT *, COUNT(*) FROM
                (SELECT c.transfer_use::text FROM credit_transfer c
                WHERE c.sender_user_id = {} AND c.transfer_status = 'COMPLETE'
                ORDER BY c.updated DESC
                LIMIT 20)
            C GROUP BY transfer_use ORDER BY count DESC
        '''.format(self.id))
        result = db.session.execute(sql)
        most_common_uses = {}
        for row in result:
            if row[0] is not None:
                for use in json.loads(row[0]):
                    most_common_uses[use] = row[1]

        return most_common_uses

    def get_reserve_token(self):
        # reserve token is master token for now
        return Organisation.master_organisation().token

    def __init__(self, blockchain_address=None, **kwargs):
        super(User, self).__init__(**kwargs)

        self.secret = ''.join(
            random.choices(string.ascii_letters + string.digits, k=16))

        self.primary_blockchain_address = blockchain_address or bt.create_blockchain_wallet(
        )

    def __repr__(self):
        if self.has_admin_role:
            return '<Admin {} {}>'.format(self.id, self.email)
        elif self.has_vendor_role:
            return '<Vendor {} {}>'.format(self.id, self.phone)
        else:
            return '<User {} {}>'.format(self.id, self.phone)
Beispiel #11
0
class User(ModelBase):
    """Establishes the identity of a user for both making transactions and more general interactions.

        Admin users are created through the auth api by registering
        an account with an email that has been pre-approved on the whitelist.
        By default, admin users only have minimal access levels (view).
        Permissions must be elevated manually in the database.

        Transaction-capable users (vendors and beneficiaries) are
        created using the POST user API or the bulk upload function
    """
    __tablename__ = 'user'

    first_name = db.Column(db.String())
    last_name = db.Column(db.String())

    _last_seen = db.Column(db.DateTime)

    email = db.Column(db.String())
    _phone = db.Column(db.String())
    _public_serial_number = db.Column(db.String())
    nfc_serial_number = db.Column(db.String())

    password_hash = db.Column(db.String(128))
    one_time_code = db.Column(db.String)
    secret = db.Column(db.String())
    _TFA_secret = db.Column(db.String(128))
    TFA_enabled = db.Column(db.Boolean, default=False)

    default_currency = db.Column(db.String())

    _location = db.Column(db.String())
    lat = db.Column(db.Float())
    lng = db.Column(db.Float())

    is_beneficiary = db.Column(db.Boolean, default=False)

    _is_vendor = db.Column(db.Boolean, default=False)
    _is_supervendor = db.Column(db.Boolean, default=False)

    _is_view = db.Column(db.Boolean, default=False)
    _is_subadmin = db.Column(db.Boolean, default=False)
    _is_admin = db.Column(db.Boolean, default=False)
    _is_superadmin = db.Column(db.Boolean, default=False)

    is_activated = db.Column(db.Boolean, default=False)
    is_disabled = db.Column(db.Boolean, default=False)

    terms_accepted = db.Column(db.Boolean, default=True)

    custom_attributes = db.Column(JSON)
    matched_profile_pictures = db.Column(JSON)

    ap_user_id = db.Column(db.String())
    ap_bank_id = db.Column(db.String())
    ap_paypal_id = db.Column(db.String())
    kyc_state = db.Column(db.String())

    cashout_authorised = db.Column(db.Boolean, default=False)

    transfer_account_id = db.Column(db.Integer,
                                    db.ForeignKey('transfer_account.id'))

    chatbot_state_id = db.Column(db.Integer, db.ForeignKey('chatbot_state.id'))
    targeting_survey_id = db.Column(db.Integer,
                                    db.ForeignKey('targeting_survey.id'))

    uploaded_images = db.relationship('UploadedImage',
                                      backref='user',
                                      lazy=True,
                                      foreign_keys='UploadedImage.user_id')

    devices = db.relationship('DeviceInfo', backref='user', lazy=True)

    referrals = db.relationship('Referral',
                                backref='referring_user',
                                lazy=True)

    transfer_card = db.relationship('TransferCard',
                                    backref='user',
                                    lazy=True,
                                    uselist=False)

    credit_sends = db.relationship(
        'CreditTransfer',
        backref='sender_user',
        lazy='dynamic',
        foreign_keys='CreditTransfer.sender_user_id')

    credit_receives = db.relationship(
        'CreditTransfer',
        backref='recipient_user',
        lazy='dynamic',
        foreign_keys='CreditTransfer.recipient_user_id')

    ip_addresses = db.relationship('IpAddress', backref='user', lazy=True)

    @hybrid_property
    def phone(self):
        return self._phone

    @phone.setter
    def phone(self, phone):
        self._phone = proccess_phone_number(phone)

    @hybrid_property
    def public_serial_number(self):
        return self._public_serial_number

    @public_serial_number.setter
    def public_serial_number(self, public_serial_number):
        self._public_serial_number = public_serial_number

        try:
            transfer_card = get_transfer_card(public_serial_number)

            if transfer_card.user_id is None and transfer_card.nfc_serial_number is not None:
                # Card hasn't been used before, and has a nfc number attached
                self.nfc_serial_number = transfer_card.nfc_serial_number
                self.transfer_card = transfer_card

        except NoTransferCardError:
            pass

    @hybrid_property
    def tfa_url(self):

        if not self._TFA_secret:
            self._set_TFA_secret()
            db.session.commit()

        secret_key = self._get_TFA_secret()
        return pyotp.totp.TOTP(secret_key).provisioning_uri(
            self.email,
            issuer_name='Sempo: {}'.format(
                current_app.config.get('DEPLOYMENT_NAME')))

    @hybrid_property
    def location(self):
        return self._location

    @location.setter
    def location(self, location):

        self._location = location

        if location is not None and location is not '':

            try:
                task = {'user_id': self.id, 'address': location}
                geolocate_task = celery_app.signature(
                    'worker.celery_tasks.geolocate_address', args=(task, ))

                geolocate_task.delay()
            except Exception as e:
                print(e)
                sentry.captureException()
                pass

    @hybrid_property
    def has_any_admin_role(self):
        return self._is_view or self._is_subadmin or self._is_admin or self._is_superadmin

    @hybrid_property
    def has_any_vendor_role(self):
        return self._is_vendor or self._is_supervendor

    @hybrid_property
    def is_vendor(self):
        return self._is_vendor or self._is_supervendor

    @is_vendor.setter
    def is_vendor(self, is_vendor):
        self._is_vendor = is_vendor

    @hybrid_property
    def is_supervendor(self):
        return self._is_supervendor

    @is_supervendor.setter
    def is_supervendor(self, is_supervendor):
        self._is_supervendor = is_supervendor

    @hybrid_property
    def is_view(self):
        return self._is_view or self._is_subadmin or self._is_admin or self._is_superadmin

    @is_view.setter
    def is_view(self, is_view):
        self._is_view = is_view

    @hybrid_property
    def is_subadmin(self):
        return self._is_subadmin or self._is_admin or self._is_superadmin

    @is_subadmin.setter
    def is_subadmin(self, is_subadmin):
        self._is_subadmin = is_subadmin

    @hybrid_property
    def is_admin(self):
        return self._is_admin or self._is_superadmin

    @is_admin.setter
    def is_admin(self, is_admin):
        self._is_admin = is_admin

    @hybrid_property
    def is_superadmin(self):
        return self._is_superadmin

    @is_superadmin.setter
    def is_superadmin(self, is_superadmin):
        self._is_superadmin = is_superadmin

    def update_last_seen_ts(self):
        cur_time = datetime.datetime.utcnow()
        if self._last_seen:
            if cur_time - self._last_seen >= datetime.timedelta(
                    minutes=1):  # default to 1 minute intervals
                self._last_seen = cur_time
        else:
            self._last_seen = cur_time

    def hash_password(self, password):
        self.password_hash = bcrypt.hashpw(password.encode(),
                                           bcrypt.gensalt()).decode()

    def verify_password(self, password):
        return bcrypt.checkpw(password.encode(), self.password_hash.encode())

    def encode_TFA_token(self, valid_days=1):
        """
        Generates the Auth Token for TFA
        :return: string
        """
        try:

            payload = {
                'exp':
                datetime.datetime.utcnow() +
                datetime.timedelta(days=valid_days, seconds=30),
                'iat':
                datetime.datetime.utcnow(),
                'user_id':
                self.id
            }

            return jwt.encode(payload,
                              current_app.config['SECRET_KEY'],
                              algorithm='HS256')
        except Exception as e:
            return e

    def encode_auth_token(self):
        """
        Generates the Auth Token
        :return: string
        """
        try:

            payload = {
                'exp':
                datetime.datetime.utcnow() +
                datetime.timedelta(days=7, seconds=0),
                'iat':
                datetime.datetime.utcnow(),
                'user_id':
                self.id,
                'is_vendor':
                self.is_vendor,
                'is_supervendor':
                self.is_supervendor,
                'is_view':
                self.is_view,
                'is_subadmin':
                self.is_subadmin,
                'is_admin':
                self.is_admin,
                'is_superadmin':
                self.is_superadmin
            }

            return jwt.encode(payload,
                              current_app.config['SECRET_KEY'],
                              algorithm='HS256')
        except Exception as e:
            return e

    @staticmethod
    def decode_auth_token(auth_token):
        """
        Validates the auth token
        :param auth_token:
        :return: integer|string
        """
        try:
            payload = jwt.decode(auth_token,
                                 current_app.config.get('SECRET_KEY'))
            is_blacklisted_token = BlacklistToken.check_blacklist(auth_token)
            if is_blacklisted_token:
                return 'Token blacklisted. Please log in again.'
            else:
                return payload

        except jwt.ExpiredSignatureError:
            return 'Signature expired.'
        except jwt.InvalidTokenError:
            return 'Invalid token.'

    def encode_single_use_JWS(self, token_type):

        s = TimedJSONWebSignatureSerializer(
            current_app.config['SECRET_KEY'],
            expires_in=current_app.config['TOKEN_EXPIRATION'])

        return s.dumps({'id': self.id, 'type': token_type}).decode("utf-8")

    @classmethod
    def decode_single_use_JWS(cls, token, required_type):

        try:
            s = TimedJSONWebSignatureSerializer(
                current_app.config['SECRET_KEY'])

            data = s.loads(token.encode("utf-8"))

            user_id = data.get('id')

            token_type = data.get('type')

            if token_type != required_type:
                return {
                    'success': False,
                    'message': 'Wrong token type (needed %s)' % required_type
                }

            if not user_id:
                return {'success': False, 'message': 'No User ID provided'}

            user = cls.query.filter_by(id=user_id).first()

            if not user:
                return {'success': False, 'message': 'User not found'}

            return {'success': True, 'user': user}

        except BadSignature:

            return {'success': False, 'message': 'Token signature not valid'}

        except SignatureExpired:

            return {'success': False, 'message': 'Token has expired'}

        except Exception as e:

            return {'success': False, 'message': e}

    def create_admin_auth(self, email, password, tier='view'):
        self.email = email
        self.hash_password(password)
        self.set_admin_role_using_tier_string(tier)

    def set_admin_role_using_tier_string(self, tier):

        tier = tier.lower()
        if tier not in ALLOWED_ADMIN_TIERS:
            raise TierNotFoundException('tier {} not found')

        self.is_view = self.is_subadmin = self.is_admin = self.is_superadmin = False
        if tier == 'superadmin':
            self.is_superadmin = True
        elif tier == 'admin':
            self.is_admin = True
        elif tier == 'subadmin':
            self.is_subadmin = True
        elif tier == 'view':
            self.is_view = True

        if self.is_admin:
            return 'admin'

    def convert_user_role_to_string(self):
        user_role = ""
        if self.is_superadmin:
            user_role = 'superadmin'
        elif self.is_admin:
            user_role = 'admin'
        elif self.is_subadmin:
            user_role = 'subadmin'
        elif self.is_view:
            user_role = 'view'
        if user_role in ALLOWED_ADMIN_TIERS:
            return user_role
        else:
            return ""

    def is_TFA_required(self):
        for role in current_app.config['TFA_REQUIRED_ROLES']:
            if self.convert_user_role_to_string() == role:
                return True
        else:
            return False

    def is_TFA_secret_set(self):
        return bool(self._TFA_secret)

    def _set_TFA_secret(self):
        secret = pyotp.random_base32()
        self._TFA_secret = encrypt_string(secret)

    def _get_TFA_secret(self):
        return decrypt_string(self._TFA_secret)

    def validate_OTP(self, input_otp):
        try:
            p = int(input_otp)
        except ValueError:
            return False
        else:
            secret = self._get_TFA_secret()
            server_otp = pyotp.TOTP(secret)
            ret = server_otp.verify(p)
            return ret

    def set_pin(self, supplied_pin=None, is_activated=False):

        self.is_activated = is_activated

        if not is_activated:
            # Use a one time code, either generated or supplied. PIN will be set to random number for now
            if supplied_pin is None:
                self.one_time_code = str(random.randint(0, 9999)).zfill(4)
            else:
                self.one_time_code = supplied_pin

            pin = str(random.randint(0, 9999999999999)).zfill(4)

        else:
            pin = supplied_pin

        self.hash_password(pin)

    def set_non_admin_auth(self,
                           is_beneficiary=False,
                           is_vendor=False,
                           is_supervendor=False):

        self.is_vendor = is_vendor
        self.is_supervendor = is_supervendor
        self.is_beneficiary = is_beneficiary

    def __init__(self, **kwargs):
        super(User, self).__init__(**kwargs)
        self.secret = ''.join(
            random.choices(string.ascii_letters + string.digits, k=16))

    def __repr__(self):
        if self.is_view:
            return '<Admin {} {}>'.format(self.id, self.email)
        elif self.is_vendor:
            return '<Vendor {} {}>'.format(self.id, self.phone)
        else:
            return '<Beneficiary {} {}>'.format(self.id, self.phone)
Beispiel #12
0
class Organisation(ModelBase):
    """
    Establishes organisation object that resources can be associated with.
    """
    __tablename__       = 'organisation'

    is_master           = db.Column(db.Boolean, default=False, index=True)

    name                = db.Column(db.String)

    external_auth_username = db.Column(db.String)

    valid_roles = db.Column(ARRAY(db.String, dimensions=1))

    _external_auth_password = db.Column(db.String)

    default_lat = db.Column(db.Float())
    default_lng = db.Column(db.Float())

    _timezone = db.Column(db.String)
    _country_code = db.Column(db.String, nullable=False)
    _default_disbursement_wei = db.Column(db.Numeric(27), default=0)
    require_transfer_card = db.Column(db.Boolean, default=False)

    # TODO: Create a mixin so that both user and organisation can use the same definition here
    # This is the blockchain address used for transfer accounts, unless overridden
    primary_blockchain_address = db.Column(db.String)

    # This is the 'behind the scenes' blockchain address used for paying gas fees
    system_blockchain_address = db.Column(db.String)

    auto_approve_externally_created_users = db.Column(db.Boolean, default=False)

    users               = db.relationship(
        "User",
        secondary=organisation_association_table,
        back_populates="organisations")

    token_id            = db.Column(db.Integer, db.ForeignKey('token.id'))

    org_level_transfer_account_id = db.Column(db.Integer,
                                                db.ForeignKey('transfer_account.id',
                                                name="fk_org_level_account"))

    # We use this weird join pattern because SQLAlchemy
    # doesn't play nice when doing multiple joins of the same table over different declerative bases
    org_level_transfer_account = db.relationship(
        "TransferAccount",
        post_update=True,
        primaryjoin="Organisation.org_level_transfer_account_id==TransferAccount.id",
        uselist=False)

    @hybrid_property
    def timezone(self):
        return self._timezone

    @timezone.setter
    def timezone(self, val):
        if val is not None and val not in pendulum.timezones:
            raise Exception(f"{val} is not a valid timezone")
        self._timezone = val

    @hybrid_property
    def country_code(self):
        return self._country_code

    @country_code.setter
    def country_code(self, val):
        if val is not None:
            val = val.upper()
            if len(val) != 2:
                # will try handle 'AD: Andorra'
                val = val.split(':')[0]
            if val not in ISO_COUNTRIES:
                raise Exception(f"{val} is not a valid country code")
        self._country_code = val

    @property
    def default_disbursement(self):
        return Decimal((self._default_disbursement_wei or 0) / int(1e16))

    @default_disbursement.setter
    def default_disbursement(self, val):
        if val is not None:
            self._default_disbursement_wei = int(val) * int(1e16)

    # TODO: This is a hack to get around the fact that org level TAs don't always show up. Super not ideal
    @property
    def queried_org_level_transfer_account(self):
        if self.org_level_transfer_account_id:
            return server.models.transfer_account.TransferAccount\
                .query.execution_options(show_all=True).get(self.org_level_transfer_account_id)
        return None

    @hybrid_property
    def external_auth_password(self):
        return decrypt_string(self._external_auth_password)

    @external_auth_password.setter
    def external_auth_password(self, value):
        self._external_auth_password = encrypt_string(value)

    credit_transfers    = db.relationship("CreditTransfer",
                                          secondary=organisation_association_table,
                                          back_populates="organisations")

    transfer_accounts   = db.relationship('TransferAccount',
                                          backref='organisation',
                                          lazy=True, foreign_keys='TransferAccount.organisation_id')

    blockchain_addresses = db.relationship('BlockchainAddress', backref='organisation',
                                           lazy=True, foreign_keys='BlockchainAddress.organisation_id')

    email_whitelists    = db.relationship('EmailWhitelist', backref='organisation',
                                          lazy=True, foreign_keys='EmailWhitelist.organisation_id')

    kyc_applications = db.relationship('KycApplication', backref='organisation',
                                       lazy=True, foreign_keys='KycApplication.organisation_id')

    attribute_maps = db.relationship('AttributeMap', backref='organisation',
                                       lazy=True, foreign_keys='AttributeMap.organisation_id')

    custom_welcome_message_key = db.Column(db.String)

    @staticmethod
    def master_organisation() -> "Organisation":
        return Organisation.query.filter_by(is_master=True).first()

    def _setup_org_transfer_account(self):
        transfer_account = server.models.transfer_account.TransferAccount(
            bound_entity=self,
            is_approved=True
        )
        db.session.add(transfer_account)
        self.org_level_transfer_account = transfer_account

        # Back setup for delayed organisation transfer account instantiation
        for user in self.users:
            if AccessControl.has_any_tier(user.roles, 'ADMIN'):
                user.transfer_accounts.append(self.org_level_transfer_account)

    def bind_token(self, token):
        self.token = token
        self._setup_org_transfer_account()

    def __init__(self, token=None, is_master=False, valid_roles=None, **kwargs):
        super(Organisation, self).__init__(**kwargs)
    
        self.external_auth_username = '******'+ self.name.lower().replace(' ', '_')
        self.external_auth_password = secrets.token_hex(16)
        self.valid_roles = valid_roles or list(ASSIGNABLE_TIERS.keys())
        if is_master:
            if Organisation.query.filter_by(is_master=True).first():
                raise Exception("A master organisation already exists")

            self.is_master = True
            self.system_blockchain_address = bt.create_blockchain_wallet(
                private_key=current_app.config['MASTER_WALLET_PRIVATE_KEY'],
                wei_target_balance=0,
                wei_topup_threshold=0,
            )

            self.primary_blockchain_address = self.system_blockchain_address or bt.create_blockchain_wallet()

        else:
            self.is_master = False

            self.system_blockchain_address = bt.create_blockchain_wallet(
                wei_target_balance=current_app.config['SYSTEM_WALLET_TARGET_BALANCE'],
                wei_topup_threshold=current_app.config['SYSTEM_WALLET_TOPUP_THRESHOLD'],
            )

            self.primary_blockchain_address = bt.create_blockchain_wallet()

        if token:
            self.bind_token(token)
Beispiel #13
0
class Post(db.Model):

    __tablename__ = "posts"

    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(40), nullable=False)
    imageurl = db.Column(db.String(128))
    condition = db.Column(db.String(16), nullable=False)
    description = db.Column(db.String(140), nullable=False)
    price = db.Column(db.Float(2), nullable=False)
    userid = db.Column(db.Integer, db.ForeignKey("users.id"))
    schoolid = db.Column(db.Integer, db.ForeignKey("schools.id"))

    user = db.relationship("User")
    school = db.relationship("School")

    def __init__(self,
                 title,
                 condition,
                 description,
                 price,
                 userid,
                 schoolid,
                 imageurl=None):

        self.title = title
        self.imageurl = imageurl
        self.condition = condition
        self.description = description
        self.price = price
        self.userid = userid
        self.schoolid = schoolid

    def add(self):

        db.session.add(self)
        db.session.commit()

    @property
    def serialize(self):

        return {
            "title": self.title,
            "image": self.image_url,
            "condition": self.condition,
            "description": self.description,
            "price": self.price,
            "user": self.user.firstName,
            "email": self.user.email
        }

    @staticmethod
    def fromForm(form):

        title = form.title.data
        imageurl = form.imageurl.data
        condition = form.condition.data
        description = form.description.data
        price = form.price.data
        userid = form.userid.data
        schoolid = form.schoolid.data
        return Post(title,
                    condition,
                    description,
                    price,
                    userid,
                    schoolid,
                    imageurl=imageurl)