From 89b49170a33fbc8a4be2cebd962c5bf0a967dcf6 Mon Sep 17 00:00:00 2001 From: geho2 Date: Mon, 25 May 2020 22:35:03 +0200 Subject: [PATCH] Second packaged release --- History.md | 7 + .../crystals/400_phonebattery/static/logo.png | Bin 71796 -> 25006 bytes .../themes/default/about.html | 4 +- .../themes/default/page3.html | 1 + wallet/crystals/420_tesla/__init__.py | 183 +++++++ wallet/crystals/420_tesla/about.json | 8 + .../crystals/420_tesla/bismuthsimpleasset.py | 226 ++++++++ wallet/crystals/420_tesla/mypolyfit.py | 82 +++ wallet/crystals/420_tesla/rainflow.py | 178 +++++++ wallet/crystals/420_tesla/static/footer.js | 27 + wallet/crystals/420_tesla/static/logo.png | Bin 0 -> 23697 bytes wallet/crystals/420_tesla/static/pdflogo.png | Bin 0 -> 8232 bytes wallet/crystals/420_tesla/static/tesla.js | 500 ++++++++++++++++++ wallet/crystals/420_tesla/static/util.js | 89 ++++ wallet/crystals/420_tesla/teslaapihandler.py | 367 +++++++++++++ .../420_tesla/themes/default/about.html | 66 +++ .../420_tesla/themes/default/json.html | 2 + .../420_tesla/themes/default/message_pop.html | 30 ++ .../420_tesla/themes/default/page1.html | 81 +++ .../420_tesla/themes/default/page2.html | 95 ++++ .../420_tesla/themes/default/page3.html | 96 ++++ wallet/wallet.py | 2 +- 22 files changed, 2040 insertions(+), 4 deletions(-) create mode 100644 wallet/crystals/420_tesla/__init__.py create mode 100644 wallet/crystals/420_tesla/about.json create mode 100644 wallet/crystals/420_tesla/bismuthsimpleasset.py create mode 100644 wallet/crystals/420_tesla/mypolyfit.py create mode 100644 wallet/crystals/420_tesla/rainflow.py create mode 100644 wallet/crystals/420_tesla/static/footer.js create mode 100644 wallet/crystals/420_tesla/static/logo.png create mode 100644 wallet/crystals/420_tesla/static/pdflogo.png create mode 100644 wallet/crystals/420_tesla/static/tesla.js create mode 100644 wallet/crystals/420_tesla/static/util.js create mode 100644 wallet/crystals/420_tesla/teslaapihandler.py create mode 100644 wallet/crystals/420_tesla/themes/default/about.html create mode 100644 wallet/crystals/420_tesla/themes/default/json.html create mode 100644 wallet/crystals/420_tesla/themes/default/message_pop.html create mode 100644 wallet/crystals/420_tesla/themes/default/page1.html create mode 100644 wallet/crystals/420_tesla/themes/default/page2.html create mode 100644 wallet/crystals/420_tesla/themes/default/page3.html diff --git a/History.md b/History.md index 58693b9..8833f72 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,12 @@ # History +## 0.1.36 - 2020-05-25 + +Second packaged release. +- updates and new crystals +- new apis +- many bugfixes and gui tweaks + ## 0.1.19 - 2019-06-13 First "stable" release, packaged. diff --git a/wallet/crystals/400_phonebattery/static/logo.png b/wallet/crystals/400_phonebattery/static/logo.png index 157ac4820510f4b801b179262a462d3d36f143cf..9cdb1dba7f461524081c8bb4ac87bc8e45439e0c 100644 GIT binary patch literal 25006 zcmb@thdZ1>_dmRP1W6FFx`>kKq6E>2=z<`+Sbg;xWt9jaTJ&y3qD9$=-WG|hZuQ=R zSe@v0mESGT^Ssyh4|w;w*x{ZtbLz~QIiEY~h1PS5oAft9AP|MBijpn}L@Wrj&u4Pj38HwDVP9)}!VYc$+~!rZdJ+Q^ONTQjvh}Q$WP}UgB@XO1j^$R25VS5= zTJnRX?;zt7{_Pm;2cbYmP+o1T`^{^L1Aw9o3<+X!+*fFTEnERKsri!(Ko?M3-p4lJ zD>9~{`P7De!e#(mbpiAyFF2>Ue#}b6eyz5pD(Uagr9^NRUt0te(k=XN-0L<-F6Q5} zf9;U9f9(8ggNE+^6XV|>`HI(r{^z6gp77d=|2~noNXh@#zQaTX{$HC_^8ZZW^?xTg z&r!z*feJV3c7y9JV~mC;nuS)W!anJ@UL9ZD7TW485nAm6S8bXD8pR5~g#mp*AnSiz zC4G*HrhxP*R$Q{14No{j`c_>RuD-2bvL{??8|41!-CYnUJ$V6n^|~E=kh(%Nnk|H) zha3lATUhI#;2mMttf$9Z_xY6~G@K{;(Is|+JG;;Z>m!d3Ee1=6XLJ0|2-&htpm%^Yp?1>r8k+{ z5E?^h{!MTgeLVrViKar3+l|L3Rowj_0n0yvts4rTxlz}C20D!VPrp*r*DpoTQP*k@ zx%{Vq&jl9(Az%63SFgL}$zE%?E&U%Gz?6CaSIPgsSuq%s2x@a;`~Ev1v#e&`f~+I1 z^7i%YAglu55a*L22TFH**_Bq_3fcC9tE^)hG9|xyLi#F%Y)6a(MD|D8CjU7ss3q^( zI1)~NMN~9T4yc;0oQ40#E+Dyshxfzf zsVNP||4iKM+$*o~ftTLSYs<9TXRCa~)-*mXEAgqVn9)t*U(5ko5%kixlSbG(fI$!a zmEWb0$k5GXYm%pLZZG*ao|go3T3rOK_cUKj?u)6YUAvcDyo#meH>H*DLaRQ%E?Rlm z7{;gTVgPRW^~E2hNhE+j>S<~)a$GMB6x^?_fLQaY%&2^_el1x@aY9?$$MUgR-zPu) z!zcmY3h~r@wbdx=DjlGNzi_)$^dDEx0j{jQvsM$^K%khZ22Nfd;CXd#$}OLu&8Y|f z37f&st?v6mN^IZrMKPQ8U*H5FBMi;JjhcPRXb7ZF+Hibpyy`g*y8!oc^Yxf==vSek zpT3&Na91jfn1Lir7x!HjqwY^eA(`q^!prFG$z@r9ZvW}mPMJUi=^KkU#zlw`kc3pn zw&{aqjiBp~LXgdZsPhA0WHNREde5E)LVzUX5)kT+sM&Fp3>YNyxjT{u&5k02I_s7G z2)X0(>sOsI=LbULs?(|K@j+~I_yl2WY_#0T-WfKkva}$X@8gY$FI;|2l)kOtT>nX`x2l#w85K|vAcK9AgyjPa}(Vvxi5^yim}o-#+;!5gi3 zW;j)x>7WQwx~%xxOd%>C+u8=JMMwN%35i44#*JxtZ3~}bR}9R`zAfc@%eu#1tA&*r zP&JrsDUmkKczQv-LZZGVHIE3yDuxj8YyTSU!!&}!*JERgA9)WghxJ@@A?E~`Uq!*t zqJFjT2|h9?mTnvr(shmlbW(Q52@aiXRZ6@;eZcNn|8GgVUgf}qlnspOF{gJT1yPJd zWz+TBwUBaHZ!r+OeJ~bJ&BpbbVdRSG!39gporDw7x`2FyBBU>_=J+Fzgc(IlQH!;d z5wxx_NJhtO;8ioEd~SUy`9oK}(5fbOx2(oObKB!Xm#C(Jz(&hTKbsh~#i za^q`O1$9X$$A0W~+sRNpHE6I7l1Q6#U+0}reSRp|kZR*L4K_zRkQT?R<#FNbhQsi*vPsD)9-1s1wK)mXg zIJMynuGc^RWOjjvHls+$HlM;iK4cmHoxrn>{#*+?+eeOWp^IFSS<)YjdKv{-jG!mj zQ}UOI?Ds0_0BwU!!U8D{CTxr4CZFfC9=7!4SVpVpjCHi@QHrF&=={+DgB#dDgM z5foC9(7ZOlV_{E`e>o@M5DXOGU%yXo@^eG1fwKhJkMk0o6) zk#^fNicAP)%IOzc>24z5fYWihciv*6GhrcS_R4C~O=m)|8y|NBEZxp5%w*TI60m0u=Q|#gf&S{;B*&H1^q7s3J9=l`)Xk)Eol^FbR>R%K!>ws>rjJ*^h0moAj*WR5gPTH z$gbwNp9O6+Fa2;SF`Z^i@i}9qhydwKvDHf1*Zi;Rlq_3xFshH|V|RhII|1}-rJhPw zvPxnueKh5&!Kz&PwNhJJRtYa#Kt?WpG`rx7A+6O0FHJ+u*%D8Mnj&Q%(M|&`IuTHt zTtb^%pF)-c`sR;@;5s~box!IoWdRY1W$Nl(4dnzO?DMXYvZb5MJ0LND6ho+1ABoVrrnk)$T)+Q1}B4H5oUP5ryox^9wL z|6}`aJY1po%IxsNm9OBJL^o12Elq!u=jY=jgNC~!{z$gyD_M(j>9`l8<$zs7km?X% zER&n%4{q1!6KV25L&gr9$CvI1PJ%247kyL$rz%!*fPyc0WYvAZ;m7Lspnsk{2s_aT z_<~J5<~VKE&$CqASjF7hY;26pRn$S)G3G+2$#-+=^MJwK*aVY?rNn#P&W*(v-#^AMqfLW`F;z z$;CE<2WiwXUgfK9)zF*u#_B7XhFzvq9(7x8#m`3rw$y1Plbdnu&z{KWkEZ{oH6M^K za5DAOTV2;-dCq2TAf>i8Y07rUooS;qYPm68qQc8fY-%7^EQu*+^shx_vIakSn z^qQq7!^BLvn#xtMf4G7pxxj{mjQSjpPW+<@L5ZTe4+pwR6}UJeX$;yEYd-7x_0hpA zxVD+&G9${vwHj8gWG!)Y)-B8-k;^wxRGjeuFCvtX*lO%>Bl>V6oG`#pN=?sOD~Y!C z@S6i|@w{(TOl=Zani=vVg{RT7Jl2OJZgXVVa({`{ZN`jquxECVMgaqs$ z(aXjao~EmMZrkpvioe;xHQa1F^ySZ)y#|<{zn0TNY&vS4ec!?qAObv)b>A^2Xd9mh zm`|3CbnkeQ^XOU3_kCqP^W7~O*|EHf-fSGFYF@pE#yn0YI`TY06;&T#)iPZK%Wu&i z7w&xXEPlHX(<#+$L^0acB2tLa>ppf|FcIE~9v^u9D_}YQ*z%DSmqh1a?+KN(FPcoI zb@IBD?j^-07biuPU{lP;;O!;)#ygr3uSe-Bfpra zrp6!lCH>*&lbt~Y71|5k(H+^TDL473X_-)$?1R+|a)w`t7c7X&#@#Hgl*67QXWT;J zPmrWEHN`WJDC$$3s-l-03-K1XING<$cq9+b8+LvCKelK!RLzL49os7Jv!S0!+sR{^ zT+J+8rJsB{(fq@FN77R!i`AZYsIYz4$R#G7UgfZSrWm{f!~NVA>t^dve4PWlWrbT z@b4ql0P&T)r;uQ(cSu^0Re^jvH(ouI-tEyJd~y1{9J)N-zmqU!ORG;~%a7rXe_Kn2 zSgb@*>=DG3tvBx0RX0EtRjm%aS`4LY)6AIM3)EamQ#6&B#Pl;C{n660Jd5J|>9PLT z8a)>kWMBgclJPD;c&_=mj%rzY?MZ)k((g0hAkqMsReEN3^gg*mqGo(2f-x*LLxSQ> zuWnW;1@V^SNNmf|f&V&2P%#EK>gD<_L-+@7tB%t0qr5Mm^lSr_dIGs5+w!i{eBSq| z@KZH)yK}{`cYamh5Pp5xLFRX?r$)2s@`1aGBe5p_p8TO3vV)n`AR*xFtg2lWR|oss-H3WE-WcJGH^8! ze~V)9!0V|N9Yg5E*Sz}|QO92H@L=I1?AMj_zzS+X6=3ajQh-(%QV=(QijK~wCFTV1 zwJkhiTk5jx^UA!hU!r;X*(p%N6V`D43lj&4^Qg{_@bO>TTr38j2DZt=mX7m6Vp>`L z2s`|7Rn@%BjO{-;xM|I%=a+#!V~Z^*&2?zc^oK1^=m%G8dN>7ciAv}mGJ0d2x$HP5 zwT>A<5a14>lJpUaxB2Io0b9#9H|G%27(Kp&LxZpp)y2LeEjJ#udxx!?$3m0EAKzeO zj_FDQg67^#jzMI4Hly=-99C=MyA;Su!B!r*+0k!3EDo^7gCO76mih!+nqp+y$6PHI zWF@k$BHx(OIws>=DxUxy$B0vn%3ox{-CArKFW#Y5^9#=#N0()N%~lN_v^MkaA>n%gH528ujDdOX@>p zQ{p4(727gd)yxQ5!mhdvVrb`i;D#DI#%;A_=pzb|lG}PH{JYPS!47+V?#ivRFOIU~ z8YH9wM@B^Q5)Is)uNgPU?BAH@ViUlB`#nv`y4{!|0JQNkNz$g2P39>vg%)D;27Qy< zU*&#%@Qf(cBTS~!RIVuV;lfFhXI|{L31tIL%u94<8E$!5eDL67z{TOhhFD5a!9-En ztci~)|9XA18KkMA($_^!mM!MO0o>up3R?aYIzC7PRt_6A4^?|2JWix6Jk!m?lE|0k zzREDE=Dew-v}2!#tD-Bs?`-$<-Eox~#g^1cCzZPwL+t0h!$KDd(2w!gg`TX59pAFOS$F_`RL->J!9CP*eFKy|-zPOHK zjF@Hcu5mB_9@S8UHv?Rj6qVqX)xzI#2;KmKO+z8ecVfHsj^w6<*Hz;ReuhiSUUQAu z!;D4SViV|G%vJzaLjR@AlT5jiOsTW(l&;Eg7Xww>hPK*NI_x;u)6|{CYCkD1Gy`ut z`{2dlk+*?heaA1^@)~s-|FL?)7%$6U+5%I)GE)20YQ-X+ZCs2c{ z3+Ew&^s1OOLfd4fg!k@sEyqygTVe_Bb9Xlt}J$HF(+LX;wXh}}^ z2h|w0s_2?B%@n2Nna*G%=)?x2=a2HT%JD|Jb%Bi_gZLd$ZyBoTSB+{VpH{HEqHb{H zm=da$JFXv<)&C^4QufhMtSo4mlMU^OUoIm$tQf}Wx7$6y-%qRv?N1&%DI|dSwO5sK zx0f4r+IZ9ky!w1E*oOsT8oG!YWtnFA@Ceq@va3x&Xm`?ImvKUizO}GD zo{~J(6WI4F+no1=VS`$D6{BZpCH`I?>8n39UW4WReFtmWeTD_Y;d0LGf_I_^>CQEe zKHtd6VVdENVfPzSiDJCS=2nf7;8UgMu69Zvf|=aN8QN8}`*PK^j^1u-DGZRd4{7On zBRigg7i-RP(cDX8NLIG~x8q%Nis!=#)ECik-3uN5%+U4eCCh@;v#};4z{aBZlJ=%H$;E$~X@C(7a}}*uPxCHDXx|Wvw9V`$>Tc z!vACC0VJ6I6$~%WyTYXz>Qm}{bnrHLGdK3-%ndd>f;r8;WQt7K?4ZWB)Ks4F-1e9F z(h0h17hd3ox#qZ#IB5j(U?$y%+rMR=W(O@`;P)mO)H_g!sOtIhc)KpKZXbg9ZBBB% z)pJC6PO9Qp|AHP(UA|A<=sm;UXMY!JDe_mDJQbgyK$GO1>E48YP`Qc2w_)8j@loCA z(53C0M`yGO2B??z(>v!)(Gh(WjA<1QOGe}x$uNAJV+sV9Mb4Z8RMQ~~u{EFD{#M;n z7tE-Xr6U0>@*+I4*>$hOxn?YIN-srusmfaRGh+YIRNjGl!ry)Zx8>({t3^ro_#J%8 zdF#vNYiuaaErvFe2|Lk;lDTW8rBs66Sh5n|57njp=G++Ab0^EM)zOhgEo$(9 zYMRCR(cb25uZTB>296@4yJ|10Tr4u1Z<@g>hkf?hKFyG7&i1Xl{|W(|%FJAN^l5K%iP%ic6!sVcS(-(K!y!EO79(FF^WNFr_ZMG-+yDqQUX_=`q;ANgNVTp`vGM%}g>B)+7C8RGJ%3Z@*IV%mXTWktzpMY& z)?xSu2k)#}#uZR-5JE|fj=lc`_-}gs;;*9!_3}Iuq12Ra;;Pnk8a}qt%$PlAaBOR} zM@x;N61EDFd??UOEboim(ah%vsMtJ+G5Q1MfNq-)z_7Ex(Z{-HCdUn7Sq9w4BEttF zT}r)@fWt1uDYk|g_d450M@@N~IG0u+6Ld4Al!_Vhci#Xie=8%a(8G=|q~7y)3&@Dl-_qKxPgS|p+Rx<` z4~X}>e7nUOa()G=H_e?h#TZlQGwTDBTa9S$FK1~p6R>t6FP*wtYmjk!tDisSvYRC@u5pAi^VdtqYuEZn{zCgt|_ z`^s|R!j^bM{5!duK%lEwDs`3D54vkE;p8x|4#&n$puiD^{yLL z@v^#sZ}G8+`il8o!smB)v3GknqQAly45L!`!w}Bphc_OzdsqA^!%{)%(rYXF zKjLrc*RuErYC-@XSqFTi(U^lo8*uZP!N)r60wFFb1Jj6yI64?n-S^P5$|sq8>}qU&~jMW$2Q=8x0vg zzPdLW{LB#4;nFqnl;LjwyrRWntG!>amC%2A8)jEl`LU|OFpEhECr}3JI{x?BctUH& znlk?|eZRS!7 z$iF73#9}8iNv5Jv5j*eo4OiA_3H52B^~7(7BvNn}hB?9e(<@$}tZ!(xz7K4v!XdNS zEvlU~eUAEGW`g3+&PHhlUWXl?j&v`zWG9-w&rJ5>z+TEvJ=7oqwiyoVqaP&|acc_p z`Ckqm>zWO`ajmSASD{`8zYP^i4Re6PD}ck~Cxzn&E2qw1P~&Nqz1=&Dxo@%i z7G^PUo{_F0=ml(hoP@Kq**}Tb{>rMthEr3QJP`<8#HP!y@Xait$uJ^r*3i&_sII_8 zr>=p_PmXPu&)Q5}?1epIOb|AS`-fxVPamWnP|Y3DQ4Tn#huz=ade!h`9KyD-NkT%1ZOHXSTkRk)= znlS0@`x1}K-0{RtYuVNYTxWYXGn)kp{cmlmz)OFY%tg2H24l!Ssk1HNlii0sF!z!k znKcezXCuKYRZ5+8fA;u6kQdg4D%NVpJnHvhJ@FMAReP>Tbfv6H^6bU}vCZd@(^pXV z2JewqLnixPQxS*CaiV3)0Lp9>fhG%Skb~Y8_B@)rCyZN<+m1>#pB}ax?WEh2+`P<0 zrV58PK_#mG@K7h5P_XP9X!RfE^l?*6EMmW`(HE!{urL4Cu#ge1J$by`Rc`gBqJ5w_ zh1E_%ey|5li95=D&EuJad^j;O6r^MWh$ZK}OE&OYdL08-7c|d$Axm$`2`rP-YKvuE z6^0h1qxz!PAUui+N`6C)l_WiSANlA=7~6-S@Q@ha@sHd z4+{XnMWw>0Rm%reNdAuOYzDxnz^dQbVYvo1Lf+<(zfUEEdpCnnFA%y+ec|NOc~qp-7(_je zM>i7c+FL)gUmahc4_83*Wf{-l9_S8H^HXcPEM<=$QY zs|QwcUK2KMnCCO^YM}i7n+A^7oTVMg{V9ev%Rvv#OHS?k>(>Y1 z3L;I5D8*6l2#F={v$=5o-A;gS&+LdhG;eaS57Pc5-+R=rz%pGU6p9=2{Rrf?=7+6o z-C+~E3OFcU+zseU-A*wHeMBizfc>MNf$zD(HOJ^NjobM=*sE;c7-EhugDE9_F{V+LED@~M1wHzjAx8@8 zVVaKw#nPb=8II>LLy`0w2C72Gp{tcxpxSwxEpY;EISkbMW!9z7q-w~XBD={xm$n^l zR{a4gY<8e9;D*L5L(LM-`T;(e(F2Qvmv9s)5S+u}&jzVxjFDWO){F(b-$;B#C}3Q7SRO z(aGzzV=2|EaG|i8V|1HsPny3Px%1(-esJ^6$@dM+=n-X050RC6*bN}Gh zzH`xT)tH`zhHQJIwZ7lWk#A!hfYRNA#m$Lg7TKhx0a@a}i)`W`69L-%_N77A1N&Fd zx@XqHZr))5=;jJR`xJBdAf}_?X@yD!C4|L(fMb9I1cKYT*r^Xjc6}Ek)j}Wo#0EcN zAv$#B(wlZRQo1Z9MGZ5HHf~r+6p75z{rN2@256j~;$8T_(p=2LuG55y;#2K5lbsJ; z#Tn-~i(wS<-k))=%AP`d>9bJ(=G)8Hz>? zsWm)s=54X~#5?DwK}!B>l4N7EtePIc8+{ZZK<2j({>onouSvz8%1P}cAC{>splwcS zhEiyKUzvpqv4w_;g%eL(37AmPyM1{#N)A--V`Pf=#x0b-3~~PCU*l(cK6-f-U6Ef* zW$oKdRQDrq$m2QI?>SAth7+om{=+ruBqYKt`>(rQW;1I&sZvKu)c0|7Umh+G1Cvwn zkwm?PS5V*jS}*{nun#9@Q0)EKA*LwFBhv0svd+4rqOW=QigIeg=&WYV+y7~M#6dw> z4a0OAXEM+3VQJ*nc#F)=#g6AJM*kthAI44hj>vx#7H&D*5q_E!piS_3;Ukbw*p$N@ z%P&x1Prco#mbdRvsvAfj9{$VMs3u+S|L&P%bojZxJt*|0eMYorHQAVS>nhR5B;ZN&@a8g1V6Dc8w0U7)sfp@8 z0vFaBW9df;1uaVg&K^xro|2K0i)h&qy(j)81?;hIEa zyMJQC(glyN;S(Ux2O#1Y6nblvW}2V&_OsF^jI=E-&{MRul-Yx7)iUxrw%Oaiw0nHU zJ2=aPKXur-yqgdn;mxnxM{?p;| z=vCLOd4dBPg9oD9ik~;|M7MII{tKJhqXndU7ilVnn!R;iW(M7c6Cm+Fb*DA?=Pq`~ z-iDTMvg{*n*?no+4tyFhb?)r>XSA$_jL8E2Zf3r9_PC~Oruct`iaC3V0a27ZHt{X~ z^O?%!YL^&k7aj!uT~@7S!qwUkf( zU*&1g0ZQQTLhUSO`r0*I&?YRmS#Hyl#%ze#xA3vK*!u5hmY!82yhI1t@t2&{%;0{S z<9wob-DJmTv}Y89U0(iAn|g<1xRc$6JrJ>a)@LpRnJ|?a|DBy4ifsmnDQjTxqD^0Z zkLSH`Zn9?hW{rfJ+2^5@NJo6g0vZ7FEe@b}nVdFefCJ99?>DAmU|I9_a1OrQ z*+htA;M9~)=zgZcVrx0 zP1l2cRL2=L80sSL0BxG6NOh_5b)LBghbcm~VfvyLZ2y5rQWubZAkx(3U2_^J^|Mn{ z8oml}iE(eWyebK9TtOw&9BZSfrc<@bo`tyI2JPi{M_;^!*SIvLMb1`@537s(gB^lE z8t*Qbn!j{^DCp~Jz2jN3e{YA@1$v1+*8{+GxIeSTO}+IB>IR8Iv|%qjb|=9HVyYXJ zj-;TEm1ZJ3?0;w}C9F75T>p{Waz)8nH4Is2A~URxN`Jq@!G_7~n_NkA)9BEL!BjR1 zc0$dl{6T`w)7f(;m~WyN4bJ;OR(vfmxaei<9~wD9ooHQwvKk!ud+oLVa+@z)b0WGn^-k|X z>WT1P7beY?vcW0FUBK{Xu=$mf6-}2JrF+#~qu5Z+z?pU4i*xTNJ}Ew zg!pTbctq<#TW>ZJbPp)42S5Ge2( zAPRigimDn)lfSqhC0~u{FR}GHj~H+EJW%;XyvQF_Ilg0#zwO@L+{TW|PJK-PvizST zMrK9U8_m}K&}!7DO@r9qbSj@4R9@($nZNuj)=WsOngpi$wtMaFFaG26th&Z(g=%yK zaIdjvBK;v|t{^_1qx>H;zK=@7?vJqNy5jGC74szmCio9bR<7Bn=8HyT!1%O%FTVq1 zCzu53Tw%pV5~;C!x)V4zRA5`WYEd=-UFC*teU@eBMrEkW=$NiPTtf)!YZ+Xevt8 zt6ljG|80`wqpUBlf>8fe+TR<-Frgots|K&ncg|{{pGe6m-kb(r*4fBYQ2gLumoYt> zJs-pE_H=Qp0CCI7ZN2SGZT9YetW~&VPsBg;WWZ=#YgJvJU z!=ku|yJ*@?@VpjmwS?2l=Y4M1IA%Qo)8J=Q?p*D(l#W2BBMsM2c2^|F{2~ewX=5uW z9%HR>#e%_ou?w_;RLCQ}E6;g)_ zH&!xdZ#^GyQ&n1R%BACGV+e(*^_ULlZ7E9cP*Bk=ixqfqVl_~(^oL? zGgL;FSm)kzblR?Pv(F2^n-fK0yZS6|FPwuAR0%067VgNASo{6OI!{gpQ;(zlU(M}i>znjiv9eg*Z1=kvvNbBGda5b`h zqEH7Lb$656R5nzdi*WKEIFzYi(8&F9M29`$J~3ij3PPS0U~~gKjm&r%?G=`_P!0>7V^@pCDH2d*_HNmhCvXWY%s_%X+Uf#ly;S=7{%xrJyl~0YSkaDiypxYetRMpdd*+9 z#OXE2JiwaMET1$E1qm<@*GKsgDNjG6h?I%5OEdmkxJYyN%fTSPk6E{LfY7k;p6&_d zUmkVdcbrUbfa8W$?P7y#7^$OJ+sP{!+UyQR;E|DI$CsdXI}TKv!@-Uw`m;E*T84Ow zc2K;a+}1J1cdpgIUNzr>P3m~faCfZ%TH}85vI5E`f$(6$m|jAh%!nhsjZ=P32+Kgp_hp*A^G3@brSTzTEFSu*u2jKH@f z@!vhqzzR3mW!C$~a0*>kkBcRO#`k?R`%Qg1uM8}n-8(cr6`PE0>FZaPTi#i;p6{qY z7b6WDc9+?xu(gmDnQlMn9(IMyz+BU@m6<`nj{q4|E$gwz0iyHpqnEw?WsV`0WzD_7 z8yrW(brgBhI_yl(RzAo)6=WTsrlxEtS)ff^-Q)M$!LsGJ@!^3U{LX3ZPO&)EMzw|1-61vq(alPKdSD%uJH|)UZ6lo?8E(pXXgW_St+Mc zBF@%VS>bn8@JIlcpZ+7#>NzEf0JHwT-%!u93*%(P@0VkO?jK>7OgqkD3b=w(PTIG- z;iTp)3jx2wn@9rVAMNkzBSbz1-F1uI&*jp)G?%J}yXQMDm218c=iVfqtxBK8He#Da`4M9>?B&$b}Y9iaspu%4#n>Dt%ILKDjU z;PL4i$truZkG;lCdGc@X_=+yRz0t5(pNjA+Jvvdu-|8eFjdUhV?oq8@{LFJ;Py(0F z3}{WI%a}k@xYyXOr*TIy(0;odpWF2gTlO<;8#HdpPdZ{>-KA#xzAEAKRcvP!8|X2; zck*?xQA;xrAvv*aUOmoVlQ8RDbunkh0NGhT4xD3B6^JRb4@N~f5-De9l>moD)OOZe z0ryhDiWdrm;20Z9Kd18TYS&P)ORnFfdCp0X*75UbkgbGCZ4bwvv9@1WS@<3MFw?T% zN%-7mj?eR;xOI-yP_OCFw&}Q`$^}k}->vp;K$JM&6}53LWtUUQ$#^(of827yp3t@-K( zdw&@4H%N}ksQ~o>4t{natW`})elQWb;~-IzzdGM5;!c$BAr2|{Nxl7k$oeWzfObMYKE8cDmE*#@L)Ye|NK zr_zo)9cORlg`2bpLsDrm<bsmhD8dXUG0x)Uz|1twL3 zx4E?l#t1!w@!R*1b*yW3!=Tmg`^$#4QX0$|jIAmndAz;hl>~YB6(62~K*h?9H#;1| zbQkD8KyGAeaj~jNZYziADEpsuHwfH)!7?70y0II$vSu6lwo;O zcq<`S@K<`=2{%P8E-p#FT(Dax9qu0S4gY5{p+c|PPtaNvC$kyVa+&pQW@y;9a#Yt+ z43gFbzOyNVFGrnjGjY48$kH*6EASUi^7w#x!vyCNZ@2iSzM~Dn_MOGuL2x0Xs)+K? zZtBL3#52Y;SC932#Pmhw@V+DxT@9=QA2#)*&-6McTMf@R1tX*~EMEZP&vi8M(C;-D zk|tQ|KW{AARppNQUUb#+98}G`t@EweCMZ#Mgd_Nt=;ZnDY!j$2{k+f4&D?zA5qc+W zl$wvMwwJ6gm^Qd_ocTjnW2y&tqR5eqlT}s;lGbKCYufx5OanD&wVIKn5|108&qe2B zd?Z9Kt76e7Uahx}&L(FhnBU5D;-Om&KDMoqv_IA8zsvB33oCiN&D?{lr~4r5ADDxV zu_f=NfW$!An(mG8x@&$tjjJ11Pv~2!MO0nF=>Im-AE!n8e_ZPUFI-CM`#*`9Q#Ofh zqL|=vPTSS*AC8zW3WWtn*aE_8JP!Rt@h(jioJ8^Ck1EMHtsZEyFNKrr>FY&w;sUZ~YxgT44vp-@ z;h(s5zjldcmd9FO?@-p=4!+wwMkI%*Yq*fwNk0k-Fx}&zJ<6VQdblT1>Tohri%?{i zCMm^c)_Cwf-9hUvBg=Txc9FdIVqws2LkSDGX{*r*bl=Hz{3PeganpWH`M1^_DF}}Y z&Weu2S^8>r)3zVRkiUHWI)cJYIoiVP>djImMU1>PtqM&71fHjDv?z*!&Pl{nOg(JD zM=rjtY^~1#(zOaZ)V;qszk}}d9(m#Jvyo)p&9o`8Hu$z%*=orftOb|2E#2De=n=Yk zkTw9PaR!o(zJ%xaKQVOFrScas!pVfnI*IomM)I+#4$g8p{9eu^wqSwEl~&T=L%!f0 zbBUs_-Y>!rac1$`kYJz|bd26GdmFM1_*8A+_ zwCxb~cVG?dzeIU1;Hg7>qhe(|H4Zm!*RY9{9nbAd zh~}sld{)-hHX7r1Z;=-%?3Cp-?M=Ou3_}~0rV(HTot5eB&#V+~%AD#XfrXNr|E^QN zW;@U5^6dk*?6yTryTDm)lBDQqgUm9{oQB%h-1W9wD~uf22H|akZFZEqmO!x(K_|t% zjPEUA_M+QZ9kR%nx#IBM;ov=u2tC-DM||Ps{tqLcD#VONec$8+GOW|r%eyP(ZLqKW zP7-(3bbrx`wNak*diN0BToH!iWW#8z(yO)qT?jE^;cg1u&15MSxKEDUhxU+8`4r5; zU1~6PT%-(ag&Qz&uQbr zTKUK0wA4S3HmiP%=;!zN!gJpwSAE^c=Omo2FG@T(TL5l%9O93R1nCr+NJMjk^{V}V zw|E{+eA#6H&LYkm!i&!Yz1mDm=wS`mr3!dBF6bz@B1SX(^Yo~kvqd1oC`$6vPv6%4 zxRI0Q=OYMqVkXfYF;jT_srgv0P#P1%Y<-kuXu?-ob9U=h2SZ^+M9tkYmal1i6@jXN zEygl&`n{{!$%N}OQnzgilJXzUeTJ#q_O}I7CxSFYxA_Cm7yJxro3F|wgA_z^@9asz zy~{Qn%Z3;>#EZ|)vy7LIJ$r}_U3&g_3Ca}7JWtM?lBUD!-15Ff(7gTu`9CbcMM7a{fFf}3Q5}7Cx)pGh;?U!f5b0?Sy6DoGf zUldIr1Cei{Y54||1lGrQ7no5s59tM4DSZ{nQGIQ;RCOC9l-1M~W$OnztX5Cbwk zEo_jvSf0)Qptj7}!wfEoD6(naRjb+8OUyg_^qk^vJf5W)P2%W`Kvuz)8pF0t?|Aaq z;@yN3qARZ|g*%OV=OWEH`OZ?uHx{=YLi|>5;!(Vj$<-O}$T|2)Y&(OAZMu#3>-(3C z9Y|u^L&@W@#{RFKylFAN<}-b1hYIJTIYK=qW>)@|1%WfaaWg<j zUv`d^K}5W9@2&3=A?)T#wfl>7ZA+P>dbu*cIGMgjPv$$5!x%?lmzuxY0YKU-*bwl} zJUFW^_vi1=t*l+j7)TXK!nF0A ziG&$F(Y$;paXf?$5;$Y}I^HSv@zo-BuVsVG_(vZG6H%w9WAyUNQS#D0L9EF5D%2j%ZkPP_zl#*xEFZXFaeA!=;&1CKq|3yx^u* z@`z;?j;4gN;vCh4w$nOQ z1tcNM_6R5aq&S^C%GjLWvw`(^C9=j3Q4%oe0S8R9a4k4>zEHY4kx?m`vTuRtPT9pR zZ?05fxVM^@c4NsUx-4%H>ILEAoL+z0#kZF8xAzO!wE$yX)aV!(W5F!l-#+GNPJGtZ z-4vIbky|Z*2)<=e&`90Uxz2ykQ>UjL*a$86xygAUPp*smbtpil;8mZl2QH`_F6P~* zqKnwBhkZTSRn3@NnW6LL?{sFs`cn{%(edCe zWX4$B4{Rw=eq?l#`m&%#;Fu%A(Y;vQt#K7^ilB= znj#%?>$+hDK~Q8?W=0De^QMu>)prZtN`>f1eDRcM;Abe4#-BP+p%DUS%6=%Wgd4jh z&WOwv)O*^_~8#q9A)$wA4pG ziBID%VfN}fc4;goJQrWq?p958NEKcA9*P1=@9ep|IpcqJbUlt|T`4k>47fFPJk{JK zcXOsrf>gzyy#x=I6iH_!lIz0wA^qhdo}-rPC_!VVTakX_S|BTTiKYza?0q)P92Iyy zAGL>!-%j~5PT6NJ&-5DKBk62Y!fYTDYw}{HG-H;D%8)nfgo4NNkvy;18;R!t#(^Ew zA0vQg6~@_2SwWU}XYNH_FY#?@o^dOsC+{FF=gbFAlGn3w?roHcBxTtUMZGj3cZ{I14RF0daAynoF>`g|3+t z6>SbKfgdfZz;OvJuI|~JI358BMs)mB6I{INRVt%fwd9)7J2Cv`b90aMDxHvY3YjCS z)$KIB`-g&xxM(5njB@I@+x1fggvsp^5sm3jrwz4rMOA#pV=79q%&e{)7pnN}!OJn| zR_FN8+;VfJ3rtMb+T}xRs67AV1d6Q#*JCH=HB@7#ZZj+5kWZrDfZTEQy1NUy*TSDq z0<5hiCoCYoFcVtly6=Rhtm!=UtU7@%x#P+m&!EZc6j(j)@mt{_yv&ZHZwWfsOU`?I zrs?5yV`bv=am@+GunH05X+qLsN7Uv)e)p)`B80fwfAH*6%GO?3Q-4WCaHgG)=|1PQ zg-oYmt2KRb@`e%3v5q+x(PuLlTh`Gk^TAJIin6~V^U;jkC&*tx?wDGCOfk30D?dXm zOH`xlCZ_(^?C1hz`Xbrot?rW3f2>d)uB-pPjiz+Sc~>2jUccfe93Q@aXlg~{cXPSk z|1n%!TgyJB;AP~!6@h|HTW>vt3xGHAxAYDtT+h2x`})~YX06tRV!Oy6nNBf>6gKwx;pM#o3Ec|MA@)y-;j&-JpZ1 ze$cS7N>E_|MqW{)W%CK+E&N^eSK;Bi{+_;LC(gB`oU^w5jWJl$&!}a{0uYeh1kgJ4 z)xV%7P0&{-KzF~;FagtbBC9*-?Dvjrt^CaXtXjbm>Ub7Wq_J$3|JfT-x=Bfab+^9T zSvXjuCA259RE&Fr$rrwMmKMbeP=nj4RsC}%PJI9VAnfK z%}d*{D@!t;4ee(PYhRM|K6AqiAdA00&gxKL7j=;TyKH5oEzh96CRlHOSU_R~;YsWg zWW%2-#pF{HRhp8wWv|1c)v^}ormy59UQe-92Uzk;fF%t}{~Rk;M5_nzlaW@7jR#ng9Jbs59PLiAiKhN=vpqAZF~Fh%8oju}^2R#f zX&jbjX*wFcWP;~;cCTsa%@|9!D@4``@@^|HW@%mRQD(V%Ej2I7f@$c{KPvF6L`$bI z>ke~2`I6$YCVR#ugP!of#Rg>`ZU1Glf(tS8?%fdWw!qc4f z*!USb)@^U}?22`GJl44cQ0TfPwowF#_Rs$8WKyx@-5gm`KH}L{fTzIY3uM*&bYb+` zO%Bk6b!o9qOiJiYv4VKUXwRQjJ%e>mOnYFZ_T!qQYo5~r;4%fhY(0$`%;|UzIrFjU zn(kQb;@x#}9bOvUOwqTwacLzv2u@Tf4eeWIdjM!CYzGCl^Kl7<+Xg?LZcEilx!MhP z`f@LU)))+?B-!SMv4Bi$;R{qShI2?meaRIxb-3tSOL({Yt-5`Qj{5b@vJz8M`-0TV zsYCfE0;NmKpH>)}V%WflZeu%G+0y|2Px{E_$GR_9ne`4II8>ym_XK!E)OutFd|?$K z&Y7S(btbeRN|MsTsnj6WY|^azJXI%%k$4s%Xa^Tc$zW5gjbxP;E>XjaE8O`n-$z2do z&o9#D!RFVsy6=%z z;*pf{XdiMn=LlCjy|_`oO$90pog=#ZksgXnxErn5R{Qm*SP79+sWR=qFXldZ6c*hl z@1k3Z4?iSa6;f8kEsxN<9X%JSNuTVcS$au&bs17Auc1&a=LL!eG8Fr!TaecI7dY5# zAkn6NAvoYelQ-+>CEy@rHtTfW;H0&RjVR3zsXs%cslnTe@zj#8RH;VXNyXKQm2=(K zGcsfiwot3xNj^Idy?{@{0|%}Ym#}F4_#M*yqb(HnTt}Dg8}_7|`0o~=6VY!7c;$P< zk83?k9y3+xgok|4)FYEMXxiqOQJr2If>o*PikK}wB)~UPX@vNhWx|_P5##%KS^;Co zuWilw`&I(?v=UBRJc5q#Nz0#F<-qwiNNo<@pehdXc#2{sYt1{U ze3HfV;~o<02QtZly3gzKpc&s46x6Ch11}bIi~DSS|p#G^ffdwK6S||GY1#fB&BC651Glly2RGo zA*8e|P0W^g_t>5eug|~%v~3Ttc98al5{>_Sl z4OV)w$Rgp}3CmPtiR8+5^cTX12;MY<$V%eh;5TW7Zi~oCKvNf%kk4(^sxvp@fj9i# zstow#{#G-Kq0pcvl|T{lh&wy_-44pA4=~hjnMPeV)07fucI&CNEn7!@`SQ zMplRuF5r9ccqN?-n|T9`Ys&*z&$7orZSe~u$GW#7WLih>PXFdL2+wQJb{5$H>}6jf%6E;kdqaf6p=d$!Od2A`9Z9g-AiW%QJn$PPB1e%PwXBaQub zu#1C~PA!*&hkNL@7P1|(hh3t7+D5ti2%B6I7zUX|DFI#B2S*xFd-ily(G4MsG9k5? z?HfDnF61ROM(T1Aj&elVsiZa`hfLad3lq~d-b#s1;{Y_E3X{5R2hoaN%pYEZ*DJX; zT8kdfLJlU2wm6i!6+p;+AXc0ndXMZ#M%_#{{=SU$aTl7A?U8nafY_17i~_yN3y1?DDQS+~WED%8w__W>p==-W+f+XYwgSr*{AO z>^tM}uT%-&q}@xlMDXS}nxVXF&2CbHe24jOCek2Pl!FeY`a`LCzcD8M4(an1?^j5k zzrPFboGsUL!PB76Ubn~La2Gyw89`4sYf@&{Y}qRwb|FVCo+sPR`_;oC52_zz4EtYc z8F(L=5=6uJ1mLH&o(7wI-0HK{J4?)O(MpfIBXluBJmHJ0_vVDa!8qJ)KqaVrK=MTe zkyJZbm8U0LcbzxAoa^ivZ(3}3d=+1?Lc}%!v3Uin$EH6qwH(+aPWvHDA>TtaKlTNN4)hJe~1M@ zz3L`F^OzmPiry1kd*_S63%t97(qt=8-KxXC zB0rAl5UOTjpQG2S>doaVa`3@DgWHj;`oNYz76wD^M#;v|>woi#h($hSp{jZ>NORh@ z^hoM(>Q_*HCgs!uG)mq_K$5*3?J#p&$GG`d*AOrQig3%mcFm32Ps{%EXvM^}=Yhkf zMW%#<_>p`A$s76+;Ms|;bk-B9zLSr8n;0D20Mr>}z8jTo6!F%a(Y4L(F%Z>rqDX>b z={lRE{ZRb}(V|GPH{NHswANDPVM_e-ffmAUjZC zK*?r>)JbUVu83REW9(*rkaA`{M8mvk_k=<00$=>g%Qu|HTnUN-O?AFFcgS_DdySk5 z6RWOo9*bBDM=R9qd64bShQ{*XiV7#z^Ez51{q=Zu6|4t|XzzXv9!Ml#X$-Yo3zwap zFY*A07`$37T$`e%*Gl!$Sd%jmPl%$tz6fefZ z66~338rR&@Gw75aOrPlcs1jK(+$8ncAz=El3k4*!sjrHh0tw&g;j*&xPY2~VXu8P} zX}51N+wLj%O-=5Jg*0ZjKh@YJKU{Mhu6!L3bld?}#m9oFt4?$GL-DIsFX{Qkw=A*^ zoV2VcWc)U?3+kKppYAzc68<;Lc7ocK(<61?dsgNz{@p>O_b!MEzpUtZk}0(!3QuGC zJUwUm>`@`ogNYR_gUEs1<~@_`AEr4@AITNPGyQ2c?1pOPhi+KCtNV@}BJ*+Y;4}6x z)lK?r8&xUAvW)fKamurS*KWB7p#-ng+NN>NmnixMY++`2GPn^fkk0-l(YCa6Q5gk;|UctlTL9ctMB7oTEcpeHhHFU#JS5;%oS>6U^HO zhB$VHt4(DY{%pir`EN2E^pOTXd_B6z#qA3;-ls`4S5^AhOD(|(WnDuj@8bnMsN6-j> zgy?T3Drp9K2=_8HG|-N;MjFA{+3iF}GiA11nIC@J-g)g3y~1WX zRn?s+onU;X`1p4#8>S7I0VjVMbetG!kg|j9eBR*!gR;zz`79Ke;QQ9xRi?FKX~Fqd zDcKO4$4M8o^vyrXEZ*(JINEf;}*W>M2W zl)^|J|Co_94*o_tsjR+Bz7|V6-3*G5{8ezQcN9YT_S8Hy&^{>gRupCTOQKZEEpT4D z&O~=zD4`wybOMN)+fWKgEywQEIbu8W+g6Vkwm0vXHx%J+g;_gTTzRoB&3P<6+`)iG zaQL4UU$FWY`+VMF;^=9bp?|OIL=wWYBBgdyjm^XrhJ06!Ot6t1cx;p)S zCqE`E`lC^~PyL~@kWdMK(R)70W>tzJ6@7x8t;wL*Xe1kyyqC&@fajwPXzVw*67tjL zdg*Wx_?0cSz+0qq7Ygy%FWGI#vLc|7Yw!0z+!%Nv#cr*juB7sQ?0CDf_vf3D8ih~P z?VW9g##N{VugDE$3i!3zkI5);sR2WGDsSOp9P10)b8x0#7zwRFiSWMI@(BtarPblTi2v5OX@s1YOD6_ z%h~~^H|7)m`!&g9Bqhke0!wEYKF8>Hh*SW3b2he)U98BjN4ondT4R<^f%t>jTjGj3 zX8^=HP2C`01*YN3aw9B z#>=$`hpcw@gU-zmW2IG}mx)JlHgmJ%6G!n9pk;b=`pGnVwe_yQe z`xy9s*Ra42YGEUfn`4ohkyWVmBg~)fL`=syES|Ez!9!wIvwo~%jt?< z-Hh~mX?p-N#sT@n*6DJ~ttel28Q><4_xBTN8bq+bi8?7wgaAN; zjng#Q{phje2vUc00O$-)#8z@`dLoI!3nFo{N2PrP&YGRYwXrBp&~Z0D(Ji%^^b8aL zgr1M8VcUr=J&wkzG$3T{#}L%LP*4i~Vyla!BsbWBB;b}b<9g&H?} zmz?g}G{me8na@=qPQCR*}W-Fl#NlA;@>er6ltfMjTB?$)_`FQ<0-O9FiMt0(`yVV|?R+E+)9kj^e8 z`iIxo$theW7xC*lpFtl}H%LAijjvwpdXC9H*KLr>U~&$#C;bOtvj3kM`~RzJ12X?# ei2qNQq@yztGQmJ6C6h#wWnGA&7W%Gp)c*j~Zqg0_ literal 71796 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 z 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

(YN)*8k%;l>f~_-D#(4pi93tnLZQ zFj1F@aDD??qr>Ami-Fvl@A^gg!~Px-1JCspPiin>Uo`T2YLeWM!8DcDr8U-i`!nCS zX^ddfb!Qe;EIx#<+69nm;@o9csb7g?3p}<6!Y@LtL2d zOyWS=mbn2!8`zO|!j1ehf|xJw%1wEx-88$2?2$8&_TDE3Hxq*ly01{KRE~+5rU>}# zy6CI)h;yV0vF4qPcisE1W!S431QiUkxI@XhE9>s62W2X)e7z0u`9OAC!<4GxpW~Lw zFJ?AT?0_7{hr<>nQOk$Zw=)(k*X-250^1}KpQeRfQAfi;Z#*1)8Lpp0S1FYVT8);c zM>S4!yVV<}FD9s*nvn)$n@*8#3&XnzPl20AT%V{^^#8HvMYSK&&*(zQssW zP;7r^A*;?9IVxl5XzS<`n+6>S`N_}z!$(;Fca<(=y+lbx4Q~X39)x^a{K?N7*OuNI$$)G4K?PjDPEN?G9RkMdGV4e4$e8U z7T|_PLAUa;cwz-e`~klaj`Ha5!FO-3d{dBK`=3dY%DkmUT+J*GC&o^z*R^#r13B}U z8=8}xkE6SB&i(~y$nM{@1D!cWk1Z$oiFX9jpbO1NL206Z3RlEK7ij6iStf>Rh7NXE z8XM>6yE29o@aUTso2|bA&batwXs5QqRn<$H&HgxPbL8QZ12bKYM(8{LjS`RY33X3p zg?dDabEA3>=`03Lm3eBl+kD%XBgv7z;0&AjxW=D195SvteFu_R?1iuCK`*^7B9!dl zE4SBc_ZbkY#?e3I-Su!t?#p0#O)Vc-{qaC*lSSF=ZY|9G!4XOob7)T6{O+X|S+3CG zsHgqgO|_zKsu65wtLSDn8|}8X_YOZ%5Q}CdNJiZc7kexsuMXUkv;<4qUCr^mYqtEU z2($FNS*`B#zC1MwCne8U&&J6vdM}FY3U6*-gP>6QfAY1H04P#!aH^HsS4kgBbs#OFj4#`Jg0lepv7rfXmdmN@*F-u?XezngPueobzk*zV~ zK8j3qj8WQ7-O%K^evKLQ!nBERNg+_;?@hX`IK4H8wKUp;;{y{pr%?Lk8`~b~bo0RA zrwIbS>%HqiaCULaEIci?aCa`(XZn}0&nO>C75pvYM=yEp-=p*B1?Y(XQ>loRxQV5p zVrSePj()=}_k*UA{FS2fb8#zp4y@4*jgj=^ zF&;D)in_g_=J91_*M4}~jjA3EUfp6m5X)z&z}s635M)CX9jJ0&0f__>@n|QCLTxnoumGcu~NF(ca-4aOge2 zQN${-@&| zoaxe{^nI=DTD*TGyD>k3q4=A$k6IHi#bBkcTqG*4|N1gO^qzPMW^^hRtEtNAaVH}k zehbz9?4bL2$M+fDN&>jWiNu$i5x>or2pFY_C!oDR6qe+uZ9js)MLGEL-^ltTUiqMR zciRzmL4ss2@8g@hiw(}}HzsVs3v>`W`x21zQ&QFJc4`D3=MVehterfD;OyWYU(R42 z3Ot@AThWhLL$v{pnaDg##?Kk|or?JJeqnlSQ0~{VjQIYBtnA7je)Jb|)R*M`riOq&kc|8kNHWUvWhSz;Hf+mF%Lvs#IHGb2Br>wpQ43E; z+niPiH8KN6KEwlC)F)Q3BiZ<%EPfo5u+Z9&1D0a(1ovrB)V0`is~($ydvmj>+z8l; zi7vu{c*kD`!AqPvF~qA_ba@h0M^bR+f&AzFCzz{lsvFmT$CR0>W;uZHY01&p?0^Wx zkW=%)An1Lvlt(O3aRC;M=w;Q3JAmFVSy^nl9n?@m(_Tlaa59Y_{O_+;9T4JQZDDDk zL^57{bs&7B0Ac35b_^^^<6TwN4|lwC@%TJf!};-4b(zIZ)}KmyX9PsDwS&h?Mg^A= z-6OpmZcD|fqbi?{c3@D@{T0332jhRnYxCt?FLV4fuh)#E8*Q;6M@%$WJ&$8oa8X(E`qPo; zpk|M9E*#D#_N)vnGx_9!m;WW5cU^B78>9x&0$a8=atriB`Z{nmYmT4gi|<~PM}x&U zIPy!#>@2$uEKk(Ouf%)bHCx~Z6q@?zS?+{>XNcU5Q}^jNCw`Q9txhb3q^dzY@4#Uw zc7VA!srhJ@=XZPWHac#(8JVq=0y&$?pY^Zk#wEJ9^yd6Hw8gnKdh?F6*}|$HqQ>&z z8b?N8Wy8&y0_J|V+WxF{j-B`zyjop>=A!~hHCQ0g~_r`tGIDe)R%CtBq`|nF%CXRzH1_%EB9ZN#=8%l8d zKYm3wNk6X}$OR1QL{N}1()76Rk0t1Wem90}Id!gMicdc(w=caN4-mfRp)awEdPu2@=sMVRjms>ln4Xk9) zn_&~r?MSffPVQySnMsYnB0j=|^<9*CvF5xla^_$OH@vB9I&R*m!6EZ3h~pO9W$fP_ zQ9U3Ngglze1F#IKU)V|YKo0@JmTR%x_a^|k|LeOq2YVbuLCl-OsgicV3lyFuS)7K3G-=V%MXr;Z`O z&v+5UHB(VPoKr!RAmjqcj8L*qH6Jb-73cJgo1`N!uncmSxi}8#FSiMDY^3kC6t(%W zv$s6_o$s2(Qx3$)1nQDRcL#X)Qcsa=Q9pG`&&+YWSHnlcJ_p|dVj?0x%~h4V1{YbI zPpLIyviT558)3xVfg^vi-I1hTTN;Q29!?NM*5SK>dAPa#4i{dyIkB73i-*hf%ud_Pu^H&fe?eKKOI3e!1IT{Rcz~ zgwg%ltJZMw9q?)%dk=oCW)-5{)j-Gw5~Ja&z?HYgedhaBD~{ob8yULL2#=n-s%CjS zAeWNJm6V3Ue2{n%)JA4<$>lKoQ2`{8{bCk5h|<6jD{`@ozZ?M&(sq5n{j%wJKu$Zf zB)dxS0+#C;P&e|Fvj7I~BX=`BRFk~GuLZIJP{rg>Z9XWH1PvMecQtGE*_-(NIw6mx z*QoXie0(GJfn?8Bggkq?Jy@Y`^b2|HkP*C7;c2KR>}wDM?-e15cFxh1TT&$Ri9L>z zP_XxB+SS+P&LH;$shho8UCU8&$rl%b8sRMOUX7IOF@)LEm(W}s@5inKbnM1UsAqXf z{<4O@(r&6|UT??`??_7Tm1~LkRc-o|{5M)Ol>ev8L+!I$d`Rv(0ytqNIFOmXlMa|y z7-(2nRIsTkr)M$L2W^GVU>n&7e3i$PXJ)BOE$Nc0w(P=nNyU15%|Qg1X6yu&0a=_> zkaP6<33U=A{K3~_E37s@!kAodXK{0Mo?u!CLRoNN@OCE`39e4AwKMJ`R*qnj0+=OW z;yIt8i3Pq|eXm+9B;12}_BGHwJus#Bh|+*DC30P%k(o^s^%^I+))RXZGZw&I6u1cq zIHpL`@CFp5!ag0r0#u@%dwgcErcr^DCldO5(22gqLMZ^I{~~v#>u=}sD{&I12=NTY zfZ!uY8wWuP;T@X>$(dW+-aBFdSjitloM9qYcRC?<$Jy)E-jl(r14luI<#rw_O~vkY zbN3%40wH@3hiH(gKz^C>Ao2=JS3=ta=^zUO60A0RiwK^bN+v&(!)2Pvnao1v7C=ge zFhtgT}Om$OQeg zJO8KLZ9}pbkW}o<_$X_B`+nuJnFZPf2Upby6LM)Zg{>@aaa51?UU{?=ID%e=dfm`3 zsHhnaQlb=sZ8u+%e@f^&AN7`Xqab~sjd3NQE-0PsNsf=R@MK-l`{4d)epoXtV8{xp z?e_KK7F-uKqP+nv;!_XomBEy)K{-o_tqxidAE8US7W1h>q}aE9Hh zfW)_N;vZ+l07fWwPxE0F{CJ?`q*YcCB0$+6jSR%nKUYlQUvJI$+yA(LOjnOQg z=zqKN&q?k9nRg5p-ri2_z$U%ASya|tP&aP%4h$O8d$G2E%hhOiKFnWqa=sA2LO=$u zT*KRBj#m=2H`GQj0A8IjYsdB72kiOo!&Ot9rA}DXG%6&oC7)cxO9KU=u1)|6NmnP- zwFauo_yfF*D;Ylk335ANz|6MMFPyWhY5^Y(0n)y~N}K^Ipw2eHF`y#rIa$TDnt`-F zw6*DSBo3@G86rzH16w1Rh<5c_Zzs4&>5;x3|C~nyI_CAMNtGn5T*}|nyG&al-Po5y zAO{7&_28F8`^C%vk_;Jll7mV~0OQOI<$V8gx*z~i_risk9ZzKUbE*3t_1c%A=P-@q z(3#vQiv5$hK&pC}!I2yrHt6IsnUApWF6f%c40PLv9O%G=+@Yg(%#`~#Hh}5K5e0|G zyRJhQMaaB`(ExnwnETh@{WHLTq*&g3H1Ke_pv9Bqr7KS+&e^v(Hn3QOMXv>>e$N8A~p>p6h3OVC%o;2@{Nd$fO@0e$5@ z*`vA7oevD)YvGZ@btlLH7);(1`kEjwFS$?FZ(87M#_8i&o_)d(p8#L=4j&pn0xko9 z50^CbRgDXbyvMx%Ch;F7{*#ISwBtWJ@t?)~&v)zvC;xfT|Kf@NBCG!wV@yen0lm-k zi%;cuUtcX4S|mKCnsj9t>|J)vAqS;G=c`xJWlXoY}D%dpiBi33qF$O?4;XS z+)U(59mbH$>`3H3^jV6vr*#@tmSCzaU$atX*W!8b{ISc{()+_NJR~#<7Bax|@}WpR z;~Ac}X_E=3WakygA@(B$yo#s_71!XjwrA#y8jj$DLeP$mKlc#Hu9Bw^D<{9usm<$ zSBqU_R`=%{^0}Jh1=nEH_^=s{b43A~aF|5;3eVd}*2-zd{|y(j=%#dXamvX(V{9vP zggFy5NIT@e;v&FECNR&pceLdvZ@mrO%3YkDhxc||9DDBZRL+Bp^}&S9kJ+tQg6Y;Q zw3NmBe_;%m5_qeJ4$N#uf7BqqpCV-B*(rAo@y>|my+Em5%MGZR{_0cK!Qu^lD#L?T z@-A#UnxKVaPKNKxc1tr&DyDkc#X;Vlk~L#8dC>B30 zD4E?|6zq0>!8f*|{PyBjV+km*2H{)cAFWqXQktD^eU^If;$K!WyYpFp)&1h&8|Ux; z@+0>foo$_V2zlXgDI5Js$u$rz;rA&zMUD){z+fL7bU!LGzNcGS-}$?JjH!*pTs`o> z>GO6D8F7KVHi=(Ij=WI4gAOz``^DnT6*+1XOK@Wt3?3ih>}11T*ps)AWm-M26Bp+k z-rzp`2X@zStFt*YBTrv}$76#T6|MD(dt5mj>bYX~^F`@Qp;S$s0>}67YOC*3n!Uy%OEG0` zWXKaHr1Y4o{qW1u)?+hIk<^xQlX^_G%tnQ%mZN*rW@JXx3BLXzwNy_^+?f`5P4dyM zVl|t!eqyrIW79fzLIA4F364B`}Bwsj!Q7~0zgz(bBXtEphp50;gZvKR{SAHrS=8s@QE@5M*h zjXc9<{@3cuaz*P`MkfZ1C;Wqz(`<&VO)vPiD_B2>4}T>3GVLK0de>rFta4wLHaTbQ zb`(n8xZlDUFiAW7@QDyrg}~m75I?v&Gp@n!uTL^MwWt`gQkI}^qRms9H^$(HarV*?qn<$CNEe$?e&GB{iAlh2;D$) zA1*eAZ~L>%gCys+JJY_TUz@Z7WH1+oB^VMTS{WP9=iWz5$_2i^Y9anv-_b)m$;y!= zz(mHbU}IOS2_;I5Mx@JUl*=LqT-#zq18yLm){lNLn4lLN_ny0GLM{jods@Duw)^`! zof(sEM`1URok0pOs}kbqe6deqJDGK`m5YeqYQkUcITPp%zu2>`jQ#tZg3PKFG%6S$ zi?ZgJ{y-*u(AJMFBka(LE7eq^qP8u3Wv|%Caz1>8)bZKKWY0eQ4mvwhoBZO6=Qp#L zH_K$F;oq#Dc8&=Phoz0f*?&6SZ&;~CPv!3QM=7o`t!Dm(&b&svn0O{xQA-oK`?+*D z=Sj+fs0kU`hy8vH*SRi?cpCBPG3A+pufSTr7$fGTUbH}#dAWJMt{*u=Ru0$}FeRIZ zBiTPYH9Qe>OQ3$NIiXkVW3Dmn)uDZf*ej0EDhiKOjT?Jr1^ zllKrQwgjHA$fHcYSOZDN;WIIA!fzu!>Ss(JbCmNmG)88wKHghxsOPZl+-0+0PV}1t z%fQ+@n9F?87pznA?TK#5s@VG2_usmVTb*jEW z%Oll}JI9mvxc2rHPgo7Lkf&T0c6-DsFE!8jxQP(p%g&G16`LA5&yI{Kg}tE-Dn9f{ zUqdX!D?}}`7E~jm(JNBeI0WtdN6js?oUjhM!;0JxR^kMCzQzl~0UQP4jvh%R*^%Ln zAIG9cWG3Tu{Ckx4S`E`+5H_EIa#^5B|E!N1jNi9Q^K=1XD>#h`K|^C&Wd)sJ08YOWL}hCCTE2nnqi-U9GxQeDIJC41vUall zdTAv!saK&L%{Mk>(z9tIeXdicjbV>HjE+5BGC*R}erof28L|yIuP2BspRMWUUhrD@-N1eJXhcOSi^jg1`TgKjk>1vhg7n5|ZQijwbf27G%0d9(=W0aPQVhf`9ND+R zc)B2OWKQM$l5gxhRp}P^jpON`CEC6Xg{M~>Qjp1C^~YyT-mh#8-GWOG@BIb=Orx?% z_t`y-Y1W@TKK}3uSud*suN2->9QPZDm93u{WtTJF%R1;1-a~@BerZn*_00d&b<)Q= z{=2JTUUR?lUJ3d`&tNwm-?URZZzQ@CiE~WXZ1=Zxb(*puOy|Vcr!=9~w7h5WA6U$jW?J8_%;AtUR zCgF>G)JJ?az!W>k~Jo#&2ktf4n}jeP4Ue;C>2tr9Qwc99I5HbE}F|vY5Em zB<|Mw%=HoNq@HvOCW6nHS?|Y;4E}Zf)#_>IpD~)+>$7_!vOP~soG^DWMqfibjsEAp zn{_?b@tHP6?`IDmk*-}c=yn`=$B%e7>Wh=D&r=;}{RxcnO}@RCODu)x3hDIzrEB|y zC#E!`>4C==Xh2Gh#2;e!(OyoGK3{Eq9`Q77L;oW;;`h!MiJMLFdzSyTswfq&gyTPX zjcH1~vw5Da`hh$G1~yMdB?6!G9;$Ny(czd-Sj}g~q(Y(eK<|`=iZl?k-aN54u0uZL z&##xw&s_JDKs=p4(pvNj*hP9}H|@V@x?gt61m{*pfoQ&M+mK1U$yi4jTV>X)<-k1y z_b7w^TE-6690@G8pNqm{V)T33K=J!j-G9;4@j0O59A?8b3#vdSy^kJ;C-fxgM}NOD zN184e?ej>=9uTD%wY^9!Dh?FSnC+Jn1$5qWwPh= zJp9kE7S~%)4bg$LrSoHeDHJ~Ks*+a{)cm@Tfwm<;=Iof;awf5uN1 zy=cEq_czIPl5%(XQcRi5{6}VHJ_bC{n9faUt`ZL0#Wi!Tf0npLt4C@C89_j=?VQd| z;U0Z>I;P;Y7QiGv{SiLYs_%at;iIB4Z6q94FLI=a^DASSqwdGYDabXrOVwta4yj~^ zDTopSgu5q7LRLDa#R0Bwc21A5nbIuzn5xF+nKsFNwQx%AOzXhW7JU>_wL*ItED$fZ z$4a3B$hAil`+R@Iro|3o!Z)0#@-MS13Hyuz8hav-4hU+~I zmN!bS{67&pX!{%s3!KLeRcN=xYhW-y)ZW=h=yMO?|G&S4_10Cx*cc9+&7l&wfi`JeRty~m^9~)V7PnTRm3`3^Uli-!#Op* zl-H7#3I&zcT8~vw#JSZQ8dOQ?9R1(x(3hYyan=Qnqh06=aCXpxGA66l#lFF?iVHr@ z)@gQ9qgBQ3PT#sB`P36En}%paTD&yx>FCQDzqX}>MEVw6X;i`sUtjaX-5DwFwbkH$y!RH2{p*bymxS<;O>Gs-YQ*Fj zvlsV@9P-?Kgrq8YIAsk{j&1U_)%{c%(80J$J0q@9v>$u={k@)!wh^A`xaP z7{91xCvlN&t6NeR2nGCYYUD`;y^q&@c-0%NEw+_qTNf!aFG8>t%V5f{K|HWC`mmSs zz+NGcNJ9MP?Oa@I%A8E$W)rTkcT=)3LE@6p-c;^s1(pP6aG`|Z=tn=;tOIXInJ{z) z3K>pbxNwwRh_Z-fA*aOF=_;l4uj#ebAU?lT! z!t1XYxa%+6QRyf{@_!>I*XP=r{cf+4s6&P)dU3R16+lQk0(sz#Ea?IE_~g&q>>UOx>RK z!I!rz_AiFw;=qjHT_@(`*umUW**OY^s!_3V)y{yk=Tv)UJXYAgUS}C3X=B*6m)kt_ z#7uU?NH^gEuMo~knE5=6T^sH>7yFs~nqw7)HCJ~}gQ+SUH~OP2BpGHN|PKWVJtkMO*JX`a48Y$I&)w>YeoD^b;C5s<(WhyrMLNQ;Oo&;(YP(&Z^e+g#*rU2Kc#9P0VkHv>$$09qH9z=;?gQX&Z zW2AB-@^FKfNg`@y+FHCSYbszZluQc8%}bQ?gcDDl5W;PZRpqWZ$Mg;K7dZJBS!TVi zWEeiaWF*{^Y*ZrL)B73w#!CF3ewMjsD7bh4<5}2saQ19Up@@SvE+h zhg2{OjJ(f-={&x4WF~gdaqb{vUEZK~;yNbAhlXk-hUt|gFWi;%P#4Qc2Ktdtp=YrC z>02n>&^*XMxW8C@nC;HD>J67ij~;0v`99sJ_EJkbR)LDgPuteLzM6UR znAGg^RL9J8trrNi=-F5Op-tN=5!!Nx5{6#LqY%D%o1DsPtvq(I34L`@%Ae#_!WJ8C z*DD_FhytZhnK%8+zzn;}`%y?5E|4FNuXKvmTAiw;el+-9uR z`e;>?sz;F3MY~172;229wsnbzLxg(HtBhE8tIq98!skSe-4&DJ$Y?xi9X2l_1*2dm z^ef-169bapIT9KgS|TEKh=TomXY`z)IzkT!l?%7Xr^g6oyUFQ!i=s1xUKv%NtbD>9 zb7*ueO+}Q5q!O;ZX=!S>%kg$q*yCcPJ*j%F{6!zG#qhlz7g)Ka`f>Am^OHf&i1H6i~z_Ly_IY%d~$2q%13noYYIF@Hw`1;kRN=9`oVLPmqYR zE>y%=%u2Cf&g5tndap`ai1ogZFKQ*U&blAAmU@(Zu`+=p8sV0==Da@P{23d;b|3$> z79I5`qiTh=@05W-sn%DDV^Ux!8Kg=srY6*#Ywb*bIriQ_SChM+*m(0J<>M5nOYqLQ zC@%I>nt{Bny+^RCx1@51pL9KPhlbkt6?mrL_7?D+LU6#m9ByakSBSNc;l<)NchzSa zsJdh41Rq^vDlp2!u(Cvd)mMf+%C^Y9*xw>4FLN@Z>qVD=z76(4x*My?{3M5Fvgp%O z&GLeOXU^Nj+SVnFDaSuj8~*`ZK$Hj|-@q@2-IG$xPOK^&z6#p6&6>E zP{yF53>Y=n;&B}bec?2;p8(5MijMhQgJ&eBk7m50Ww+5Xo8)Q2p>*n^n_fVph^2Kh zgMi|VSC@{BxFmdc3Ve`J3eckNY;YGc=$ehhy@?6#dM0m+<1rJ-;IrdJ9&tx$NpD=3 zxS`VCOKJ(fFjQBO*+L#lfY|oCG_%7*sJ0SSgT)spb^0FTR5*wI71v>{gtPy9&f`yX zmfR)PC}n5>jaU`T4M?g#1HwN~lKsV8P|jPyq|2X;p`oF9%iIwNUmWQ@o_VhR+Krm4 zdEt=g0f`Xr)6|iw1dPsp)$;jFPXdAx_sO;{?p8R^D{q%eJ-029D-Xbr;&p^V<4y=HYn0va|dmd{ik+K!}F?sCf(tfj;MOXWRC9 z$Y~;PM5Q)0H5r5sr`?yo2<)h~)5*8&olZPnI`o;!Xns{vlrA*F+HI~t&(WnD`Y;;z z=RK~n67a<80-sNoJ@!?DFZv4q$+JaU2>KL5oMhO#N8=7!3H6=#1wH}tuq&1)&x5|B zl_Bq)5>p7~Z%iGs8T(g43b5TC>D`N~nAqwpIs&97r$9{dhX330a%V^6agDd;Kbhk~g;ADMR9~aM=l#@MmCC@m!B;GLxWF)xuPgP$Na21;gyAV% zjrqN20(r^GM-;>dZUf2iqj?@2bMhf##a4Qk>dJ|z_~PEB6kB*}iUPx1&Nbmn`;)gv zX8ra%eXaev8MoA~wN^Sr8uim1bTNpCZwC6eW!O3xR@HrFZ76i|WV3t(ck1$3MIP?TUk0oENefxXbj!XC7jHg8b4r;BCLeDucML$ z|LUv{VL_9pq?3@5DCGrC0rFb9!sH<&HbRD*^qHTo9@ zDoX#L`+uQ3oSh(WO`%<=yDu>+2VdcNL#E3`yDeMdMei3>PH!j9Dg1b2FMsiH4h_B& zi5x5p0nX=nQjhaoM|$lcgTl**BWqcgHgss+6E^}x!>=94;+P>63R)=_opK`*C0-(& z;~F4oq&iFNLS2UMsL@@>_sw%VV-O7J!i>EKqR^rXWU9Dps@dsX(cXb9Yrmm>1;$rW zedJ|td*c}jcHoD6Cja8(Qgz{79X&HgDhyRH{+IZHg?#5Xk{GBeMvC9QZTp_%gtf08 zyR9NuLNJGB7dl)m1c;)K&zhO!oe}s#{QEy|`X2UfADxv(=B)fl%xb%Ay)P=`j;t-Qa?Mo(--7s+oE*|V45t-wh+R1O zT`^G8S}?y35`?p9ShM`o=9w4!^L#F--HviSkgc72H`lV5fp~w7>TP>$z21CzT>0LS-(hXr$Mu|fF2(+O zeNf@pQ4rX4z3pYbh$ZymFTacc9xJNIdWGE^Owh$-o}*(O0f)LvcDNOamqOA&4(rXW zfpuBkHw#~ukRylJ*v?ckjc4g}?@JRgOL0gLiF}xqYVm>Uz8WTPnC}uR5GSFZVP>xo z5pBZHzn0cEg$?VU0UGwpwO%=4LZJYJ=*R8M#m3`_eM$NSK0T1(Q8SFMDauFL#;rfX zSL~a#^|&%9I9n2KdbTP_r6tGR1{us#L!BTEI`o%1=L9Cyu{d@?0qB;o2HtXA0!a%d ze1|#@*9zebiN!T`RSz8$G39NB`q>=Tg5UQit>I3~Lp*Z9uF#gq@G=ad!P6!~18GH! z%s4r~O`O z@V@h7Upp?$T+(j>5YNRN@?-x_$T_goqbzY`9B2K0~ z!E)z`E29gc)am~;i&nV~^8g!Y)({yyac4vl&T;+?)L*Y(qO;tE!jo`!Qa%)cEI_l& zi^n=6{zB5UZ3TsKMR#R9KYn$;S^Ki*F$0O7tOqpve(3x3C8>ynuextLCeCWE-Qj|a ztv)A5hL$WLox{leVi4(?l)rRpSClm`1R>#yK&sn`UMmi)w#aq=nY*h!2^bNrC8>zX z+fG7xb{2N!ljm@;GqUgG03|RmKfh2a$yjHOHqCM?s9(8m($OlrWR%JZeBk%TTB0U> zARu|9%>3u`jGV$xC>qFno1wMJm0X9E&c!v@dg_9VslysX>}fq=erjpe1&ttSQsQao zibjlXF4x=Tj@B2;LR7rt7D`=FEDy+dCV-0M>}K6H7IbBRVy>3@JbW%L{y6_y=!#zP z>4SA!l1L`9G)6b{02~x|4Kk%gOwW9uI;f}+o(Yiv$dKGH+1eqoYP}8NRBc=SA-I1a zL562?DQ8H(XYMdnJCh51r}9vU>`CN1Rs*2gIn55!IQ`<43&7gfMffQm*NgYRS{oi5 zbN2*i_+|x^5nCtyN}JC*uLJQ;Cm@lt`ChNPptl%~Xdx!er|JuZ7Q-0oorFPqX|#>l zmZI3W;u4R^MyIC}EAkYuX(D~&AVAGNZ4QFMzbbKUwd2M5<2mR7v{phH5B;g1>sfIc4U%Rdcf0Gl6b@v|l>T z_+!?|+#PN#lwcr>>{z=Tfs8vl8XwEl`?fAQW?6JSe=uF!guC5~w|OixZRj76j#LDC z+R(UDy(;!mffi6Nw>+-$fqn_ys$qWKH<{4m1@qumSf0O$;GWXu9oKCxPD#oq~>H4qO~pfWo;FgH=2InC9u`08TLf3Gbg|R zAs!q9$eFKS7-rcOEHwW3ZW zZYh*{pAYwZe5xZyr>wk|=8F~WioR-nK48SG2rjSJwsaCfX!Da%N`nV@uUwp`D_xFD z|JA`K2hui==vFU5K1B7jeF=PiNgmKB_9b7h`zdcx0h849Kur*5hq?Q_3Jwky2PtT* z@v3a&)!2-Hk*V(%kfAJyhREdrdmW;;=)bXT~`ICXLg(C3Q{BmwVnb12a^k zs%A&Do%sO(BhTaJt+s=7)|9-s79FCin8|Iw`VZF9FBh5*j641=NP~u%vL97@iFIC_ zI)0u8FDxt$Cq?-&C$2>I8GY@81r<7S0XS5ZGX|mZtym{xaQjzJPmh?$3rKuSto&eS zsDSt!CTu|bDK=(khmz7>yzgI!K94|mV!-PddtbR$U$jo4T*vwZn(K~pRNs6qhW4{% zpLqE)O2=xSI-i%XcnEcuyfYFbg@WWwsHgSg3sLEGW9IjnceFUw`AXI2G}z**W3(oD z(8?~IuZQc~*dWTRyYx`l-5W`|5(YHU#dXIF%)!6sYhc0{oUxNui%Df|6+f|y23TFy z&|`p#UAwXS-IfSoiQ64Kw|A6fx6SwCF=f;XZL0^*tbs)X88_G76oS$ctD}K$PJEL= zq&4^v{Wi5jIeEFWLH4oC6+7P#;SdpULu2oP!QjEx{pCf*3@W3(GfCf13|s^xcMU){ zHa1o{B+=>Rxfwusywmw4hRs_wS_LET_B{7x2L5kdx9x%R1-lbRm>~U%IeMcoKqZ$l zei%*oIYhYe*t+`_gwY&!b><#oSC`|mfGm(X!}4UYJ}~KtgOxKa-(H#n?5@*xN7>bW z3~X)8gQN_CvE%V6ahH?|F}b*a$2WKC67vI)bo)V$NH2TbV0*!wMic%PB#p6;)^%Mi zxsMfKv~8qoXovf}M<%*oBG9iRuD0n^pxVwt+UD7!eB4|Gc?O7L*s}D5IsyKIXp_a2 z^|in^4s)Vbu2|P#VBwaJg;*f7FlVw;8zRdazJh^o(12EmT?@Le02vW7Q75q2l)t0uo?lph zKX2x`lMUti>g}v%o2gAptSyBrZDGA?Qg0So_NCj{$?!xMZU5L!FN$j~pdn{=M0U^p zwWzJ*yHO+kpzrqP6?PWQ*toRz_+73CP-L{EWuX?K^ByuK_N!N9VyPso5z08-zqXY% z4{&j_Bw4@8LEFmCOoj|uR!z6IXdT?2*|D;O1HWRGYXndXf><|X9aql3fYj-|=xD8D zF3$X~#RFdLJ1Gy?uy~J#~?0AN?^Zzl+|L~ zZKrd#yU1NLVf}(zl5*dA%i3k$J{2#w9>xz5`<6fTA4TCn**7SA(E&FG6d^9w)Mg95 zW>vQfaNn66#uNU9Uo7y*+ob+BRFK#a7)Fx={BjIuwN4eZNSv6^nJ4%T+~VjZ_>LA` z^pd{8Ytx~r&zs*jGP{<6X?Y`h(X;{;{PsXc1Z0CZc8-c0eZ*QGHbhHO#kfU ziGer>!mYHlw0J=>ABW%JVh@ulx2>b2Kz-AkD)UoCfr{3Rjpd0iWo1%!X}s(4>8!}# zej=8?KwecrC$C=iaw&y(2+_1{6SrA@jd;J+>};n~45GDI*hDO0VdvNbNtUuc(sDG2J*a+-im6@`WHqIaQ@J{bB1b_{O#l%v>oOhy_<-iCr1#2l0yi>FcFLaW?4q)s zd}%Eg**DPZ&grIZNLW)gp&Q{|-8laU)o^^M3dmgV(#Ojv3011|PliBX@NNuePH~HP z=$Ow->ZiLV3xjcHQ)?66j&7S7y<)L!rV8_Km|^kF`l6#Zr3|^}&6qo7PZpG`;OJ8p z*?T=80ITn0b_J9S%0M#XCitrFu2`U@NCrzrBc~=h77?hM#WTZLhss=HRYLbX8T>s> z``2c(P6*)9!y=v_vyg%BKAQE%pHU|=4vV~zp{H>V>Vv}iXRBUzhCAFkKI-mL{P2L& znj0dk?vG3a*nezAPA5o2H~3Cmg)?ayDi?}^Z9^mb-VUVTIqcD|4OB5w)z8Z`{q596 z^1iaF;S9utGj-M)Vl9u!&!1ysGZqI6sc2!!Fdi~1Qi^xw$JT$72%2c|MLz(zc03+< z$FqfEyozSr70T~h#YrLQ56U=btSKd*A)fs7>F-)*$p}z);x~*fM2-Y&q-F;IKmqnP zXxN}iNyyjRm3SZ8OyaQOfIl72)O}czy!J?qkH?JOtSP_>#D8G(2Y3a$@3wg_gS&p# z|K2!SYkG}-q-&K~lRkb(`}^*zw=k@$TNTn1?5wJ+$weVliqsi$(Y~va7wTa?SpZ<5 z@1j+#hz~E%$`6m$+IX#WG2OYg)HSTIi`!8%Nkd_xKuiYC-omfgj#8RowLBqn6Hv4Z z1ETWd)`IK-R9mqfstj}I*(U25!rU+4TKLEjgIGOUA?YF_>MYbl9a%H1UhJ&hSb&W5 z-r}#=_B^kMxjnn_vt(`k!c~}0DC9AjOt#9L%@MI%j6^$2MtM?-iGk6jd3u({f>6T9 zo%MIt-RIrzbzESGpZonm3kG(dB(8hNZr-q6Z?+|h-l)NwzrA}@C0rv&FeI1`Hk0i* zsL{8Thm)E^xZrvLx`nDaS&U@=VeH`)%%0B)bZvu}ctptMQFe0m@9Hd_#Y!PKsc68y z^p>*aMuLY@0h-D%JcsrA+ehm>WjkyYbKxheFUG?b)Ya@tXSZ)HY2G7F{A6uQ zN?dX-sCj)=9oOCVeB?+@@>ve&iR>6hiWY;v;W1{#wTuJ?(pA^44$PCA% z@+zH$Oq%_~n+Zvq6}}f-t*dXCacRF=iMHl^+U2O>A2hAWd(7vAOXitOR?W5cvcMf> z0KY{MLnPo`=u~er>V}Qo*e9?eO1=WxP-xrUcwtD5U?6ggORixfjdtjk>~k^xS;QwR zNM5J#o0v?AH>N80@I5|Gy>9t|mn0)Pj{#$L^=t3YCwa=Cn0^)je>$Yp+(T}*)tdO4 zyG|{qyc}4D*keS~eS`w_TD1pP5jOM*z21N3YELbTjZ~(1kpCMESdrBXp`^e3OdwZ( zQl?5ZY_=}Y#XxLaf1CE9KnD+N(s1kb5bwJHYT$HSoqv4_)e7x{Og-rPvekQ)C0&7Y z%kKpc-V6(Scc0s0<4t2#3jP)C4575ApT8814spM4U{E5uV}65Gn+D(UXTtg3KhayEv}q{PXJ@R4S^X}H%BO#G0_Gt>wXO1_L12p_ z2LjNOV3v(G;=UeYGN#zJ&WdkA+%Lh#CxyuPS%7msDY_-cQ-JHmslOfGwz6zF=fD93 z!~JD-|0_gEN=s6$@^043lc;pr&#`iiB{X@MWWZM8&*q92bQCgP2xUy^FZew1Tpk-s zwDRq~^Kh>7vTyQWpCB;_TdIB48qSRN>MQO3>mRsw^58b0bmFY)}HftXBl ztemw8W_{w^pb*j}A~@5lVtiAETGqw)50n-t-?*t7w|?2XiK)X>>nyB44GMqlJ_-1* z-2*^!JTwuVxU}HeXd(zL_WETmqD2xkOF^4y9&U> zh~3|K*^-#mCnr001?9F=@V0Rikh>L1FsEU?OM_2m8u`^&;~^>O@=2uib8b10-F=vR zC#&5~Mlb%zR`!=w`^MO}u*(HI!;kE=AKn>x`AyKY-R$9SKQC>N8-5ERe!yO;+jVNI zL3l2A{&?8!3#?WTYkp&;6^LPFUVI_?0>js}{SOL=);4=pcEdFQtj^phbWf;9G-f&1 zNJh<1(BoE{^F+%lX5bU6rZ#%!BY@4MhPm1y9@PIX}Mm+xJ zlrVNHPXkAAp5dL!2hKONu%o5FbVtPC9M@(krej5i42%+p?y5q*;umFGSO$};qxu|` zWKkCDP9l%=Q8Lg3R73FhoX!k2x2@Y3GHwry-^W`3(?_G7@<{s> z^H`LBxM!Fy7_8(_#n8aMQG;en#g#!`A%|X#wsYvb2qtFyv{=UHh;+KKci-uLM!L6| z+KYc0T67EUSva4pg2gx7^PHR&HVcUKM#{eA=m<>EfL)HbS5l=WW@7kh zWO*TXXemKZqR{`8JqbiBt_#JaLs`$K(RMjVsFDA5&|0uu3De=^y-%t5t zP!P(V3cK4MuVELI0RE!8*g!)i1N&ojL9TflGW9+kR?d7QEgn8Y zpPk*GZTTsJOII6j&L5Be@${2?chsfgPn|(Nf5?w0#e(d*`z$T~;0H;&>-x z!eA=-jm=5#AU`{G|FFj5S|NC&(lHogbyB=m2dWxCe`%*Jq_^KMj(r%!rQG2bAtzh1 zuIx$h8KIs-*|Hj{z@`jzYj!Qmx@fYm&OeGPzbjb)&T)NZ(~vla-PF`CK+(6VK0GG~ zD5xB4$T2xS#o`szkmlIupza?S<=}ki`vq`Fr*=pJq?t-xR$XY+yI5m%=L><4N#Mw$ zIc2K=_rk@ZP@uARFG3(u@6sy~TK5bgb2-b`U9FeQRu2DM0LKPsB>#`HzB`cWw*UVi zBV;5iWv`OGve&V9k;vY%vWjCIvNw^v$;zl?W|RuqTXrFiT^xJ;u8-W$_j&H$KmOpj z#(TWp`#LC45cV2K%F=t-(-@~sNH$#NscyF#eUIiX%xXj^({QnEgbt1d+od^m9(zSJ zbw$~0U-|BRp4qnLYEH_$KbThTILf@j(Q_FnNwFTC`Gbj?Ahx8W+()1(BGKi{f9A+2 z&kn=-fUjp;3X}oNQ)G$&Lmi&Kn7Kc^ynXww0<82pD z`^IhL(ltLZ>?$AqE#Y2J*`qS1L_yyG+KNY~gb*53d&3LcMSO{P76m)etuwOOdZ5`9PMZ@H z{raZi)GhZaVNx%D*WWYFi3g#v?a35FDDmgV%X;+%^oTyUS@dVc@| z)qkCYiB<&ZGeJ;&o_`2Csx2$>z}fH{BPhk(u;E-ozs9}1>6`FZ*^Z*;+Wlgpyw9mjc#0U~$JC5Iz? z7k^0zk{Yj@9P7%?XP@4lYH_#mn5>`=atRqw!21?aSNQ6a<8H5FCz?e~2MG5?;{`+E9Pvhi$K6O1$NzYj6M1Ap& z5@=s3#!5WziWHsJ>TmBXBJ=Ri+pSTwG#v6X)JpK!wl&a_cH23MGI(fb#4nZ;MO)vL zZp%#rF4mDZwsLHbzf#S`Q`MumhxO|H1A!B=d7rmjbigLR%*B%Ew* zW)1qwiHb;E{O7K_NO3PR<=$RX_=3?%uY5v1TkPT?<2EdUUDncP{-ssP&ReR;UvM%X z>Zy94tWU<10c;m%1P+5m<>8~o^{z~Aa+DBuSyQgTL#f=E8gxX|6E~AGTZ(uc@@|Hd4C9^YZauP>iEsEvKopX^foTcw5X{NC;ILb-hXiQIRVVLADBOpQaOfJ~TO zS}vE*kj*B~`Y=b=x+eCD1|=gJ!GIxQSjN-8+MJR^%l^ZCjW_B63g?@{1BTU?iV)lY zs$q^j2R6Rg1<5c7!!DZyb?Xf2N)P8bo^A&Jm&5g-->|Tr8hOY4mbRZlA2E;xz5>Ab zbz4N8f9wIV{zj}8@F)k+NT3Vzbby1R?c71@5&If(JjGAWOX8)olO_L4ceyBfOax8!l8)e1QZ%->#)ueitTI}xKy4(5W4G-D+xN|jF zm-d(@U;>Mt&nRFkoj}f`1B{pIJ7!Z{w$WWtS}E)x@s#Sp-Gm>*o?NZbE{)6nes)Ec z{iC$WY0vF%?vKHBbg#?^KR4zEIeS49CCWdN+p*LoAMehWT!N*DzZh?^I*X4(OEO-G z6Lu_l>2mV}*V1=T+U-125ETB17kv3qEoTGEdaqBkPE#biR1;kQB-wl{X{Rr|e7yx; z3HW+ppQhzqakmTOg*6f3AbKbVHEgbFscKpl$m-EtyN6`JIn)QTpcWDx>6JQG?|jwH zzk9Ey_ljjBTZ8D*cX_+YsHN-==lzxJYN!<6BY4}+#wvV&`E46`m2t7k;OO4qcGBVJ z`zka^zmsP0-8l&?LDmlgt7+p;*XiSB(;6uz@`i7}(+Y#CyEMauq(9lq7De)(DWRcBKi?4MLhHZ{?ePFY_o&%sgtg7eX?T`XPM30I$?X*}7`0(i!o25LvfG@N)ewLdeP_dbToW+`DUG#XbW z@KuVwMJ)O}mdsqzoGBRP~bWbB-*O zy^DLE(_l5#fSwra;UN=Vm0aO8GSTKBgH$kT5-ub?aHV0AA4#!j3drVmWqY<~Kj|~w z!u8toutFlt=4Oh{@-7UwG;p|wJhM%|mW^O-X64Egf zR+IK0xDV)cFr(6uwr=M}18va*Jg;xfvuT3{k!L@^fJub!(bO7Ef%x#_s)zvN+3%_A zCLb@OiTS{7gvv*`TBVz8!v+j_c% zY}{K5c7rpxdfj_=^O|cy$}G~se3>V+f`&r* zGTkbyEoE^L2t_M{Jtu~S&rMQ*MeJsh{Aa1%-lUZXgqc*47S_oOBn|GNX`D5@{i~C< z`+b-d2^9I)5CKp$sGt4#c8&-EjLB)xDA^^`Tl|$kCZONL4;eG@2Z}L@zpj4(EEmsa z8Y+oWOF=hlksB6zI2%J%Y&;79-_*=YdtHsQ2!~th$&8i5&!Z?O0U#K|CvS~shg>mJ zRJeuiZ`-NB9OXMxFTlXB^hj+9lmrtRWLEx!d)3yn9fzOItB_ru(V4GA5;Krjn8;Yu zfpxBL4y2X+9`kn#r3ul^>PD##pgZ$A78aJh!cZgQtkg3nf1S_?!Rr9Or+%E?+hyy& zZ;-5wFTuZ9@Vd&RE5$cZ2`h1?U|Vs~Xthi|5#yZ&(4EYK>+AkLOE*tgx|$royg`2u z(fWO4>T+~YI0}mBw718xsK@HFf%8yS8KXZQUXEMWd~*d42T@KlMQKS}TOXx`D(AJ4 zx}9Tunxcet&1BU$2G+!v@~ue9683u@&y>3tgqhR+Qh?> zE+4hku=)L7SN({T!6DhMo6FKtw*7GqdSY!hlJf&G1fKFBEiF1Py7xUVBy#97%XJiJ zOjU9zwlZs#uH0rk)J$z(zplWJ{w}-0k3HNa7A61#T~E~6bK=2xSPSRjt@_@tW(9jt zU|FDQVSc5yl%~lenrm@1d|O;<=ux1gqC4>4a&i#K2S3-?A`iz@1o~MVH6!FWDJOtd z8pCHZy{0NGgJd;Fx*A)843C+w#N+sb>9%=;J#|LHxS_*tB6d@$qdj264%I5NBvS@1 zXXXKK-^~Y~;jWR|#p+9jy8Z!9de>}oW>pY!j>2VBsGUrm0$je>$unNr+}i6tqRsw@ z15L&&uz@wQgHaJAH7QO<$Sv)L2LGM$>Vsb4(@c@JZaqKN&t#%vN$$QO!Xkz;meA2} z;Nakl-uZdyFz^>OYFB=cW(6Oxm2!X>Fy8(Br@i>AD=gs1^f#0KsMFMvn)!=^M&t_d-kk68p^HZ{4da>FTrT1UM08kb}nQ@Z8&aBgQE^!O%Bd}IVrNI<5t z=&~&ZXu%y{um?Fgmo1%CB-Da1R{B6%@x5(BX;{|%woMrBcJ?QAN|7ne+9b-hx4#%aP4$~<@>+iT71+KPN`?H}O|8?%48M$KD`=GLi+3Qh zBtZf0Cuv;NZ^jA|EMI6I06?4rG)ZA=5NDs6@SaQ?r+6802c(hF>3L2P?oMNd^VB^R z9d9%}ahS+OXC_}i?|R3r6FjXF%LzdITJ>o1O!nqw_w6(7w1AjTSk)$$AUwDV%32k7 z<|252e5$|4pikTAt?oVO>|<_)Ra>g?o#>hQ7I}$CiIJ+;TJCqxhf7DJaYrp8Q+5oQB;saY9-Y!F+;LBX02{sa zFaZMIY2_EL&8;WT2Zi`Sf=|Ifu7`#cATJQ09FEAlzz~>5r?g+6mu4i?qJz3z?PTI{ z8r;oC-}dxN6I*-pKMwdKVAj;A$Ab&uo{V5$J*AaH&g#}2p?gW}dz`dDsvWz5*wOmfPZK6re8G=~MBoa_`5< z4Fw)|JMrnXp9y%C3>8rPJw;1DNrtLTtVvyV-l>%PH$xteiufks+=RIYlz;-y0J?9m zXWO=cW3}2tWvEKrmMPqf&`AG-m2FOy^=BZT@8aHj^x_(h$rP%LL4f|<{8|6PIF=+?P6mRK=Rxj*R2mF3LhuAK;mxR zF}~fgRsND(1>v6<(hwNXYYEDiJgtRXkc^I$RVBRh4N*l9lj5LqCG5xzd5$y$I6vM+ zPWW{!H|bZF-!}jexT)-zldwhUB9$Iw!$YBBs+%9y2?jVnp&G zmVg9K99l%Diff)@3dm+dT^6VSfGN`fcL|&W{GH!W%hPzDAet7&>MDLB__?`1;0x#= zhjlV+b4*1}_M7I&t$&iX;R8!inSG`Ne^Gfns+s)0nX2X#U!c7mHHXR5uMwWy%_Gbe%RKnzKYBfTnXYPaZ(Ugwwrv0|eB zU_>KVQ{!JCxF%QJ>Lija-2%Mq9BcW@0K%Ykb8kWR)=F@-_mvE;*6#+kS0i9niBF`~ z`@z$ED^u4Lj|lSjkHzuaBjMX_?te1+1QYm`ny2i&y*{-$x! zjj9izNm!o>S2~X&$pyv?(@hB1uRS2`|MPZXD)QahFJ;crg>!aq>Wa{yYOmG4Rkkx4 zyuLzsAq#)O2mkTtI5~ACFVUT{KcbZPujU$LnreJSGiCUtnGv?M-NqD^98>kSs<$RE z6Z^X!5dfW}Z5otba?RVz3~NdTa^;Y%{vRFxP$gK@I9v4Qp}7j94&5{~TtlIg-Td+= zCtLEn2>+N^mZjOOq~miMHzHZo>I}y$4tO|IB0^#9H4?TD+9Y5ZGdM3No8Gi>v@y$8d!0>LlaDe>+-kr30F0izzKlXWe9@fs z^I=0I=w`x_HWTbyt{Mlv)$#aOqn}(iW&RI|jCc|u^dJF&;3&=rSl29CFG@LRC07dg zlDDiz*pQYO+EKpT?99qeP%%vWYRUc@Vy73X6`@KG-Wpfw7Hf3V0j)ad4%sEYpTIGyj|4i zVI6-G2D*V$+t827SaSkbYkapGzsaTheJI1_qjy`gFd|z(sW!aXY5W@7Q0slX&ygHxd8PgfTR9bZM~(~wqvuU z?wQV$CtS^)kuW>pPkXf+t!CkU<28N?+9Q43&9yuh3V*JeLDaKVN1=zV5~4W>rqEN9 zK(*rLw7Ob@{tCD#RB^sGP~e8p8}-mYs0KvqsR0V^qP_6GR=&-YOj=H0kh0RnNN91| zK1;LnZA?dT8MmKvac85sTVf%j<%ZF>);jzT@#o>mUrb7aipg4E3P*Z7>)Sx<*sV9D z0ovhO&Ku8qZAQOMz%tVB8ARfJF4)lSL9z_}*>=nO;CT}&NFMarfS&8_)v1?}5x@tD zPp4a&)ku@OdEr$VJ?w_ICRG>xz-W&(y%r1!B?sw>M31v7r*)fySh%OnHg7 zlcAk6l;S~QX5H7ho6lL45YD4E(a^INEM9;A%RejD({=l~u4gloH(;bqf&rCKozMo* zCGl5za-fQ1x1HG8yKeg_-{8OJS);psUG#~jKY#Wep7a=So@?(0`_sac5)J!( zbro_RJ3h>=oJiJ>)qz65wvJhHvmr_WrLDXgzoo_YkBOpi2N#Ru{X3+Fol1jJq@3{l z`W{}am=$lDS)C!u(j+urcNT;9`AgsWX-8Chw>;c4+~WoiO-tjYwZtjkd8m&$QQ3!O z409q~$*Y*`zezDF*n6Ny@x*Zpypv|k%SF1t2>aU9(Oj2g%2cu6?Wuvdg?`*BqC z*&i%_^L!UjcO>}!-uS_jd2%eW+>PZdXezXjTDg3y-P-TamOGDd-fBO;{R2NO2a>r{ zwH&G;(=YAJRz{#-y;vT8F02#_=H;1Ba)~Y+;NC!>tK&`vnay!0JL`pKx9k2ygokTA zLqB=6Vv$5`SN@LDCX0B9)QajEai7CkclL+7k?h>nF_0}4KPm%r5-d}e8%?uiA20h7 zwr^n|T>kuHBHp}#K1BF;Dobihxc`I!TiBSFk94JR5xc4>)k>kMJu}-yqgxZi3-KW? zy~{1V4aAnata*((-N3=~m$diwSmHbL1hFVfOCYWK-X5@sJv{!#xL8f2J=j2GDV_rE z^E4M`2x1={j~_zXhjmYorrQe|dr%+qF2i#?eF~!HTVI&`&LSw9izu628d{(phR+xX z>VLQyf(^L2@L&wQh?7RkkyB%2>^V<(QJdk7G%BDmyaJmreWXl(JNGx!<*#2lcVy?9dX;}NgWBr(Qqk?T2Vd6NHP}cTA1>1l z*H7o$`C<5OCxq?3gG_lh;&n+5v7?@!92o1Z;$uC0*&OU=0d@VsbC!gPE_`H;NYH%? z>nI?#X-@Qg$d_s0TBiDtpK&--p+ypy(P(O`cZ;`9N`lzv^q+mkYipI4FB@YfUH`QR zgLGeK=otIe8YO59k3}ioT2hiUNtQkNC|yJy0Rm4x=nnbntHfO*43CeJZ`gx5AJ*e1 zUuIuyAMqxrxn{Q>D-zU~Z$-js9=ti5%8#2s*g)+=Uy^H`K&8iD{5MdZoS`Ko)j_MC zXoPwag~AZox3yqGa#Tt5EgaDvEfAE(3-LgPqir zHzGZJRqQxJ2(;>W`t3r@bL4u`s30Z%JkhsY#L}c`h-d`kglQxv5-QGi0@v@S$cwfm z+BP&LlBqy_WU@qk4=Yb>ZVA?`pWI+<_JJr7Yw)0#-*wGsxmiqr z2LCk><6GKOgG(3lf}_5)ZKx@h&?A4!A+Z7NHs0UJnA&5$i=uVhw2%?mQvxx0xTpZA zVGp3mh*Ce@F9h>au;IoSWx3#+Ij6hDNM?TT2Lck^tPXEaQ^7Rqc`HJuH9L;z#|{Xx z(kc&P*d1CtIJCh_9JXa_eSx5Tn||8L*4Ea5cdU-Pq4Q)AJYvRZQuodDG}F+9Ky~pF zv_mp5SqcKv27^;c-E@34ZB++V<6#m;r)|&xJRuXxm7g5z?>0)Kyb#^S4qpxy4xml7 zy*ZcN8DqiwAa~I`n_IimEv@c8+E?_Jec?@pje8%ixqBReQRY$Ls$M-HhfGb`uazB! zid09*(Y9?`ht{OQ`oNS_cwGX<5D`<4_%iojLEiynh(z9?ok zGDPZ)^alT$ngUMUPot(z1CdD^Rg|cj;+#VLSe#E+fgTS%=49 z9ygswLs5Fu|AHey1W?TcJVQIOsrQ3+^%N?VpujsE0vIQ0xU%F~nu5!;1L)P(tqm~4 z;}Py-wkkMO@>Sv`X=@DJ4=zXooX{2FN~o^Q#l0ctw_NN(aq@1)mHMlo`X*@X5&lOf zTfc%TG;G*;r~ynr8%@o0y0Z>&+w_EKu~krOfPsYDD#-{6YvyWN#zRGjcRNgh>`PD>VWkC(T>c3R zu48%C*3#ClA)R(hZdTxGLwdvBgG{sRr30VlGwF4YQQEBgM-Enbk>f$-^F3lC!dtd7 z8|m7YL_>4&-2G=pdGG)YDdc1pICiEUeaq%Q-qGj3`XT||;_+UjfyL<68_Al2kgF#~ zQGSQTE2`={Zx2Umr9uWab!8a z+^8Zz&g#k~QJ7AtO?YE|p?B%5#|JPCEA2ZP6|}%EaF%&`kB}8%ce4*Ku;Vn!uhqVq zsoG22#7dNmfgunOiDv&ySnT=qrvx6T!k~&tZCQ z64Z?l#(oTh_z!7dF?C0u>3ar;bCv%pdJC75!5+5nQBvwXyK?}`eC6*^*U^|ZsO{ENG|ezF^CwWX+0+QhJ0H)spi-GgwE{0dzK4p;>I(1PSGc0 zJ@|)k6amm@VKYbitcYhud-ohSgKrm)rNzfJ61PvJ&L(fXkYYv1shdpH4S7pN;wzP9 zt$K^C-rhVh*7;b6IKS0k>EQH$_H!-wJ+}&@L#yS0{aY z;j{A8!_I(gG6zqAeUVN67W?z(Pij^KGV^R)KwMlrP^DOC@%Yi|lvw>I3)$04;GbWy zTADfE_bqE9dZr!alHYvx#&L<*bZBIV@qm%y8_ye@Q0XO}YrqL6gKo8xrh#S>oE7Gb z=5PvIl|EbMt%5;d z$#(nsdqAci%k()P3!E7n7?!_KxQkfwlw*2wh9H(xZDm|8 zNAKz-(6I>-_*xTto82PXXIIvKn@8_CCi-RW4_T^J{@8vCgkSv9P@;xk)ZN<1aIXAa z_v-K&-nl8&;)tz&2ce?$A%c>gY8x@Od*oygTdf!@f0DBz=-ejL;PEXz>+O3pEiX76 zSrf@wPe1z{;jvEblb~J#X^xYEgtU?PYh7yiDVukW&2+a1{!0uVD$&f|?wW1f=$)IA zly85QZKY9kQ-w!pmQcpu9oc_q zLBJsbYVI{*d6aKj^!KFJ&69d^RbUahO@U-mBiiH4dZp9_-dloQ@*XX6s^;-AOo&UdWlj&4PD!9vxoy9V z#}=mSN<)9wea1;=z-)Q}=!}2&2J|1NG^4X`iu5&gq|{%+uEmzn%2pyYkaIU!`>tO* z5LJYQAc;;q9awg^j+xHsEV?tgw<$hy0)7PnLnGDpyB15@ z$saZgTsoC2u2{R-H`Qm9u9wc6AdplK;HfMr(6R2_IZl*#MOs9ABvT#Ddb;y^?B11nzOQh+9E>w7AH= z6SUQpi!{Y?IA#lTOq3pJ@t$BoJGYALwW84TtPP0=uZxh`$UH-`prL1cJXk$g+B@zQ zQf0z;=*JLl{pg3NePUIddEl$Qe-5iyGPH5x?6^`91MtTIZ^CHP1-x$bRSi*N%dWr3tE9 z^Q@cCXa(c%lPue(nwN&Apn8VeJbyix%Gjd*C=Pr&e=vdPZk_gc4Bpaaqr~c$@T_DE zEwj)=c#px0IiiJG6nU3m*9xqOJjt9(%Z`0`CqVrXdWWm=jN~8 zXYYy+o9Piu&Yo3CSpL;Vz4P`HEq)(SlrAB~v!)vvPm&b=B)@M;7QGf3=0KOxJgc*5 zw#H`6&k!_iClQDNq)8%EzpZX0jYemIk<2%xzNFIr;Fd1C|2V53Db|CuJNzSOd?QtX zE2~X=-jK8VzEp+QlD?k<4^o0^@DrBxL2-y)5u`~n z3({mFU2tc<0rGA|-@a0nCM_!XX@m@i$Em3X(pE#tnX%|n{O7!m#`rsv81=?;OZWlC z%77?04gpcOk1Z~br8h*ir11h#q-Z%?C}BS`;PVSYTO^0 z&RZ!KK}LmV*yRC>FV`(ozc7feaov>&OK*VV$CzQLdqTLeMc$ctX*S;of8J0qtE+*Q z#M@Hdk(RQ@ObQo*M4)#G!3Q3Ryj>}#ls)D$rx~q5sZc~P=EI~0=AJ5Jd~1)1$b2)Z zeF9c7is&qHFqJ|fx3xMRqiV&__TYM$1Ag>U`}K9Sw-IDTON zq136y$rN_kIiJ3t6a~cmk|?IHtXnop@!tA=5%95|#q_}fibK#a6v4%rKb2Wf89Rm9S1?{y#Tym;iwns59) zwVfvQ{`QHUD@hY?#OeKOUaC)=WV+vpfOIxucGrrxS7_-Sz3Z3n4X5_NFKY3vkXUcP z5w`z(8`(kkt9rV~s+R}8UI}r3e+NJu^CsAkjrp514l*MZm;TSe_hsDGp|Ru4D9`xT zR+CRt2A&U6P|g5vSx;5m4)w`II7E%8bTy4Pt7{m~95lG;62fXzQ3=OM!Ak?AOHXrw z8x0Xp_GL+RduZj=kZHLd@9>Ld7UPj!8pZ_m!C7EC{> z=_4HZ`BC~Suw#4Kb+odGup z4Gr!lr=s{4^=0cA@cynLn>EjvVGI@Mm`B_}uM?{bS^+2JUt}m)i6`crhYdSK|3YuB zGT@?J6+YO8S8SSRu~D%I!QPwT{MI%`RB>@Q1^1T0{f0Nm- zMubBhoJe4RoKW=NWD8#Ojb}=+0~7e5XU%DDv+UDO92@Th%R_=Kwi~htlY9|hDI=^4 z`rG=YfjDPJ|59Y*I9!kS_oegSGxcNqy)nZt&1+5m`Z%!e1B=J>c@hFU!2jK3-+7cYJ>Y4uU-aC7Ch7l70aNMmcQtZ&?9g&pyTfPWL*U4L}f zIK>F|Lw~g#xOf9B;i}IqsFv@IOep~IS+tTJUHn$_IC8@us4P?eWo7=;P7o_r?~9bbNh~CKChU%N zalcI)vQ)=6jb;8>3tDYfbUy;yMbA7AXTR~R2dQ0}7qATyL$saQyyXJy~E~i*_af4n4-+e zdL>rVn_uDID($)99J%)C4knE3u4S{xPs&Ixi=fw9frB!636{>SBuL(^8y7lLA-+dv zL6Le1H0U_yX=`j)8*#;56y$(nt$cibnsN+!qoQT^sCdIV$qRQKULPTcIS!j zGUwV)ayx(oz2`ef!7hn1g!j6gg^6#1_LT9@aHPBo>Hm@|A$e(_Udn0_=C9l{#;}&= zTIo=2@QA0ATvQD0*BmbS>4xUMBJG8`s&Mr6*<{hcdh03EI%?VZo}a4$$Z?$SWZ7Cz z=>`1toXK?!!GT}jHnes3%n~_R2XxaxieV9P?^7AR#u>LJ??3nKOUQ^8r=y3g8OY#$ z{U6%11x1NH`{MQIYd00Fzddkql1y?zEQqw?M;3k8VhYNr`=CHppA;UmxGaBeY=W3q zNj;VCv@0y4*_$2V|IJ3WAaq80<6cz?C(QrkaoYNHrT;ygxUlvy3kWY*t1iI^7jwq5 z6MvI$Eb+FI&(eLl%ekH`t`zJ>kUjEOTY{L68CUXZ_S)`fi{I})*h@pXMu1@WFRw4S z6jmWL@KR<6ZjB40`t7%~AB26FO!ZS|G|BaSIG2T^w8{%4$lJ9ggkOM|fYeQw4VG>a zmR1|TitrhE7m&L-XA$lNob+tNbxsJZabQ&YhpNZvS2tdrWs_=z*6f*sFhqeK+xlgA z1}HCtcrsqdoZT+|$umhhy?bDknPL&WeeL!?x(?|mDJp1Kpv57Qke>r4&9%zCM69__ zhpOdkwaYCPOGgnu`1-4 zS_4B8`_p>xQKeHTKHbt0UFVl$7T z`SXS0?_sa@5Q7|unNaZ>M%gjQo530WdO{E-FR=9TqMr7GZZ{@OtN&2-{>Iy=Clluh zu$hkr&k5;cx17Z<$lj_?p6S;J`Jk-DcR{XN`f6u~GKLPUW8?(caFl(Z=M0!Lw9c!o+NEzgUKNixc7&{S!u=t?0BKD} z@n!p<-&duM2h6+$2GYGqNKmg{;$7r>s+Wc}QEL zmvPD%c4Oeju2{bD}e`z+s&QA7I@C5pXy9?~c zn^X@<9fzLltre_WB4kG2(pbMC=T5M7i6pu~F=$jENQd`X`gZ#ls-l5!xl*{>ycz_h zm4X9w+x`swZj*5g7n0_6Js_*sj2WDKo^B~we(5xE;|fd{sG8FY*A`mG7^iRSpSK4d zOt)tk$0%tsKD#tBCcMu^i^e;bW8xRX+ca*cb&z_vKcO<^CwDFdaf>Uh=%qvHf6l*! z8<0hW=lO$nj9b;lSrztq#?S)5*!ixV#w12=T1F{Ig`lhrzf7q<_ zcQG3TVFmC1ZF{Ii~x*ONstxclHaZuFDzZxXc_MXiDOQ+YnKqN^UBW5x2$xv znr_$e$kv9)f~T2>puEFAhq^m9F&k!+^Hq$$nTdlyX-!~1l&z{B=gs0VD!Pkirh;z> zSK7|eKNTOBtT?1%B24%svL{O>@)aa$jC4xxM9*=DGj12(OF0$~lh$+)Uyf%E*q3K@ zO#&6e27yHPfHh5?fppJ@q^N|X|5YmGuWm^91|Hz^it@vlk^N@8EKe(!&-#QuW^ z|C~Yh;_qB$G$CR{Hkp2UuD;v~)4K2Sdp@YLYeR~M?yOY) z_D#3b^6jPG>5#{P7=7Sw5P+zZ#!Ute5r^DaQ>P3OAP$lHJ`L`#b7sGWz^3)Nbl6}I zY8Ox@3Ht`92@mPBG4v~YZO4@uQ)Eirup@$b*z_!|4mQPzMzo}8hloQEjXM6xdZsEO&;+@Ez&6SGNOEAyHxbbD}TYVS<%d`BQT3_^K-A}d1 z>O0_c=A1R3?(h%TKVSL7+qI}Aq19msL1+rUWeYFG+P~fy z0xl5h4NT%k6H6kF|n~kff$;aZkhoTDCRd%Mn*69LAMs0_X4A~UURrv|Ap3LeRFCr0vOqHWPw>N7E z%Cz>~?V!9IxF4JP_!)E8)0}inbnGnb3Qz6T9-H7AvB^tQ`(=yUgbYa{UX|^HXuB)@uesm8nAn>c=HAJ3Ee>oV}6U zF}xc2a)3D5kg>GT=+oRWSSDREZe^Dn0k0gVgJfAD!U2LSzaID|`*{5iU)IA+_S3l> zgt^W%ciY|cfxz3(r8S+z$1M_!x88?K+LlTY=gkTZAO+*AfEa;*I-F4pS$L zYThCHpnl*eu;;*HGz>?CKf?IUBEy(-3W}2zG4_y_cVISs#R3mKCw-CB+`fS+LG81+ zl(zD=pgDYQ*0R*-{(4QO6nklm!pK0)-dxkHz2EEVn`7lzutdwgmDY~$GUoSi^Sk-u zsP4<+iB&fVyCD+8rAFYryg-fyUaOAU?x(q?eH{*;dyroz}Z;JQ>-?1$DVj+-UnuDqhnHOJp2YpFgj&&3eah;KJ zi>NAH4`JQu9`Ccxnhx61hRHCu4{{Re diff --git a/wallet/crystals/400_phonebattery/themes/default/about.html b/wallet/crystals/400_phonebattery/themes/default/about.html index 9241a2b..458c909 100644 --- a/wallet/crystals/400_phonebattery/themes/default/about.html +++ b/wallet/crystals/400_phonebattery/themes/default/about.html @@ -20,9 +20,7 @@

About

Blog: https://hypernodes.bismuth.live/?p=1696
Public website: https://phone.batterylife.info


-
- -
+ diff --git a/wallet/crystals/400_phonebattery/themes/default/page3.html b/wallet/crystals/400_phonebattery/themes/default/page3.html index d6a15ea..a78f2da 100644 --- a/wallet/crystals/400_phonebattery/themes/default/page3.html +++ b/wallet/crystals/400_phonebattery/themes/default/page3.html @@ -40,6 +40,7 @@

Data Selection



+

   diff --git a/wallet/crystals/420_tesla/__init__.py b/wallet/crystals/420_tesla/__init__.py new file mode 100644 index 0000000..be79f4b --- /dev/null +++ b/wallet/crystals/420_tesla/__init__.py @@ -0,0 +1,183 @@ +""" +Tesla Crystal for Tornado wallet +""" + +import sys +import time +from os import path +from modules.basehandlers import CrystalHandler +from modules.i18n import get_dt_language +from modules.helpers import base_path +from modules.helpers import async_get_with_http_fallback + +sys.path.append('crystals/420_tesla') +from bismuthsimpleasset import BismuthSimpleAsset +from teslaapihandler import TeslaAPIHandler + +DEFAULT_THEME_PATH = path.join(base_path(), "crystals/420_tesla/themes/default") + +MODULES = {} + +__version__ = "1.0.0" + +class TeslaHandler(CrystalHandler): + def initialize(self): + # Parent init + super().initialize() + data = "" + self.bismuth_vars["extra"] = { + "header": "", + "footer": data, + } + reg = "tesla:register" + unreg = "tesla:unregister" + transfer = "tesla:transfer" + op_data = "tesla:battery" + + self.teslahandler = TeslaAPIHandler(self.bismuth,reg,unreg,op_data) + address = "Bis1TeSLaWhTC2ByEwZnYWtsPVK5428uqnL46" + thresholds = {"reg": 50} + checkfunc = {"f": self.teslahandler.checkID} + self.assethandler = BismuthSimpleAsset(self.bismuth,address,reg,unreg,transfer,thresholds,checkfunc) + + async def message_popup(self, params=None): + title = self.get_argument("title", default=None, strip=False) + message = self.get_argument("msg", default=None, strip=False) + type = self.get_argument("type", default=None, strip=False) + self.render("message_pop.html", bismuth=self.bismuth_vars, title=title, message=message, type=type) + + async def about(self, params=None): + namespace = self.get_template_namespace() + self.bismuth_vars["dtlanguage"] = get_dt_language(self.locale.translate) + kwargs = {"bismuth": self.bismuth_vars} + namespace.update(kwargs) + self.render("about.html", bismuth=self.bismuth_vars) + + async def fetch_asset_id(self, params=None): + """" + Fetch asset ID associated with email and password + """ + email = self.get_argument("email", default=None, strip=False) + password = self.get_argument("password", default=None, strip=False) + pwd = self.get_argument("pwd", default=None, strip=False) #For XOR + data = self.teslahandler.tesla_vins(email, password, pwd) + time.sleep(1) + self.render("json.html", data=data) + + async def fetch_api_data(self, params=None): + """ + Returns a dict with vehicle data for all VINs associated with email and password + """ + email = self.get_argument("email", default=None, strip=False) + password = self.get_argument("password", default=None, strip=False) + pwd = self.get_argument("pwd", default=None, strip=False) #For XOR + out = self.teslahandler.fetch_vehicle_data(email,password,pwd) + self.render("json.html", data=out) + + async def check_vin_registrant(self, params=None): + """ + Returns registrant given asset id (vin number in vin_input) + """ + vin = self.get_argument("vin_input", default=None, strip=False) + # First check if this is a valid VIN + data = self.teslahandler.checkVIN(vin) + if data != -1: + # Second check if active wallet address is registrant + data = -1 + registrant = self.assethandler.get_registrant(vin) + if registrant == self.bismuth.address: + data = 1 + self.render("json.html", data=registrant) + + async def check_vin_register(self, params=None): + """ + Checks if an asset id (VIN number) is valid and registered + """ + vin = self.get_argument("vin_input", default=None, strip=False) + # First check if this is a valid VIN + data = self.teslahandler.checkID(vin) + if data != -1: + # Second check if VIN is already registered + registrant = self.assethandler.get_registrant(vin) + if len(registrant) > 0: + data = -1 + self.render("json.html", data=data) + + async def check_vin_unregister(self, params=None): + """ + Unregisters VIN if valid and current address has previously registered it + """ + vin = self.get_argument("vin_input", default=None, strip=False) + # First check if this is a valid VIN + data = self.teslahandler.checkID(vin) + if data != -1: + # Second check if this account has registered this VIN + registrant = self.assethandler.get_registrant(vin) + if registrant != self.bismuth.address: + data = -1 + self.render("json.html", data=data) + + async def get_chain_data(self, params=None): + """ + Returns vehicle data as specified by 'variable' between start and end dates + Used for displaying data by DataTable and ChartJS + """ + vin = self.get_argument("vin", default=None, strip=False) + addresses = self.get_argument("address", default=None, strip=False) + variable = self.get_argument("variable", default=None, strip=False) + filter = self.get_argument("filter", default=None, strip=False) + range_unit = self.get_argument("range", default=None, strip=False) + temperature = self.get_argument("temperature", default=None, strip=False) + startdate = self.get_argument("startdate", default=None, strip=False) + enddate = self.get_argument("enddate", default=None, strip=False) + if variable == "battery_cycles": + out = self.teslahandler.get_cycle_data(vin,addresses,"battery_level",filter,range_unit,temperature,startdate,enddate) + else: + out = self.teslahandler.get_chain_data(vin,addresses,variable,filter,range_unit,temperature,startdate,enddate) + self.render("json.html", data=out) + + async def get_all_asset_ids(self, params=None): + asset_search = self.get_argument("asset_search", default=None, strip=False) + out = self.assethandler.get_all_asset_ids(asset_search) + self.render("json.html", data=out) + + async def page1(self, params=None): + namespace = self.get_template_namespace() + self.bismuth_vars["dtlanguage"] = get_dt_language(self.locale.translate) + kwargs = {"bismuth": self.bismuth_vars} + namespace.update(kwargs) + self.render("page1.html", bismuth=self.bismuth_vars) + + async def page2(self, params=None): + namespace = self.get_template_namespace() + self.bismuth_vars["dtlanguage"] = get_dt_language(self.locale.translate) + kwargs = {"bismuth": self.bismuth_vars} + namespace.update(kwargs) + self.render("page2.html", bismuth=self.bismuth_vars) + + async def page3(self, params=None): + namespace = self.get_template_namespace() + self.bismuth_vars["dtlanguage"] = get_dt_language(self.locale.translate) + kwargs = {"bismuth": self.bismuth_vars} + namespace.update(kwargs) + self.render("page3.html", bismuth=self.bismuth_vars) + + async def get(self, command=""): + command, *params = command.split("/") + if not command: + command = "about" + await getattr(self, command)(params) + + async def post(self, command=""): + command, *params = command.split("/") + if not command: + command = "about" + await getattr(self, command)(params) + + def get_template_path(self): + """Override to customize template path for each handler.""" + return DEFAULT_THEME_PATH + + def static(self): + """Defining this method will automagically create a static handler pointing to local /static crystal dir""" + pass diff --git a/wallet/crystals/420_tesla/about.json b/wallet/crystals/420_tesla/about.json new file mode 100644 index 0000000..52bf032 --- /dev/null +++ b/wallet/crystals/420_tesla/about.json @@ -0,0 +1,8 @@ +{ + "author": "GH2", + "description": "Crystal for logging data from Tesla vehicles", + "email": "N/A", + "version": "1.0.0", + "date": "2020-04-05", + "url":"https://hypernodes.bismuth.live" +} diff --git a/wallet/crystals/420_tesla/bismuthsimpleasset.py b/wallet/crystals/420_tesla/bismuthsimpleasset.py new file mode 100644 index 0000000..2106cb8 --- /dev/null +++ b/wallet/crystals/420_tesla/bismuthsimpleasset.py @@ -0,0 +1,226 @@ +from collections import OrderedDict + +#Usage: +#from bismuthsimpleasset import BismuthSimpleAsset +#from bismuthclient import bismuthclient +# +#def asset_validity_function(asset_id): +# return 1 +# +#servers=["wallet2.bismuth.online:8150"] +#bismuth_client = bismuthclient.BismuthClient(verbose=False, servers_list=servers) +# +#register = "myapp:register" +#unregister = "myapp:unregister" +#transfer = "myapp:transfer" +#address = "3c59ca96b59fe713e3f2e5f27abf7fe809383fccd3d9ff96ffdfabce" +#thresholds = {"reg": 9.5} # Price to register, for spam filtering +#checkfunc = {"f": asset_validity_function} #Must be supplied by user +#assethandler = BismuthSimpleAsset(bismuth_client,address,register, +# unregister,transfer,thresholds,checkfunc) +# +#out1 = assethandler.get_all_asset_ids() +#N = out1["total"] +#if N>0: +# print("All registered asset ids={}".format(out1)) +# for i in range(0,N): +# asset_id = out1["asset_id"][str(i)] +# address = assethandler.get_registrant(asset_id) +# print("Current registrant of {} = {}".format(asset_id,address)) +#else: +# print("There are no '{}' transactions with Bismuth recipient '{}'".format(register,address)) + +class BismuthSimpleAsset(): + + def __init__(self,bismuth,address,reg,unreg,transfer,thresholds,checkfunc): + self.bismuth = bismuth + self.SERVICE_ADDRESS = address + self.register = reg + self.unregister = unreg + self.transfer = transfer + self.thresholds = thresholds #Amount required to register + self.checkfunc = checkfunc #User-supplied function to check for valid ids + + def get_registrant(self,asset_id): + """ + Returns the current registrant of a specified asset id + """ + regs = self.__get_reg_unreg_sorted(asset_id) + registrant = self.__get_registrant_from_regs(regs) + return registrant + + def get_all_asset_ids(self,asset_search): + """ + Returns a dict with all valid asset ids submitted to the SERVICE_ADDRESS. + An asset id can be associated with multiple accounts, if registered and + unregistered multiple times. Only assets with substring = asset_search + are returned. + """ + data = {} + command = "addlistop" + to = self.SERVICE_ADDRESS + t_start = 0 + t_end = 9e10 + L = len(asset_search) + + op = self.register #Check for registrations + bismuth_params = [to,op,self.thresholds["reg"],False,False,t_start,t_end] + bisdata = self.bismuth.command(command, bismuth_params) + j = 0 + for i in range(0,len(bisdata)): + asset_id = bisdata[i][11] + if (L==0) or (asset_id.find(asset_search)>=0): + data[j] = {} + data[j]["asset_id"] = bisdata[i][11] + data[j]["from"] = bisdata[i][2] + data[j]["timestamp"] = bisdata[i][1] + data[j]["type"] = "reg" + j = j + 1 + + op = self.unregister #Check for unregistrations + bismuth_params = [to,op,0,False,False,t_start,t_end] + bisdata = self.bismuth.command(command, bismuth_params) + for i in range(0,len(bisdata)): + asset_id = bisdata[i][11] + if (L==0) or (asset_id.find(asset_search)>=0): + data[j] = {} + data[j]["asset_id"] = bisdata[i][11] + data[j]["from"] = bisdata[i][2] + data[j]["timestamp"] = bisdata[i][1] + data[j]["type"] = "unreg" + j = j + 1 + + op = self.transfer #Check for transfers + bismuth_params = [to,op,0,False,False,t_start,t_end] + bisdata = self.bismuth.command(command, bismuth_params) + for i in range(0,len(bisdata)): + try: + #Openfield = Comma separated id,recipient for transfers + [asset_id,recipient] = bisdata[i][11].split(",",1) + if (L==0) or (asset_id.find(asset_search)>=0): + data[j] = {} + data[j]["asset_id"] = asset_id + data[j]["from"] = bisdata[i][2] + data[j]["timestamp"] = bisdata[i][1] + data[j]["type"] = "unreg" + j = j + 1 + data[j] = {} + data[j]["asset_id"] = asset_id + data[j]["from"] = recipient + data[j]["timestamp"] = bisdata[i][1] + 0.001 + data[j]["type"] = "reg" + j = j + 1 + except: + pass + + out = self.__get_reg_unreg_all_sorted(data) + out = self.__get_all_valid_asset_ids(out) + return out + + def __get_all_valid_asset_ids(self,regs): + """ + Returns a dict with all valid asset ids and associated addresses in input dict regs + """ + out = {} + asset_ids = {} + j = 0 + for item in regs: + asset_id = item[1]["asset_id"] + check = self.checkfunc["f"](asset_id) #Checks if valid asset_id + if check == 1: + if item[1]['type'] == "reg": + try: + N = len(out[asset_id]["address"]) + if len(out[asset_id]["address"][N-1]) == 0: + out[asset_id]["address"][N-1] = item[1]["from"] + except: + out[asset_id] = {} + out[asset_id]["address"] = [] + out[asset_id]["address"].append(item[1]["from"]) + asset_ids[str(j)] = asset_id + j = j + 1 + elif item[1]['type'] == "unreg": + try: + N = len(out[asset_id]["address"]) + if out[asset_id]["address"][N-1] == item[1]["from"]: + out[asset_id]["address"].append("") + except: + pass + + # Remove duplicates and sort by asset_id + sorted_x = sorted(asset_ids.items(), key=lambda x: x[1]) + asset_ids = {} + i = 0 + for item in sorted_x: + asset_id = item[1] + asset_ids[str(i)] = asset_id + i = i + 1 + addr = out[asset_id]["address"] + out[asset_id]["address"] = list(OrderedDict.fromkeys(addr)) + + out["asset_id"] = asset_ids + out["total"] = j + return out + + def __get_reg_unreg_all_sorted(self,data): + """ + Returns a dict of reg and unreg data, sorted by timestamp, multiple asset_id numbers + It is assumed that the input data dict contains the following fields: + timestamp, from, asset_id, type + where from is the sender address and type is "reg" or "unreg" + """ + regs = {} + for i in range(0,len(data)): + regs[data[i]['timestamp']]={'from': data[i]['from'], 'asset_id': data[i]['asset_id'], + 'type': data[i]['type']} + + out = sorted(regs.items(), key=lambda x: x[0]) + return out + + def __get_reg_unreg_sorted(self,asset_id): + """ + Returns a dict of reg, unreg and transfer data, sorted by timestamp, for a single asset_id + """ + regs = {} + command = "listexactopdata" + bismuth_params = [self.register, asset_id] + data = self.bismuth.command(command, bismuth_params) + for i in range(0,len(data)): + regs[data[i][1]]={'from': data[i][2], 'type': 'reg'} + + bismuth_params = [self.unregister, asset_id] + data = self.bismuth.command(command, bismuth_params) + for i in range(0,len(data)): + regs[data[i][1]]={'from': data[i][2], 'type': 'unreg'} + + command = "addlistop" + t_start = 0 + t_end = 9e10 + to = self.SERVICE_ADDRESS + op = self.transfer + bismuth_params = [to,op,self.thresholds["reg"],False,False,t_start,t_end] + data = self.bismuth.command(command, bismuth_params) + for i in range(0,len(data)): + [id,recipient]=data[i][11].split(",",1) + if id == asset_id: + regs[data[i][1]]={'from': data[i][2], 'type': 'unreg'} + regs[data[i][1]+0.001]={'from': recipient, 'type': 'reg'} + + out = sorted(regs.items(), key=lambda x: x[0]) + return out + + def __get_registrant_from_regs(self,regs): + """ + Returns the current registrant in the input dict regs. + Handles multiple register and unregister events. + """ + registrant = "" + for item in regs: + if item[1]['type'] == "reg": + if len(registrant) == 0: + registrant = item[1]['from'] + elif item[1]['type'] == "unreg": + if item[1]['from'] == registrant: + registrant = "" + + return registrant diff --git a/wallet/crystals/420_tesla/mypolyfit.py b/wallet/crystals/420_tesla/mypolyfit.py new file mode 100644 index 0000000..b370f9b --- /dev/null +++ b/wallet/crystals/420_tesla/mypolyfit.py @@ -0,0 +1,82 @@ +# Makes polyfit and interpolate available without having to install the entire numpy lib +# Date: 2020-04-26, GH2 +# + +def polyfit(x,y,degree,filter): + w = [] + M = float(filter)*max(y) + N = len(y) + Y = [[y[row] for col in range(1)] for row in range(N) if y[row]>M] + NY = len(Y) + if NY>=degree: + A = [[x[row]**(degree-1-col) for col in range(degree)] for row in range(N) if y[row]>M] + AT = matrix_transpose(A) + ATA = matrix_mult(AT,A) + b = matrix_mult(AT,Y) + w = gauss(ATA,b) #Weights found by pseudo-inverse of A*w = Y + return w + +def interpolate(xmin,xmax,N,w): + out = {} + out["x"] = [] + out["y"] = [] + try: + Nw = len(w) + if (xmax>xmin) and (N>1) and (Nw>1): + stepsize = (xmax - xmin) / (N-1) + for i in range(N): + x = xmin + i*stepsize + out["x"].append(x) + temp = 0 + for j in range(Nw): + temp += (x**(Nw-1-j))*w[j] + out["y"].append(temp) + except: + pass + return out + +def matrix_transpose(A): + N = len(A) + M = len(A[0]) + AT = [[A[col][row] for col in range(N)] for row in range(M)] + return AT + +def matrix_mult(X,Y): + result = [[sum(a*b for a,b in zip(X_row,Y_col)) for Y_col in zip(*Y)] for X_row in X] + return result + +def gauss(ATA,b): + # Assemble A from ATA and b + n = len(ATA) + A = [[0 for col in range(n+1)] for row in range(n)] + for i in range(0,n): + for j in range(0,n): + A[i][j] = ATA[i][j] + A[i][n] = b[i][0] + + # From here: https://martin-thoma.com/solving-linear-equations-with-gaussian-elimination + n = len(A) + for i in range(0, n): + maxEl = abs(A[i][i]) + maxRow = i + for k in range(i + 1, n): + if abs(A[k][i]) > maxEl: + maxEl = abs(A[k][i]) + maxRow = k + for k in range(i, n + 1): + tmp = A[maxRow][k] + A[maxRow][k] = A[i][k] + A[i][k] = tmp + for k in range(i + 1, n): + c = -A[k][i] / A[i][i] + for j in range(i, n + 1): + if i == j: + A[k][j] = 0 + else: + A[k][j] += c * A[i][j] + x = [0 for i in range(n)] + for i in range(n - 1, -1, -1): + x[i] = A[i][n] / A[i][i] + for k in range(i - 1, -1, -1): + A[k][n] -= A[k][i] * x[i] + return x diff --git a/wallet/crystals/420_tesla/rainflow.py b/wallet/crystals/420_tesla/rainflow.py new file mode 100644 index 0000000..ff5c7d4 --- /dev/null +++ b/wallet/crystals/420_tesla/rainflow.py @@ -0,0 +1,178 @@ +# coding: utf-8 +""" +Implements rainflow cycle counting algorythm for fatigue analysis +according to section 5.4.4 in ASTM E1049-85 (2011). +""" +from __future__ import division +from collections import deque, defaultdict +import math + +#try: +# from importlib import metadata as _importlib_metadata +#except ImportError: +# import importlib_metadata as _importlib_metadata +# +#__version__ = _importlib_metadata.version("rainflow") + + +def _get_round_function(ndigits=None): + if ndigits is None: + def func(x): + return x + else: + def func(x): + return round(x, ndigits) + return func + + +def reversals(series): + """Iterate reversal points in the series. + + A reversal point is a point in the series at which the first derivative + changes sign. Reversal is undefined at the first (last) point because the + derivative before (after) this point is undefined. The first and the last + points are treated as reversals. + + Parameters + ---------- + series : iterable sequence of numbers + + Yields + ------ + Reversal points as tuples (index, value). + """ + series = iter(series) + + x_last, x = next(series), next(series) + d_last = (x - x_last) + + yield 0, x_last + for index, x_next in enumerate(series, start=1): + if x_next == x: + continue + d_next = x_next - x + if d_last * d_next < 0: + yield index, x + x_last, x = x, x_next + d_last = d_next + yield index + 1, x_next + + +def extract_cycles(series): + """Iterate cycles in the series. + + Parameters + ---------- + series : iterable sequence of numbers + + Yields + ------ + cycle : tuple + Each tuple contains (range, mean, count, start index, end index). + Count equals to 1.0 for full cycles and 0.5 for half cycles. + """ + points = deque() + + def format_output(point1, point2, count): + i1, x1 = point1 + i2, x2 = point2 + rng = abs(x1 - x2) + mean = 0.5 * (x1 + x2) + return rng, mean, count, i1, i2 + + for point in reversals(series): + i, x = point + points.append(point) + + while len(points) >= 3: + # Form ranges X and Y from the three most recent points + x1, x2, x3 = points[-3][1], points[-2][1], points[-1][1] + X = abs(x3 - x2) + Y = abs(x2 - x1) + + if X < Y: + # Read the next point + break + elif len(points) == 3: + # Y contains the starting point + # Count Y as one-half cycle and discard the first point + yield format_output(points[0], points[1], 0.5) + points.popleft() + else: + # Count Y as one cycle and discard the peak and the valley of Y + yield format_output(points[-3], points[-2], 1.0) + last = points.pop() + points.pop() + points.pop() + points.append(last) + else: + # Count the remaining ranges as one-half cycles + while len(points) > 1: + yield format_output(points[0], points[1], 0.5) + points.popleft() + + +def count_cycles(series, ndigits=None, nbins=None, binsize=None): + """Count cycles in the series. + + Parameters + ---------- + series : iterable sequence of numbers + ndigits : int, optional + Round cycle magnitudes to the given number of digits before counting. + Use a negative value to round to tens, hundreds, etc. + nbins : int, optional + Specifies the number of cycle-counting bins. + binsize : int, optional + Specifies the width of each cycle-counting bin + + Arguments ndigits, nbins and binsize are mutually exclusive. + + Returns + ------- + A sorted list containing pairs of range and cycle count. + The counts may not be whole numbers because the rainflow counting + algorithm may produce half-cycles. If binning is used then ranges + correspond to the right (high) edge of a bin. + """ + if sum(value is not None for value in (ndigits, nbins, binsize)) > 1: + raise ValueError( + "Arguments ndigits, nbins and binsize are mutually exclusive" + ) + + counts = defaultdict(float) + cycles = ( + (rng, count) + for rng, mean, count, i_start, i_end in extract_cycles(series) + ) + + if binsize is not None: + for rng, count in cycles: + n = math.ceil(rng / binsize) + counts[n * binsize] += count + + rng = binsize + while rng < max(counts): + counts.setdefault(rng, 0.0) + rng += binsize + + elif nbins is not None: + binsize = (max(series) - min(series)) / nbins + for rng, count in cycles: + n = math.ceil(rng / binsize) + counts[n * binsize] += count + + for i in range(nbins): + rng = (i + 1) * binsize + counts.setdefault(rng, 0.0) + + elif ndigits is not None: + round_ = _get_round_function(ndigits) + for rng, count in cycles: + counts[round_(rng)] += count + + else: + for rng, count in cycles: + counts[rng] += count + + return sorted(counts.items()) diff --git a/wallet/crystals/420_tesla/static/footer.js b/wallet/crystals/420_tesla/static/footer.js new file mode 100644 index 0000000..2d1db09 --- /dev/null +++ b/wallet/crystals/420_tesla/static/footer.js @@ -0,0 +1,27 @@ +$(document).ready(function() { + try { + display_mode(localStorage.getItem("tesla_mode")); + } catch(e) {} + try { //Page1 and 2 + if($("#tesla_pwd").val() === "") { + $("#tesla_pwd").val(localStorage.getItem("tesla_pwd")); + } + } catch(e) {} + try { //Page2 + if($("#tesla_email").val() === "") { + $("#tesla_email").val(localStorage.getItem("tesla_email")); + $("#tesla_password").val(localStorage.getItem("tesla_password")); + a = localStorage.getItem("tesla_range"); + $("#range option:contains(", a, ")").attr('selected', 'selected'); + a = localStorage.getItem("tesla_temperature"); + $("#temperature option:contains(", a, ")").attr('selected', 'selected'); + } + } catch(e) {} + try { //Page3 + if(localStorage.getItem("tesla_startdate") === null) { + localStorage.setItem("tesla_startdate", "2020-01-01"); + localStorage.setItem("tesla_enddate", today()); + localStorage.setItem("tesla_pdfimage", "car.png"); + } + } catch(e) {} +}); diff --git a/wallet/crystals/420_tesla/static/logo.png b/wallet/crystals/420_tesla/static/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..ad638064a29501c7b4dae5c32ab6a87f2420f18f GIT binary patch literal 23697 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 literal 0 HcmV?d00001 diff --git a/wallet/crystals/420_tesla/static/tesla.js b/wallet/crystals/420_tesla/static/tesla.js new file mode 100644 index 0000000..12efa04 --- /dev/null +++ b/wallet/crystals/420_tesla/static/tesla.js @@ -0,0 +1,500 @@ +// Storage for (multiple) vehicle data +var vehicle_data; +var plottype_desc = ["Line", "Line (Filled)", "Bar"]; +var plottype_opt = ["line","line","bar"]; +var plotoptions_desc = ["Battery Level (%)", "Usable Battery Level (%)", "Battery Range", "Ideal Battery Range", "Estimated Battery Range", "Last Charge Current Request (A)", "Last Charge Energy Added (kWh)", "Last Charge Range Added Ideal", "Last Charge Range Added Rated", "Inside Temperature", "Outside Temperature", "Odometer", "Estimated Max Range","Estimated Max Range vs. Odometer", "Number of Battery Cycles"]; +var plotoptions_opt = ["battery_level", "usable_battery_level", "battery_range", "ideal_battery_range", "est_battery_range", "charge_current_request", "charge_energy_added", "charge_miles_added_ideal", "charge_miles_added_rated", "inside_temp", "outside_temp", "odometer", "est_max_range","max_range_vs_odometer", "battery_cycles"]; +var tablevar_desc = ["Estimated Max Range","Car Version","Sum Charge Energy Added (kWh)","Distance Driven", "Number of Battery Cycles"]; +var tablevar_opt = ["est_max_range","car_version","charge_energy_added","odometer","battery_cycles"]; +var temperature_desc = ["Celsius","Fahrenheit"]; +var temperature_opt = ["C","F"]; +var range_desc = ["Kilometers","Miles"]; +var range_opt = ["km","mi"]; +var PDFOptions_desc = ["A4 + Portrait", "A4 + Landscape", "US Legal + Portrait", "US Legal + Landscape"]; +var PDFOptions_opt = ["1","2","3","4"]; +var filteroptions_desc = ["1%", "2%", "3%", "4%", "5%", "10%", "15%", "20%", "25%", "All"]; +var filteroptions_opt = ["0.99", "0.98", "0.97", "0.96", "0.95", "0.90", "0.85", "0.80", "0.75", "0.0"]; + +// Start of function declarations + +function display_mode(mode) { + //Defines Day or Night Mode + localStorage.setItem("tesla_mode",mode); + if(mode == "Day") { + var bg = "#ffffff"; + var bg_card = "#eeeeee"; + var fg = "#000000"; + } else { + var bg = "#192035"; + var bg_card = "#202940"; + var fg = "#8b92a9"; + } + document.body.style.background = bg; + + var elements = document.getElementsByClassName("card"); + for (var i = 0; i < elements.length; i++) { + elements[i].style.backgroundColor=bg_card; + } + var elements = document.getElementsByClassName("input"); + for (var i = 0; i < elements.length; i++) { + elements[i].style.backgroundColor=bg_card; + } + var elements = document.getElementsByClassName("card-body"); + for (var i = 0; i < elements.length; i++) { + elements[i].style.color=fg; + } +} + +function getColors() { + out = "style='background-color:#202940;color:#8b92a9;'"; + mode = localStorage.getItem("tesla_mode"); + if(mode == "Day") { + out = "style='background-color:#eeeeee;color:#000000;'"; + } + return out; +} + +function getbgColor() { + out = "#202940;"; + mode = localStorage.getItem("tesla_mode"); + if(mode == "Day") { + out = "#eeeeee;"; + } + return out; +} + +function FetchVIN() { + //Fetches VIN numbers from Tesla API and displays in $('#vin_input') + var xsrf = $("[name='_xsrf']").val(); + message_post('Tesla Account','Checking credentials','info'); + + email = $("#tesla_email").val(); + password = $("#tesla_password").val(); + pwd = $("#tesla_pwd").val(); + + localStorage.setItem("tesla_email", email); + localStorage.setItem("tesla_password", password); + localStorage.setItem("tesla_pwd", pwd); + + $.post('fetch_asset_id', { pwd: pwd, email: email, password: password, _xsrf: xsrf }, function(text){ + text = text.replace(/'/g,'"'); + text = text.replace(/"/g,'"'); + var data = JSON.parse(text); + if(data.count>0) { + $('#vin_input').html(''); + for(i=0; i').text(value).attr('value', value)); + } + var x = document.getElementById("div_reg"); + x.style.display = "block"; + } else { + message_post('Tesla Account','Invalid credentials','warning'); + } + }); +} + +function range(miles) { + //Displays range (miles or km) in $("#range") + out = miles; + var range_select = $("#range option:selected").index(); + if(range_select == 0) { + out = miles_to_km(out); + } + return out; +} + +function range2(miles) { + //Displays range (miles or km) in $("#range") + out = miles; + var rangeval = getLocal("tesla_range",range_opt[0]); + if(rangeval == "km") { + out = miles_to_km(out); + } + return out; +} + +function temperature(celsius) { + //Displays temperature (C or F) in $("#temperature") + var temp_select = $("#temperature option:selected").index(); + out = celsius; + if(temp_select == 1) { + out = celsius_to_fahrenheit(celsius); + } + return out; +} + +function updateHTML(vin) { + //Displays Tesla API data on Page2 + try { + var ts = new Date(vehicle_data[vin].timestamp); + $("#battery_level").html(vehicle_data[vin].battery_level); + $("#battery_range").html(range(vehicle_data[vin].battery_range)); + $("#usable_battery_level").html(vehicle_data[vin].usable_battery_level); + $("#charge_current_request").html(vehicle_data[vin].charge_current_request); + $("#charge_energy_added").html(vehicle_data[vin].charge_energy_added); + $("#charge_miles_added_ideal").html(range(vehicle_data[vin].charge_miles_added_ideal)); + $("#charge_miles_added_rated").html(range(vehicle_data[vin].charge_miles_added_rated)); + $("#ideal_battery_range").html(range(vehicle_data[vin].ideal_battery_range)); + $("#est_battery_range").html(range(vehicle_data[vin].est_battery_range)); + $("#inside_temp").html(temperature(vehicle_data[vin].inside_temp)); + $("#outside_temp").html(temperature(vehicle_data[vin].outside_temp)); + $("#odometer").html(range(vehicle_data[vin].odometer)); + $("#car_version").html(sanitize(vehicle_data[vin].car_version)); + $("#battery_type").html(vehicle_data[vin].battery_type); + $("#timestamp").html(ts.toLocaleString()); + } catch(e) { + } +} + +function teslaSubmit() { + //Submit tesla:battery data to chain + var xsrf = $("[name='_xsrf']").val(); + var address = "Bis1TeSLaWhTC2ByEwZnYWtsPVK5428uqnL46"; + var operation = "tesla:battery"; + var data = vehicle_data; + delete(data.total); + var out = JSON.stringify(data); + var vin_input = $("#selectBox option:selected").text(); + + //If the account can unregister, it can also submit data + $.post('check_vin_unregister', { vin_input: vin_input, _xsrf: xsrf }, function(data){ + if(data != -1) { + send(address, 10.0, operation, out); + } else { + message_post("Data submission not possible.","Check if the current wallet address has previously registered this VIN: " + vin_input,"warning"); + } + }); +} + +function FetchVehicleData() { + //Fetches vehicle data and stores in variable vehicle_data, updates display on Page2 + var xsrf = $("[name='_xsrf']").val(); + message_post('Tesla Account','Checking your credentials and fetching data. Wait typically 10-30 seconds.','info'); + + email = $("#tesla_email").val(); + password = $("#tesla_password").val(); + pwd = $("#tesla_pwd").val(); + localStorage.setItem("tesla_email", email); + localStorage.setItem("tesla_password", password); + localStorage.setItem("tesla_pwd", pwd); + localStorage.setItem("tesla_range", $("#range option:selected").val()); + localStorage.setItem("tesla_temperature", $("#temperature option:selected").val()); + + $.post('fetch_api_data', { email: email, password: password, pwd: pwd, _xsrf: xsrf }, function(text){ + text = text.replace(/'/g,'"'); + text = text.replace(/"/g,'"'); + var data = JSON.parse(text); + if(data.total>0) { + var html = ""); + $("#p_selectBox").html(html); + vehicle_data = data; + changeFunc(); + } + }); +} + +function RegisterVehicle() { + //Registers the VIN number on chain, if no one else has done it previously + var to = "Bis1TeSLaWhTC2ByEwZnYWtsPVK5428uqnL46"; + var operation = "tesla:register"; + var vin_input = document.getElementById("vin_input").value + var xsrf = $("[name='_xsrf']").val(); + $.post('check_vin_register', { vin_input: vin_input, _xsrf: xsrf }, function(data){ + if(data != -1) { + send(to, 50.0, operation, vin_input); + } else { + message_post("Invalid VIN or already registered.",vin_input,"warning"); + } + }); +} + +function UnregisterVehicle() { + //Unregisters the VIN number to allow others to take it over + var to = "Bis1TeSLaWhTC2ByEwZnYWtsPVK5428uqnL46"; + var operation = "tesla:unregister"; + var vin_input = document.getElementById("vin_input").value + var xsrf = $("[name='_xsrf']").val(); + $.post('check_vin_unregister', { vin_input: vin_input, _xsrf: xsrf }, function(data){ + if(data != -1) { + send(to, 0.0, operation, vin_input); + } else { + message_post("Unregister not possible.","Check if the current wallet address has previously registered this VIN: " + vin_input,"warning"); + } + }); + +} + +function changeFunc() { + //Updates table on Page2 + var vin = $("#selectBox option:selected").text(); + updateHTML(vin); + $("#table1").show(); +} + +function updateLocalStorage() { + //Stores user input selections in localStorage + localStorage.setItem("tesla_startdate", $("#startdate").val()); + localStorage.setItem("tesla_enddate", $("#enddate").val()); + localStorage.setItem("tesla_pdfimage", $("#pdf_image").val()); +} + +function showTable() { + //Fetches vehicle data for a given VIN and displays DataTable on Page3 + var xsrf = $("[name='_xsrf']").val(); + var variable = getLocal("tesla_tablevar",tablevar_opt[0]); + var variable_desc = getLocal("tesla_tablevar_desc",tablevar_desc[0]); + var temperature = getLocal("tesla_temperature",temperature_opt[0]); + var range = getLocal("tesla_range",range_opt[0]); + var pdfoptions = getLocal("tesla_PDFOptions",PDFOptions_opt[0]); + var pdfoptions_desc = getLocal("tesla_PDFOptions_desc",PDFOptions_desc[0]); + + var selected_id = $("#selected_id").val().split(","); + var asset_id = selected_id[0]; + var address = selected_id[1]; + + var pdfimage = $("#pdf_image").val(); + var d0 = $("#startdate").val(); + var d1 = $("#enddate").val(); + var mysum = 0.0; + + if(variable_desc.indexOf("Temperature")>=0) { + variable_desc = variable_desc + " (deg " + getLocal("tesla_temperature",temperature_opt[0]) + ")"; + } else if((variable_desc.indexOf("Distance")>=0) || (variable_desc.indexOf("Range")>=0)) { + variable_desc = variable_desc + " (" + getLocal("tesla_range",range_opt[0]) + ")"; + } + + orientation = "portrait"; + pageSize = "A4"; + if((pdfoptions == 2) || (pdfoptions == 4)) { + orientation = "landscape"; + } + if((pdfoptions == 3) || (pdfoptions == 4)) { + pageSize = "LEGAL"; + } + updateLocalStorage(); + $(".content").css({"cursor":"wait"}); + $('#table1').DataTable().clear().destroy(); + $('#table1').show(); + + img = new Image(); + img.onload = function(){ + var ctx = $("#canvas")[0].getContext("2d"); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(img, 0, 0); + var dataURL = $("#canvas")[0].toDataURL(); + var table = $('#table1').DataTable({"pageLength": 25, dom: 'Bfrtip', buttons: [ { extend: 'pdfHtml5', orientation: orientation, pageSize: pageSize, title: 'Vehicle: ' + asset_id, customize: function ( doc ) { doc.content.splice( 1, 0, { margin: [ 0, 0, 0, 12 ], alignment: 'center', image: dataURL });} }, { extend: 'csvHtml5', title: asset_id } ], retrieve: true, columnDefs: [{ targets: -1, className: 'dt-body-right' } ] }); + + $.post('get_chain_data', { vin: asset_id, address: address, variable: variable, range: range, temperature: temperature, startdate: d0, enddate: d1, _xsrf: xsrf }, function(text){ + text = text.replace(/'/g,'"'); + text = text.replace(/"/g,'"'); + var data = JSON.parse(text); + var xname = table.column(0).header(); + var column = table.column(1).header(); + if(variable == "battery_cycles") { + $(xname).html("Cycle Percentage Levels"); + } else { + $(xname).html("Date/Time"); + } + $(column).html(variable_desc); + for(i=0; i0) { + table.row.add(["Full Cycle Equivalent", data.full_cycle_equivalent]); + } + } + table.draw(); + $(".content").css({"cursor":"default"}); + }); + } + imgFile = "static/" + pdfimage; + if(fileExists(imgFile)) { + img.src = imgFile; + } else { + img.src = "static/pdflogo.png"; + } +} + +function updateSelect2() { + $('#selectBox').select2( { width: '100%', theme: 'bootstrap' } ); +} + +function radioClick(id_array,desc,val,i,N) { + var id = id_array.split(","); + localStorage.setItem(id[0], desc); + localStorage.setItem(id[1], val); + for(j=0; j " + desc[i]; + if(id[1] != "tesla_filteroptions") { + out = out + "
"; + } else { + out = out + " "; + if(i==4) { + out = out + "
"; + } + } + } + return out; +} + +function searchVehicles() { + //Searches for all vehicles on chain and displays menu on Page3 + var xsrf = $("[name='_xsrf']").val(); + var asset_search = $("#asset_search").val(); + + $.post('get_all_asset_ids', { asset_search: asset_search, _xsrf: xsrf }, function(text){ + text = text.replace(/'/g,'"'); + text = text.replace(/"/g,'"'); + var data = JSON.parse(text); + if(data.total>0) { + var html = ("

"); + html = html.concat("
Vehicle IDAddress
"); + html = html.concat("
"); + html = html.concat("
Selected ID: 
Start Date: "); + html = html.concat("
End Date:"); + html = html.concat("

"); + html = html.concat("Select Plot Variable:
", radio(plotoptions_desc,plotoptions_opt,["tesla_plotoptions_desc", "tesla_plotoptions"]), "
"); + html = html.concat("Range Filter for Curve Fit:
", radio(filteroptions_desc,filteroptions_opt,["tesla_filteroptions_desc", "tesla_filteroptions"]), "
"); + html = html.concat("Plot Type:
", radio(plottype_desc,plottype_opt,["tesla_plottype_desc", "tesla_plottype"]), "
"); + html = html.concat(""); + html = html.concat("
"); + html = html.concat("Show Range As:   
", radio(range_desc,range_opt,["tesla_range_desc","tesla_range"]), "
"); + html = html.concat("Show Temperature As:   
", radio(temperature_desc,temperature_opt,["tesla_temperature_desc","tesla_temperature"]), "
"); + html = html.concat("Select Table Variable:"); + html = html.concat("
", radio(tablevar_desc,tablevar_opt,["tesla_tablevar_desc", "tesla_tablevar"]), "
"); + html = html.concat("PDF Format:
", radio(PDFOptions_desc,PDFOptions_opt,["tesla_PDFOptions_desc", "tesla_PDFOptions"]), "
"); + html = html.concat(""); + html = html.concat("
"); + $("#p_selectBox").html(html); + $(".content").css({"cursor":"wait"}); + $('#table2').DataTable().clear().destroy(); + $('#table2').show(); + var table = $('#table2').DataTable({"info": false}); + for(i=0; i0) { + $('#selected_id').val(row[0]); + } + try { + if(localStorage.getItem("tesla_selected_id").length>0) { + $('#selected_id').val(localStorage.getItem("tesla_selected_id")); + } + } catch(e) {} + $('#table2 tbody').on('click', 'tr', function () { $('#selected_id').val(table.row(this).data()); localStorage.setItem('tesla_selected_id',$('#selected_id').val()); }); + $(".content").css({"cursor":"default"}); + } + }); +} + +function plotData() { + //Fetches vehicle data for a given VIN and displays ChartJS plot on Page3 + var xsrf = $("[name='_xsrf']").val(); + var variable = getLocal("tesla_plotoptions",plotoptions_opt[0]); + var variable_desc = getLocal("tesla_plotoptions_desc",plotoptions_desc[0]); + var rangeval = getLocal("tesla_range",range_opt[0]); + var temperature = getLocal("tesla_temperature",temperature_opt[0]); + var plottype = getLocal("tesla_plottype",plottype_opt[0]); + var plot_desc = getLocal("tesla_plottype_desc",plottype_desc[0]); + var f = getLocal("tesla_filteroptions",filteroptions_opt[0]); + var d0 = $("#startdate").val(); + var d1 = $("#enddate").val(); + var selected_id = $("#selected_id").val().split(","); + var asset_id = selected_id[0]; + var address = selected_id[1]; + + if(variable_desc.indexOf("Temperature")>=0) { + variable_desc = variable_desc + " (deg " + getLocal("tesla_temperature",temperature_opt[0]) + ")"; + } else if((variable_desc.indexOf("Odometer")>=0) || (variable_desc.indexOf("Range")>=0)) { + variable_desc = variable_desc + " (" + getLocal("tesla_range",range_opt[0]) + ")"; + } + + updateLocalStorage(); + + $.post('get_chain_data', { vin: asset_id, address: address, variable: variable, filter: f, range: rangeval, temperature: temperature, startdate: d0, enddate: d1, _xsrf: xsrf }, function(text){ + text = text.replace(/'/g,'"'); + text = text.replace(/"/g,'"'); + var data = JSON.parse(text); + var x = new Array(); + var y = new Array(); + var xy = new Array(); + for(i=0; i'); + var ctx = $('#myChart').get(0).getContext('2d'); + var col = "rgba(77,166,83,1.0)"; + var fill = false; + if(plot_desc.indexOf("Fill") >= 0) { fill = true; } + if(variable == "battery_cycles") { + plottype = "bar"; + } + + if(variable.search("_vs_odometer")>0) { + var z_x = new Array(); + var z_y = new Array(); + var xz = new Array(); + for(i=0; i", options[i], ""); + } + return out; +} + +function fileExists(url) { + if(url){ + var req = new XMLHttpRequest(); + req.open('GET', url, false); + req.send(); + return req.status==200; + } else { + return false; + } +} + +function is_static_available() { + return 1; +} diff --git a/wallet/crystals/420_tesla/teslaapihandler.py b/wallet/crystals/420_tesla/teslaapihandler.py new file mode 100644 index 0000000..2892ae3 --- /dev/null +++ b/wallet/crystals/420_tesla/teslaapihandler.py @@ -0,0 +1,367 @@ +import time +import json +import hashlib +import datetime +import requests +import rainflow +import mypolyfit as mp +from requests_oauth2 import OAuth2BearerToken + +class TeslaAPIHandler(): + + def __init__(self,bismuth,reg,unreg,op_data): + self.CLIENT_ID = "81527cff06843c8634fdc09e8ac0abefb46ac849f38fe1e431c2ef2106796384" + self.CLIENT_SECRET = "c7257eb71a564034f9419ee651c7d0e5f7aa6bfbd18bafb5c5c033b093bb2fa3" + self.TESLA_URL = "https://owner-api.teslamotors.com" + self.API = "/api/1" + self.address = "Bis1TeSLaWhTC2ByEwZnYWtsPVK5428uqnL46" + self.bismuth = bismuth + self.register = reg + self.unregister = unreg + self.op_data = op_data + + def fetch_vehicle_data(self,email,password,pwd): + """ + Returns a dict with vehicle data for all VINs using Tesla API + """ + data = self.tesla_data(email,password,pwd) + N = data["count"] + out = {} + out["total"] = N + out["vin"] = {} + + for i in range(0,N): + vin = data["vehicle"][i]["vin"] + if len(pwd)>0: + vin = (vin + pwd).encode("utf-8") + vin = hashlib.sha256(vin).hexdigest() + checksum = self.checksum(vin,True) + vin = vin + checksum + out["vin"][str(i)] = vin + out[vin] = {} + out[vin]["battery_type"] = data["vehicle"][i]["battery_type"] + out[vin]["battery_level"] = data["vehicle"][i]["battery_level"] + out[vin]["battery_range"] = data["vehicle"][i]["battery_range"] + out[vin]["usable_battery_level"] = data["vehicle"][i]["usable_battery_level"] + out[vin]["charge_current_request"] = data["vehicle"][i]["charge_current_request"] + out[vin]["charge_energy_added"] = data["vehicle"][i]["charge_energy_added"] + out[vin]["charge_miles_added_ideal"] = data["vehicle"][i]["charge_miles_added_ideal"] + out[vin]["charge_miles_added_rated"] = data["vehicle"][i]["charge_miles_added_rated"] + out[vin]["ideal_battery_range"] = data["vehicle"][i]["ideal_battery_range"] + out[vin]["est_battery_range"] = data["vehicle"][i]["est_battery_range"] + out[vin]["outside_temp"] = data["vehicle"][i]["outside_temp"] + out[vin]["inside_temp"] = data["vehicle"][i]["inside_temp"] + out[vin]["odometer"] = data["vehicle"][i]["odometer"] + out[vin]["timestamp"] = data["vehicle"][i]["timestamp"] + out[vin]["car_version"] = data["vehicle"][i]["car_version"] + + return out + + def get_chain_data(self,asset_id,addresses,variable,filter,range_unit,temperature,startdate,enddate): + """ + Returns vehicle data on chain as specified by 'variable' between start and end dates + """ + out = {} + out["x"] = [] + out["y"] = [] + out["z"] = [] + command = "addlistopfromto" + rec = self.address + op = self.op_data + t0 = time.mktime(datetime.datetime.strptime(startdate, "%Y-%m-%d").timetuple()) + t1 = time.mktime(datetime.datetime.strptime(enddate, "%Y-%m-%d").timetuple()) + t1 = t1 + 24*60*60 #Enddate Time 23:59:59 + + for sender in addresses.split(","): + bisdata = self.bismuth.command(command, [sender,rec,op,10,False,t0,t1]) + for i in range(0,len(bisdata)): + data = json.loads(bisdata[i][11]) + vin = data["vin"]["0"] + if variable == "max_range_vs_odometer": + data[vin]["max_range_vs_odometer"] = self.__get_max_range(data[vin]) + if variable == "est_max_range": + data[vin]["est_max_range"] = self.__get_max_range(data[vin]) + ts = int(data[vin]["timestamp"])/1000 + mydate = datetime.datetime.fromtimestamp(ts) + try: + if vin == asset_id: + out["y"].append(self.data_units(data[vin][variable],variable,range_unit,temperature)) + if "_vs_odometer" in variable: + out["x"].append(data[vin]["odometer"]) + else: + out["x"].append(f"{mydate:%Y-%m-%d %H:%M:%S}") + out["z"] = 0 + except: + pass + + if variable == "max_range_vs_odometer": + out["z_x"] = [] + out["z_y"] = [] + if len(out["x"])>2: + w = mp.polyfit(out["x"],out["y"],2,filter) + data = mp.interpolate(min(out["x"]),max(out["x"]),20,w) + out["z_x"] = data["x"] + out["z_y"] = data["y"] + + return out + + def get_cycle_data(self,asset_id,addresses,variable,filter,range_unit,temperature,startdate,enddate): + """ + Returns cycle data on chain as specified by 'variable' between start and end dates + """ + out = {} + out["x"] = [] + out["y"] = [] + data = self.get_chain_data(asset_id,addresses,variable,filter,range_unit,temperature,startdate,enddate) + try: + cycles = rainflow.count_cycles(data["y"], binsize=10.0) + sum = 0.0 + for i in range(len(cycles)): + out["x"].append("Cycle {}-{}%".format(cycles[i][0]-10,cycles[i][0])) + out["y"].append(cycles[i][1]) + sum += self.full_cycle_equivalent(cycles[i]) + out["full_cycle_equivalent"] = round(100*sum)/100 + except: + pass + return out + + def __tesla_connect(self,email, pwd): + """ + Checks if valid email and password + """ + data = {} + data['grant_type']='password' + data['client_id']=self.CLIENT_ID + data['client_secret']=self.CLIENT_SECRET + data['email']=email + data['password']=pwd + + r = requests.post(self.TESLA_URL + '/oauth/token',data) + S = json.loads(r.text) + time.sleep(1) + return S + + def tesla_vins(self,email,password,pwd): + """ + Returns all VIN numbers associated with specified email and password + """ + S = self.__tesla_connect(email, password) + out = {} + out["count"] = 0 + with requests.Session() as s: + try: + s.auth = OAuth2BearerToken(S['access_token']) + r = s.get(self.TESLA_URL + self.API + '/vehicles') + vehicle = r.json() + L = vehicle['count'] + out["vehicle"] = {} + out["count"] = L + for i in range(0,L): + vin=vehicle['response'][i]['vin'] + if len(pwd) == 0: + asset_id=vin + else: + asset_id = (vin + pwd).encode("utf-8") + asset_id = hashlib.sha256(asset_id).hexdigest() + checksum = self.checksum(asset_id,True) + out["vehicle"][str(i)] = asset_id + checksum + except: + pass + + return out + + def tesla_data(self,email,password,pwd): + """ + Returns selected vehicle data given email, password. + If owner has multiple vehicles, data for all of them is returned. + """ + S = self.__tesla_connect(email,password) + out = {} + out["vehicle"] = {} + out["count"] = 0 + + with requests.Session() as s: + try: + s.auth = OAuth2BearerToken(S['access_token']) + r = s.get(self.TESLA_URL + self.API + '/vehicles') + vehicle = r.json() + L = vehicle['count'] + out["count"] = L + + for i in range(0,L): + while True: + time.sleep(2) + url = self.TESLA_URL + self.API + '/vehicles/' + str(vehicle['response'][i]['id']) + "/wake_up" + r = s.post(url) + R = json.loads(r.text) + if "vehicle_id" in R['response']: + print("----> Vehicle woke up") + break + else: + print("----> Trying to wake up vehicle again") + + while True: + time.sleep(5) + url = self.TESLA_URL + self.API + '/vehicles/' + str(vehicle['response'][i]['id']) + '/vehicle_data' + r = s.get(url) + data = r.json() + try: + if "vin" in data["response"]: + print("----> Received vehicle data") + break + except: + print("----> Trying to receive vehicle data again") + pass + + data = data["response"] + battery_type = "" + option_codes = data["option_codes"].split(",") + for j in range(len(option_codes)): + if option_codes[j].find("BT") == 0: + battery_type = option_codes[j] + break + + vin=data["vin"] + out["vehicle"][i] = {} + out["vehicle"][i]["charge_energy_added"] = data["charge_state"]["charge_energy_added"] + out["vehicle"][i]["charge_miles_added_rated"] = data["charge_state"]["charge_miles_added_rated"] + out["vehicle"][i]["charge_miles_added_ideal"] = data["charge_state"]["charge_miles_added_ideal"] + out["vehicle"][i]["est_battery_range"] = data["charge_state"]["est_battery_range"] + out["vehicle"][i]["ideal_battery_range"] = data["charge_state"]["ideal_battery_range"] + out["vehicle"][i]["charge_current_request"] = data["charge_state"]["charge_current_request"] + out["vehicle"][i]["usable_battery_level"] = data["charge_state"]["usable_battery_level"] + out["vehicle"][i]["battery_range"] = data["charge_state"]["battery_range"] + out["vehicle"][i]["battery_level"] = data["charge_state"]["battery_level"] + out["vehicle"][i]["timestamp"] = data["charge_state"]["timestamp"] + out["vehicle"][i]["odometer"] = data["vehicle_state"]["odometer"] + out["vehicle"][i]["inside_temp"] = data["climate_state"]["inside_temp"] + out["vehicle"][i]["outside_temp"] = data["climate_state"]["outside_temp"] + out["vehicle"][i]["car_version"] = data["vehicle_state"]["car_version"] + out["vehicle"][i]["battery_type"] = battery_type + out["vehicle"][i]["vin"] = vin + + except: + pass + + return out + + def __decode_char(self,c): + """ + Transliteration keys, see https://en.wikipedia.org/wiki/Vehicle_identification_number + """ + options = {'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9, + 'A':1,'B':2,'C':3,'D':4,'E':5,'F':6,'G':7,'H':8, + 'J':1,'K':2,'L':3,'M':4,'N':5, 'P':7, 'R':9, + 'S':2,'T':3,'U':4,'V':5,'W':6,'X':7,'Y':8,'Z':9} + try: + w = 0 + w = options[c] + except: + pass + return w + + def __weight(self,pos): + """ + Weight factors, see https://en.wikipedia.org/wiki/Vehicle_identification_number + """ + options= {0:8,1:7,2:6,3:5,4:4,5:3,6:2,7:10,8:0,9:9,10:8,11:7,12:6,13:5,14:4,15:3,16:2,17:6} + try: + w = 0 + w = options[pos] + except: + pass + return w + + def checkVIN(self,vin): + """ + Returns out=1 if specified VIN is valid, out=-1 otherwise. + """ + out=-1 + try: + sum = 0 + N = len(vin) + if N>10: + check_in=vin[8] + for i in range(0,N): + sum += self.__weight(i)*self.__decode_char(vin[i]); + compare = sum % 11 + if (compare == 11): + check = 'X' + else: + check = str(compare) + + if (check_in == check): + out = 1 + except: + pass + + return out; + + def checkID(self,asset_id): + """ + Returns out=1 if specified asset ID is valid, out=-1 otherwise. + """ + out=-1 + crc=self.checksum(asset_id,False) + if asset_id.endswith(crc): + out=1 + return out; + + def data_units(self,data,var,range_unit,temperature): + """ + Returns either range or temperature with the specified unit. + Range or temperature is chosen based on keyword in the string var. + It is assumed that the input data is by default in either miles or Celsius. + """ + out = data + if temperature == "F": + if var.find("temp") > 0: + out = round(data*1.8 + 32.0,1) + + if range_unit == "km": + if (var.find("miles") > 0) or (var.find("range") > 0) or (var.find("meter") > 0): + out = round(data*1.609344,3) + + return out + + def __get_max_range(self,data): + range = 0 + if data["battery_level"]>0: + range = data["battery_range"] / (data["battery_level"]/100.0) + range = round(range,3) + return range + + def __normalize(self,out): + y = out["y"] + M = max(y) + out["y"] = [x/M for x in y] + return out + + def encryptDecrypt(self,input,key): + N = len(key) + if N>0: + out = "" + M = len(input) + j = 0 + for i in range(M): + out = out[:i] + chr(ord(input[i]) ^ ord(key[j])) + j = j + 1 + if j == N: + j = 0 + else: + out=input + return out + + def checksum(self,input,lastchar : bool = True): + if lastchar == True: + M = len(input) + else: + M = len(input) - 1 + out = 0 + for i in range(M): + out = out + ord(input[i]) + return format(out % 16,"x") + + def full_cycle_equivalent(self,cycle): + factor = { 10: 0.043, 20: 0.086, 30: 0.155, 40: 0.225, 50: 0.400, 60: 0.500, 70: 0.600, 80: 0.700, 90: 0.850, 100: 1.0 } + return round(100 * factor[cycle[0]] * cycle[1])/100.0 diff --git a/wallet/crystals/420_tesla/themes/default/about.html b/wallet/crystals/420_tesla/themes/default/about.html new file mode 100644 index 0000000..153d339 --- /dev/null +++ b/wallet/crystals/420_tesla/themes/default/about.html @@ -0,0 +1,66 @@ +{% extends "base.html" %} + + +{% block body %} + + +
+ +
+
+
+

About

+

+
+
+ EV Battery Monitoring for Bismuth's Tornado Wallet
+ Version 1.0.0
+
+ GitHub Link: https://github.com/bismuthfoundation/TornadoWallet/tree/master/wallet/crystals/420_tesla
+ Blog: https://hypernodes.bismuth.live/?p=1318
+ Public website: https://ev.batterylife.info
+

+ + +
+
+
+ +
+
+
+

Menu

+

+
+ +
+
+ +
+ +
+ +{% end %} + +{% block footer %} + + + +{% end %} diff --git a/wallet/crystals/420_tesla/themes/default/json.html b/wallet/crystals/420_tesla/themes/default/json.html new file mode 100644 index 0000000..3978751 --- /dev/null +++ b/wallet/crystals/420_tesla/themes/default/json.html @@ -0,0 +1,2 @@ +{{ data }} + diff --git a/wallet/crystals/420_tesla/themes/default/message_pop.html b/wallet/crystals/420_tesla/themes/default/message_pop.html new file mode 100644 index 0000000..a7138f7 --- /dev/null +++ b/wallet/crystals/420_tesla/themes/default/message_pop.html @@ -0,0 +1,30 @@ +{% block body %} + + + +{% end %} diff --git a/wallet/crystals/420_tesla/themes/default/page1.html b/wallet/crystals/420_tesla/themes/default/page1.html new file mode 100644 index 0000000..cb62605 --- /dev/null +++ b/wallet/crystals/420_tesla/themes/default/page1.html @@ -0,0 +1,81 @@ +{% extends "base.html" %} + + +{% block header %} + +{% end %} + +{% block body %} +{% raw xsrf_form_html() %} + + +
+ +
+
+
+

Tesla Credentials

+

+
+
+ + + +
Email address: 
Password:
+
+ Anonymizer Password (remember this for later): +
+ +
+
+
+
+ +
+
+
+

Register

+

+
+
+ You will be able to register your Tesla vehicle(s) on the Bismuth blockchain if you supply valid Tesla credentials and are able to fetch valid VIN numbers. +

+ +
+
+
+ + + +
+ +
+ +{% end %} + +{% block footer %} + + + + + + + + + + + + + +{% end %} diff --git a/wallet/crystals/420_tesla/themes/default/page2.html b/wallet/crystals/420_tesla/themes/default/page2.html new file mode 100644 index 0000000..978458a --- /dev/null +++ b/wallet/crystals/420_tesla/themes/default/page2.html @@ -0,0 +1,95 @@ +{% extends "base.html" %} + + +{% block header %} + + +{% end %} + +{% block body %} +{% raw xsrf_form_html() %} + + +
+ +
+
+
+

Tesla Credentials

+

+
+
+ + + +
Email address: 
Password:
+

+ Anonymizer Password (same password as when you registered the vehicle): +
+ Show Range As: +
+ Show Temperature As: +

+ +
+
+
+
+ +
+
+
+

Vehicle Data

+

+
+
+ Anonymized Asset IDs:

+
+ + + + + + + + + + + + + + + + +
Battery Type
Battery Level (%)
Usable Battery Level (%)
Battery Range
Ideal Battery Range
Estimated Battery Range
Charge Current Request (A)
Charge Energy Added (kWh)
Charge Range Added Ideal 
Charge Range Added Rated
Inside Temperature
Outside Temperature
Odometer
Car Version
Timestamp
+
+ +
+
+
+ + + +
+ +
+ +{% end %} + +{% block footer %} + + + + + + + + + + + + + +{% end %} diff --git a/wallet/crystals/420_tesla/themes/default/page3.html b/wallet/crystals/420_tesla/themes/default/page3.html new file mode 100644 index 0000000..edc673d --- /dev/null +++ b/wallet/crystals/420_tesla/themes/default/page3.html @@ -0,0 +1,96 @@ +{% extends "base.html" %} + + +{% block header %} + + +{% end %} + +{% block body %} +{% raw xsrf_form_html() %} + + +
+ +
+
+
+

Data Selection

+

+
+
+ Search filter for vehicle asset ids (empty=all):
+ + +

+

+ +

+ +    + +
+

+
+
+
+
+ +
+
+
+

Plot

+

+
+
+
+ +
+
+
+ +
+
+
+
+ + + +
+ +
+ +{% end %} + +{% block footer %} + + + + + + + + + + + + + + + + +{% end %} diff --git a/wallet/wallet.py b/wallet/wallet.py index d51ecf6..8447808 100644 --- a/wallet/wallet.py +++ b/wallet/wallet.py @@ -39,7 +39,7 @@ from modules.crystals import CrystalManager from modules import i18n # helps pyinstaller, do not remove -__version__ = "0.1.35" +__version__ = "0.1.36" define("port", default=8888, help="run on the given port", type=int) define(