fix merge
Showing
9 changed files
with
317 additions
and
17 deletions
... | @@ -1649,3 +1649,69 @@ CA_COMPARE_FIELD = { | ... | @@ -1649,3 +1649,69 @@ CA_COMPARE_FIELD = { |
1649 | } | 1649 | } |
1650 | 1650 | ||
1651 | CONTRACT_SHEET_NAME = '合同' | 1651 | CONTRACT_SHEET_NAME = '合同' |
1652 | |||
1653 | # ------------------CMS-------------------------------------- | ||
1654 | BANK_INFO_MAP = ( | ||
1655 | ('accountNo', 'bankAccountDetails.accountNo'), | ||
1656 | ('bankName', 'bankAccountDetails.bankName'), | ||
1657 | ('accountHolderName', 'bankAccountDetails.accountHolderName'), | ||
1658 | ) | ||
1659 | |||
1660 | VEHICLE_INFO_MAP = ( | ||
1661 | ('vehicleStatus', 'vehicleStatus'), | ||
1662 | ('vinNo', 'vehicleInformation.vinNo'), | ||
1663 | ('dealer', 'dealerName'), | ||
1664 | ('vehicleTransactionAmount', 'totalFinanceAmount'), | ||
1665 | ) | ||
1666 | |||
1667 | CORPORATE_INFO_MAP = ( | ||
1668 | ('firstIdType', ), | ||
1669 | ('firstIdNo', ), | ||
1670 | ('companyName', ), | ||
1671 | ('legalRepName', ), | ||
1672 | ('businessLicenseNo', ), | ||
1673 | ('organizationCreditCode', ), | ||
1674 | ('taxRegistrationCertificateNo', ), | ||
1675 | ('establishmentDate', ), | ||
1676 | ('businessLicenseDueDate', ), | ||
1677 | ) | ||
1678 | |||
1679 | INDIVIDUAL_INFO_MAP = ( | ||
1680 | ('applicantType', 'applicantInformation.applicantType'), | ||
1681 | ('customerType', 'applicantInformation.customersubType'), | ||
1682 | |||
1683 | ('idType', 'applicantInformation.IDInformation.idType'), | ||
1684 | ('customerName', 'applicantInformation.name'), | ||
1685 | ('idNum', 'applicantInformation.IDInformation.idNum'), | ||
1686 | ('dateOfBirth', 'applicantInformation.dateOfBirth'), | ||
1687 | ('idExpiryDate', 'applicantInformation.IDInformation.idExpiryDate'), | ||
1688 | ('hukouProvince', ), | ||
1689 | ('hukouCity', ), | ||
1690 | |||
1691 | ('secondIdType', 'applicantInformation.IDInformation.idType'), | ||
1692 | ('secondIdNum', 'applicantInformation.IDInformation.idNum'), | ||
1693 | |||
1694 | ('companyName', ), | ||
1695 | ('registeredCapital', ), | ||
1696 | ('selfEmployedSubType', ), | ||
1697 | ) | ||
1698 | |||
1699 | CMS_TO_POS = [ | ||
1700 | ('bank_info', BANK_INFO_MAP), | ||
1701 | ('vehicle_info', VEHICLE_INFO_MAP), | ||
1702 | ('corporate_cus_info', CORPORATE_INFO_MAP), | ||
1703 | ('individual_cus_info', INDIVIDUAL_INFO_MAP), | ||
1704 | ] | ||
1705 | |||
1706 | TENANT_MAP = { | ||
1707 | AFC_PREFIX: 1, | ||
1708 | HIL_PREFIX: 2, | ||
1709 | } | ||
1710 | |||
1711 | APPLICANT_TYPE_MAP = { | ||
1712 | 'Borrower': 'CUSTR', | ||
1713 | 'Co Borrower': 'COAPP', | ||
1714 | 'Guarantor': 'GAUTR1', | ||
1715 | 'Mortgager': 'GAUTR2' | ||
1716 | } | ||
1717 | ... | ... |
... | @@ -13,9 +13,14 @@ class OCR2Exception(Exception): | ... | @@ -13,9 +13,14 @@ class OCR2Exception(Exception): |
13 | class OCR4Exception(Exception): | 13 | class OCR4Exception(Exception): |
14 | pass | 14 | pass |
15 | 15 | ||
16 | |||
16 | class LTGTException(Exception): | 17 | class LTGTException(Exception): |
17 | pass | 18 | pass |
18 | 19 | ||
19 | 20 | ||
20 | class GCAPException(Exception): | 21 | class GCAPException(Exception): |
21 | pass | 22 | pass |
23 | |||
24 | |||
25 | class CMSException(Exception): | ||
26 | pass | ... | ... |
... | @@ -244,6 +244,33 @@ class HILSEComparisonInfo(models.Model): | ... | @@ -244,6 +244,33 @@ class HILSEComparisonInfo(models.Model): |
244 | 244 | ||
245 | 245 | ||
246 | # 比对信息表 | 246 | # 比对信息表 |
247 | class AFCSECMSInfo(models.Model): | ||
248 | id = models.BigAutoField(primary_key=True, verbose_name="id") # 主键 | ||
249 | application_id = models.CharField(max_length=64, verbose_name="申请id") # 索引 | ||
250 | content = models.TextField(verbose_name="CMS信息") | ||
251 | update_time = models.DateTimeField(auto_now=True, verbose_name='修改时间') | ||
252 | create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') # 索引 | ||
253 | |||
254 | class Meta: | ||
255 | managed = False | ||
256 | db_table = 'afc_se_cms_info' | ||
257 | situ_db_label = 'afc' | ||
258 | |||
259 | |||
260 | # 比对信息表 | ||
261 | class HILSECMSInfo(models.Model): | ||
262 | id = models.BigAutoField(primary_key=True, verbose_name="id") # 主键 | ||
263 | application_id = models.CharField(max_length=64, verbose_name="申请id") # 索引 | ||
264 | content = models.TextField(verbose_name="CMS信息") | ||
265 | update_time = models.DateTimeField(auto_now=True, verbose_name='修改时间') | ||
266 | create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') # 索引 | ||
267 | |||
268 | class Meta: | ||
269 | managed = False | ||
270 | db_table = 'hil_se_cms_info' | ||
271 | |||
272 | |||
273 | # 比对信息表 | ||
247 | class HILComparisonInfo(models.Model): | 274 | class HILComparisonInfo(models.Model): |
248 | id = models.BigAutoField(primary_key=True, verbose_name="id") # 主键 | 275 | id = models.BigAutoField(primary_key=True, verbose_name="id") # 主键 |
249 | uniq_seq = models.CharField(max_length=128, verbose_name="唯一序列号") # 索引? | 276 | uniq_seq = models.CharField(max_length=128, verbose_name="唯一序列号") # 索引? | ... | ... |
src/apps/doc/ocr/cms.py
0 → 100644
1 | import requests | ||
2 | from settings import conf | ||
3 | from common.redis_cache import redis_handler as rh | ||
4 | from apps.doc.exceptions import CMSException | ||
5 | |||
6 | |||
7 | class CMS: | ||
8 | |||
9 | def __init__(self): | ||
10 | self.oauth_url = conf.CMS_OAUTH_URL | ||
11 | self.url = conf.CMS_URL | ||
12 | self.oauth_payload = { | ||
13 | 'grant_type': 'client_credentials', | ||
14 | 'client_id': conf.CMS_OAUTH_ID, | ||
15 | 'client_secret': conf.CMS_OAUTH_SECRET, | ||
16 | 'scope': conf.CMS_OAUTH_SCOPE | ||
17 | } | ||
18 | self.token_type = 'bearer' | ||
19 | self.token = None | ||
20 | self.token_key = 'access_token' | ||
21 | self.expires_key = 'expires_in' | ||
22 | self.token_type_key = 'token_type' | ||
23 | |||
24 | def update_token(self): | ||
25 | response = requests.post(self.oauth_url, data=self.oauth_payload, files=[]) | ||
26 | if response.status_code != 200: | ||
27 | raise CMSException('CMS Oauth response with code: {0}'.format(response.status_code)) | ||
28 | token = response.json().get(self.token_key) | ||
29 | if not isinstance(token, str): | ||
30 | raise CMSException('CMS Oauth can not get token: {0}'.format(response.json())) | ||
31 | self.token = token | ||
32 | self.token_type = response.json().get(self.token_type_key, self.token_type) | ||
33 | expires = response.json().get(self.expires_key, 3600) | ||
34 | rh.set_cms_token(self.token, expires) | ||
35 | |||
36 | def get_token(self): | ||
37 | if self.token is None: | ||
38 | self.token = rh.get_cms_token() | ||
39 | if self.token is None: | ||
40 | self.update_token() | ||
41 | return self.token | ||
42 | |||
43 | def send(self, payload): | ||
44 | token = self.get_token() | ||
45 | headers = { | ||
46 | 'Authorization': '{0} {1}'.format(self.token_type.capitalize(), token), | ||
47 | 'Content-Type': 'application/json', | ||
48 | } | ||
49 | response = requests.post(self.url, headers=headers, json=payload) | ||
50 | if response.status_code != 200: | ||
51 | raise CMSException('CMS Oauth response with code: {0}'.format(response.status_code)) | ||
52 | return response.json() | ||
53 | |||
54 | |||
55 | cms = CMS() | ||
56 | |||
57 | |||
58 | |||
59 |
... | @@ -22,8 +22,10 @@ from .models import ( | ... | @@ -22,8 +22,10 @@ from .models import ( |
22 | GCAPRecords, | 22 | GCAPRecords, |
23 | AFCComparisonInfo, | 23 | AFCComparisonInfo, |
24 | AFCSEComparisonInfo, | 24 | AFCSEComparisonInfo, |
25 | AFCSECMSInfo, | ||
25 | HILComparisonInfo, | 26 | HILComparisonInfo, |
26 | HILSEComparisonInfo, | 27 | HILSEComparisonInfo, |
28 | HILSECMSInfo, | ||
27 | AFCCompareOfflineReport, | 29 | AFCCompareOfflineReport, |
28 | HILCompareOfflineReport, | 30 | HILCompareOfflineReport, |
29 | AFCCACompareResult, | 31 | AFCCACompareResult, |
... | @@ -327,7 +329,7 @@ info_args = { | ... | @@ -327,7 +329,7 @@ info_args = { |
327 | 'customersubType': fields.Str(required=True, validate=validate.Length(max=32)), | 329 | 'customersubType': fields.Str(required=True, validate=validate.Length(max=32)), |
328 | 'selfEmployedSubType': fields.Str(required=False, validate=validate.Length(max=32)), | 330 | 'selfEmployedSubType': fields.Str(required=False, validate=validate.Length(max=32)), |
329 | 'name': fields.Str(required=True, validate=validate.Length(max=64)), | 331 | 'name': fields.Str(required=True, validate=validate.Length(max=64)), |
330 | 'legalRepName': fields.Str(required=True, validate=validate.Length(max=64)), | 332 | 'legalRepName': fields.Str(required=False, validate=validate.Length(max=64)), |
331 | 'dateOfBirth': CustomDate(required=False), | 333 | 'dateOfBirth': CustomDate(required=False), |
332 | 'nationality': fields.Str(required=False, validate=validate.Length(max=64)), | 334 | 'nationality': fields.Str(required=False, validate=validate.Length(max=64)), |
333 | 'establishmentDate': CustomDate(required=False), | 335 | 'establishmentDate': CustomDate(required=False), |
... | @@ -358,10 +360,10 @@ payment_schedule = { | ... | @@ -358,10 +360,10 @@ payment_schedule = { |
358 | } | 360 | } |
359 | 361 | ||
360 | associated_services = { | 362 | associated_services = { |
361 | 'associatedServices': fields.Str(required=True, validate=validate.Length(max=64)), | 363 | 'associatedServices': fields.Str(required=False, validate=validate.Length(max=64)), |
362 | 'price': CustomDecimal(required=True), | 364 | 'price': CustomDecimal(required=False), |
363 | 'financed': CustomDecimal(required=True), | 365 | 'financed': CustomDecimal(required=False), |
364 | 'total': CustomDecimal(required=True), | 366 | 'total': CustomDecimal(required=False), |
365 | } | 367 | } |
366 | 368 | ||
367 | vehicle_info = { | 369 | vehicle_info = { |
... | @@ -382,13 +384,13 @@ insurance_details = { | ... | @@ -382,13 +384,13 @@ insurance_details = { |
382 | } | 384 | } |
383 | 385 | ||
384 | corporate_info = { | 386 | corporate_info = { |
385 | 'hashCode': fields.Str(required=True, validate=validate.Length(max=64)), | 387 | 'hashCode': fields.Str(required=False, validate=validate.Length(max=64)), |
386 | 'borrowerName': fields.Str(required=True, validate=validate.Length(max=64)), | 388 | 'borrowerName': fields.Str(required=False, validate=validate.Length(max=64)), |
387 | 'fiscalYear': fields.Int(required=True), | 389 | 'fiscalYear': fields.Int(required=False), |
388 | 'totaAssets': CustomDecimal(required=True), | 390 | 'totaAssets': CustomDecimal(required=False), |
389 | 'totalLiabilitiesAndOwnersEquity': CustomDecimal(required=True), | 391 | 'totalLiabilitiesAndOwnersEquity': CustomDecimal(required=False), |
390 | 'cashAndCashEquivalentAtEndOfPeriod': CustomDecimal(required=True), | 392 | 'cashAndCashEquivalentAtEndOfPeriod': CustomDecimal(required=False), |
391 | 'netProfit': CustomDecimal(required=True), | 393 | 'netProfit': CustomDecimal(required=False), |
392 | } | 394 | } |
393 | 395 | ||
394 | se_verification = { | 396 | se_verification = { |
... | @@ -411,13 +413,13 @@ se_cms_content = { | ... | @@ -411,13 +413,13 @@ se_cms_content = { |
411 | 'dealerName': fields.Str(required=True, validate=validate.Length(max=512)), | 413 | 'dealerName': fields.Str(required=True, validate=validate.Length(max=512)), |
412 | 'tier': fields.Str(required=True, validate=validate.Length(max=64)), | 414 | 'tier': fields.Str(required=True, validate=validate.Length(max=64)), |
413 | 'province': fields.Str(required=True, validate=validate.Length(max=64)), | 415 | 'province': fields.Str(required=True, validate=validate.Length(max=64)), |
414 | 'fapiaoIssuerDealer': fields.Str(required=True, validate=validate.Length(max=512)), | 416 | 'fapiaoIssuerDealer': fields.Str(required=False, validate=validate.Length(max=512)), |
415 | 'customerName': fields.Str(required=True, validate=validate.Length(max=64)), | 417 | 'customerName': fields.Str(required=True, validate=validate.Length(max=64)), |
416 | 'customerIdNo': fields.Str(required=True, validate=validate.Length(max=64)), | 418 | 'customerIdNo': fields.Str(required=True, validate=validate.Length(max=64)), |
417 | 'vehicleStatus': fields.Str(required=True, validate=validate.Length(max=64)), | 419 | 'vehicleStatus': fields.Str(required=True, validate=validate.Length(max=64)), |
418 | 'applicationSource': fields.Str(required=True, validate=validate.Length(max=64)), | 420 | 'applicationSource': fields.Str(required=True, validate=validate.Length(max=64)), |
419 | 'contractSource': fields.Str(required=True, validate=validate.Length(max=64)), | 421 | 'contractSource': fields.Str(required=True, validate=validate.Length(max=64)), |
420 | 'applicationRating': fields.Int(required=True), | 422 | 'applicationRating': fields.Int(required=False), |
421 | 423 | ||
422 | 'applicantInformation': fields.List(fields.Nested(info_args), | 424 | 'applicantInformation': fields.List(fields.Nested(info_args), |
423 | required=True, validate=validate.Length(min=1, max=4)), | 425 | required=True, validate=validate.Length(min=1, max=4)), |
... | @@ -647,7 +649,7 @@ class CompareView(GenericView): | ... | @@ -647,7 +649,7 @@ class CompareView(GenericView): |
647 | corporate_cus_info=corporate_cus_info, | 649 | corporate_cus_info=corporate_cus_info, |
648 | ) | 650 | ) |
649 | # 触发比对 | 651 | # 触发比对 |
650 | compare.apply_async((application_id, business_type, uniq_seq, None, True), | 652 | compare.apply_async((application_id, business_type, uniq_seq, None, True, False), |
651 | queue='queue_compare') | 653 | queue='queue_compare') |
652 | return response.ok() | 654 | return response.ok() |
653 | 655 | ||
... | @@ -708,7 +710,7 @@ class SECompareView(GenericView): | ... | @@ -708,7 +710,7 @@ class SECompareView(GenericView): |
708 | quotationt_info=quotationt_info | 710 | quotationt_info=quotationt_info |
709 | ) | 711 | ) |
710 | # 触发比对 | 712 | # 触发比对 |
711 | compare.apply_async((application_id, business_type, uniq_seq, None, False), | 713 | compare.apply_async((application_id, business_type, uniq_seq, None, False, False), |
712 | queue='queue_compare') | 714 | queue='queue_compare') |
713 | return response.ok() | 715 | return response.ok() |
714 | 716 | ||
... | @@ -1091,7 +1093,21 @@ class SECMSView(GenericView): | ... | @@ -1091,7 +1093,21 @@ class SECMSView(GenericView): |
1091 | # pos上传比对信息接口 SE | 1093 | # pos上传比对信息接口 SE |
1092 | @use_args(se_cms_args, location='data') | 1094 | @use_args(se_cms_args, location='data') |
1093 | def post(self, request, args): | 1095 | def post(self, request, args): |
1094 | self.running_log.info('cms in') | 1096 | cms_info = args.get('content', {}) |
1097 | business_type = consts.AFC_PREFIX if cms_info.get('financeCompany', '').startswith('宝马') else consts.HIL_PREFIX | ||
1098 | src_application_id = cms_info.get('settlemnetVerification', {}).get('applicationNo', '') | ||
1099 | application_id = src_application_id[:src_application_id.rfind('-')] | ||
1100 | content_str = json.dumps(cms_info) | ||
1101 | |||
1102 | comparison_class = HILSECMSInfo if business_type in consts.HIL_SET else AFCSECMSInfo | ||
1103 | comparison_class.objects.create( | ||
1104 | application_id=application_id, | ||
1105 | content=content_str, | ||
1106 | ) | ||
1107 | |||
1108 | # 触发比对 | ||
1109 | compare.apply_async((application_id, business_type, None, None, False, True), | ||
1110 | queue='queue_compare') | ||
1095 | return response.ok() | 1111 | return response.ok() |
1096 | 1112 | ||
1097 | post.openapi_doc = ''' | 1113 | post.openapi_doc = ''' | ... | ... |
This diff is collapsed.
Click to expand it.
... | @@ -36,6 +36,7 @@ class RedisHandler: | ... | @@ -36,6 +36,7 @@ class RedisHandler: |
36 | self.common_queue_key = '{0}:common_queue'.format(self.prefix) | 36 | self.common_queue_key = '{0}:common_queue'.format(self.prefix) |
37 | self.priority_queue_key = '{0}:priority_queue'.format(self.prefix) | 37 | self.priority_queue_key = '{0}:priority_queue'.format(self.prefix) |
38 | self.session_id_key = '{0}:session_id'.format(self.prefix) | 38 | self.session_id_key = '{0}:session_id'.format(self.prefix) |
39 | self.cms_token_key = '{0}:cms_token'.format(self.prefix) | ||
39 | 40 | ||
40 | def enqueue(self, tasks, is_priority=False): | 41 | def enqueue(self, tasks, is_priority=False): |
41 | # 1 | 42 | # 1 |
... | @@ -57,3 +58,9 @@ class RedisHandler: | ... | @@ -57,3 +58,9 @@ class RedisHandler: |
57 | def get_session_id(self): | 58 | def get_session_id(self): |
58 | return self.redis.get(self.session_id_key) | 59 | return self.redis.get(self.session_id_key) |
59 | 60 | ||
61 | def get_cms_token(self): | ||
62 | return self.redis.get(self.cms_token_key) | ||
63 | |||
64 | def set_cms_token(self, token, expires=None): | ||
65 | return self.redis.set(self.cms_token_key, token, expires) | ||
66 | ... | ... |
src/common/tools/des.py
0 → 100644
1 | from Crypto.Cipher import DES3 | ||
2 | from Crypto.Hash import MD5 | ||
3 | import struct | ||
4 | import base64 | ||
5 | import codecs | ||
6 | |||
7 | |||
8 | def mds_hash(target_str): | ||
9 | h = MD5.new() | ||
10 | h.update(target_str.encode('utf-8')) | ||
11 | return h.digest() | ||
12 | |||
13 | |||
14 | def des_pad(data): | ||
15 | data = data.encode('utf-8') | ||
16 | e = len(data) | ||
17 | x = (e + 4) % 8 | ||
18 | y = 0 if x == 0 else 8 - x | ||
19 | size_byte = struct.pack('>I', e) | ||
20 | result_byte = list(range(len(size_byte) + e + y)) | ||
21 | result_byte[0:4] = size_byte | ||
22 | result_byte[4:4 + e] = data | ||
23 | for i in range(0, y): | ||
24 | result_byte[e + 4 + i] = 0 | ||
25 | result_str = bytearray(result_byte).decode('utf-8') | ||
26 | return result_str | ||
27 | |||
28 | |||
29 | # DES3加密数据 | ||
30 | def encode_des(to_encode_str, des_key): | ||
31 | """ | ||
32 | DES3加密数据 | ||
33 | Args: | ||
34 | to_encode_str(str): 要被加密的原字符串,这里的字符串需要被des_pad一下 | ||
35 | des_key(str): 加密的key | ||
36 | Returns: | ||
37 | |||
38 | """ | ||
39 | key = mds_hash(des_key) | ||
40 | des3 = DES3.new(key, DES3.MODE_ECB) | ||
41 | data = des3.encrypt(des_pad(to_encode_str)) | ||
42 | data = codecs.encode(data, 'hex') | ||
43 | return data | ||
44 | |||
45 | |||
46 | def un_des_pad(data): | ||
47 | result_byte = data[0:4] | ||
48 | e = struct.unpack('>I', result_byte)[0] | ||
49 | x = (e + 4) % 8 | ||
50 | y = 0 if x == 0 else 8 - x | ||
51 | return data[4:] if y == 0 else data[4:-y] | ||
52 | |||
53 | |||
54 | def decode_des(to_decode_str, des_key): | ||
55 | """ | ||
56 | 解密数据 | ||
57 | Args: | ||
58 | to_decode_str(str): 要解密的原字符串 | ||
59 | des_key(str): 解密的key | ||
60 | Returns: | ||
61 | |||
62 | """ | ||
63 | key = mds_hash(des_key) | ||
64 | des3 = DES3.new(key, DES3.MODE_ECB) | ||
65 | param = base64.b64decode(to_decode_str) | ||
66 | |||
67 | param = des3.decrypt(param) | ||
68 | param = un_des_pad(param).decode('utf-8') | ||
69 | return param |
src/common/tools/mssql_script9.py
0 → 100644
1 | import pyodbc | ||
2 | |||
3 | hil_sql = """ | ||
4 | create table afc_se_cms_info | ||
5 | ( | ||
6 | id bigint identity primary key, | ||
7 | application_id nvarchar(64) not null, | ||
8 | content nvarchar(max) not null, | ||
9 | update_time datetime not null, | ||
10 | create_time datetime not null | ||
11 | ); | ||
12 | |||
13 | create index afc_se_cms_info_application_id_index | ||
14 | on afc_se_cms_info (application_id); | ||
15 | |||
16 | create index afc_se_cms_info_create_time_index | ||
17 | on afc_se_cms_info (create_time); | ||
18 | """ | ||
19 | |||
20 | afc_sql = """ | ||
21 | create table hil_se_cms_info | ||
22 | ( | ||
23 | id bigint identity primary key, | ||
24 | application_id nvarchar(64) not null, | ||
25 | content nvarchar(max) not null, | ||
26 | update_time datetime not null, | ||
27 | create_time datetime not null | ||
28 | ); | ||
29 | |||
30 | create index hil_se_cms_info_application_id_index | ||
31 | on hil_se_cms_info (application_id); | ||
32 | |||
33 | create index hil_se_cms_info_create_time_index | ||
34 | on hil_se_cms_info (create_time); | ||
35 | """ | ||
36 | |||
37 | hil_cnxn = pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};', autocommit=True) | ||
38 | |||
39 | hil_cursor = hil_cnxn.cursor() | ||
40 | hil_cursor.execute(hil_sql) | ||
41 | |||
42 | hil_cursor.close() | ||
43 | hil_cnxn.close() | ||
44 | |||
45 | afc_cnxn = pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};', autocommit=True) | ||
46 | |||
47 | afc_cursor = afc_cnxn.cursor() | ||
48 | afc_cursor.execute(afc_sql) | ||
49 | |||
50 | afc_cursor.close() | ||
51 | afc_cnxn.close() |
-
Please register or sign in to post a comment