From 84bd8f45a14f98e337268c4d8336e9f0624a1982 Mon Sep 17 00:00:00 2001 From: youhuo Date: Thu, 23 Jun 2022 20:40:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0testlib=E7=9A=84?= =?UTF-8?q?=E9=80=9A=E7=94=A8=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/__init__.py | 0 common/enums.py | 181 +++++++++++++++++++++++++++++++++++ common/http.py | 82 ++++++++++++++++ common/static/temp.xlsx | Bin 0 -> 8849 bytes common/token.py | 46 +++++++++ common/tone.py | 6 ++ common/tone/__init__.py | 0 common/tone/api.py | 4 + common/tools.py | 58 +++++++++++ common/utils/dict_compare.py | 6 ++ common/utils/excel.py | 47 +++++++++ 11 files changed, 430 insertions(+) create mode 100644 common/__init__.py create mode 100644 common/enums.py create mode 100644 common/http.py create mode 100644 common/static/temp.xlsx create mode 100644 common/token.py create mode 100644 common/tone.py create mode 100644 common/tone/__init__.py create mode 100644 common/tone/api.py create mode 100644 common/tools.py create mode 100644 common/utils/dict_compare.py create mode 100644 common/utils/excel.py diff --git a/common/__init__.py b/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/common/enums.py b/common/enums.py new file mode 100644 index 0000000..ea7bb3b --- /dev/null +++ b/common/enums.py @@ -0,0 +1,181 @@ +from enum import Enum, unique + + +@unique +class Status_EN(str, Enum): + ANALYZING = 'analyzing' + REVIEWING = 'reviewing' + ACCEPTED = 'accepted' + REFUSED = 'refused' + + INIT = 'init' + WAITING = 'waiting' + PENDING = 'pending' + STARTING = 'starting' + RUNNING = 'running' + STOPPING = 'stopping' + + FINISH = 'finish' + STOPPED = 'stopped' + PAUSE = 'pause' + SKIP = 'skip' + CANCEL = 'cancel' + + SUCCESS = 'success' + FAIL = 'fail' + ERROR = 'error' + IGNORE = 'ignore' + + ONLINE = 'online' + OFFLINE = 'offline' + INSTALLING = 'installing' + UNINSTALLING = 'uninstalling' + + +@unique +class Status_CN(str, Enum): + ANALYZING = '分析中' + REVIEWING = '评审中' + ACCEPTED = '接受' + REFUSED = '拒绝' + + INIT = '新建' + WAITING = '等待中' + PENDING = '准备中' + STARTING = '启动中' + RUNNING = '运行中' + STOPPING = '停止中' + + FINISH = '完成' + STOPPED = '停止' + PAUSE = '暂停' + SKIP = '跳过' + CANCEL = '取消' + + SUCCESS = '执行成功' + FAIL = '执行失败' + ERROR = '执行出错' + IGNORE = '忽略失败' + + ONLINE = '在线' + OFFLINE = '离线' + INSTALLING = '安装中' + UNINSTALLING = '卸载中' + + +@unique +class Device_Type_EN(str, Enum): + UNLIMIT = 'unlimit' + VM = 'vm' + DOCKER = 'docker' + PHYSICS = 'physics' + + +@unique +class Device_Type_CN(str, Enum): + UNLIMIT = '不限制' + VM = '虚拟机' + DOCKER = 'docker' + PHYSICS = '物理机' + + +@unique +class Case_Run_Method(str, Enum): + MANUAL = 'manual' + AUTO = 'auto' + + +@unique +class Task_Run_Method(str, Enum): + MANUAL = 'manual' + AUTO = 'auto' + + +@unique +class Case_Run_Model(str, Enum): + SINGLE = 'single' + CLUSTER = 'cluster' + + +@unique +class Case_Level(int, Enum): + PRIORITY_0 = 0 + PRIORITY_1 = 1 + PRIORITY_2 = 2 + PRIORITY_3 = 3 + + +@unique +class Device_Arch(str, Enum): + X86 = 'x86' + ARCH64 = 'arch64' + RISCV = 'risc-v' + LOONGARCH = 'loongarch' + NOARCH = 'noarch' + OTHERS = 'others' + + +@unique +class Case_Type(str, Enum): + FUNCTIONAL = 'functional' + PERFORMANCE = 'performance' + STRESS = 'stress' + LOAD = 'load' + SECURITY = 'security' + COMPATIBILITY = 'compatibility' + OTHERS = 'others' + + +@unique +class User_Role(str, Enum): + ADMIN = 'admin' + SENIOR = 'senior' + JUNIOR = 'junior' + COMMON = 'common' + + +@unique +class User_Role_Review_Status(str, Enum): + INIT = 'init' + PASS = 'pass' + FAIL = 'fail' + + +@unique +class User_Role_Op_Method(str, Enum): + APPROVE = 'approve' + UPGRADE = 'upgrade' + DOWNGRADE = 'downgrade' + DELETE = 'delete' + + +@unique +class Tone_Job_State(str, Enum): + PENDING = 'pending' + RUNNING = 'running' + SUCCESS = 'success' + FAIL = 'fail' + STOP = 'stop' + SKIP = 'skip' + + +@unique +class Test_Type(str, Enum): + FUNCTIONAL = 'functional' + PERFORMANCE = 'performance' + + +@unique +class Case_Result(str, Enum): + SUCCESS = 'success' + FAIL = 'fail' + SKIP = 'skip' + + +@unique +class Track_Result(str, Enum): + NA = 'na' + INVALID = 'invalid' + NORMAL = 'normal' + DECLINE = 'decline' + INCREASE = 'increase' diff --git a/common/http.py b/common/http.py new file mode 100644 index 0000000..912d212 --- /dev/null +++ b/common/http.py @@ -0,0 +1,82 @@ +import asyncio +from json import loads + +from aiohttp import ClientSession +from sanic.response import json + +from common import token + + +def rsp(code=200, msg='success', data=None, paginate=None): + response = dict( + code=code, + msg=msg, + data=data + ) + if paginate: + response.update(paginate) + return json(response, ensure_ascii=False) + + +def http_headers(key): + timestamp, sign = token.common_sign(key=key) + headers = { + 'Content-Type': 'application/json;charset=UTF-8', + 'X-Sanic-Token': sign, + 'X-Sanic-Timestamp': str(timestamp) + } + return headers + + +def anolis_headers(key): + timestamp, sign, nonce = token.anolis_sign(key=key) + headers = { + 'Content-Type': 'application/json;charset=UTF-8', + 'Timestamp': str(timestamp), + 'Nonce': nonce, + 'Signature': sign + } + return headers + + +async def http_request(url, method, data=None, headers=None, verify_ssl=False, timeout=15, retry=3): + result = None + status = None + if headers is None: + headers = {'Content-Type': 'application/json;charset=UTF-8'} + async with ClientSession() as session: + while True: + try: + func = getattr(session, method) + async with func(url, data=data, headers=headers, timeout=timeout, verify_ssl=verify_ssl) as res: + result = await res.text() + status = res.status + break + except: + if retry > 0: + retry -= 1 + await asyncio.sleep(15) + continue + else: + break + try: + result = loads(result) + return status, result + except Exception as e: + return status, result + + +def handle_no_auth(_): + return rsp(code=401, msg='unauthorized') + + +async def read_file_content(url): + async with ClientSession() as session: + async with session.get(url) as resp: + if resp.status == 200: + res = b'' + async for data in resp.content.iter_chunked(1024): + res += data + return res + else: + return None diff --git a/common/static/temp.xlsx b/common/static/temp.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..3ded07dcf345a2ec041c18fe761fa3c4ca1c8611 GIT binary patch literal 8849 zcmeHtg;yNe_I2YBtdS7h-CYwxaJS%^H15*4LxL0BEkFqF?jGEOB)AiT2WX^`U(d{& zFEg3>{(|?aS68iGUFURF-M#PL_ncN$KtRL?AOlbV000#Lbd+Uj1P1`bA_4$-091H= z2?u)@GkX_f4NpfiupzsLoh@Y^B0OUb03LS!|F-|)Cs39+qTI^~l)99?mDpmFU#by9 z2wz7K24u=B8b01p^RO3!8d2^qRS=QLwH3WtAx2j`A1`c*< z8j%Qad>S~Q>wH2K@2sbLoP$py#zkao{LVZBkm}UftIjLA{zATt*zz%fXuxLH!hjae zYJW3EiC&S=;9~chvepWY;BfJ0;N}$GjI|Y$x6#=qn;c>!wf7Q%o6$#ADxH^1AoXVF zn-ql(Fk=(X>?;%a70!^@cC6*`RpizV0dgf?;+o=!^Af%OSR0m39|Iq+PR8iSbJd#` z{>PTaZ2H+^fmA`$UjV8$k#z*+-1ZZ`ea|0%P#ihwpcmQt&*6wWXHssDic!M zv95g_$d*(Q5?~JUD;ykZ66{hrG$Hk|FPK8vLX^6Cz=z=MwVZW{1Bbe(XnjKu*9P}Q zybmU~_1_$wChz%k-y;J64-W_c)xXiQUXzpN9ELST80*ksv@~`$vjuao|F-`h9si3t z_?KHRi&s+V<-`a%mc0!fyqsN*1&S-UzmRRE((ntATf(k?_kosVxs!nus6qS&LB_w` z?{;```RTjwgH(_W-m*wsd=Z)k_wtaG8z)y3W_ssjX{WOFJ{-5%i&;pTjJzkKTYC(1 zX;V?I!q6&>^ygE_YTyLBHZdCBN74|2XK8_Y{YttkpsNbFS@D;LK;+Ntjy~c(@1O_e&4avC_eK&7$#xtl9X2J6DW!M4=xFs3p&vr$czH$2ncOt-+II z!!XQ@)NKXaynT649#q%ZNSV%LzWvZFu?RtaALL0fbw-3H_Y~6;F!gPQish@w$sRbeO4g@dxY3`LX%d z(7@~}Y^47Q8O@DD+&3_|oFV}L*swETkohZE@-+-q7CG^K-`737x_%~j^u$h5I4RjW zsiBw{fjn-%>75kFqTw?>)jhN#p+NPfhf@AFE`#(v__&&*iTWaM+>5}%b46L&4QF!#FWsy1L9I&bFbOsO@;Z#@ca2M+EQz1oHx@e=+nkH5=WHn+)z!M^hgEn5y zzy%dBOZNPZbJqU@(G_(mvkm& z8Ww5{w2SF^42u``s*e{GVQI1SErU~7ly({a>?oY#uk+5^(5{Y35@Udwd&O-zw*=7xVD1Q=EG6^HB)| z0KgjN)-nIUE7--;)(rgH)RwO`X1^#1?BJMmr}WTf97>+&r+w^13WwCv1uqxaK8L|5%bE&XT-_Pwy$%!6oEuCgJUO=7q|Z3JMSG8+Oz5|4sdQO8td18%wV zDXIOg7%%(VfL8Jtt((fCnf)KN8l;<7STfe+9Ar0d5L!OKZ((xEZ5p84#Udr;$d*f~ z4GG7DPWV4Sw-e5oi?=o=btNn;T@ROOLzzWroN!v3tlFH6V#Ko$vg>@@W@a3tgd-C6 z@Rs1f>sm$_*KGH8#C%)b!Gd!fE*A(A#Jw+SM~g7%izrsfnTxW6vcG+6@Uv)Z3?UEV zB(|fp6D!K(M>=22$&6e|o7SnC7GDT)a6htfMd~E6W6S)^%PzQVEft&q`ogt>){btM z{;Ks2KS_R}cd7Sw_?zRYiJJyey+;;?fjeD4${bQg&zjHW0(ph-Hu~w#WIW4W0+{&d z)Vpars=OGZaxSQ5aG6mEQb^c7krby(hr`@;{LRYgoOR}YOM0KbVicxV;LR0(b< zMvWt`YptgDQ{^*-?ne2Vak_VVoKhloS14b;r#}mIMV==&I7;3fhtI3CsAPgZ5*z=} zBg(vAEjGw!5JYiw`~cmp#2FjQMjx5wp+g$Y6AIsIO`S6wD)$*+%+c(YZ+QZNG^MV6 zz7o(nX*VEk_uF+}XaJ&#zFTeSKs9GawmDcN#^UjrS~)@VZ!@}U(J{8j6}Z#}I|#py zYYfw};Yc?spWC-x551p#{~>gpAv+jC_QnKHF{M6Wp!a+T!nBfUi$<;_d{fRW<~{0* zGk!b5c+zgWfwJNvy}RPWAKu3LsOQ4Ar6sz?vhSJK`q_E{E|&c<7eAo}Pv+1MPkROA_vpSQnvi8l1rix7`aHCkdPvaG(xE~uoDmv>B#V7a%%no9qPESl z6EIdVn`NtmWN<1a;VIyj6U)oAnBOJge`XaUQVu*gT#pSwyx<#p1(t6cS>t-C&hb`} z)N;D->+7niq7ocuY7xa8iBUbclkh9G_fJ*fKswAGQ;(Q;@XBnB47*7_(HA5iupREF zsZ>{f0lw41RKdYES1fJUI~#^Xb-;1fhwZzyX4$(+PXHVVg9@~gxySSz+845?SpyH| zr53W}+ttCRbT>BW+yaY-Ztc2*pm5^YJ3albN^Ro$a!nKmF#==buK5N1Eal#AlGslI z1Sjn*W71=vNK|_)`SrwXLLpvA7euCmU!$LNV4YmZ!@d7NNo+p#Q5VCrAo-JC#4&gZ zApn`X%raz#mO+A49N*nh7>I9!?D1x8nxqwfIGAn>eMoiEpgss2j6uXzc=`SMvNHR* z6znbJcXe>Jt}+lqPWJqEYTB9qXpNQK!RPwW!r$+X>t=ILfP=z+ePD$9ex5?~dV6=` zPHZrzS(2l}=f`3#14qZr`QHUBO?hSH4Ihykw1RQ0 zOn@EsUWGjAS~;4zs6j?ke=AFl$5p`mz$KY3HZGUeOz*XHyl|J{xNZh|C7ivb5gq#F zfbM|>d%xL|Xn&~a3R$L5#bGqKDE)Jei|wjh7|jlOZ(0x6P?-1)83oS9)Dii|XhU}^ z#-=Ta;3Kt}{u!HiQ8K|m-qkvZmnS2Ag*;6Q)niA7GdrV&7yLplak0Qu&$q{TJE^C_ zu$Rm~KWt_bTaEwh@Ty4LsPVW)?mKmG z9u372b4H9XnliX@lviQ+3-%VOIR1xc6+S11N9JLE$j{eoi(8M?H=3a5;*O#H z(XwZNDTn^Nq6OtUB`Zh^Xp~W7)BTf&m#|&;)Rsbc79O1k(UGx^N8_pBA`Ngq5@n*b z1zK^0{BYgU%9=8vXSa()kiO+NsVwLbTKZ>jfjYi<(UIHB{ff_b}5`sfP)7QyF zhLd{6BP@1;F9$DzN@I%Tv2ZgbVhs6cj9JmAt0jd}I2Y5*czojV>c#}+a>w`+UK>te zG?2@dxp|tV%n#SDM@6OKoACH66O~)RTbMPtZ6Bs;mTM@C?UZQo<)x|b7V*C%@@Xm} zW*sj>GeiwqK`L7cz%d-bszo!Wopx&kpUtqPUj(Fj!+zhn}Bzw3HfA z$2`Q5=E0h5)JyHr7g>J>53}kG9b;z2VuL z6%PX}1NU`2Fkc{D4()VOhTe3J2`TjE^C|jTzweh-6^zlKM*aGx#x(C3A2hphP(!Qx zVo*{;&LB;{8f7cCs-q$h)3*VIF;G=tHlGmz*`51-X_xv9a<#qleQ{Qd(#}}}N{dJp zTk^7hzfryx^UcQ(IeX8biYk08D3wBACOl^rBIJ?JUM;d9s`(D_lgmDl)h9=p6#ObhqL`j&rxS4m=~-lKiendECNdtJ7T9x{ zusYBM@6>>uPcN2tucHigzErz5f4)d1j)#~=e1}*H4-yWf8rKg$e|p;O>v+y@a2k^q z53zoLGdS%|eC^PBI^NiA$j7mk#`V5R1&@!y zRZ_6a=iv!xeG?uyecBT?ulOWyW%p{}^6-G1X5Z3Kwb&+mQ^5YTcjv6|LO0`0xu82L zp>@XQa5FtkUrhvy^=`{L-$CL6^)LbNuwf`;qxS6-+W&V-1-1*I5UE81fw**qEdv%s8NWBrOS(mvly|A?(Q6 zi1${pR`U*tiLa#f4O@gb6DoEVA=;)=jIG?ukmup_M!;T`9*Q-wB%iYUtE1s04AVW7 zp=51thm@h{yus7eqK7fce!s8~s)M`U3|FePx4zngC)@)uP7q>~8r{ar-NH2ssC_i) zvhQX*&%g_s{z2o3tT~kTSMUlroD2NjJnB1r;m@6z0tq8XR(#pJ4&N*D4*5B}$2(8G zN%it{p7no@U2nU%e{Ve*t#_Zl&7s!xh;Y4@*+VX78Z+|p49XimE!f;@?>D>4VQ|2L z5*w^-q6g0xC5!gp+f$#PQqdhvRCQpPrS+m%#`W4U%uclLOmzqq&}In-^al7m&|F7~ zQho^b2=Q)frWux8H>^~~jgp?BMaQDpumHdJ z=Z5s{<`m0<1)_JXTKBNG>hf=&NLxCg>CW|Wj*PNlscr|Ny##1UG1!>H{h%=!J( z7s3U%7O241XBsei;{QQFu;puKGt-wY&Q|sozc0pI;#CxSISE6LgI065j<}y;#5IU8 z23cGnB(m8D^Jgj1du3Fu?<5{^E~n}chLt|CP(z0H-f}DGbq2=;iR;H4L$^|%9xH6! zHe1PB4AQp*Kdwp}f=62q(wuRUgBy^6k}Qf*YxGKuRdK3!AyHFSR#XnXRh2X4QWHy? z9>#i`uPR+2&8>y>`h_y<;!F|aAujD>5-TZn>9BOqA(!;V?J<%SB4+bO!jTxwn{VHTo1M*}pB>-ZzuC|(`uDPp zuZWnjDr^AkVFQ2ztMr*Vn5a5CID$D$9GuPm)1&%dfe(hvsCZ+QUP}B=t4g<6n8$2) zAI#$$6lM;Fr0^OoeP|q8ar7wz-0mRLZl#9j-7-f)w}QJNKAx7}*U+OL%M&n$gYW}P zBhZ^!Cf#ZuJz=bhmRGi+%M~Njsbl5fa4tZ5iJbjz`c)=?#3R1$?H>GL)@MYM5y+$} ziD&3J=%b=U*7J0-xkTSdvNhgl2S+R#lNe(#0HFW&P)cl5HP3( z`L|&ynmn{|GV)l^7!>*znk)%r)T(7Jp|H@KEH7~BX*Wk7>AVP#@>J4SzMDId) zA01_=JZ$6kP^Fv8{q8ewRAmqCVjp^ZKW*eHl+@x9TDO@aRB$c8H8OZ{807(f zkSYhHHWO$5%HPSJo`78BN4q4CZ?b($Q6EXwhF!$WKv6;%l|(DZ-9I||IfZ_f?Hs!e zS3G71@Ut8{_c?vyT;(ID_FHF1R&LM%##qW z_|7IrP7hjFEN*~M$X{Vv#B6i&G!@F{l^p8nN9)x4O@S3VJu8=x)Pk*Zz7C^c^FzjV zgIt$h7XQ0iWWi;1V_iIi;D}ll&N*<7aO)RB7x`#2W+(Q^C7BWZEEXaA*`9#jT9>yK zT{z6w!m25#hCbk`xH@akxx95CPtEGffKcjIq#mp?Ae$t5Hr^`+lJpKYOsAQNm`=av zcUNa;FNW$U%zcHDqivwND&8-cA;DSQU2z1^w+bUzd-{B#gAZ`o(ShC}aPD2YGht6N zg?9EGGL^kH?`CgATI`e8NePYf7AZ=kFmXl_A?ypwVul4XdHagv)+a2i#=H;gEexGR z_x0K2NNTn&I!t`ZFE? tC?Ey^{-LwKhW~v={A)NH*-Ai+i#0C)s@1;C^nO7YvL{{y0=ScCuo literal 0 HcmV?d00001 diff --git a/common/token.py b/common/token.py new file mode 100644 index 0000000..7d73f1f --- /dev/null +++ b/common/token.py @@ -0,0 +1,46 @@ +import time +import hmac +import base64 +import random +import string +import hashlib + + +def common_sign(key=None, timestamp=None): + if not timestamp: + timestamp = int(round(time.time() * 1000)) + if not key: + return timestamp, key + string_to_sign = '{}\n{}'.format(int(timestamp), key) + key_enc = key.encode('utf-8') + string_to_sign_enc = string_to_sign.encode('utf-8') + hmac_code = hmac.new(key_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest() + sign = base64.b64encode(hmac_code) + return timestamp, sign.decode('utf-8') + + +def gitee_sign(key=None, timestamp=None): + if not timestamp: + timestamp = int(round(time.time() * 1000)) + if not key: + return timestamp, key + string_to_sign = '{}\n{}'.format(int(timestamp), key) + key_enc = key.encode('utf-8') + string_to_sign_enc = string_to_sign.encode('utf-8') + hmac_code = hmac.new(key_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest() + sign = base64.b64encode(hmac_code) + return timestamp, sign.decode('utf-8') + + +def anolis_sign(key=None, timestamp=None, nonce=None): + if not timestamp: + timestamp = int(round(time.time() * 1000)) + if not nonce: + nonce = ''.join(random.sample(string.ascii_letters + string.digits, 24)) + if not key: + return timestamp, key, nonce + string_to_sign = '{}:{}:{}'.format(int(timestamp), nonce, key) + hash_obj = hashlib.sha256() + hash_obj.update(string_to_sign.encode('utf-8')) + sign = base64.b64encode(hash_obj.digest()) + return timestamp, sign.decode('utf-8'), nonce diff --git a/common/tone.py b/common/tone.py new file mode 100644 index 0000000..02ba2d4 --- /dev/null +++ b/common/tone.py @@ -0,0 +1,6 @@ +def fetch_cases(): + pass + + +def run_task(): + pass diff --git a/common/tone/__init__.py b/common/tone/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/common/tone/api.py b/common/tone/api.py new file mode 100644 index 0000000..639cb3c --- /dev/null +++ b/common/tone/api.py @@ -0,0 +1,4 @@ +TONE_CREATE_JOB = 'api/job/create/' +TONE_SEARCH_CASE = 'api/case/get_case_list/' +TONE_JOB_QUERY = 'api/job/query/' +TONE_SUITE_QUERY = 'api/case/get_suite_all/' diff --git a/common/tools.py b/common/tools.py new file mode 100644 index 0000000..a861a77 --- /dev/null +++ b/common/tools.py @@ -0,0 +1,58 @@ +import os +import uuid +import time +import socket +import hashlib +import datetime + + +def new_id(): + s = str(os.getpid()) + '_' + uuid.uuid1().hex + return uuid.uuid3(uuid.NAMESPACE_OID, s).hex + + +def worker_id(port): + s = str(port) + '_' + uuid.uuid1().hex[-12:] + return uuid.uuid3(uuid.NAMESPACE_OID, s).hex + + +def md5(value): + if value: + value = hashlib.md5(value.encode('utf-8')).hexdigest() + return value + + +def datetime_toString(dt): + return dt.strftime("%Y-%m-%d %H:%M:%S") + + +def string_toDatetime(st): + return datetime.datetime.strptime(st, "%Y-%m-%d %H:%M:%S") + + +def string_toTimestamp(st): + return time.mktime(time.strptime(st, "%Y-%m-%d %H:%M:%S")) + + +def timestamp_toString(sp): + return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(sp)) + + +def datetime_toTimestamp(dt): + return time.mktime(dt.timetuple()) + + +def get_host(): + return socket.getfqdn(socket.gethostname()) + + +def get_ip(): + hostname = socket.getfqdn(socket.gethostname()) + try: + ip = socket.gethostbyname(hostname) + except socket.gaierror: + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect(('8.8.8.8', 1)) + ip = s.getsockname()[0] + s.close() + return ip diff --git a/common/utils/dict_compare.py b/common/utils/dict_compare.py new file mode 100644 index 0000000..bba2392 --- /dev/null +++ b/common/utils/dict_compare.py @@ -0,0 +1,6 @@ +def is_A_contain_B(list_a, list_b): + dict_a = {i: 0 for i in list_a} + for item in list_b: + if f'{item}' not in dict_a: + return False + return True diff --git a/common/utils/excel.py b/common/utils/excel.py new file mode 100644 index 0000000..3c480b0 --- /dev/null +++ b/common/utils/excel.py @@ -0,0 +1,47 @@ +import io +from datetime import datetime + +import numpy as np +import pandas as pd +from openpyxl.utils import get_column_letter + +try: + from io import BytesIO +except ImportError: + from cStringIO import StringIO as BytesIO + + +async def read_excel(content, columns_map): + df = pd.read_excel(io.BytesIO(content), keep_default_na=False) + new_fields = list(columns_map.values()) + df.rename(columns=columns_map, inplace=True) + df = df[new_fields] + return df.to_dict(orient='records') + + +async def write_excel(excel_content): + df = pd.DataFrame(excel_content) + bio = BytesIO() + writer = pd.ExcelWriter(bio, engine='openpyxl') + df.to_excel(writer, sheet_name='Sheet1', index=False) + __to_excel_auto_column_weight(df, writer, 'Sheet1') + writer.save() + return bio.getvalue() + + +def __to_excel_auto_column_weight(df, writer, sheet_name): + # 计算表头的字符宽度 + column_widths = ( + df.columns.to_series().apply(lambda x: len(x.encode('gbk'))).values + ) + # 计算每列的最大字符宽度 + max_widths = ( + df.astype(str).applymap(lambda x: len(x.encode('gbk'))).agg(max).values + ) + # 计算整体最大宽度 + widths = np.max([column_widths, max_widths], axis=0) + # 设置列宽 + worksheet = writer.sheets[sheet_name] + for i, width in enumerate(widths, 1): + # openpyxl引擎设置字符宽度时会减小0.5左右个字符,所以+2使左右都空出一个字宽 + worksheet.column_dimensions[get_column_letter(i)].width = width + 2 -- Gitee