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
class ShoppingCartItemModel(BasicModel): cart = Fields.KeyProperty(verbose_name=u'購物車', kind=ShoppingCartModel) spec = Fields.KeyProperty(verbose_name=u'產品規格', kind=ProductSpecificationModel) user = Fields.ApplicationUserProperty(verbose_name=u'使用者') product_object = Fields.KeyProperty(verbose_name=u'所屬產品', kind=ProductModel) title = Fields.StringProperty(verbose_name=u'產品名稱', default=u'') product_name = Fields.StringProperty(verbose_name=u'產品名稱(系統)', default=u'') product_no = Fields.StringProperty(verbose_name=u'產品型號', default=u'') product_image = Fields.StringProperty(verbose_name=u'產品圖片', default=u'') price = Fields.FloatProperty(verbose_name=u'銷售價格', default=-1) cost = Fields.FloatProperty(verbose_name=u'成本', default=0.0) spec_full_name = Fields.StringProperty(verbose_name=u'完整規格名稱', default=u'') # 庫存相關 try: from plugins.product_stock.models.stock_keeping_unit_model import StockKeepingUnitModel except ImportError: class StockKeepingUnitModel(BasicModel): pass sku = Fields.KeyProperty(verbose_name=u'最小庫存單位', kind=StockKeepingUnitModel) sku_full_name = Fields.StringProperty(verbose_name=u'產品最小庫存名稱') expired_time = Fields.FloatProperty(verbose_name=u'庫存回收時間') quantity = Fields.IntegerProperty(verbose_name=u'數量', default=0) quantity_has_count = Fields.IntegerProperty(verbose_name=u'已計入庫存的數量', default=0) can_add_to_order = Fields.BooleanProperty(verbose_name=u'加至訂單中', default=False) try: from plugins.supplier.models.supplier_model import SupplierModel except ImportError: class SupplierModel(BasicModel): pass supplier = Fields.CategoryProperty(kind=SupplierModel, verbose_name=u'供應商') # 0=訂購(無庫存), 1=現貨, 2預購 order_type = Fields.StringProperty(verbose_name=u'訂購方式') order_type_value = Fields.IntegerProperty(verbose_name=u'訂購方式(值)') size_1 = Fields.FloatProperty(verbose_name=u'長度(公分)', default=10.0) size_2 = Fields.FloatProperty(verbose_name=u'寬度(公分)', default=10.0) size_3 = Fields.FloatProperty(verbose_name=u'高度(公分)', default=10.0) weight = Fields.FloatProperty(verbose_name=u'重量(公斤)', default=10.0) @classmethod def get_or_create(cls, cart, user, product, spec, order_type=0): try: user_key = user.key except AttributeError: user_key = user item = cls.query(cls.cart == cart.key, cls.user == user_key, cls.product_object == product.key, cls.spec == spec.key, cls.order_type_value == order_type).get() need_put = False if item is None: item = cls() item.cart = cart.key item.user = user_key item.product_object = product.key item.spec = spec.key item.order_type_value = order_type item.order_type = [u'訂購', u'現貨', u'預購'][order_type] item.spec_full_name = spec.full_name item.product_name = product.name item.product_image = product.image need_put = True if order_type == 0: item.can_add_to_order = True for field_name in [ 'title', 'product_no', 'price', 'cost', 'size_1', 'size_2', 'size_3', 'weight' ]: if getattr(item, field_name) != getattr(product, field_name): setattr(item, field_name, getattr(product, field_name)) need_put = True if need_put: item.put() return item @classmethod def get(cls, user, sku, order_type_value=0): return cls.query(cls.sku == sku.key, cls.user == user.key, cls.order_type_value == order_type_value).get() @classmethod def get_or_create_with_spec(cls, user, spec, quantity=0, order_type_value=0): product = spec.product_object.get() item = cls.query(cls.sku == spec.key, cls.user == user.key, cls.order_type_value == order_type_value).get() if item is None: item = cls() item.spec = spec.key item.user = user.key item.order_type_value = order_type_value item.product_object = product.key try: item.supplier = product.supplier.get().key except: pass if order_type_value == 0: item.order_type = u'訂購' else: item.order_type = u'預購' item._spec = spec item._product = product item.title = product.title item.product_no = product.product_no item.product_image = product.image item.spec_full_name = spec.spec_full_name item.change_quantity(quantity) item.put() return item @classmethod def all_with_user(cls, user): key = None if user is not None: key = user.key return cls.query(cls.user == key).order(-cls.sort) @classmethod def before_delete(cls, key): item = key.get() if item.order_type_value == 0: if item.quantity > 0: try: sku = item.sku_instance sku.change_estimate_quantity(item.quantity_has_count) sku.put() except: pass def volumetric_weight(self, divisor=6000.0): n = self.size_1 * self.size_2 * self.size_3 / float(divisor) return n @property def spec_instance(self): if not hasattr(self, '_spec'): self._spec = self.spec.get() return self._spec @property def sku_instance(self): if not hasattr(self, '_sku'): self._sku = self.sku.get() return self._sku @property def product_instance(self): if not hasattr(self, '_product'): self._product = self.spec_instance.product_object.get() return self._product def change_quantity(self, quantity): spec = self.spec_instance product = self.product_instance if self.order_type_value == 0: config = ConfigModel.get_config() if config.stock_recover: self.expired_time = time() + config.stock_recover_time else: self.expired_time = time() + 525600 can_use_quantity = spec.quantity_can_be_used + int( self.quantity_has_count) old_quantity_has_count = self.quantity_has_count if can_use_quantity >= quantity and product.can_order: self.can_add_to_order = True self.quantity = quantity self.quantity_has_count = quantity else: self.can_add_to_order = False self.quantity = 0 self.quantity_has_count = 0 spec.change_estimate_quantity(sub_quantity=old_quantity_has_count, add_quantity=self.quantity) spec.put() else: if product.can_pre_order: self.can_add_to_order = True self.quantity = quantity else: self.can_add_to_order = False self.quantity = 0 spec.change_pre_order_quantity(sub_quantity=int( self.quantity_has_count), add_quantity=self.quantity) spec.put() def quantity_can_be_order(self, user=None, sku=None): if sku is None: sku = self.sku.get() if self.order_type_value > 0: return 999 if user and self.quantity is not None: c = sku.quantity_can_be_used + self.quantity if c > 0: return c return sku.quantity_can_be_used
class ShoppingCartModel(BasicModel): title = Fields.StringProperty(verbose_name=u'購物車名稱', default=u'預設') user = Fields.ApplicationUserProperty(verbose_name=u'使用者') total_size = Fields.FloatProperty(verbose_name=u'尺寸合計', default=0.0) total_volume = Fields.FloatProperty(verbose_name=u'體積合計', default=0.0) total_price = Fields.FloatProperty(verbose_name=u'金額合計', default=0.0) total_weight = Fields.FloatProperty(verbose_name=u'重量總重', default=0.0) total_volumetric_weight = Fields.FloatProperty(verbose_name=u'材積總重', default=0.0) cost_for_items = Fields.FloatProperty(verbose_name=u'成本(項目)', default=0.0, tab_page=4) freight = Fields.FloatProperty(verbose_name=u'運費', default=0.0) freight_type = Fields.KeyProperty(verbose_name=u'運送方式', kind=FreightTypeModel) try: from plugins.supplier.models.supplier_model import SupplierModel except ImportError: class SupplierModel(BasicModel): pass supplier = Fields.CategoryProperty(verbose_name=u'供應商', kind=SupplierModel) @classmethod def get_or_create(cls, user, supplier=None, supplier_key=None): if supplier is not None and supplier_key is None: supplier_key = supplier.key cart = cls.query(cls.user == user.key, cls.supplier == supplier_key).get() if cart is None: cart = cls() cart.user = user.key if supplier_key: cart.supplier = supplier_key cart.put() return cart def get_shopping_cart_item(self, product, spec, item_order_type): from shopping_cart_item_model import ShoppingCartItemModel return ShoppingCartItemModel.get_or_create(self, self.user, product, spec, item_order_type) @property def items(self): from shopping_cart_item_model import ShoppingCartItemModel return ShoppingCartItemModel.query( ShoppingCartItemModel.cart == self.key).fetch() def clean(self): if len(self.items) == 0: self.key.delete() def calc_size_weight_price_and_put(self, items=None): if items is None: items = self.items total_volume = 0.0 total_size = 0.0 total_price = 0.0 total_weight = 0.0 total_volumetric_weight = 0.0 total_cost_for_items = 0.0 for cart_item in items: total_size += cart_item.quantity * ( cart_item.size_1 + cart_item.size_2 + cart_item.size_3) total_volume += cart_item.quantity * cart_item.size_1 * cart_item.size_2 * cart_item.size_3 total_price += cart_item.quantity * cart_item.price total_weight += cart_item.quantity * cart_item.weight total_volumetric_weight += cart_item.quantity * cart_item.volumetric_weight( ) total_cost_for_items += cart_item.quantity * cart_item.cost self.total_volume = total_volume self.total_size = total_size self.total_price = total_price self.total_weight = total_weight self.total_volumetric_weight = total_volumetric_weight self.cost_for_items = total_cost_for_items self.put()