Ejemplo n.º 1
0
    def closure(cls):
        class_name = cls.__name__ + 'Localized'
        tablename = plural_underscored(class_name)

        if db.metadata.tables.get(tablename) is not None:
            return cls

        cls_columns = cls.__table__.get_children()
        columns = dict([(c.name, c.copy()) for c in cls_columns
                        if isinstance(c.type, (db.Unicode, db.UnicodeText))])
        localized_names = columns.keys()

        columns.update({
            'parent_id':
            db.Column(db.Integer,
                      db.ForeignKey(cls.__tablename__ + '.id',
                                    ondelete="CASCADE",
                                    onupdate="CASCADE"),
                      nullable=True),
            'parent':
            db.relationship(cls, backref='localized_ref'),
            'locale':
            db.Column(db.Unicode(255), default=lang, index=True)
        })

        cls_localized = type(class_name, (db.Model, CRUDMixin), columns)

        for field in localized_names:
            create_property(cls, cls_localized, columns, field)

        return cls
Ejemplo n.º 2
0
class ProductDelivery(db.Model, CRUDMixin):

    delivery_type = db.Column(db.String(128))
    variant_id = db.Column(db.String(255), nullable=True, index=True)
    cost = db.Column(db.Numeric(precision=18, scale=2))

    country_id = db.Column(db.Integer,
                           db.ForeignKey('countries.id'),
                           nullable=True)
    country = db.relationship('Country')

    __table_args__ = (db.UniqueConstraint('delivery_type',
                                          'variant_id',
                                          'country_id',
                                          name='uq_type_variant_country'), )
Ejemplo n.º 3
0
class SocialConnection(db.Model, CRUDMixin):
    user_id = db.Column(db.Integer(), db.ForeignKey('users.id'))
    user = db.relationship('User', backref=db.backref('connections',
                           lazy='dynamic'), cascade='all')
    provider_id = db.Column(db.String(255))
    provider_user_id = db.Column(db.String(255))
    access_token = db.Column(db.String(255))
    secret = db.Column(db.String(255))
    display_name = db.Column(db.Unicode(255))
    profile_url = db.Column(db.String(512))
    image_url = db.Column(db.String(512))
    rank = db.Column(db.Integer)
Ejemplo n.º 4
0
 def parent_id(cls):
     table_name = plural_underscored(cls.__name__)
     return db.Column(
         db.Integer,
         db.ForeignKey("{}.id".format(table_name),
                       ondelete="CASCADE",
                       onupdate="CASCADE"))
Ejemplo n.º 5
0
class BankAccount(db.Model, CRUDMixin):
    bank_name = db.Column(db.Unicode(512))
    iban = db.Column(db.String(256))
    swift = db.Column(db.String(256))
    updated_at = db.Column(db.DateTime, onupdate=datetime.now)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    user = db.relationship('User', backref=db.backref('accounts',
                           lazy='dynamic'))

    def check_owner(self, user):
        return user.id == self.user_id

    def save(self, commit=True):
        instance = super(BankAccount, self).save(commit)
        billing_data_changed.send(self, user_id=instance.user_id)
        return instance
Ejemplo n.º 6
0
class FlatPage(db.Model, SlugMixin):
    """ A flatpage representation model
    """
    content = db.Column(db.UnicodeText)
    template_name = db.Column(db.Unicode(512))
    registration_required = db.Column(db.Boolean, default=False)

    @hybrid_property
    def slug(self):
        return self._slug

    @slug.setter
    def slug(self, name):
        self._slug = slugify(name, prefix=False)

    def save(self, commit=True):
        if not self.slug:
            return super(FlatPage, self).save(commit)
        return super(SlugMixin, self).save(commit)
Ejemplo n.º 7
0
class GalleryMixin(CRUDMixin):
    """Base mixin for Gallery objects
    """
    name = db.Column(db.Unicode(512), nullable=False, default=u'')
    description = db.Column(db.UnicodeText, nullable=False, default=u'')
    is_public = db.Column(db.Boolean, default=True)

    @declared_attr
    def author_id(cls):
        return db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)

    @declared_attr
    def author(cls):
        return db.relationship('User',
                               backref=db.backref(cls.__tablename__,
                                                  lazy='dynamic'))

    def __repr__(self):
        return "<%s:%s>" % (self.__class__.__name__, self.name)
Ejemplo n.º 8
0
class Role(db.Model, CRUDMixin, RoleMixin):
    """ User's role representation as datastore persists it.
        By default model inherits id and created_at fields from the CRUDMixin
    """
    name = db.Column(db.String(255), unique=True, index=True, nullable=False)

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

    def __repr__(self):
        return "<Role: %r>" % self.name

    @classmethod
    def get_or_create(cls, name=None):
        role_name = name or current_app.config['USER_ROLE']
        instance = cls.query.filter_by(name=role_name).first() or \
                        cls.create(name=role_name)

        return instance
Ejemplo n.º 9
0
class Address(db.Model, CRUDMixin):
    """ Represents address data for users
        By default model inherits id and created_at fields from the CRUDMixin
    """
    __mapper_args__ = {
        'order_by': ['country_id', 'city']
    }
    city = db.Column(db.Unicode(255), nullable=False)
    street = db.Column(db.Unicode(255), nullable=False)
    apartment = db.Column(db.Unicode(20))
    zip_code = db.Column(db.String(20))
    customer_id = db.Column(db.Integer, db.ForeignKey('customers.id',
                                                      ondelete='CASCADE',
                                                      use_alter=True,
                                                      name='fk_customer_id'))
    country_id = db.Column(db.Integer, db.ForeignKey('countries.id'))
    country = db.relationship('Country')

    def __repr__(self):
        return "<Address:('%s','%s')>" % (self.city, self.street)

    def save(self, commit=True):
        instance = super(Address, self).save(commit)
        if (instance.customer_id is not None and
                    instance.id == instance.customer.billing_address_id):
            billing_data_changed.send(self, user_id=instance.customer.user_id)
        return instance

    def _type_get(self):
        if self.id == self.customer.billing_address_id:
            return 'billing'
        elif self.id == self.customer.delivery_address_id:
            return 'delivery'
        else:
            return None

    type = property(_type_get, lambda x, y: None)

    def as_dict(self, include=None, exclude=None):
        include = include or []
        include.extend(['type'])

        return super(Address, self).as_dict(include, exclude)
Ejemplo n.º 10
0
 def name(cls):
     return db.Column(db.Unicode(512), nullable=False)
Ejemplo n.º 11
0
class User(db.Model, CRUDMixin, UserMixin):
    """ User representation from the datastore view.
        By default model inherits id and created_at fields from the CRUDMixin
    """
    api_fields = ['email', 'active', 'created_at', 'logged_at',
                  'current_login_at', 'first_name', 'last_name', 'phone',
                  'billing_address']
    __mapper_args__ = {
        'order_by': ['email']
    }

    email = db.Column(db.String(80), unique=True, index=True)
    password = db.Column(db.String(512))
    logged_at = db.Column(db.DateTime, default=datetime.utcnow)
    active = db.Column(db.Boolean, default=True)
    remember_token = db.Column(db.String(255), unique=True, index=True)
    authentication_token = db.Column(db.String(255), unique=True, index=True)
    confirmed_at = db.Column(db.DateTime)
    current_login_at = db.Column(db.DateTime)
    current_login_ip = db.Column(db.String(128))
    login_count = db.Column(db.Integer)
    avatar_id = db.Column(db.String(24))

    roles = db.relationship('Role', secondary=user_roles,
                                backref=db.backref('users', lazy='dynamic'))

    def __repr__(self):
        return "<User: %r>" % self.email

    def __init__(self, **kwargs):
        """ User creation process, set up role for user
            :params kwargs: should contains `email`, `password` and `active`
                            flag to set up base user data
        """
        admin_role = current_app.config['ADMIN_ROLE']
        user_role = current_app.config['USER_ROLE']
        email, admins = kwargs['email'], current_app.config['ADMINS']
        # detect if user should have an admin role
        role = email in admins and admin_role or user_role
        kwargs['roles'] = [Role.get_or_create(name=role)]

        customer_args = {
            'first_name': kwargs.pop('first_name', ''),
            'last_name': kwargs.pop('last_name', ''),
            'phone': kwargs.pop('phone', ''),
            'email': kwargs['email']
        }
        self.customer = Customer(**customer_args)
        super(User, self).__init__(**kwargs)

    @classmethod
    def create(cls, **kwargs):
        raise NotImplementedError("You should use security datastore"
                                  " 'create_user' method for this operation")

    @classmethod
    def is_unique(cls, email):
        """ uniqueness check on email property
            :params email: email to check against existing users
        """
        return cls.query.filter_by(email=email).count() == 0

    @hybrid_property
    def first_name(self):
        return self.customer and self.customer.first_name or ''

    @first_name.setter
    def first_name(self, value):
        self.customer.first_name = value

    @hybrid_property
    def last_name(self):
        return self.customer and self.customer.last_name or ''

    @last_name.setter
    def last_name(self, value):
        self.customer.last_name = value

    @hybrid_property
    def phone(self):
        return self.customer and self.customer.phone or None

    @phone.setter
    def phone(self, value):
        self.customer.phone = value

    @hybrid_property
    def addresses(self):
        return self.customer.addresses

    @addresses.setter
    def addresses(self, value):
        if not isinstance(value, list):
            value = [value]

        map(self.customer.addresses.append, value)

    @property
    def billing_address(self):
        return self.customer and self.customer.billing_address or None

    def is_superuser(self):
        """ Flag signalized that user is superuser """
        # Todo — rewrite on Principal approach
        return self.has_role(current_app.config['ADMIN_ROLE'])

    @property
    def full_name(self):
        """ User full name helper """
        full_name = " ".join([self.first_name or '', self.last_name or ''])
        return full_name.strip() or self.email

    @property
    def product_count(self):
        return BaseProduct.objects(created_by=self.id).count()

    def as_dict(self, include=None, exclude=None):
        include, exclude = include or [], exclude or []
        exclude.extend(['password', 'remember_token', 'authentication_token'])
        include.extend(['first_name', 'last_name', 'phone', 'billing_address',
                        'is_superuser', 'roles'])

        result = super(User, self).as_dict(include, exclude)

        result['products'] = {'created': self.product_count}

        return result
Ejemplo n.º 12
0
 def created_at(cls):
     return db.Column(db.DateTime, default=datetime.utcnow)
Ejemplo n.º 13
0
        elif self.id == self.customer.delivery_address_id:
            return 'delivery'
        else:
            return None

    type = property(_type_get, lambda x, y: None)

    def as_dict(self, include=None, exclude=None):
        include = include or []
        include.extend(['type'])

        return super(Address, self).as_dict(include, exclude)


user_roles = db.Table('user_roles', db.metadata,
    db.Column('user_id', db.Integer, db.ForeignKey('users.id'),
              primary_key=True),
    db.Column('role_id', db.Integer, db.ForeignKey('roles.id'),
              primary_key=True),
)


class CustomerIsTooOldError(Exception):
    pass


class Customer(db.Model, CRUDMixin):
    MIN_BIRTHDATE_YEAR = 1900

    sex = db.Column(db.Unicode(1), index=True)
    birthdate = db.Column(db.DateTime, index=True)
    first_name = db.Column(db.Unicode(255), default=u'')
Ejemplo n.º 14
0
class CartMixin(CRUDMixin):
    """ Cart record for concrete product
    """
    product_id = db.Column(db.String, nullable=False)
    product_variant_id = db.Column(db.String, nullable=False)
    price_option_id = db.Column(db.String, nullable=False)
    amount = db.Column(db.Integer, default=0)  # amount of the same items
    price = db.Column(db.Numeric(precision=18, scale=2))
    is_ordered = db.Column(db.Boolean, default=False, index=True)

    @declared_attr
    def order_id(cls):
        return db.Column(db.Integer, db.ForeignKey('orders.id'))

    @declared_attr
    def customer_id(cls):
        return db.Column(db.Integer, db.ForeignKey('customers.id'),
                         nullable=False)

    @declared_attr
    def customer(cls):
        return db.relationship('Customer',
                               backref=db.backref('carts', **lazy_cascade))

    @classmethod
    def create(cls, commit=True, **kwargs):
        """ Cart creation method. Accepted params are:
        :param product: BaseProduct or it's subclass instance
        :param product_variant: instance of BaseProductVariant subclass
        :param price_option: instance of BasePriceOption subclass
        :param amount: amount of products to place in cart
        :param customer_id: instance of Customer model
        """
        instance_kwargs = {
            'product_id': str(kwargs['product'].id),
            'product_variant_id': str(kwargs['product_variant'].id),
            'price_option_id': str(kwargs['price_option'].id),
            'customer_id': kwargs['customer'].id,
            'amount': kwargs['amount'],
            'price': kwargs['product'].get_price(kwargs['price_option'].id,
                                                 kwargs['amount']),
        }
        instance = super(CartMixin, cls).create(commit, **instance_kwargs)
        return instance

    @classmethod
    def for_customer(cls, customer, is_ordered=False):
        """ helper method for obtaining cart records for concrete customer
        """
        return cls.query.filter_by(customer_id=customer.id,
                                   is_ordered=is_ordered)

    @classmethod
    def expired(cls, max_age):
        """ Returns all unordered carts with age >= max_age
            :param max_age: timedelta object
        """
        min_created_at = datetime.utcnow() - max_age
        return cls.query.filter(cls.created_at <= min_created_at,
                                cls.is_ordered == False)

    @classmethod
    def get_price(cls, query):
        # return sum(map(attrgetter('price'), carts_query))
        return db.session.query(func.sum(cls.price)) \
            .filter(query._criterion).scalar()

    @classmethod
    def mark_ordered(cls, carts_query, order):
        assert isinstance(order, OrderMixin)
        return carts_query.update({'is_ordered': True, 'order_id': order.id})

    def order_is_paid(self):
        if self.order_id is None:
            return False

        order_cls = get_order_class()
        order = order_cls.query.get(self.order_id)
        return order and order.state == OrderStates.paid
Ejemplo n.º 15
0
 def id(cls):
     return db.Column(db.Integer, primary_key=True)
Ejemplo n.º 16
0
 def author_id(cls):
     return db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
Ejemplo n.º 17
0
 def order_id(cls):
     return db.Column(db.Integer, db.ForeignKey('orders.id'))
Ejemplo n.º 18
0
 def customer_id(cls):
     return db.Column(db.Integer, db.ForeignKey('customers.id'),
                      nullable=False, index=True)
Ejemplo n.º 19
0
 def delivery_country_id(cls):
     return db.Column(db.Integer, db.ForeignKey('countries.id',
                      use_alter=True, name='fk_delivery_country'))
Ejemplo n.º 20
0
class OrderMixin(CRUDMixin):
    shop_id = db.Column(db.String(128), default=SHOP_ID)

    billing_city = db.Column(db.Unicode(255))
    billing_street = db.Column(db.Unicode(255))
    billing_apartment = db.Column(db.Unicode(20))
    billing_zip_code = db.Column(db.String(20))

    delivery_city = db.Column(db.Unicode(255))
    delivery_street = db.Column(db.Unicode(255))
    delivery_apartment = db.Column(db.Unicode(20))
    delivery_zip_code = db.Column(db.String(20))
    # summary cost of all cart items linked with this order
    goods_price = db.Column(db.Numeric(precision=18, scale=2))

    vat = db.Column(db.Numeric(precision=18, scale=2))
    total_price = db.Column(db.Numeric(precision=18, scale=2))

    payment_method = db.Column(db.String, nullable=False, index=True)
    state = db.Column(db.Integer, index=True)
    # stored cost for the order delivery
    #delivery_method = db.Column(db.String, nullable=False, index=True)
    delivery_price = db.Column(db.Numeric(precision=18, scale=2))

    @declared_attr
    def billing_country_id(cls):
        return db.Column(db.Integer, db.ForeignKey('countries.id',
                         use_alter=True, name='fk_billing_country'))

    @declared_attr
    def delivery_country_id(cls):
        return db.Column(db.Integer, db.ForeignKey('countries.id',
                         use_alter=True, name='fk_delivery_country'))

    @declared_attr
    def customer_id(cls):
        return db.Column(db.Integer, db.ForeignKey('customers.id'),
                         nullable=False, index=True)

    @declared_attr
    def customer(cls):
        return db.relationship('Customer',
                               backref=db.backref('orders', **lazy_cascade))

    @declared_attr
    def goods(cls):
        return db.relationship('Cart', backref='order', **lazy_cascade)

    @classmethod
    def create_from_api(cls, customer_id, **kwargs):
        """ This method should be overrided in Order model implemetation
        """
        raise NotImplementedError()

    def mark_paid(self):
        order_paid.send(current_app._get_current_object(), order=self)
        return self.update(state=OrderStates.paid)

    def resolve_payment(self, method=None):
        payment_method = self.payment_method or method
        method = current_app.config['PAYMENT_METHODS'][payment_method]
        class_string = method['module']
        PaymentMethod = import_string(class_string)
        return PaymentMethod(self)

    def set_payment_details(self, **kwargs):
        raise NotImplementedError("Payment Details: %s", kwargs)

    @classmethod
    def get_by_payment_details(cls, **kwargs):
        raise NotImplementedError("Payment Details: %s", kwargs)

    @classmethod
    def _prepare_address(cls, addr_type, address_instance):
        exclude_fields = ['customer_id', 'created_at', 'id', 'type']
        address_dict = address_instance.as_dict(exclude=exclude_fields)
        return dict(('{}_{}'.format(addr_type, key), value)
                    for key, value in address_dict.iteritems())

    @classmethod
    def _resolve_delivery(cls, delivery, address):
        return delivery.calculate_price(address)

    @classmethod
    def cancel_payment(cls, payment_method, **kwargs):
        """
        This method is called when payment is canceled by customer. Override it

        :param payment_method: string which identifies payment method
        :param kwargs: additional params passed to identify the payment
        """
        raise NotImplementedError()

    def cancel_by_merchant(self):
        """ Cancels order as if merchant decided to do so, e.g.
        when customer didn't pay in time
        """
        self._delete_carts()
        self.update(state=OrderStates.merchant_canceled)

    def _delete_carts(self):
        cart_cls = get_cart_class()
        carts = cart_cls.query.filter_by(order_id=self.id)

        # we don't use bulk delete, because there's a special Cart.delete()
        for cart in carts:
            cart.delete()

    @classmethod
    def expired(cls, max_age):
        """ Returns all order items in 'created' state with age >= max_age
            :param max_age: timedelta object
        """
        min_created_at = datetime.utcnow() - max_age
        return cls.query.filter(cls.created_at <= min_created_at,
                                cls.state == OrderStates.created)
Ejemplo n.º 21
0
class Customer(db.Model, CRUDMixin):
    MIN_BIRTHDATE_YEAR = 1900

    sex = db.Column(db.Unicode(1), index=True)
    birthdate = db.Column(db.DateTime, index=True)
    first_name = db.Column(db.Unicode(255), default=u'')
    last_name = db.Column(db.Unicode(255), default=u'')
    email = db.Column(db.String(80), index=True)
    phone = db.Column(db.String(80), default='')
    fax = db.Column(db.String(80), default='')
    gender = db.Column(db.String(1), default='')
    company = db.Column(db.Unicode(255), default=u'')
    updated_at = db.Column(db.DateTime, default=datetime.utcnow)
    notes = db.Column(db.UnicodeText)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
    user = db.relationship("User", backref=db.backref("customer",
                                                      uselist=False))
    addresses = db.relationship('Address', backref=db.backref('customer'),
                        primaryjoin="Address.customer_id==Customer.id",
                        cascade='all, delete', lazy='dynamic')
    billing_address_id = db.Column(db.Integer, db.ForeignKey('addresses.id',
                        use_alter=True, name='fk_billing_address'))
    _billing_address = db.relationship("Address", cascade='all, delete',
                        primaryjoin="Customer.billing_address_id==Address.id")
    delivery_address_id = db.Column(db.Integer, db.ForeignKey('addresses.id',
                        use_alter=True, name='fk_delivery_address'))
    _delivery_address = db.relationship("Address", cascade='all, delete',
                        primaryjoin="Customer.delivery_address_id==Address.id")


    @validates('birthdate')
    def validate_birthdate(self, key, value):
        min_date = datetime(self.MIN_BIRTHDATE_YEAR, 1, 1)
        if value < min_date:
            raise CustomerIsTooOldError()

        return value

    def __unicode__(self):
        return u"{0.first_name} {0.last_name}".format(self)

    @property
    def __addresses_ids(self):
        return map(attrgetter('id'), self.addresses)

    def set_address(self, addr_type, value):
        """
        :param addr_type: Either `billing` or `delivery` to describe type the
                          address will be used for
        :param value:     Instance of the Address model
        """
        if value.id not in self.__addresses_ids:
            self.addresses.append(value)

        setattr(self, "{}_address_id".format(addr_type), value.id)
        db.session.commit()
        if addr_type == 'billing':
            billing_data_changed.send(self, user_id=self.user_id)
        return self

    @hybrid_property
    def billing_address(self):
        """ Hybrid property allowing only one billing-address per-customer
        """
        return self._billing_address

    @billing_address.setter
    def billing_address(self, value):
        """ setter for billing_address property
        """
        self.set_address('billing', value)

    @hybrid_property
    def delivery_address(self):
        """ Hybrid property allowing only one delivery_address per-customer
        """
        return self._delivery_address

    @delivery_address.setter
    def delivery_address(self, value):
        """ setter for delivery_address property
        """
        self.set_address('delivery', value)

    @property
    def organizer_ready(self):
        if self.user:
            return self.billing_address and self.user.accounts.count()
        else:
            return False
Ejemplo n.º 22
0
 def _slug(cls):
     return db.Column(db.String(128), nullable=False, unique=True)