Пример #1
0
class MailRecordModel(BasicModel):
    title = Fields.StringProperty(verbose_name=u'標題', default=u'')
    send_to = Fields.StringProperty(verbose_name=u'發送對象', default=u'')
    cc = Fields.StringProperty(verbose_name=u'發送對象 cc', default=u'')
    content = Fields.RichTextProperty(verbose_name=u'信件內容', default=u'')
    send_date = Fields.DateTimeProperty(verbose_name=u'發送時間',
                                        auto_now_add=True)
    send_system = Fields.HiddenProperty(verbose_name=u'發送的系統', default=u'')

    @classmethod
    def replace_context(cls, name, title=None, content=None):
        record = cls.get_or_create_by_name(name)
        record.name = name
        if title:
            record.title = title
        if content:
            record.content = content
        record.put()
class StockKeepingUnitModel(BasicModel):
    class Meta:
        label_name = {
            'is_enable': u'啟用',
            'title': u'完整規格名稱',
            'sku_full_name': u'sku 完整編號'
        }

    name = Fields.StringProperty(verbose_name=u'識別名稱')
    product_no = Fields.StringProperty(verbose_name=u'產品編號')
    sku_no = Fields.StringProperty(verbose_name=u'sku 編號')
    spec_full_name = Fields.StringProperty(verbose_name=u'完整規格名稱')
    image = Fields.ImageProperty(verbose_name=u'圖片')
    use_price = Fields.BooleanProperty(verbose_name=u'使用獨立銷售價格', default=False)
    price = Fields.FloatProperty(verbose_name=u'獨立銷售價格', default=-1)
    use_cost = Fields.BooleanProperty(verbose_name=u'使用成本', default=False)
    cost = Fields.FloatProperty(verbose_name=u'成本', default=0.0)
    quantity = Fields.IntegerProperty(verbose_name=u'現存數量', default=0)
    estimate = Fields.IntegerProperty(verbose_name=u'預估數量', default=0)
    in_order_quantity = Fields.IntegerProperty(verbose_name=u'在訂單中的數量',
                                               default=0)
    pre_order_quantity = Fields.IntegerProperty(verbose_name=u'在預購中的數量',
                                                default=0)
    low_stock_quantity = Fields.IntegerProperty(verbose_name=u'庫存警戒線',
                                                default=-1)
    last_in_quantity = Fields.IntegerProperty(verbose_name=u'最後入庫數量',
                                              default=0)
    last_in_datetime = Fields.DateTimeProperty(verbose_name=u'最後入庫時間',
                                               auto_now_add=True)
    last_out_quantity = Fields.IntegerProperty(verbose_name=u'最後出庫數量',
                                               default=0)
    last_out_datetime = Fields.DateTimeProperty(verbose_name=u'最後入庫時間',
                                                auto_now_add=True)

    is_enable = Fields.BooleanProperty(verbose_name=u'顯示於前台', default=True)
    can_be_purchased = Fields.BooleanProperty(verbose_name=u'可購買',
                                              default=True)

    use_automatic_increment = Fields.BooleanProperty(verbose_name=u'自動增量',
                                                     default=False)
    automatic_increment_quantity = Fields.IntegerProperty(
        verbose_name=u'增量的數量', default=0)
    product = Fields.SearchingHelperProperty(verbose_name=u'所屬產品',
                                             target='product_object',
                                             target_field_name='title')
    product_object = Fields.KeyProperty(verbose_name=u'所屬產品',
                                        kind=ProductModel)
    spec_name_1 = Fields.HiddenProperty(verbose_name=u'規格名稱 1')
    spec_name_2 = Fields.HiddenProperty(verbose_name=u'規格名稱 2')
    spec_name_3 = Fields.HiddenProperty(verbose_name=u'規格名稱 3')
    spec_name_4 = Fields.HiddenProperty(verbose_name=u'規格名稱 4')
    spec_name_5 = Fields.HiddenProperty(verbose_name=u'規格名稱 5')
    spec_value_1 = Fields.HiddenProperty(verbose_name=u'規格值 1')
    spec_value_2 = Fields.HiddenProperty(verbose_name=u'規格值 2')
    spec_value_3 = Fields.HiddenProperty(verbose_name=u'規格值 3')
    spec_value_4 = Fields.HiddenProperty(verbose_name=u'規格值 4')
    spec_value_5 = Fields.HiddenProperty(verbose_name=u'規格值 5')

    @property
    def sku_full_name(self):
        product_no = u''
        if self.product_no is not u'' and self.product_no is not None:
            product_no = '%s' % self.product_no
        sku_post_name = u''
        if self.name is not u'' and self.name is not None:
            sku_post_name = self.name
        if self.sku_no is not u'' and self.sku_no is not None:
            sku_post_name = self.sku_no
        if product_no is not u'' and sku_post_name is not u'':
            return '%s-%s' % (product_no, sku_post_name)
        return '%s%s' % (product_no, sku_post_name)

    def before_put(self):
        super(StockKeepingUnitModel, self).before_put()
        product_no = u''
        cat = self.product_object
        if cat is not None:
            try:
                cat = cat.get()
                product_no = cat.product_no
            except:
                pass
        spec_list = (u'%s' % self.spec_full_name).split(u',')
        i = 0
        setattr(self, 'product_no', product_no)
        for index in range(0, len(spec_list)):
            spec = spec_list[index].split(u':')
            setattr(self, 'spec_name_%s' % (index + 1), spec[0])
            setattr(self, 'spec_value_%s' % (index + 1), spec[1])

    @property
    def title(self):
        return self.spec_full_name

    @property
    def quantity_percent(self):
        quantity = self.quantity if self.quantity is not None else 0
        last_in_quantity = self.last_in_quantity if self.last_in_quantity is not None else 0
        if last_in_quantity == 0:
            return 0
        if last_in_quantity < self.low_stock_quantity:
            last_in_quantity = self.low_stock_quantity
        return int((float(quantity) / float(last_in_quantity)) * 10000) / 100.0

    @property
    def is_low_stock_level(self):
        quantity = self.quantity if self.quantity is not None else 0
        low_stock_quantity = self.low_stock_quantity if self.low_stock_quantity is not None else -1
        return quantity <= low_stock_quantity

    @classmethod
    def all_enable(cls, product=None, *args, **kwargs):
        cat = None
        if product:
            cat = ProductModel.get_by_name(product)
        if cat is None:
            return cls.query(cls.is_enable == True).order(-cls.sort)
        else:
            return cls.query(cls.product_object == cat.key,
                             cls.is_enable == True).order(-cls.sort)

    @classmethod
    def find_by_product(cls, product):
        return cls.query(cls.product_object == product.key).order(
            cls.spec_full_name)

    def change_estimate_quantity(self, sub_quantity=0, add_quantity=0):
        self.estimate = self.estimate - abs(sub_quantity) + abs(add_quantity)
        if self.estimate < 0:
            self.estimate = 0

    def change_pre_order_quantity(self, sub_quantity=0, add_quantity=0):
        self.pre_order_quantity = self.pre_order_quantity - abs(
            sub_quantity) + abs(add_quantity)
        if self.pre_order_quantity < 0:
            self.pre_order_quantity = 0

    def change_in_order_quantity(self, sub_quantity=0, add_quantity=0):
        self.in_order_quantity = self.in_order_quantity - abs(
            sub_quantity) + abs(add_quantity)
        if self.in_order_quantity < 0:
            self.in_order_quantity = 0

    @property
    def quantity_can_be_used(self):
        n = self.quantity - self.in_order_quantity - self.estimate
        if n < 0:
            return 0
        return n
Пример #3
0
class FileModel(BasicModel):
    class Meta:
        label_name = {
            'title': u'顯示名稱',
            'display_name': u'顯示名稱',
            'children': u'子項目',
            'path_as_url': u'URL'
        }

    name = Fields.StringProperty(verbose_name=u'名稱')
    path = Fields.StringProperty(verbose_name=u'檔案路徑')
    content_length = Fields.IntegerProperty(verbose_name=u'檔案大小', default=0)
    content_type = Fields.StringProperty(verbose_name=u'檔案類型', default='blob')
    content_language = Fields.StringProperty(verbose_name=u'語系')
    parent_resource = Fields.CategoryProperty(kind=selfReferentialModel,
                                              verbose_name=u'所屬目錄')
    is_collection = Fields.BooleanProperty(verbose_name=u'是否為目錄',
                                           default=False)
    is_root = Fields.BooleanProperty(verbose_name=u'是否為根目錄', default=False)
    created = Fields.DateTimeProperty(auto_now_add=True)
    modified = Fields.DateTimeProperty(auto_now=True)
    etag = Fields.StringProperty(verbose_name=u'ETag')
    resource_data = Fields.CategoryProperty(kind=FileDataModel,
                                            verbose_name=u'檔案實體')
    last_version = Fields.IntegerProperty(verbose_name=u'最新的版本', default=0)
    last_md5 = Fields.StringProperty(verbose_name=u'MD5', default=u'')
    file = Fields.BlobKeyProperty(verbose_name=u'BlobKey')
    theme = Fields.StringProperty(verbose_name=u'所屬樣式', default=u'')

    @property
    def abs_path(self):
        if self.is_collection is False:
            if str(self.path).startswith('themes/'):
                return '/assets/' + self.path
        return '/' + self.path

    @property
    def children(self):
        if self.is_collection:
            logging.info(self.key)
            return FileModel().all().filter(
                FileModel.parent_resource == self.key)
        else:
            return []

    @property
    def title(self):
        return self.name

    @property
    def display_name(self):
        return os.path.basename('%s' % self.path)

    @property
    def path_as_url(self):
        return pathname2url(self.path)

    @property
    def content_type_or_default(self):
        if self.is_collection:
            return 'httpd/unix-directory'
        else:
            mimetype = mimetypes.guess_type(self.path, strict=False)[0]
            return mimetype if mimetype else 'application/octet-stream'

    @property
    def is_code_file(self):
        return self.content_type in ([
            'css', 'js', 'javascript', 'html', 'text/css', 'text/html',
            'text/javascript'
        ])

    @classmethod
    def all_without_root(cls):
        return cls.query(cls.is_root == False).order(-cls.sort)

    @classmethod
    def code_files(cls):
        return cls.query(
            cls.content_type.IN([
                'css', 'js', 'javascript', 'html', 'text/css', 'text/html',
                'text/javascript'
            ]), ).order(-cls.sort, -cls.key)

    def make_directory(self):
        path = u'' + self.path
        if path[0:1] is u'/':
            path = path[1:]
        paths = path.split(u'/')
        last_parent = FileModel.root()
        if len(paths) > 1:
            for i in xrange(1, len(paths)):
                path_str = u'/'.join(paths[:i])
                if path_str is not u'':
                    collection = FileModel.get_by_path(path_str)
                    if collection is None:
                        collection = FileModel()
                        collection.name = paths[i]
                        collection.path = path_str
                        collection.parent_resource = last_parent.key
                        collection.is_collection = True
                        collection.put()
                    if collection != self and collection.is_collection is True:
                        last_parent = collection
        self.parent_resource = last_parent.key
        self.put()

    @classmethod
    def root(cls):
        root = cls.all().filter(cls.is_root == True).get()
        if not root:
            root = cls(path='', is_collection=True)
            root.name = '-Root-'
            root.is_root = True
            root.put()
        return root

    @classmethod
    def all_by_path(cls, path=''):
        query = cls.all().filter(cls.path == path + '%')
        return query

    @classmethod
    def get_by_path(cls, path=None):
        return cls.all().filter(cls.path == path).get() if path else cls.root()

    @classmethod
    def exists_with_path(cls, path='', is_collection=None):
        query = cls.all().filter(cls.path == path)
        if is_collection is not None:
            query = query.filter(cls.is_collection == True)
        return query.get(keys_only=True) is not None

    @classmethod
    def content_type_sort_by_name(cls, content_type):
        """ get record with content-type and sort by title """
        return cls.query(cls.content_type == content_type).order(cls.name)

    @classmethod
    def get_or_create(cls, path, content_type):
        n = cls.get_by_path(path)
        if n is None:
            n = cls()
            n.name = os.path.basename('%s' % path)
            n.path = path
            n.content_type = content_type
            n.put()
        return n

    def move_to_path(self, destination_path):
        """Moves this resource and all its children (if applicable) to a new path.
           Assumes that the new path is free and clear."""

        if self.is_collection:
            for child in self.children:
                child_name = os.path.basename(child.path)
                child_path = os.path.join(destination_path, child_name)
                child.move_to_path(child_path)

        self.path = destination_path
        self.put()

    def put(self):
        # type: () -> object
        # workaround for general non-solveable issue of no UNIQUE constraint concept in app engine datastore.
        # anytime we save, we look for the possibility of other duplicate Resources with the same path and delete them.

        try:
            for duped_resource in FileModel.all().filter(
                    FileModel.path == self.path):
                if self.key().id() != duped_resource.key().id():
                    logging.info(
                        'Deleting duplicate resource %s with path %s.' %
                        (duped_resource, duped_resource.path))
                    duped_resource.delete()
            if self.name != '-Root-':
                self.name = self.display_name
        except:
            pass
        paths = self.path.split('/')
        theme = ''
        if len(paths) >= 2 and paths[0] == 'themes':
            theme = paths[1]
        self.theme = theme
        super(FileModel, self).put()

    def delete(self):
        """Override delete to delete our associated ResourceData entity automatically."""
        if self.resource_data:
            n = self.resource_data.get()
            if n:
                n.key.delete()
        if self.last_version > 0:
            try:
                from plugins.code.models.code_model import CodeModel
                CodeModel.delete_with_target(self.key)
            except:
                pass
        self.key.delete()

    def delete_recursive(self):
        """Deletes this entity plus all of its children and other descendants."""
        if self.is_collection:
            for child in self.children:
                child.delete_recursive()
        self.delete()

    def export_response(self, href=None):
        datetime_format = '%Y-%m-%dT%H:%M:%SZ'

        response = ET.Element('D:response', {'xmlns:D': 'DAV:'})
        ET.SubElement(response, 'D:href').text = href
        propstat = ET.SubElement(response, 'D:propstat')
        prop = ET.SubElement(propstat, 'D:prop')

        if self.created:
            ET.SubElement(
                prop,
                'D:creationdate').text = self.created.strftime(datetime_format)

        ET.SubElement(prop, 'D:displayname').text = self.display_name

        if self.content_language:
            ET.SubElement(prop, 'D:getcontentlanguage').text = str(
                self.content_language)

        ET.SubElement(prop,
                      'D:getcontentlength').text = str(self.content_length)
        ET.SubElement(prop, 'D:getcontenttype').text = str(
            self.content_type_or_default)

        if self.modified:
            ET.SubElement(prop,
                          'D:getlastmodified').text = self.modified.strftime(
                              datetime_format)

        resourcetype = ET.SubElement(prop, 'D:resourcetype')

        if self.is_collection:
            ET.SubElement(resourcetype, 'D:collection')

        ET.SubElement(propstat, 'D:status').text = 'HTTP/1.1 200 OK'
        return response