Пример #1
0
class Photo(DB.Model):
    """Model for representing a photo stored on S3."""
    __tablename__ = 'photo'
    object_id = DB.Column(DB.Integer, primary_key=True)

    filename = DB.Column(
        DB.Unicode(50),
        nullable=False
    )
    full_url = DB.Column(
        DB.Unicode(250),
        nullable=False
    )
    thumb_url = DB.Column(
        DB.Unicode(250),
        nullable=False
    )

    verified = DB.Column(
        DB.Boolean,
        nullable=True
    )

    def __init__(self, filename, full_url, thumb_url):
        self.filename = filename
        self.full_url = full_url
        self.thumb_url = thumb_url

    def __repr__(self):
        return '<Photo {0}: {1}>'.format(self.object_id, self.filename)
Пример #2
0
class EwayTransaction(DB.Model):
    """Model for representing an eWay Transaction."""
    __tablename__ = 'eway_transaction'
    object_id = DB.Column(DB.Integer, primary_key=True)

    # This holds the order_id
    access_code = DB.Column(DB.Unicode(200), nullable=False)
    charged = DB.Column(DB.Integer(), nullable=False)

    completed = DB.Column(DB.DateTime(), nullable=True)
    result_code = DB.Column(DB.Unicode(2), nullable=True)
    # This holds the PASREF field for realex
    eway_id = DB.Column(DB.String(50), nullable=True)
    refunded = DB.Column(DB.Integer(), nullable=False, default=0)

    def __init__(self, access_code, charged):
        self.access_code = access_code
        self.charged = charged

    @property
    def status(self):
        """Get a better representation of the status of this transaction.

        The eWay API returns statuses as 2 digit codes; this function provides
        a mapping from these codes to a boolean success value and associated
        explanation.

        Returns:
            (bool, str) pair of success value and explanation
        """
        try:
            return REALEX_RESULT_CODES[self.result_code[0]]
        except KeyError as err:
            return (False, 'Unknown response: {0}'.format(err.args[0]))

    @property
    def success(self):
        """Get whether the transaction was completed successfully."""
        success = self.status[0]
        if success is None:
            return 'Uncompleted'
        elif success:
            return 'Successful'
        else:
            return 'Unsuccessful'
Пример #3
0
class PayPalTransaction(transaction.Transaction):
    """Model for representing a paypal transaction."""
    __tablename__ = 'paypal_transaction'
    __mapper_args__ = {'polymorphic_identity': 'PayPal'}
    object_id = DB.Column(DB.Integer, primary_key=True)

    object_id = DB.Column(DB.Integer(),
                          DB.ForeignKey('transaction.object_id'),
                          primary_key=True)

    # This holds the order_id
    access_code = DB.Column(DB.Unicode(200), nullable=True)
    charged = DB.Column(DB.Integer(), nullable=False, default=0)

    completed = DB.Column(DB.DateTime(), nullable=True)
    result_code = DB.Column(DB.Unicode(2), nullable=True)
    # This holds the PASREF field for realex
    paypal_id = DB.Column(DB.String(50), nullable=True)
    refunded = DB.Column(DB.Integer(), nullable=False, default=0)

    order_id = DB.Column(DB.Integer(), nullable=True)

    # eway_transaction_id = DB.Column(
    #     DB.Integer(),
    #     DB.ForeignKey('eway_transaction.object_id'),
    #     nullable=True
    # )
    # eway_transaction = DB.relationship(
    #     'EwayTransaction',
    #     backref=DB.backref(
    #         'transactions',
    #         lazy='dynamic'
    #     )
    # )

    def __init__(self, user, eway_trans=None):
        super(PayPalTransaction, self).__init__(user, 'PayPal')

        # if eway_transaction is not None:
        #     self.eway_transaction = eway_trans

    def __repr__(self):
        return '<PayPalTransaction({0}): {1} item(s)>'.format(
            self.object_id, self.items.count())
Пример #4
0
class Category(DB.Model):
    """Model for a products."""
    __tablename__ = 'category'
    object_id = DB.Column(DB.Integer, primary_key=True)

    name = DB.Column(DB.Unicode(50), unique=True, nullable=False)

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return '<Category {0}: {1}>'.format(self.object_id, self.name)
Пример #5
0
class Affiliation(DB.Model):
    """Model for representing a users affiliation to their college."""
    __tablename__ = 'affiliation'
    object_id = DB.Column(DB.Integer, primary_key=True)

    name = DB.Column(DB.Unicode(25), nullable=False)

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return '<Affiliation {0}: {1}>'.format(self.object_id, self.name)
Пример #6
0
class College(DB.Model):
    """Model for a user's college."""
    __tablename__ = 'college'
    object_id = DB.Column(DB.Integer, primary_key=True)

    name = DB.Column(
        DB.Unicode(50),
        unique=True,
        nullable=False
    )

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return '<College {0}: {1}>'.format(self.object_id, self.name)
Пример #7
0
class User(DB.Model):
    """Model for users."""
    __tablename__ = 'user'

    object_id = DB.Column(DB.Integer, primary_key=True)

    # Class level properties for Flask-Login
    #
    # Sessions don't expire, and no users are anonymous, so these can be hard
    # coded
    is_authenticated = True
    is_anonymous = False

    email = DB.Column(DB.Unicode(120), unique=True, nullable=False)
    new_email = DB.Column(DB.Unicode(120), unique=True, nullable=True)
    password_hash = DB.Column(DB.BINARY(60), nullable=False)
    forenames = DB.Column(DB.Unicode(120), nullable=False)
    surname = DB.Column(DB.Unicode(120), nullable=False)
    full_name = DB.column_property(forenames + ' ' + surname)
    phone = DB.Column(DB.Unicode(20), nullable=False)
    phone_verification_code = DB.Column(DB.Unicode(6), nullable=True)
    phone_verified = DB.Column(DB.Boolean, nullable=False, default=False)
    prepaid_classes = DB.Column(DB.Integer, nullable=False, default=0)
    secret_key = DB.Column(DB.Unicode(64), nullable=True)
    secret_key_expiry = DB.Column(DB.DateTime(), nullable=True)
    verified = DB.Column(DB.Boolean, default=False, nullable=False)
    deleted = DB.Column(DB.Boolean, default=False, nullable=False)
    note = DB.Column(DB.UnicodeText, nullable=True)
    role = DB.Column(DB.Enum('User', 'Admin'), nullable=False)

    college_id = DB.Column(DB.Integer,
                           DB.ForeignKey('college.object_id'),
                           nullable=False)
    college = DB.relationship('College',
                              backref=DB.backref('users', lazy='dynamic'))

    affiliation_id = DB.Column(DB.Integer,
                               DB.ForeignKey('affiliation.object_id'),
                               nullable=False)
    affiliation = DB.relationship('Affiliation',
                                  backref=DB.backref('users', lazy='dynamic'))

    # battels_id = DB.Column(
    #     DB.Integer,
    #     DB.ForeignKey('battels.object_id'),
    #     nullable=True
    # )
    # battels = DB.relationship(
    #     'Battels',
    #     backref=DB.backref(
    #         'user',
    #         uselist=False
    #     )
    # )

    affiliation_verified = DB.Column(DB.Boolean, default=False, nullable=True)

    photo_id = DB.Column(DB.Integer,
                         DB.ForeignKey('photo.object_id'),
                         nullable=True)
    photo = DB.relationship('Photo', backref=DB.backref('user', uselist=False))

    def has_membership(self):
        if membership.Membership.query.filter_by(
                owner_id=self.object_id).count() > 0:
            return True
        return False

    def memberships(self):
        if self.has_membership():
            return membership.Membership.query.filter_by(
                owner_id=self.object_id).all()
        return None

    def has_unpaid_memberships(self):
        if membership.Membership.query.filter_by(owner_id=self.object_id,
                                                 paid=0).count() > 0:
            return True
        return False

    def has_paid_memberships(self):
        if membership.Membership.query.filter_by(owner_id=self.object_id,
                                                 paid=1).count() > 0:
            return True
        return False

    def can_claim_membership(self):
        return False

#todo: check logic
# def has_collected_memberships(self):
#     return False
# def has_uncollected_memberships(self):
#     return False

    def has_held_membership(self):
        # if membership.Membership.query.filter_by(holder_id=self.object_id):
        #     return True
        return False

    def can_update_details(self):
        return APP.config['ENABLE_CHANGING_DETAILS']

    def __init__(self, email, password, forenames, surname, phone, college,
                 affiliation, photo):
        self.email = email
        self.forenames = forenames
        self.surname = surname
        self.phone = phone
        self.college = college
        self.affiliation = affiliation
        self.photo = photo

        self.set_password(password)

        self.secret_key = util.generate_key(64)
        self.verified = False
        self.deleted = False
        self.role = 'User'
        if affiliation.name == 'None':
            self.affiliation_verified = True
        else:
            self.affiliation_verified = False
            #todo add logic for checking if they are on the member list

        # self.battels = battels.Battels.query.filter(
        #     battels.Battels.email == email
        # ).first()

    def __repr__(self):
        return '<User {0}: {1} {2}>'.format(self.object_id, self.forenames,
                                            self.surname)

    def can_pay_by_battels(self):
        return False

    @property
    def group_purchase_requests(self):
        """Get this user's group purchase requests.

        Not just a database backref so that old requests can hang around when
        the user leaves a group.
        """
        if self.purchase_group:
            for request in self.purchase_group.requests:
                if request.requester == self:
                    yield request

    def group_purchase_requested(self, membership_type_slug):
        return 0

    @property
    def total_group_purchase_requested(self):
        return 0

    @property
    def total_group_purchase_value(self):
        return 0

    def check_password(self, candidate):
        """Check if a password matches the hash stored for the user.

        Runs the bcrypt.Bcrypt checking routine to validate the password.

        Args:
            candidate: (str) the candidate password

        Returns:
            (bool) whether the candidate password matches the stored hash
        """
        return BCRYPT.check_password_hash(self.password_hash, candidate)
        # return check_password_hash(self.password_hash, candidate)

    def set_password(self, password):
        """Set the password for the user.

        Hashes the password using bcrypt and stores the resulting hash.

        Args:
            password: (str) new password for the user.
        """
        self.password_hash = BCRYPT.generate_password_hash(password)
        # self.password_hash = generate_password_hash(password)

    def promote(self):
        """Make the user an admin."""
        self.role = 'Admin'

    def demote(self):
        """Make the user an ordinary user (no admin privileges)"""
        self.role = 'User'

    @property
    def is_admin(self):
        """Check if the user is an admin, or is currently being impersonated.

        For future-proofing purposes, the role of the impersonating user is also
        checked.
        """
        return self.role == 'Admin' or (
            'actor_id' in flask.session
            and User.get_by_id(flask.session['actor_id']).role == 'Admin')

    @property
    def is_waiting(self):
        """Is the user on the waiting list?"""
        return self.waiting.count() > 0

    @property
    def memberships(self):
        """Get the active memberships owned by the user."""
        return self.memberships.filter(membership.Membership.cancelled == False  # pylint: disable=singleton-comparison
                                       )

    @property
    def active_membership_count(self):
        """How many active memberships does the user own?"""
        return self.active_memberships.count()

    @property
    def waiting_for(self):
        return 0

    @property
    def is_verified(self):
        """Has the user's email address been verified?"""
        return self.verified

    @property
    def is_deleted(self):
        """Has the user been deleted?

        In order to maintain referential integrity, when a user is deleted we
        scrub their personal details, but maintain the user object referenced by
        log entries, memberships, transactions etc.
        """
        return self.deleted

    @property
    def is_active(self):
        """Is the user active?

        This method is specifically for the use of the Flask-Login extension,
        and refers to whether the user can log in.
        """
        return self.is_verified and not self.is_deleted

    def get_id(self):
        """What is this user's ID?

        This method is specifically for the use of the Flask-Login extension,
        and is a defined class method which returns a unique identifier for the
        user, in this case their database ID.
        """
        return unicode(self.object_id)

    @staticmethod
    def get_by_email(email):
        """Get a user object by the user's email address."""
        user = User.query.filter(User.email == email).first()

        if not user:
            return None

        return user

    def add_manual_battels(self):
        """Manually add a battels account for the user

        If we don't have a battels account automatically matched to the user,
        the admin can manually create one for them.
        """
        self.battels = battels.Battels.query.filter(
            battels.Battels.email == self.email).first()

        if not self.battels:
            self.battels = battels.Battels(None, self.email, None,
                                           self.surname, self.forenames, True)
            DB.session.add(self.battels)

        DB.session.commit()

    @staticmethod
    def write_csv_header(csv_writer):
        """Write the header of a CSV export file."""
        csv_writer.writerow([
            'User ID',
            'Email',
            'Forenames',
            'Surname',
            'Phone Number',
            'Notes',
            'Role',
            'College',
            'Affiliation',
            'Battels ID',
        ])

    def write_csv_row(self, csv_writer):
        """Write this object as a row in a CSV export file."""
        csv_writer.writerow([
            self.object_id,
            self.email,
            self.forenames,
            self.surname,
            self.phone,
            self.note,
            self.role,
            self.college.name,
            self.affiliation.name,
            self.battels.battels_id if self.battels is not None else 'N/A',
        ])
Пример #8
0
class Product(DB.Model):
    """Model for a products."""
    __tablename__ = 'product'
    object_id = DB.Column(DB.Integer, primary_key=True)

    name = DB.Column(DB.Unicode(50), unique=True, nullable=False)

    price_ = DB.Column(DB.Integer(), nullable=False)

    lot_of = DB.Column(DB.Integer(), nullable=False, default=1)

    description = DB.Column(DB.Text(), nullable=False)

    photo_id = DB.Column(DB.Integer,
                         DB.ForeignKey('photo.object_id'),
                         nullable=True)
    photo = DB.relationship('Photo',
                            backref=DB.backref('product', uselist=False))
    category_id = DB.Column(DB.Integer,
                            DB.ForeignKey('category.object_id'),
                            nullable=True)
    category = DB.relationship('Category',
                               backref=DB.backref('product_cat',
                                                  uselist=False))

    stock = 1000

    def __init__(self,
                 name,
                 category_id,
                 description="a thing",
                 price=1,
                 lot=1):
        self.name = name
        self.price = price
        self.category_id = category_id
        self.photo = None
        self.description = description
        self.lot_of = lot

    def __repr__(self):
        return '<Product {0}: {1}>'.format(self.object_id, self.name)

    @property
    def price_pounds(self):
        """Get the price of this membership as a string of pounds and pence."""
        price = '{0:03d}'.format(self.price)
        return price[:-2] + '.' + price[-2:]

    @property
    def price(self):
        """Get the price of the membership."""
        return self.price_

    @price.setter
    def price(self, value):
        """Set the price of the membership."""
        self.price_ = max(value, 0)

        if self.price_ == 0:
            self.mark_as_paid()
Пример #9
0
class Log(DB.Model):
    """Model for log entries persisted to the database."""
    __tablename__ = 'log'
    object_id = DB.Column(DB.Integer, primary_key=True)

    timestamp = DB.Column(DB.DateTime, nullable=False)
    ip_address = DB.Column(DB.Unicode(45), nullable=False)
    action = DB.Column(DB.UnicodeText())

    actor_id = DB.Column(DB.Integer(),
                         DB.ForeignKey('user.object_id'),
                         nullable=True)
    actor = DB.relationship('User',
                            backref=DB.backref('actions', lazy='dynamic'),
                            foreign_keys=[actor_id])

    user_id = DB.Column(DB.Integer(),
                        DB.ForeignKey('user.object_id'),
                        nullable=True)
    user = DB.relationship('User',
                           backref=DB.backref('events', lazy='dynamic'),
                           foreign_keys=[user_id])

    memberships = DB.relationship('Membership',
                                  secondary=LOG_MEMBERSHIP_LINK,
                                  backref=DB.backref('events', lazy='dynamic'),
                                  lazy='dynamic')
    products = DB.relationship('Product',
                               secondary=LOG_PRODUCT_LINK,
                               backref=DB.backref('events2', lazy='dynamic'),
                               lazy='dynamic')

    transaction_id = DB.Column(DB.Integer(),
                               DB.ForeignKey('transaction.object_id'),
                               nullable=True)
    transaction = DB.relationship('Transaction',
                                  backref=DB.backref('events3',
                                                     lazy='dynamic'))

    # purchase_group_id = DB.Column(
    #     DB.Integer(),
    #     DB.ForeignKey('purchase_group.object_id'),
    #     nullable=True
    # )
    # purchase_group = DB.relationship(
    #     'PurchaseGroup',
    #     backref=DB.backref(
    #         'events',
    #         lazy='dynamic'
    #     )
    # )

    # admin_fee_id = DB.Column(
    #     DB.Integer(),
    #     DB.ForeignKey('admin_fee.object_id'),
    #     nullable=True
    # )
    # admin_fee = DB.relationship(
    #     'AdminFee',
    #     backref=DB.backref(
    #         'events',
    #         lazy='dynamic'
    #     )
    # )

    def __init__(self,
                 ip_address,
                 action,
                 actor,
                 user,
                 memberships=None,
                 products=None,
                 transaction=None,
                 purchase_group=None,
                 admin_fee=None):
        if memberships is None:
            memberships = []
        if products is None:
            products = []

        self.timestamp = datetime.datetime.utcnow()
        self.ip_address = ip_address
        self.action = action
        self.actor = actor
        self.user = user
        self.memberships = memberships
        self.products = products
        self.transaction = transaction
        # self.purchase_group = purchase_group
        self.admin_fee = admin_fee

    def __repr__(self):
        return '<Log {0}: {1}>'.format(
            self.object_id, self.timestamp.strftime('%Y-%m-%d %H:%m (UTC)'))

    @staticmethod
    def write_csv_header(csv_writer):
        """Write the header of a CSV export file."""
        csv_writer.writerow([
            'Log Entry ID',
            'Timestamp',
            'IP Address',
            'Action',
            'Actor\'s User ID',
            'Actor\'s Name',
            'Target\'s User ID',
            'Target\'s Name',
            'Relevant Membership IDs',
            # 'Relevant Transaction ID',
            # 'Relevant Purchase Group ID',
            # 'Relevant Admin Fee ID',
        ])

    def write_csv_row(self, csv_writer):
        """Write this object as a row in a CSV export file."""
        csv_writer.writerow([
            self.object_id,
            self.timestamp.strftime('%Y-%m-%d %H:%M:%S'),
            self.ip_address,
            self.action,
            self.actor_id if self.actor_id is not None else 'N/A',
            self.actor if self.actor is not None else 'N/A',
            self.user_id if self.user_id is not None else 'N/A',
            self.user if self.user is not None else 'N/A',
            # ','.join(str(membership.object_id) for membership in self.memberships),
            # (
            #     self.transaction_id
            #     if self.transaction_id is not None
            #     else 'N/A'
            # ),
            # (
            #     self.purchase_group_id
            #     if self.purchase_group_id is not None
            #     else 'N/A'
            # ),
            # (
            #     self.admin_fee_id
            #     if self.admin_fee_id is not None
            #     else 'N/A'
            # ),
        ])
Пример #10
0
class Membership(DB.Model):
    """Model for membership."""
    __tablename__ = 'membership'
    object_id = DB.Column(DB.Integer, primary_key=True)

    membership_type = DB.Column(DB.Unicode(50), nullable=False)
    owner_id = DB.Column(DB.Integer,
                         DB.ForeignKey('user.object_id'),
                         nullable=False)
    owner = DB.relationship('User',
                            backref=DB.backref(
                                'memberships',
                                lazy='dynamic',
                                order_by=b'Membership.cancelled'),
                            foreign_keys=[owner_id])

    paid = DB.Column(DB.Boolean(), default=False, nullable=False)
    cancelled = DB.Column(DB.Boolean(), default=False, nullable=False)
    price_ = DB.Column(DB.Integer(), nullable=False)
    note = DB.Column(DB.UnicodeText(), nullable=True)
    expires = DB.Column(DB.DateTime(), nullable=True)
    barcode = DB.Column(DB.Unicode(20), unique=True, nullable=True)

    def __init__(self, owner, membership_type, price):
        self.owner = owner
        self.membership_type = membership_type
        self.price = price

        y = datetime.datetime.utcnow().year
        m = datetime.datetime.utcnow().month
        if m >= 10:
            y = y + 1

        self.expires = datetime.datetime(y, 10,
                                         1)  #(datetime.datetime.utcnow()) +
        #APP.config['MEMBERSHIP_EXPIRY_TIME'])

    def generate_barcode(self):
        # generate barcode
        key = util.generate_key(20).decode('utf-8')
        self.barcode = key

    def generate_qrcode(self):
        # generate QR
        qrcode_img = pyqrcode.create(
            '{0}admin/membership/validate-ticket/{1}/{2}'.format(
                APP.config['EISITIRIO_URL'], self.object_id, self.barcode))
        buffer = io.BytesIO()
        qrcode_img.png(buffer, scale=20)

        f = open(
            '/Users/Gwyneth/Documents/repositories/oussshop/flaskshop/tmp.png',
            'wb')
        f.write(buffer)
        f.close()
        return buffer.getvalue()

    def __repr__(self):
        return '<Membership {0} owned by {1} ({2})>'.format(
            self.object_id, self.owner.full_name, self.owner.object_id)

    def can_be_cancelled(self):
        return False

    def can_be_resold(self):
        return False

    def can_be_claimed(self):
        return False

    def can_be_reclaimed(self):
        return False

    def has_holder(self):
        return False

    def can_be_paid_for(self):
        if self.paid == 0:
            return True
        return False

    def can_be_collected(self):
        return False

    def is_assigned(self):
        return True

    @property
    def price_pounds(self):
        """Get the price of this membership as a string of pounds and pence."""
        price = '{0:03d}'.format(self.price)
        return price[:-2] + '.' + price[-2:]

    @property
    def transaction(self):
        """Get the transaction this membership was paid for in."""
        for transaction_item in self.transaction_items:
            if transaction_item.transaction.paid:
                return transaction_item.transaction

        return None

    @property
    def payment_method(self):
        """Get the payment method for this membership."""
        transaction = self.transaction

        if transaction:
            return transaction.payment_method
        else:
            return 'Unknown Payment Method'

    @property
    def price(self):
        """Get the price of the membership."""
        return self.price_

    @property
    def status(self):
        """Get the status of this membership."""
        if not self.paid:
            return 'Awaiting payment. Expires {0}.'.format(
                self.expires.strftime('%H:%M %d/%m/%Y'))
        elif APP.config['REQUIRE_USER_PHOTO']:
            if not self.holder.photo.verified:
                return 'Awaiting verification of holder photo.'
        else:
            return 'Valid membership'

    @price.setter
    def price(self, value):
        """Set the price of the membership."""
        self.price_ = max(value, 0)

        if self.price_ == 0:
            self.mark_as_paid()

    @hybrid.hybrid_property
    def collected(self):
        """Has this membership been assigned a barcode."""
        return self.barcode != None  # pylint: disable=singleton-comparison

    def mark_as_paid(self):
        """Mark the membership as paid, and clear any expiry."""
        self.paid = True
        self.expires = None

    def add_note(self, note):
        """Add a note to the membership."""
        if not note.endswith('\n'):
            note = note + '\n'

        if self.note is None:
            self.note = note
        else:
            self.note = self.note + note

    @staticmethod
    def count():
        """How many memberships have been sold."""
        # TODO
        return Membership.query.filter(Membership.paid == True).count()  # pylint: disable=singleton-comparison

    @staticmethod
    def write_csv_header(csv_writer):
        """Write the header of a CSV export file."""
        csv_writer.writerow([
            'Membership ID',
            'Membership Type',
            'Paid',
            'Cancelled',
            'Price (Pounds)',
            'Notes',
            'Expires',
            'Barcode',
            'Owner\' User ID',
            'Owner\'s Name',
        ])

    def write_csv_row(self, csv_writer):
        """Write this object as a row in a CSV export file."""
        csv_writer.writerow([
            self.object_id,
            self.membership_type,
            'Yes' if self.paid else 'No',
            'Yes' if self.cancelled else 'No',
            self.price_pounds,
            self.note,
            self.expires.strftime('%Y-%m-%d %H:%M:%S')
            if self.expires is not None else 'N/A',
            self.barcode if self.barcode is not None else 'N/A',
            self.owner_id,
            self.owner.full_name.encode('utf-8'),
        ])