class AdminAbnormalOrdersView(AdminBaseView): """后台-订单-获取异常订单列表""" pagination_class = StandardResultsSetPagination @AdminBaseView.permission_required( [AdminBaseView.staff_permissions.ADMIN_ORDER]) @use_args( { "order_types": StrToList( required=False, missing=[OrderType.GROUPON], validate=[validate.ContainsOnly([OrderType.GROUPON])], comment="订单类型筛选 1: 普通订单, 5: 拼团订单", ), "order_pay_types": StrToList( required=False, missing=[OrderPayType.ON_DELIVERY, OrderPayType.WEIXIN_JSAPI], validate=[ validate.ContainsOnly( [OrderPayType.WEIXIN_JSAPI, OrderPayType.ON_DELIVERY]) ], comment="订单支付方式筛选 1: 微信支付, 2: 货到付款", ), "order_delivery_methods": StrToList( required=False, missing=[ DeliveryType.StaffDelivery, DeliveryType.ExpressDelivery ], validate=[ validate.ContainsOnly([ DeliveryType.ExpressDelivery, DeliveryType.StaffDelivery ]) ], comment="订单配送方式筛选 1: 送货上门, 2: 自提", ), "order_status": StrToList( required=False, missing=[OrderStatus.REFUND_FAIL], validate=[validate.ContainsOnly([OrderStatus.REFUND_FAIL])], comment="订单状态筛选 6: 退款失败", ), "order_num": fields.String(comment="订单号搜索,与其他条件互斥"), }, location="query") def get(self, request, args): orders = list_shop_abnormal_orders(self.current_shop.id, **args) orders = self._get_paginated_data(orders, AdminOrderSerializer) return self.send_success(data_list=orders)
class FilterPublishersSchema(FilterSchema): sort_by = fields.Str(validate=sort_one_of( ['id', 'name', 'airtime', 'comment', 'created_at', 'updated_at']), missing='created_at') query = fields.Str(validate=validate.Length(min=3, max=100), missing=None) query_fields = fields.List(fields.Str(), validate=validate.ContainsOnly( ['name', 'comment']), missing=['name', 'comment'])
class AdminProductGroupsView(AdminBaseView): """后台-货品-批量更新货品分组""" @AdminBaseView.permission_required([AdminBaseView.staff_permissions.ADMIN_PRODUCT]) @use_args( { "product_ids": fields.List( fields.Integer(required=True), required=True, validate=[validate.Length(1)], comment="货品ID列表", ), "group_id": fields.Integer(required=True, comment="货品分组ID"), }, location="json" ) def put(self, request, args): shop = self.current_shop group_id = args.get("group_id") product_ids = args.pop("product_ids") # 校验分组是否存在 product_group = get_product_group_by_shop_id_and_id(shop.id, group_id) if not product_group: return self.send_fail(error_text="货品分组不存在") # 获取货品,更新货品信息 update_product_product_group_by_ids(product_ids, group_id) return self.send_success() @AdminBaseView.permission_required([AdminBaseView.staff_permissions.ADMIN_PRODUCT]) @use_args( { "status": StrToList( required=False, missing=[ProductStatus.ON, ProductStatus.OFF], validate=[ validate.ContainsOnly( [ProductStatus.ON, ProductStatus.OFF] ) ], comment="货品状态,上架/下架", ) }, location="query" ) def get(self, request, args): shop = self.current_shop product_group_with_count = list_product_group_with_product_count(shop.id, **args) serializer = AdminProductGroupSerializer(product_group_with_count, many=True) return self.send_success(data_list=serializer.data)
class MallOrdersView(MallBaseView): """商城-订单列表""" pagination_class = StandardResultsSetPagination @use_args( { "order_status": StrToList( required=False, missing=[], validate=[ validate.ContainsOnly([ OrderStatus.CANCELED, OrderStatus.UNPAID, OrderStatus.PAID, OrderStatus.CONFIRMED, OrderStatus.FINISHED, OrderStatus.REFUNDED, OrderStatus.REFUND_FAIL, OrderStatus.WAITTING, ]) ], comment="订单状态列表", ), }, location="query", ) def get(self, reuqest, args, shop_code): self._set_current_shop(reuqest, shop_code) user_id = self.current_user.id shop_id = self.current_shop.id customer = get_customer_by_user_id_and_shop_id_interface( user_id, shop_id) # 不是客户在这个店肯定没单 if not customer: return self.send_success(data_list=[]) order_list = list_customer_order_by_customer_ids( [customer.id], args.get('order_status')) order_list = self._get_paginated_data(order_list, MallOrdersSerializer) return self.send_success(data_list=order_list)
class AdminGrouponAttendsView(AdminBaseView): """后台-玩法-拼团-参与拼团列表""" pagination_class = StandardResultsSetPagination @AdminBaseView.permission_required( [AdminBaseView.staff_permissions.ADMIN_PROMOTION]) @use_args( { "groupon_id": fields.Integer(required=True, comment="拼团id"), "groupon_attend_status": StrToList( required=False, missing=[ GrouponAttendStatus.WAITTING, GrouponAttendStatus.SUCCEEDED, GrouponAttendStatus.FAILED, ], validate=validate.ContainsOnly([ GrouponAttendStatus.WAITTING, GrouponAttendStatus.SUCCEEDED, GrouponAttendStatus.FAILED, ]), comment="拼团参与状态,1:拼团中 2:已成团 3:已失败", ), }, location="query") def get(self, request, args): success, groupon = get_shop_groupon_by_id(self.current_shop.id, args.pop("groupon_id")) if not success: return self.send_fail(error_text=groupon) groupon_attends = list_groupon_attends_by_groupon( groupon, args["groupon_attend_status"]) groupon_attends = self._get_paginated_data( groupon_attends, AdminGrouponAttendSerializer) return self.send_success(data_list=groupon_attends)
class GrampsObjectResource(GrampsObjectResourceHelper, Resource): """Resource for a single object.""" @use_args( { "backlinks": fields.Boolean(missing=False), "extend": fields.DelimitedList( fields.Str(validate=validate.Length(min=1)), validate=validate.ContainsOnly( choices=[ "all", "citation_list", "event_ref_list", "family_list", "note_list", "parent_family_list", "person_ref_list", "primary_parent_family", "place", "source_handle", "father_handle", "mother_handle", "media_list", "reporef_list", "tag_list", "backlinks", "child_ref_list", ] ), ), "formats": fields.DelimitedList( fields.Str(validate=validate.Length(min=1)) ), "format_options": fields.Str(validate=validate.Length(min=1)), "keys": fields.DelimitedList(fields.Str(validate=validate.Length(min=1))), "locale": fields.Str(missing=None, validate=validate.Length(min=1, max=5)), "profile": fields.DelimitedList( fields.Str(validate=validate.Length(min=1)), validate=validate.ContainsOnly( choices=[ "all", "self", "families", "events", "age", "span", "ratings", "references", ] ), ), "skipkeys": fields.DelimitedList( fields.Str(validate=validate.Length(min=1)) ), "soundex": fields.Boolean(missing=False), "strip": fields.Boolean(missing=False), }, location="query", ) def get(self, args: Dict, handle: str) -> Response: """Get the object.""" try: obj = self.get_object_from_handle(handle) except HandleError: abort(404) locale = get_locale_for_language(args["locale"], default=True) get_etag = hash_object(obj) return self.response( 200, self.full_object(obj, args, locale=locale), args, etag=get_etag ) def delete(self, handle: str) -> Response: """Delete the object.""" require_permissions([PERM_DEL_OBJ]) try: obj = self.get_object_from_handle(handle) except HandleError: abort(404) get_etag = hash_object(obj) for etag in request.if_match: if etag != get_etag: abort(412) trans_dict = delete_object( self.db_handle_writable, handle, self.gramps_class_name ) # update search index indexer: SearchIndexer = current_app.config["SEARCH_INDEXER"] with indexer.get_writer(overwrite=False, use_async=True) as writer: indexer.delete_object(writer, handle) return self.response(200, trans_dict, total_items=len(trans_dict)) def put(self, handle: str) -> Response: """Modify an existing object.""" require_permissions([PERM_EDIT_OBJ]) try: obj_old = self.get_object_from_handle(handle) except HandleError: abort(404) get_etag = hash_object(obj_old) for etag in request.if_match: if etag != get_etag: abort(412) obj = self._parse_object() if not obj: abort(400) db_handle = self.db_handle_writable with DbTxn("Edit object", db_handle) as trans: try: update_object(db_handle, obj, trans) except ValueError: abort(400) trans_dict = transaction_to_json(trans) # update search index indexer: SearchIndexer = current_app.config["SEARCH_INDEXER"] with indexer.get_writer(overwrite=False, use_async=True) as writer: for _trans_dict in trans_dict: handle = _trans_dict["handle"] class_name = _trans_dict["_class"] indexer.add_or_update_object(writer, handle, db_handle, class_name) return self.response(200, trans_dict, total_items=len(trans_dict))
class AdminProductsView(AdminBaseView): """后台-货品-获取货品列表&批量修改货品(上架,下架)&批量删除货品""" pagination_class = StandardResultsSetPagination @AdminBaseView.permission_required([AdminBaseView.staff_permissions.ADMIN_PRODUCT]) @use_args( { "keyword": fields.String(required=False, missing="", comment="货品关键字"), "group_id": fields.Integer(required=True, comment="分组ID"), "page": fields.Integer(required=False, missing=1, comment="页码"), "status": StrToList( required=False, missing=[ProductStatus.ON, ProductStatus.OFF], validate=[validate.ContainsOnly([ProductStatus.ON, ProductStatus.OFF])], comment="货品状态,上架、下架", ), "promotion_types": StrToList( required=False, missing=[], validate=[ validate.ContainsOnly({PromotionType.NORMAL, PromotionType.GROUPON}) ], comment="货品营销类型", ), }, location="query" ) def get(self, request, args): page = args.pop("page") shop = self.current_shop product_list = list_product_by_filter(shop.id, **args) # page为-1时不分页 if page < 0: product_list = {"results": AdminProductsSerializer(product_list, many=True).data} else: product_list = self._get_paginated_data(product_list, AdminProductsSerializer) return self.send_success(data_list=product_list) @AdminBaseView.permission_required([AdminBaseView.staff_permissions.ADMIN_PRODUCT]) @use_args( { "product_ids": fields.List( fields.Integer(required=True), required=True, validate=[validate.Length(1)], commet="货品ID列表", ), "operation_type": fields.Integer( required=False, missing=1, validate=[validate.OneOf( [ProductOperationType.ON, ProductOperationType.OFF] )], comment="操作类型,1:上架,2:下架" ) }, location="json" ) def put(self, request, args): operation_type = args.get("operation_type") product_ids = args.get("product_ids") product_list = list_product_by_ids(self.current_shop.id, product_ids) product_ids = [pl.id for pl in product_list] product_ids_set = list_alive_groupon_by_product_ids_interface(product_ids) product_name_list = update_products_status( product_list, operation_type, product_ids_set ) if product_name_list: if operation_type == ProductOperationType.ON: log_operate_type = ProductLogType.ON_PRODUCT else: log_operate_type = ProductLogType.OFF_PRODUCT log_info = { "shop_id": self.current_shop.id, "operator_id": self.current_user.id, "operate_type": log_operate_type, "operate_content": "、".join(product_name_list), } create_product_log_interface(log_info) return self.send_success() @AdminBaseView.permission_required([AdminBaseView.staff_permissions.ADMIN_PRODUCT]) @use_args( { "product_ids": fields.List( fields.Integer(required=True), required=True, validate=[validate.Length(1)], commet="货品ID列表", ) }, location="json", ) def delete(self, request, args): product_ids = args.get("product_ids") product_list = list_product_by_ids(self.current_shop.id, product_ids) product_ids = [pl.id for pl in product_list] product_ids_set = list_alive_groupon_by_product_ids_interface(product_ids) product_name_list = delete_product_by_ids_and_shop_id(product_list, product_ids_set) # 记录日志 if product_name_list: log_info = { "shop_id": self.current_shop.id, "operator_id": self.current_user.id, "operate_type": ProductLogType.DELETE_PRODUCT, "operate_content": "、".join(product_name_list), } create_product_log_interface(log_info) return self.send_success()
class SearchResource(GrampsJSONEncoder, ProtectedResource): """Fulltext search resource.""" @property def db_handle(self) -> DbReadBase: """Get the database instance.""" return get_db_handle() def get_object_from_handle( self, handle: str, class_name: str, args: Dict, locale: GrampsLocale ) -> GrampsObject: """Get the object given a Gramps handle.""" query_method = self.db_handle.method("get_%s_from_handle", class_name) obj = query_method(handle) if "profile" in args: if class_name == "person": obj.profile = get_person_profile_for_object( self.db_handle, obj, args["profile"], locale=locale ) elif class_name == "family": obj.profile = get_family_profile_for_object( self.db_handle, obj, args["profile"], locale=locale ) elif class_name == "event": obj.profile = get_event_profile_for_object( self.db_handle, obj, args["profile"], locale=locale ) elif class_name == "citation": obj.profile = get_citation_profile_for_object( self.db_handle, obj, args["profile"], locale=locale ) elif class_name == "place": obj.profile = get_place_profile_for_object( self.db_handle, obj, locale=locale ) elif class_name == "media": obj.profile = get_media_profile_for_object( self.db_handle, obj, args["profile"], locale=locale ) return obj @use_args( { "locale": fields.Str(missing=None, validate=validate.Length(min=1, max=5)), "query": fields.Str(required=True, validate=validate.Length(min=1)), "page": fields.Int(missing=1, validate=validate.Range(min=1)), "pagesize": fields.Int(missing=20, validate=validate.Range(min=1)), "sort": fields.DelimitedList(fields.Str(validate=validate.Length(min=1))), "profile": fields.DelimitedList( fields.Str(validate=validate.Length(min=1)), validate=validate.ContainsOnly( choices=["all", "self", "families", "events", "age", "span"] ), ), "strip": fields.Boolean(missing=False), }, location="query", ) def get(self, args: Dict): """Get search result.""" searcher = current_app.config["SEARCH_INDEXER"] total, hits = searcher.search( query=args["query"], page=args["page"], pagesize=args["pagesize"], # search in private records if allowed to include_private=has_permissions([PERM_VIEW_PRIVATE]), sort=args.get("sort"), ) if hits: locale = get_locale_for_language(args["locale"], default=True) for hit in hits: try: hit["object"] = self.get_object_from_handle( handle=hit["handle"], class_name=hit["object_type"], args=args, locale=locale, ) except HandleError: pass # filter out hits without object (i.e. if handle failed) hits = [hit for hit in hits if "object" in hit] return self.response(200, payload=hits or [], args=args, total_items=total)
class GrampsObjectsResource(GrampsObjectResourceHelper, Resource): """Resource for multiple objects.""" @use_args( { "backlinks": fields.Boolean(missing=False), "dates": fields.Str( missing=None, validate=validate.Regexp( r"^([0-9]+|\*)/([1-9]|1[0-2]|\*)/([1-9]|1[0-9]|2[0-9]|3[0-1]|\*)$|" r"^-[0-9]+/([1-9]|1[0-2])/([1-9]|1[0-9]|2[0-9]|3[0-1])$|" r"^[0-9]+/([1-9]|1[0-2])/([1-9]|1[0-9]|2[0-9]|3[0-1])-$|" r"^[0-9]+/([1-9]|1[0-2])/([1-9]|1[0-9]|2[0-9]|3[0-1])-" r"[0-9]+/([1-9]|1[0-2])/([1-9]|1[0-9]|2[0-9]|3[0-1])$" ), ), "extend": fields.DelimitedList( fields.Str(validate=validate.Length(min=1)), validate=validate.ContainsOnly( choices=[ "all", "citation_list", "event_ref_list", "family_list", "note_list", "parent_family_list", "person_ref_list", "primary_parent_family", "place", "source_handle", "father_handle", "mother_handle", "media_list", "reporef_list", "tag_list", "backlinks", "child_ref_list", ] ), ), "filter": fields.Str(validate=validate.Length(min=1)), "formats": fields.DelimitedList( fields.Str(validate=validate.Length(min=1)) ), "format_options": fields.Str(validate=validate.Length(min=1)), "gramps_id": fields.Str(validate=validate.Length(min=1)), "keys": fields.DelimitedList(fields.Str(validate=validate.Length(min=1))), "locale": fields.Str(missing=None, validate=validate.Length(min=1, max=5)), "page": fields.Integer(missing=0, validate=validate.Range(min=1)), "pagesize": fields.Integer(missing=20, validate=validate.Range(min=1)), "profile": fields.DelimitedList( fields.Str(validate=validate.Length(min=1)), validate=validate.ContainsOnly( choices=[ "all", "self", "families", "events", "age", "span", "ratings", "references", ] ), ), "rules": fields.Str(validate=validate.Length(min=1)), "skipkeys": fields.DelimitedList( fields.Str(validate=validate.Length(min=1)) ), "sort": fields.DelimitedList(fields.Str(validate=validate.Length(min=1))), "soundex": fields.Boolean(missing=False), "strip": fields.Boolean(missing=False), }, location="query", ) def get(self, args: Dict) -> Response: """Get all objects.""" locale = get_locale_for_language(args["locale"], default=True) if "gramps_id" in args: obj = self.get_object_from_gramps_id(args["gramps_id"]) if obj is None: abort(404) return self.response( 200, [self.full_object(obj, args, locale=locale)], args, total_items=1 ) query_method = self.db_handle.method("get_%s_handles", self.gramps_class_name) if self.gramps_class_name in ["Event", "Repository", "Note"]: handles = query_method() else: handles = query_method(sort_handles=True, locale=locale) if "filter" in args or "rules" in args: handles = apply_filter( self.db_handle, args, self.gramps_class_name, handles ) if args["dates"]: handles = self.match_dates(handles, args["dates"]) if "sort" in args: handles = self.sort_objects(handles, args["sort"], locale=locale) total_items = len(handles) if args["page"] > 0: offset = (args["page"] - 1) * args["pagesize"] handles = handles[offset : offset + args["pagesize"]] query_method = self.db_handle.method( "get_%s_from_handle", self.gramps_class_name ) return self.response( 200, [ self.full_object(query_method(handle), args, locale=locale) for handle in handles ], args, total_items=total_items, )
class GrampsObjectResource(GrampsObjectResourceHelper, Resource): """Resource for a single object.""" @use_args( { "backlinks": fields.Boolean(missing=False), "extend": fields.DelimitedList( fields.Str(validate=validate.Length(min=1)), validate=validate.ContainsOnly( choices=[ "all", "citation_list", "event_ref_list", "family_list", "note_list", "parent_family_list", "person_ref_list", "primary_parent_family", "place", "source_handle", "father_handle", "mother_handle", "media_list", "reporef_list", "tag_list", "backlinks", "child_ref_list", ] ), ), "formats": fields.DelimitedList( fields.Str(validate=validate.Length(min=1)) ), "format_options": fields.Str(validate=validate.Length(min=1)), "keys": fields.DelimitedList(fields.Str(validate=validate.Length(min=1))), "locale": fields.Str(missing=None, validate=validate.Length(min=1, max=5)), "profile": fields.DelimitedList( fields.Str(validate=validate.Length(min=1)), validate=validate.ContainsOnly( choices=[ "all", "self", "families", "events", "age", "span", "ratings", "references", ] ), ), "skipkeys": fields.DelimitedList( fields.Str(validate=validate.Length(min=1)) ), "soundex": fields.Boolean(missing=False), "strip": fields.Boolean(missing=False), }, location="query", ) def get(self, args: Dict, handle: str) -> Response: """Get the object.""" try: obj = self.get_object_from_handle(handle) except HandleError: abort(404) locale = get_locale_for_language(args["locale"], default=True) return self.response(200, self.full_object(obj, args, locale=locale), args)
class GrampsObjectsResource(GrampsObjectResourceHelper, Resource): """Resource for multiple objects.""" @use_args( { "backlinks": fields.Boolean(missing=False), "dates": fields.Str( missing=None, validate=validate.Regexp( r"^([0-9]+|\*)/([1-9]|1[0-2]|\*)/([1-9]|1[0-9]|2[0-9]|3[0-1]|\*)$|" r"^-[0-9]+/([1-9]|1[0-2])/([1-9]|1[0-9]|2[0-9]|3[0-1])$|" r"^[0-9]+/([1-9]|1[0-2])/([1-9]|1[0-9]|2[0-9]|3[0-1])-$|" r"^[0-9]+/([1-9]|1[0-2])/([1-9]|1[0-9]|2[0-9]|3[0-1])-" r"[0-9]+/([1-9]|1[0-2])/([1-9]|1[0-9]|2[0-9]|3[0-1])$" ), ), "extend": fields.DelimitedList( fields.Str(validate=validate.Length(min=1)), validate=validate.ContainsOnly( choices=[ "all", "citation_list", "event_ref_list", "family_list", "note_list", "parent_family_list", "person_ref_list", "primary_parent_family", "place", "source_handle", "father_handle", "mother_handle", "media_list", "reporef_list", "tag_list", "backlinks", "child_ref_list", ] ), ), "filter": fields.Str(validate=validate.Length(min=1)), "formats": fields.DelimitedList( fields.Str(validate=validate.Length(min=1)) ), "format_options": fields.Str(validate=validate.Length(min=1)), "gramps_id": fields.Str(validate=validate.Length(min=1)), "keys": fields.DelimitedList(fields.Str(validate=validate.Length(min=1))), "locale": fields.Str(missing=None, validate=validate.Length(min=1, max=5)), "page": fields.Integer(missing=0, validate=validate.Range(min=1)), "pagesize": fields.Integer(missing=20, validate=validate.Range(min=1)), "profile": fields.DelimitedList( fields.Str(validate=validate.Length(min=1)), validate=validate.ContainsOnly( choices=[ "all", "self", "families", "events", "age", "span", "ratings", "references", ] ), ), "rules": fields.Str(validate=validate.Length(min=1)), "skipkeys": fields.DelimitedList( fields.Str(validate=validate.Length(min=1)) ), "sort": fields.DelimitedList(fields.Str(validate=validate.Length(min=1))), "soundex": fields.Boolean(missing=False), "strip": fields.Boolean(missing=False), "filemissing": fields.Boolean(missing=False), }, location="query", ) def get(self, args: Dict) -> Response: """Get all objects.""" locale = get_locale_for_language(args["locale"], default=True) if "gramps_id" in args: obj = self.get_object_from_gramps_id(args["gramps_id"]) if obj is None: abort(404) return self.response( 200, [self.full_object(obj, args, locale=locale)], args, total_items=1 ) query_method = self.db_handle.method("get_%s_handles", self.gramps_class_name) if self.gramps_class_name in ["Event", "Repository", "Note"]: handles = query_method() else: handles = query_method(sort_handles=True, locale=locale) if "filter" in args or "rules" in args: handles = apply_filter( self.db_handle, args, self.gramps_class_name, handles ) if self.gramps_class_name == "Media" and args.get("filemissing"): handles = get_missing_media_file_handles(self.db_handle, handles) if args["dates"]: handles = self.match_dates(handles, args["dates"]) if "sort" in args: handles = self.sort_objects(handles, args["sort"], locale=locale) total_items = len(handles) if args["page"] > 0: offset = (args["page"] - 1) * args["pagesize"] handles = handles[offset : offset + args["pagesize"]] query_method = self.db_handle.method( "get_%s_from_handle", self.gramps_class_name ) return self.response( 200, [ self.full_object(query_method(handle), args, locale=locale) for handle in handles ], args, total_items=total_items, ) def post(self) -> Response: """Post a new object.""" require_permissions([PERM_ADD_OBJ]) obj = self._parse_object() if not obj: abort(400) db_handle = self.db_handle_writable with DbTxn("Add objects", db_handle) as trans: try: add_object(db_handle, obj, trans, fail_if_exists=True) except ValueError: abort(400) trans_dict = transaction_to_json(trans) # update search index indexer: SearchIndexer = current_app.config["SEARCH_INDEXER"] with indexer.get_writer(overwrite=False, use_async=True) as writer: for _trans_dict in trans_dict: handle = _trans_dict["handle"] class_name = _trans_dict["_class"] indexer.add_or_update_object(writer, handle, db_handle, class_name) return self.response(201, trans_dict, total_items=len(trans_dict))
class AdminOrdersView(AdminBaseView): """后台-订单-获取订单列表""" pagination_class = StandardResultsSetPagination @AdminBaseView.permission_required( [AdminBaseView.staff_permissions.ADMIN_ORDER]) @use_args( { "order_types": StrToList( required=False, missing=[OrderType.NORMAL, OrderType.GROUPON], validate=[ validate.ContainsOnly( [OrderType.NORMAL, OrderType.GROUPON]) ], comment="订单类型筛选 1: 普通订单, 5: 拼团订单", ), "order_pay_types": StrToList( required=False, missing=[OrderPayType.WEIXIN_JSAPI, OrderPayType.ON_DELIVERY], validate=[ validate.ContainsOnly( [OrderPayType.WEIXIN_JSAPI, OrderPayType.ON_DELIVERY]) ], comment="订单支付方式筛选 1: 微信支付, 2: 货到付款", ), "order_delivery_methods": StrToList( required=False, missing=[ OrderDeliveryMethod.HOME_DELIVERY, OrderDeliveryMethod.CUSTOMER_PICK, ], validate=[ validate.ContainsOnly([ OrderDeliveryMethod.HOME_DELIVERY, OrderDeliveryMethod.CUSTOMER_PICK, ]) ], comment="订单配送方式筛选 1: 送货上门, 2: 自提", ), "order_status": StrToList( required=False, missing=[ OrderStatus.PAID, OrderStatus.CONFIRMED, OrderStatus.FINISHED, OrderStatus.REFUNDED, ], validate=[ validate.ContainsOnly([ OrderStatus.PAID, OrderStatus.CONFIRMED, OrderStatus.FINISHED, OrderStatus.REFUNDED, ]) ], comment="订单状态筛选 2: 未处理 3: 处理中 4: 已完成 5: 已退款", ), "num": fields.String( required=False, data_key="order_num", comment="订单号搜索,与其他条件互斥"), }, location="query") def get(self, request, args): shop_id = self.current_shop.id order_list = list_shop_orders(shop_id, **args) order_list = self._get_paginated_data(order_list, AdminOrdersSerializer) return self.send_success(data_list=order_list)
class TimelineFamiliesResource(ProtectedResource, GrampsJSONEncoder): """Families timeline resource.""" @use_args( { "dates": fields.Str( missing=None, validate=validate.Regexp( r"^-[0-9]+/([1-9]|1[0-2])/([1-9]|1[0-9]|2[0-9]|3[0-1])$|" r"^[0-9]+/([1-9]|1[0-2])/([1-9]|1[0-9]|2[0-9]|3[0-1])-$|" r"^[0-9]+/([1-9]|1[0-2])/([1-9]|1[0-9]|2[0-9]|3[0-1])-" r"[0-9]+/([1-9]|1[0-2])/([1-9]|1[0-9]|2[0-9]|3[0-1])$"), ), "discard_empty": fields.Boolean(missing=True), "event_classes": fields.DelimitedList( fields.Str(validate=validate.Length(min=1)), validate=validate.ContainsOnly(choices=EVENT_CATEGORIES), ), "events": fields.DelimitedList(fields.Str(validate=validate.Length(min=1))), "filter": fields.Str(validate=validate.Length(min=1)), "keys": fields.DelimitedList(fields.Str(validate=validate.Length(min=1))), "handles": fields.DelimitedList(fields.Str(validate=validate.Length(min=1))), "locale": fields.Str(missing=None, validate=validate.Length(min=1, max=5)), "page": fields.Integer(missing=0, validate=validate.Range(min=1)), "pagesize": fields.Integer(missing=20, validate=validate.Range(min=1)), "ratings": fields.Boolean(missing=False), "rules": fields.Str(validate=validate.Length(min=1)), "skipkeys": fields.DelimitedList(fields.Str(validate=validate.Length(min=1))), "strip": fields.Boolean(missing=False), }, location="query", ) def get(self, args: Dict): """Get consolidated list of events in timeline for a list of families.""" db_handle = get_db_handle() locale = get_locale_for_language(args["locale"], default=True) events = prepare_events(args) try: timeline = Timeline( db_handle, dates=args["dates"], events=events, ratings=args["ratings"], discard_empty=args["discard_empty"], locale=locale, ) except ValueError: abort(422) if "handles" in args: handles = args["handles"] else: handles = db_handle.get_family_handles(sort_handles=True, locale=locale) try: if "filter" in args or "rules" in args: handles = apply_filter(db_handle, args, "Family", handles) for handle in handles: timeline.add_family(handle) except HandleError: abort(404) payload = timeline.profile(page=args["page"], pagesize=args["pagesize"]) return self.response(200, payload, args, total_items=len(timeline.timeline))
class PersonTimelineResource(ProtectedResource, GrampsJSONEncoder): """Person timeline resource.""" @use_args( { "ancestors": fields.Integer(missing=1, validate=validate.Range(min=1, max=5)), "dates": fields.Str( missing=None, validate=validate.Regexp( r"^-[0-9]+/([1-9]|1[0-2])/([1-9]|1[0-9]|2[0-9]|3[0-1])$|" r"^[0-9]+/([1-9]|1[0-2])/([1-9]|1[0-9]|2[0-9]|3[0-1])-$|" r"^[0-9]+/([1-9]|1[0-2])/([1-9]|1[0-9]|2[0-9]|3[0-1])-" r"[0-9]+/([1-9]|1[0-2])/([1-9]|1[0-9]|2[0-9]|3[0-1])$"), ), "discard_empty": fields.Boolean(missing=True), "event_classes": fields.DelimitedList( fields.Str(validate=validate.Length(min=1)), validate=validate.ContainsOnly(choices=EVENT_CATEGORIES), ), "events": fields.DelimitedList(fields.Str(validate=validate.Length(min=1))), "first": fields.Boolean(missing=True), "keys": fields.DelimitedList(fields.Str(validate=validate.Length(min=1))), "last": fields.Boolean(missing=True), "locale": fields.Str(missing=None), "offspring": fields.Integer(missing=1, validate=validate.Range(min=1, max=5)), "omit_anchor": fields.Boolean(missing=True), "page": fields.Integer(missing=0, validate=validate.Range(min=1)), "pagesize": fields.Integer(missing=20, validate=validate.Range(min=1)), "precision": fields.Integer(missing=1, validate=validate.Range(min=1, max=3)), "ratings": fields.Boolean(missing=False), "relative_event_classes": fields.DelimitedList( fields.Str(validate=validate.Length(min=1)), validate=validate.ContainsOnly(choices=EVENT_CATEGORIES), ), "relative_events": fields.DelimitedList( fields.Str(validate=validate.Length(min=1)), ), "relatives": fields.DelimitedList( fields.Str(validate=validate.Length(min=1)), validate=validate.ContainsOnly(choices=RELATIVES), ), "skipkeys": fields.DelimitedList(fields.Str(validate=validate.Length(min=1))), "strip": fields.Boolean(missing=False), }, location="query", ) def get(self, args: Dict, handle: str): """Get list of events in timeline for a person.""" locale = get_locale_for_language(args["locale"], default=True) events = prepare_events(args) relatives = [] if "relatives" in args: relatives = args["relatives"] relative_events = [] if "relative_events" in args: relative_events = args["relative_events"] if "relative_event_classes" in args: relative_events = relative_events + args["relative_event_classes"] try: timeline = Timeline( get_db_handle(), dates=args["dates"], events=events, ratings=args["ratings"], relatives=relatives, relative_events=relative_events, discard_empty=args["discard_empty"], omit_anchor=args["omit_anchor"], precision=args["precision"], locale=locale, ) timeline.add_person( Handle(handle), anchor=True, start=args["first"], end=args["last"], ancestors=args["ancestors"], offspring=args["offspring"], ) except ValueError: abort(422) except HandleError: abort(404) payload = timeline.profile(page=args["page"], pagesize=args["pagesize"]) return self.response(200, payload, args, total_items=len(timeline.timeline))
class AdminLogsView(AdminBaseView): """后台-员工-操作日志""" @AdminBaseView.permission_required( [AdminBaseView.staff_permissions.ADMIN_STAFF]) @use_args( { "operator_ids": fields.Function( deserialize=lambda x: x.replace(" ", "").split(","), missing=[], comment="操作人ID", ), "operate_module_ids": StrToList( missing=[], validate=[ validate.ContainsOnly(list(all_module_dict.values())) ], comment="模块ID", ), }, location="query") def get(self, request, args): args["operate_module_ids"] = [ int(_) for _ in args.get("operate_module_ids") ] # django时区问题 args["end_date"] = make_aware(datetime.datetime.today() + datetime.timedelta(1)) args["from_date"] = make_aware(datetime.datetime.today() - datetime.timedelta(90)) shop_id = self.current_shop.id # 查询单个还是多个 operate_module_ids = args.pop("operate_module_ids") # 查单个 if len(operate_module_ids) == 1: module_id = operate_module_ids[0] log_list = list_one_module_log_by_filter(shop_id, module_id, **args) module_id_2_log_list = {module_id: log_list} # 查询多个 else: module_id_2_log_list = dict_more_modules_log_by_filter( shop_id, operate_module_ids, **args) """ module_2_log_list = { 1: [log, log ...], 2: [log, log ...], ... } """ # 封装数据 module_id_2_name = {v: k.lower() for k, v in all_module_dict.items()} all_log_list = [] for module_id, log_list_query in module_id_2_log_list.items(): def_name = "format_{}_data".format(module_id_2_name.get(module_id)) log_list = getattr(self, def_name)(log_list_query) all_log_list.extend(log_list) all_log_list = sorted(all_log_list, key=lambda x: x["operate_time"], reverse=True) return self.send_success(data_list=all_log_list) def format_order_data(self, log_list_query): """封装订单日志数据""" for log in log_list_query: if log.operate_type in [ OrderLogType.HOME_DELIVERY_AMOUNT, OrderLogType.HOME_MINIMUM_FREE_AMOUNT, OrderLogType.HOME_MINIMUM_ORDER_AMOUNT, OrderLogType.PICK_MINIMUM_FREE_AMOUNT, OrderLogType.PICK_SERVICE_AMOUNT, ]: log.old_value = log.operate_content.split("|")[0] log.new_value = log.operate_content.split("|")[1] log.operate_content = "" order_log_serializer = OrderLogSerializer(log_list_query, many=True) log_list = order_log_serializer.data return log_list def format_config_data(self, log_list_query): """封装设置日志数据""" log_list = ConfigLogSerializer(log_list_query, many=True).data return log_list def format_product_data(self, log_list_query): """封装货品日志数据""" log_list = ProductLogSerializer(log_list_query, many=True).data return log_list def format_promotion_data(self, log_list_query): """封装货品日志数据""" log_list = PromotionLogSerializer(log_list_query, many=True).data return log_list
class AdminCustomerOrdersView(AdminBaseView): """后台-客户-历史订单查询""" pagination_class = StandardResultsSetPagination @AdminBaseView.permission_required( [AdminBaseView.staff_permissions.ADMIN_CUSTOMER]) @use_args( { "customer_id": fields.Integer( required=True, validate=[validate.Range(1)], comment="客户ID"), "order_types": StrToList( required=False, missing=[], validate=[ validate.ContainsOnly( [OrderType.NORMAL, OrderType.GROUPON]) ], comment="订单类型, 1: 普通订单, 5: 拼团订单", ), "order_pay_types": StrToList( required=False, missing=[], validate=[ validate.ContainsOnly( [OrderPayType.WEIXIN_JSAPI, OrderPayType.ON_DELIVERY]) ], comment="订单支付类型, 1: 微信支付, 2: 货到付款", ), "order_delivery_methods": StrToList( required=False, missing=[], validate=[ validate.ContainsOnly([ OrderDeliveryMethod.HOME_DELIVERY, OrderDeliveryMethod.CUSTOMER_PICK, ]) ], comment="配送类型, 1: 送货上门, 2: 自提", ), "order_status": StrToList( required=False, missing=[ OrderStatus.PAID, OrderStatus.CONFIRMED, OrderStatus.FINISHED, OrderStatus.REFUNDED, ], validate=[ validate.ContainsOnly([ OrderStatus.PAID, OrderStatus.CONFIRMED, OrderStatus.FINISHED, OrderStatus.REFUNDED, ]) ], comment="订单状态, 2: 未处理, 3: 处理中, 4: 已完成, 5: 已退款", ), }, location="query") def get(self, request, args): args["shop_id"] = self.current_shop.id order_list = list_customer_orders_interface(**args) order_list = self._get_paginated_data(order_list, AdminOrdersSerializer) return self.send_success(data_list=order_list)
fields.Int( required=False, missing=0, validate=[validate.Range(min=0, max=1)]) }, location="query") def get_random(length, specials, digits): return generate_password(length, specials, digits) # Homework 3. Bitcoin rate function @app.route("/bitcoin_rate") @use_kwargs( { "currency": fields.Str(required=False, missing="USD", validate=[validate.ContainsOnly(string.ascii_uppercase)]) }, location="query") def get_bitcoin_rate(currency): return bitcoin_request(currency) # Homework 2. Reading CSV file function @app.route("/avr_data") def avr_data(): height_lst, weight_lst = [], [] with open("hw.csv", newline="") as csvfile: reader = csv.DictReader(csvfile) for row in reader: height_lst.append(float(row[' "Height(Inches)"'])) weight_lst.append(float(row[' "Weight(Pounds)"']))