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
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