4
+    "一": 1,
5
+    "壹": 1,
6
+    "幺": 1,
7
+    "二": 2,
8
+    "贰": 2,
9
+    "两": 2,
10
+    "三": 3,
11
+    "叁": 3,
12
+    "四": 4,
13
+    "肆": 4,
14
+    "五": 5,
15
+    "伍": 5,
16
+    "六": 6,
17
+    "陆": 6,
18
+    "七": 7,
19
+    "柒": 7,
20
+    "八": 8,
21
+    "捌": 8,
22
+    "九": 9,
23
+    "玖": 9,
24
+}
25
+UNIT_CN2AN = {
26
+    "十": 10,
27
+    "拾": 10,
28
+    "百": 100,
29
+    "佰": 100,
30
+    "千": 1000,
31
+    "仟": 1000,
32
+    "万": 10000,
33
+    "亿": 100000000,
34
+}
35
+UNIT_LOW_AN2CN = {
36
+    10: "十",
37
+    100: "百",
38
+    1000: "千",
39
+    10000: "万",
40
+    100000000: "亿",
41
+}
42
+NUMBER_LOW_AN2CN = {
43
+    0: "零",
44
+    1: "一",
45
+    2: "二",
46
+    3: "三",
47
+    4: "四",
48
+    5: "五",
49
+    6: "六",
50
+    7: "七",
51
+    8: "八",
52
+    9: "九",
53
+}
54
+NUMBER_UP_AN2CN = {
55
+    0: "零",
56
+    1: "壹",
57
+    2: "贰",
58
+    3: "叁",
59
+    4: "肆",
60
+    5: "伍",
61
+    6: "陆",
62
+    7: "柒",
63
+    8: "捌",
64
+    9: "玖",
65
+}
66
+UNIT_LOW_ORDER_AN2CN = [
67
+    "",
68
+    "十",
69
+    "百",
70
+    "千",
71
+    "万",
72
+    "十",
73
+    "百",
74
+    "千",
75
+    "亿",
76
+    "十",
77
+    "百",
78
+    "千",
79
+    "万",
80
+    "十",
81
+    "百",
82
+    "千",
83
+]
84
+UNIT_UP_ORDER_AN2CN = [
85
+    "",
86
+    "拾",
87
+    "佰",
88
+    "仟",
89
+    "万",
90
+    "拾",
91
+    "佰",
92
+    "仟",
93
+    "亿",
94
+    "拾",
95
+    "佰",
96
+    "仟",
97
+    "万",
98
+    "拾",
99
+    "佰",
100
+    "仟",
101
+]
102
+STRICT_CN_NUMBER = {
103
+    "零": "零",
104
+    "一": "一壹",
105
+    "二": "二贰",
106
+    "三": "三叁",
107
+    "四": "四肆",
108
+    "五": "五伍",
109
+    "六": "六陆",
110
+    "七": "七柒",
111
+    "八": "八捌",
112
+    "九": "九玖",
113
+    "十": "十拾",
114
+    "百": "百佰",
115
+    "千": "千仟",
116
+    "万": "万",
117
+    "亿": "亿",
118
+}
119
+NORMAL_CN_NUMBER = {
120
+    "零": "零〇",
121
+    "一": "一壹幺",
122
+    "二": "二贰两",
123
+    "三": "三叁仨",
124
+    "四": "四肆",
125
+    "五": "五伍",
126
+    "六": "六陆",
127
+    "七": "七柒",
128
+    "八": "八捌",
129
+    "九": "九玖",
130
+    "十": "十拾",
131
+    "百": "百佰",
132
+    "千": "千仟",
133
+    "万": "万",
134
+    "亿": "亿",
135
+}

+ 29 - 0
utils/cn2an/performance.py

@@ -0,0 +1,29 @@
1
+import torbjorn as tbn
2
+
3
+from .an2cn import An2Cn
4
+from .cn2an import Cn2An
5
+
6
+ac = An2Cn()
7
+ca = Cn2An()
8
+
9
+an = 9876543298765432
10
+cn = "九千八百七十六万五千四百三十二亿九千八百七十六万五千四百三十二"
11
+
12
+
13
+@tbn.run_time
14
+def run_cn2an_ten_thousand_times() -> None:
15
+    for _ in range(10000):
16
+        result = ca.cn2an(cn)
17
+        assert result == an
18
+
19
+
20
+@tbn.run_time
21
+def run_an2cn_ten_thousand_times() -> None:
22
+    for _ in range(10000):
23
+        result = ac.an2cn(an)
24
+        assert result == cn
25
+
26
+
27
+if __name__ == '__main__':
28
+    run_cn2an_ten_thousand_times()
29
+    run_an2cn_ten_thousand_times()

+ 104 - 0
utils/cn2an/transform.py

@@ -0,0 +1,104 @@
1
+import re
2
+from warnings import warn
3
+
4
+from .cn2an import Cn2An
5
+from .an2cn import An2Cn
6
+from .conf import UNIT_CN2AN
7
+
8
+
9
+class Transform(object):
10
+    def __init__(self) -> None:
11
+        self.all_num = "零一二三四五六七八九"
12
+        self.all_unit = "".join(list(UNIT_CN2AN.keys()))
13
+        self.cn2an = Cn2An().cn2an
14
+        self.an2cn = An2Cn().an2cn
15
+        self.cn_pattern = f"负?([{self.all_num}{self.all_unit}]+点)?[{self.all_num}{self.all_unit}]+"
16
+        self.smart_cn_pattern = f"-?([0-9]+.)?[0-9]+[{self.all_unit}]+"
17
+
18
+    def transform(self, inputs: str, method: str = "cn2an") -> str:
19
+        if method == "cn2an":
20
+            inputs = inputs.replace("廿", "二十").replace("半", "0.5").replace("两", "2")
21
+            # date
22
+            inputs = re.sub(
23
+                fr"((({self.smart_cn_pattern})|({self.cn_pattern}))年)?([{self.all_num}十]+月)?([{self.all_num}十]+日)?",
24
+                lambda x: self.__sub_util(x.group(), "cn2an", "date"), inputs)
25
+            # fraction
26
+            inputs = re.sub(fr"{self.cn_pattern}分之{self.cn_pattern}",
27
+                            lambda x: self.__sub_util(x.group(), "cn2an", "fraction"), inputs)
28
+            # percent
29
+            inputs = re.sub(fr"百分之{self.cn_pattern}",
30
+                            lambda x: self.__sub_util(x.group(), "cn2an", "percent"), inputs)
31
+            # celsius
32
+            inputs = re.sub(fr"{self.cn_pattern}摄氏度",
33
+                            lambda x: self.__sub_util(x.group(), "cn2an", "celsius"), inputs)
34
+            # number
35
+            output = re.sub(self.cn_pattern,
36
+                            lambda x: self.__sub_util(x.group(), "cn2an", "number"), inputs)
37
+
38
+        elif method == "an2cn":
39
+            # date
40
+            inputs = re.sub(r"(\d{2,4}年)?(\d{1,2}月)?(\d{1,2}日)?",
41
+                            lambda x: self.__sub_util(x.group(), "an2cn", "date"), inputs)
42
+            # fraction
43
+            inputs = re.sub(r"\d+/\d+",
44
+                            lambda x: self.__sub_util(x.group(), "an2cn", "fraction"), inputs)
45
+            # percent
46
+            inputs = re.sub(r"-?(\d+\.)?\d+%",
47
+                            lambda x: self.__sub_util(x.group(), "an2cn", "percent"), inputs)
48
+            # celsius
49
+            inputs = re.sub(r"\d+℃",
50
+                            lambda x: self.__sub_util(x.group(), "an2cn", "celsius"), inputs)
51
+            # number
52
+            output = re.sub(r"-?(\d+\.)?\d+",
53
+                            lambda x: self.__sub_util(x.group(), "an2cn", "number"), inputs)
54
+        else:
55
+            raise ValueError(f"error method: {method}, only support 'cn2an' and 'an2cn'!")
56
+
57
+        return output
58
+
59
+    def __sub_util(self, inputs, method: str = "cn2an", sub_mode: str = "number") -> str:
60
+        try:
61
+            if inputs:
62
+                if method == "cn2an":
63
+                    if sub_mode == "date":
64
+                        return re.sub(fr"(({self.smart_cn_pattern})|({self.cn_pattern}))",
65
+                                      lambda x: str(self.cn2an(x.group(), "smart")), inputs)
66
+                    elif sub_mode == "fraction":
67
+                        if inputs[0] != "百":
68
+                            frac_result = re.sub(self.cn_pattern,
69
+                                                 lambda x: str(self.cn2an(x.group(), "smart")), inputs)
70
+                            numerator, denominator = frac_result.split("分之")
71
+                            return f"{denominator}/{numerator}"
72
+                        else:
73
+                            return inputs
74
+                    elif sub_mode == "percent":
75
+                        return re.sub(f"(?<=百分之){self.cn_pattern}",
76
+                                      lambda x: str(self.cn2an(x.group(), "smart")), inputs).replace("百分之", "") + "%"
77
+                    elif sub_mode == "celsius":
78
+                        return re.sub(f"{self.cn_pattern}(?=摄氏度)",
79
+                                      lambda x: str(self.cn2an(x.group(), "smart")), inputs).replace("摄氏度", "℃")
80
+                    elif sub_mode == "number":
81
+                        return str(self.cn2an(inputs, "smart"))
82
+                    else:
83
+                        raise Exception(f"error sub_mode: {sub_mode} !")
84
+                else:
85
+                    if sub_mode == "date":
86
+                        inputs = re.sub(r"\d+(?=年)",
87
+                                        lambda x: self.an2cn(x.group(), "direct"), inputs)
88
+                        return re.sub(r"\d+",
89
+                                      lambda x: self.an2cn(x.group(), "low"), inputs)
90
+                    elif sub_mode == "fraction":
91
+                        frac_result = re.sub(r"\d+", lambda x: self.an2cn(x.group(), "low"), inputs)
92
+                        numerator, denominator = frac_result.split("/")
93
+                        return f"{denominator}分之{numerator}"
94
+                    elif sub_mode == "celsius":
95
+                        return self.an2cn(inputs[:-1], "low") + "摄氏度"
96
+                    elif sub_mode == "percent":
97
+                        return "百分之" + self.an2cn(inputs[:-1], "low")
98
+                    elif sub_mode == "number":
99
+                        return self.an2cn(inputs, "low")
100
+                    else:
101
+                        raise Exception(f"error sub_mode: {sub_mode} !")
102
+        except Exception as e:
103
+            warn(str(e))
104
+            return inputs

+ 40 - 0
utils/cn2an/transform_test.py

@@ -0,0 +1,40 @@
1
+import unittest
2
+
3
+from .transform import Transform
4
+
5
+
6
+class TransformTest(unittest.TestCase):
7
+    def setUp(self) -> None:
8
+        self.strict_data_dict = {
9
+            "小王捡了100块钱": "小王捡了一百块钱",
10
+            "用户增长最快的3个城市": "用户增长最快的三个城市",
11
+            "小王的生日是2001年3月4日": "小王的生日是二零零一年三月四日",
12
+            "小王的生日是2012年12月12日": "小王的生日是二零一二年十二月十二日",
13
+            "今天股价上涨了8%": "今天股价上涨了百分之八",
14
+            "第2天股价下降了-3.8%": "第二天股价下降了百分之负三点八",
15
+            "抛出去的硬币为正面的概率是1/2": "抛出去的硬币为正面的概率是二分之一",
16
+            "现在室内温度为39℃,很热啊!": "现在室内温度为三十九摄氏度,很热啊!",
17
+            "创业板指9月9日早盘低开1.57%": "创业板指九月九日早盘低开百分之一点五七"
18
+        }
19
+
20
+        self.smart_data_dict = {
21
+            "约2.5亿年~6500万年": "约250000000年~65000000年",
22
+            "廿二日,日出东方": "22日,日出东方",
23
+            "大陆": "大陆",
24
+            "半斤": "0.5斤",
25
+            "两个": "2个",
26
+        }
27
+
28
+        self.t = Transform()
29
+
30
+    def test_transform(self) -> None:
31
+        for strict_item in self.strict_data_dict.keys():
32
+            self.assertEqual(self.t.transform(strict_item, "an2cn"), self.strict_data_dict[strict_item])
33
+            self.assertEqual(self.t.transform(self.strict_data_dict[strict_item], "cn2an"), strict_item)
34
+
35
+        for smart_item in self.smart_data_dict.keys():
36
+            self.assertEqual(self.t.transform(smart_item, "cn2an"), self.smart_data_dict[smart_item])
37
+
38
+
39
+if __name__ == '__main__':
40
+    unittest.main()

kodo - Gogs: Go Git Service

暫無描述

contract_mp_views.py 9.4KB

    # -*- coding: utf-8 -*- import base64 import json import requests from django_logit import logit from django_response import response from TimeConvert import TimeConvert as tc from account.models import LensmanInfo from apps.contract.models import LensmanContributionContractInfo from apps.lensman.activity.models import LensmanContributionActivityIncomeExpensesInfo from member.models import MemberActivityContributionInfo from utils import cn2an from utils.error.errno_utils import ContractStatusCode from utils.redis.rimage import get_images_data from utils.tencentcloud.ess import (callback_decode, create_document, create_flow, create_scheme_url, start_flow, test_upload_document_files, upload_document_files) from utils.thumbnail_utils import make_thumbnail2 @logit(res=True) def get_contribtion_contract_status_api(request): user_id = request.POST.get('user_id', '') activity_id = request.POST.get('activity_id', '') contribution_id = request.POST.get('contribution_id', '') try: contract = LensmanContributionContractInfo.objects.get(user_id=user_id, activity_id=activity_id, contribution_id=contribution_id) except LensmanContributionContractInfo.DoesNotExist: return response(ContractStatusCode.CONTRACT_NOT_FOUND) return response(200, data={ 'contract_status': contract.contract_status, }) @logit(res=True) def get_contribtion_contract_api(request): user_id = request.POST.get('user_id', '') lensman_id = request.POST.get('lensman_id', '') activity_id = request.POST.get('activity_id', '') contribution_id = request.POST.get('contribution_id', '') lensman = LensmanInfo.objects.get(lensman_id=lensman_id) contract, _ = LensmanContributionContractInfo.objects.update_or_create( user_id=user_id, lensman_id=lensman_id, activity_id=activity_id, contribution_id=contribution_id, ) file_ids = upload_contribution_images(contribution_id) flow_id = create_contribution_contract_flow(lensman) contract.flow_id = flow_id contract.save() document_id, fields = create_contribution_contract_document(lensman, contribution_id, file_ids, flow_id) contract.contract_content_fields = fields contract.document_id = document_id contract.save() # 发起签署流程 flow_status = start_contribution_contract_flow(flow_id) scheme_url = get_contribtion_contract_sign_mppath(lensman, flow_id) return response(200, data={ 'scheme_url': scheme_url }) def get_contribtion_contract_sign_mppath_api(request): user_id = request.POST.get('user_id', '') flow_id = request.POST.get('flow_id', '') lensman_id = request.POST.get('lensman_id', '') contribution_id = request.POST.get('contribution_id', '') lensman = LensmanInfo.objects.get(lensman_id=lensman_id) try: contract = LensmanContributionContractInfo.objects.get( user_id=user_id, contribution_id=contribution_id, lensman_id=lensman_id, ) except LensmanContributionContractInfo.DoesNotExist: return response(ContractStatusCode.CONTRACT_NOT_FOUND) scheme_url = get_contribtion_contract_sign_mppath(lensman, contract.flow_id) return response(200, data={ 'contract': contract.mpdata, 'scheme_url': scheme_url }) def generate_file_from_qiniu(file_url): try: data = requests.get(file_url).content data = make_thumbnail2(data) data = base64.b64encode(data).decode('utf-8') except Exception: data = None return data def generate_files_from_qiniu(file_urls): files = [] for file_url in file_urls: file_b64str = generate_file_from_qiniu(file_url) if not file_b64str: continue files.append({ 'FileBody': file_b64str, 'FileName': file_url.split('/')[-1] }) return files def upload_contribution_images(contribution_id): # 上传MemberActivityContributionInfo图片 https://qian.tencent.com/developers/companyApis/templatesAndFiles/UploadFiles contribtuon = MemberActivityContributionInfo.objects.get(contribution_id=contribution_id) file_urls = [image['image_url'] for image in contribtuon.images] file_names = [file_url.split('/')[-1] for file_url in file_urls] file_type = file_names[0].split('.')[-1] files = get_images_data(file_names) # Redis 已无缓存的数据 if len(files) != len(file_names): files = generate_files_from_qiniu(file_urls) # files = [ # { # "FileBody": "文件base64编码,不含逗号前字符,即data:image/png;base64,", # "FileName": "test.png" # } # ] # file_type = 'png' return upload_document_files(files, file_type=file_type) # upload_files_result = test_upload_document_files(files, file_type=file_type) def create_contribution_contract_flow(lensman): # 创建签署流程 https://qian.tencent.com/developers/companyApis/startFlows/CreateFlow # 创建签署流程参数 Operator FlowName = lensman.identity_card_name + u"的投稿合同" + tc.local_string(format='%Y%m%d') FlowType = u"活动投稿授权书" Approvers = [{ "ApproverType": 1, "Required": True, "NotifyType": 'SMS', "ApproverMobile": lensman.phone, "ApproverName": lensman.identity_card_name, "ApproverIdCardType": "ID_CARD", "ApproverIdCardNumber": lensman.identity_card_number, }] create_flow_result = create_flow(flow_name=FlowName, flow_type=FlowType, approvers=Approvers) if not create_flow_result: return '' return create_flow_result.FlowId def create_contribution_contract_document(lensman, contribution_id, file_ids, FlowId): # 创建电子签文档 https://qian.tencent.com/developers/companyApis/startFlows/CreateDocument IMAGE_COMPONENTIDS = ['ComponentId_37', 'ComponentId_38', 'ComponentId_39', 'ComponentId_40', 'ComponentId_41', 'ComponentId_42', 'ComponentId_43', 'ComponentId_44', 'ComponentId_45', 'ComponentId_46', 'ComponentId_47', 'ComponentId_48', 'ComponentId_49', 'ComponentId_50', 'ComponentId_51', 'ComponentId_52', 'ComponentId_53', 'ComponentId_54', 'ComponentId_55', 'ComponentId_55', 'ComponentId_56', 'ComponentId_57', 'ComponentId_58', 'ComponentId_59', 'ComponentId_60', 'ComponentId_9', 'ComponentId_10', 'ComponentId_11', 'ComponentId_12', 'ComponentId_13', 'ComponentId_14', 'ComponentId_15', 'ComponentId_16', 'ComponentId_17', 'ComponentId_18', 'ComponentId_19', 'ComponentId_20', 'ComponentId_21', 'ComponentId_22', 'ComponentId_23', 'ComponentId_24', 'ComponentId_25', 'ComponentId_26', 'ComponentId_27', 'ComponentId_28', 'ComponentId_29', 'ComponentId_30', 'ComponentId_31', 'ComponentId_32'] try: amount = LensmanContributionActivityIncomeExpensesInfo.objects.get(contribution_id=contribution_id, lensman_id=lensman.lensman_id).amount except LensmanContributionActivityIncomeExpensesInfo.DoesNotExist: amount = 0 FormFields = [{ "ComponentId": "ComponentId_0", "ComponentValue": lensman.identity_card_name }, { "ComponentId": "ComponentId_1", "ComponentValue": lensman.identity_card_number, }, { "ComponentId": "ComponentId_2", "ComponentValue": str(amount), }, { "ComponentId": "ComponentId_3", "ComponentValue": cn2an.an2cn(amount, mode='rmb'), }] for idx, file_id in enumerate(file_ids): FormFields.append({ "ComponentId": IMAGE_COMPONENTIDS[idx], "ComponentValue": file_id, }) create_document_result = create_document(flow_id=FlowId, form_fields=FormFields) if not create_document_result: return '', FormFields return create_document_result.DocumentId, FormFields def start_contribution_contract_flow(FlowId): # 发起签署流程 https://qian.tencent.com/developers/companyApis/startFlows/StartFlow start_flow_result = start_flow(flow_id=FlowId) if not start_flow_result: return '' return start_flow_result.Status def get_contribtion_contract_sign_mppath(lensman, FlowId): # 获取签署链接 https://qian.tencent.com/developers/companyApis/startFlows/CreateSchemeUrl create_scheme_url_result = create_scheme_url(flow_id=FlowId, name=lensman.identity_card_name, mobile=lensman.phone, card_type='ID_CARD', card_number=lensman.identity_card_number) if not create_scheme_url_result: return '' return create_scheme_url_result.SchemeUrl @logit(body=True, res=True) def ess_callback(request): # curl http://127.0.0.1:8888/api/mp/ess/callback -H 'Content-type: application/json' -X POST -d '{"encrypt":"62KE4r5Wz0yHzEpMOwVRbM1KV0"}' data = json.loads(request.body) data = callback_decode(data['encrypt']) # https://qian.tencent.com/developers/company/callback_types_contracts_sign MsgType = data.get('MsgType') if MsgType == 'FlowStatusChange': MsgData = data.get('MsgData', {}) FlowId = MsgData.get('FlowId') # DocumentId = MsgData.get('DocumentId') FlowCallbackStatus = MsgData.get('FlowCallbackStatus', -1) Approvers = MsgData.get('Approvers') or [{}] ApproveCallbackStatus = Approvers[-1].get('ApproveCallbackStatus', -1) LensmanContributionContractInfo.objects.filter(flow_id=FlowId).update(tencent_contract_status=FlowCallbackStatus, tencent_approve_status=ApproveCallbackStatus) return response()