From 87d76a8ff5eb10844654a29b7efad431201e2631 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 3 May 2024 15:35:48 +0300 Subject: [PATCH 001/167] Flask web interface added. --- flask_app.py | 165 ++++++++++++++ static/images/mapilio.png | Bin 0 -> 74572 bytes static/images/mapilioWatermark.svg | 17 ++ static/images/mapilio_beta.svg | 13 ++ static/images/mapilio_ico-modified.png | Bin 0 -> 91314 bytes static/images/uploadIcon.svg | 8 + static/scripts/main.js | 300 +++++++++++++++++++++++++ static/styles/style.css | 37 +++ templates/about.html | 86 +++++++ templates/login.html | 49 ++++ templates/support.html | 84 +++++++ templates/upload.html | 121 ++++++++++ 12 files changed, 880 insertions(+) create mode 100755 flask_app.py create mode 100755 static/images/mapilio.png create mode 100755 static/images/mapilioWatermark.svg create mode 100755 static/images/mapilio_beta.svg create mode 100755 static/images/mapilio_ico-modified.png create mode 100755 static/images/uploadIcon.svg create mode 100644 static/scripts/main.js create mode 100644 static/styles/style.css create mode 100644 templates/about.html create mode 100755 templates/login.html create mode 100644 templates/support.html create mode 100644 templates/upload.html diff --git a/flask_app.py b/flask_app.py new file mode 100755 index 0000000..f9a2ef7 --- /dev/null +++ b/flask_app.py @@ -0,0 +1,165 @@ +from flask import Flask, render_template, request, redirect, url_for, jsonify +from flaskwebgui import FlaskUI +import webbrowser +import os +import subprocess +import shutil +import re + +from mapilio_kit.base import authenticator +from mapilio_kit.components.edit_config import edit_config +from mapilio_kit.components.login import list_all_users + +app = Flask(__name__) + +UPLOAD_FOLDER = os.path.join(os.path.expanduser("~"), ".cache", "mapilio", "MapilioKit", "images/") + + +def get_args_mapilio(func): + arg_names = func.__code__.co_varnames[:func.__code__.co_argcount] + return {arg: None for arg in arg_names} + + +def check_authenticate(): + global authentication_status, token + if len(list_all_users()) == 0: + authentication_status = False + token = None + elif len(list_all_users()) >= 2: + token = None + authentication_status = False + username = input("Found multiple Mapilio accounts. Please specify your username.\n") + else: + token = list_all_users()[0]['user_upload_token'] + authentication_status = True + + return token, authentication_status + + +@app.route("/", methods=["GET", "POST"]) +def index(): + token, authentication_status = check_authenticate() + if authentication_status: + return render_template("upload.html", token=token) + else: + return render_template('login.html') + + +@app.route('/login', methods=['GET','POST']) +def mapilio_login(): + + if request.method == 'POST': + args = get_args_mapilio(edit_config) + email = request.form['email'].strip() + password = request.form['password'].strip() + + username = email.split('@')[0] + + args["user_name"] = username + args["user_email"] = email + args["user_password"] = str(password) + args["gui"] = True + check_authenticate = authenticator().perform_task(args) + + if check_authenticate['status']: + message = check_authenticate['message'] + token = check_authenticate['token'] + return render_template("upload.html", message=message, token=token) + else: + message = check_authenticate['message'] + return render_template('login.html', message=message) + else: + return render_template('login.html') + + +@app.route('/logout', methods=['GET']) +def remove_accounts(): + MAPILIO_CONFIG_PATH = os.getenv( + "MAPILIO_CONFIG_PATH", + os.path.join( + os.path.expanduser("~"), + ".config", + "mapilio", + "configs", + "CLIENT_USERS", + ), + ) + if os.path.exists(MAPILIO_CONFIG_PATH): + os.remove(MAPILIO_CONFIG_PATH) + else: + return jsonify(success=True, message="No accounts found!"), 200 + return jsonify(success=True, message="Account successfully removed!"), 200 + + +@app.route('/upload', methods=['GET', 'POST']) +def mapilio_upload_page(): + if request.method == 'GET': + token, authentication_status = check_authenticate() + + if authentication_status: + return render_template('upload.html', token=token) + else: + return redirect(url_for("mapilio_login")) + elif request.method == 'POST': + if 'file' not in request.files: + return jsonify(success=False, message="No file part") + + for file in request.files.getlist('file'): + if file.filename == '': + continue + if not os.path.exists(UPLOAD_FOLDER): + os.makedirs(UPLOAD_FOLDER, exist_ok=True) + file.save(UPLOAD_FOLDER + file.filename) + + command = f"mapilio_kit upload {UPLOAD_FOLDER} --dry_run" + try: + result = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + + if result.returncode == 0: + try: + shutil.rmtree(UPLOAD_FOLDER) + output_lines = result.stderr.split('\n') + output_text = '\n'.join(output_lines) + + total_images_pattern = r'"total_images": (\d+)' + processed_images_pattern = r'"processed_images": (\d+)' + failed_images_pattern = r'"failed_images": (\d+)' + + total_images_match = re.search(total_images_pattern, output_text) + processed_images_match = re.search(processed_images_pattern, output_text) + failed_images_match = re.search(failed_images_pattern, output_text) + + total_images = int(total_images_match.group(1)) if total_images_match else 0 + processed_images = int(processed_images_match.group(1)) if processed_images_match else 0 + failed_images = int(failed_images_match.group(1)) if failed_images_match else 0 + + return jsonify(success=True, message="Images uploaded successfully", total_images=total_images, processed_images=processed_images, failed_images=failed_images), 200 + except OSError as err: + print(f"Error: {UPLOAD_FOLDER} could not be deleted. - {err}") + except subprocess.CalledProcessError as e: + return jsonify(success=False, message="Error occurred while running command"), 500 + else: + return jsonify(success=False, message="Method Not Allowed"), 500 + +@app.route('/support', methods=['GET', 'POST']) +def mapilio_support_page(): + token, authentication_status = check_authenticate() + + if authentication_status: + return render_template('support.html', token=token) + else: + return redirect(url_for("mapilio_login")) + +@app.route('/about', methods=['GET', 'POST']) +def mapilio_about_page(): + token, authentication_status = check_authenticate() + + if authentication_status: + return render_template('about.html', token=token) + else: + return redirect(url_for("mapilio_login")) + +if __name__ == "__main__": + # webbrowser.open("http://127.0.0.1:8080/") + app.run(host="0.0.0.0", port=8080, debug=True) + # FlaskUI(app=app, server="flask", port=5050, width=1200, height=800).run() diff --git a/static/images/mapilio.png b/static/images/mapilio.png new file mode 100755 index 0000000000000000000000000000000000000000..07c14718bbf3f3e9de33c0596014ac88fad14665 GIT binary patch literal 74572 zcmYg%WmKC{vvq>IyB7`a4kZ+KcXxO902LgHTY=*4PSHXs6nEF+#ofQ~zW1*0=KRQ7 zk@L*4IkWfdiBwgVK}R7$0RRB#aJ1|W0N8!YNy@{@%#}luP%<}Sb zPN5(Zr}eJqB*g*tY~tbM#T46=zu)l1LK%(^R4M;rWrH@lpY!evqld{X~syGYgwsefVj2 zcQ+?9CnvaWZ3{LOT)eEri8Te!C$hVCcz7t%vfX|_h=#zQoR-EMUb;#>I#6e=kRPDj3g**K4#{srCvFQ)NSPO+w^V=NRI+G*-irT)l zT6aHfyr74FPO~Wc{QP)(Nq8@NB+s*F186QZ>hPA4@`#fp6AoD@J;y5#;9TnIPwV6^ z1@<6+3iF^dJ(ntQ$0`dgoQE!gT*${YKIo?7f2K1(^{*JB`O(aHZbAtuosK7_+qH5o zWxLzY+%aK`f2H_~^0us7-+?8$WaqjC+1yc3Tj-*OW2YzXC1gf65k<_EaFh*uQetCV zimQ$5ZMFNHq}gQO_I>%=FUt>=o_guPa!=gu^?gs@X%>r;kWz6ODw=1#EjVP;Dxa*? zLaXNLze0B!c*+5~&kDspxR53Q2C`mo%5EXw3!Kr^kgCZCxBBWgeKFX{ml}t^Pjx_-W_e`Q{Nkl_jKJR;U;Vs#C+Z84BE0f4Fr1br zu#G_`u7{i@n`qI~mq^#9;s~hfQ{!Q{MfoS(k&h zAH;DjdPg-Zt6~9mhs?RGNi)y8*!%ZGP4r(M`UM?g@!JrO`vZ+@{^Mpae!7|d)6BU! z5l}onN_O2tBTrXZ)MHI?^T9j$emYlK67`%yfBA8@+W28suZv9*lgD0?1KxGgC+oRGa8|H1ieXWbG3`mCALG<=zbcn2}NI__shznx2D;ev{{B z4DCvnVf~v|hr1C_UB-@jxK^-u+<>#b5T~ihhjXV zrn-^NtO3F({jE`IMH9$YH! zJJ%gYonUW%gkB+$R4jN3qXy4+^Q4K?Z^K}E8-|wh)XsZauv=Lx+`+HPIz#d$1*@%n zM>sdyMOJS77Nsohwi$Z7_v(Q)0WMECoF@S)nEug{u2?d(=o>a&rY!Iv__GgAuop zz>3%}?UZ6|YT{^|^#CfHG?Fs|Se(4Os!3W>6{zPE=9qQAUKQR%03eYsHCZuBD=1H| zmOr7-#bC=&6zj5J%RjV6kIIQqM zQGL=)z@^ud^Qx(Fs|v6gIeRD1!DPt1USoX9Nb@V6+gk_$FqaStOHz8g3c(#2qO`r# zVKw6Ub?{W*`Q|LP-Y%tn;iU9x&?NLe9F~E<14l6BoRv{4Cj<|jLB<8 z6mZ*`i!?MZ{g`33JYc%$j`DU3g5I1Bh*@CrOB#S~Ql<3Zq3`6I(AXMschI@1%rQx; z$3M$K+TpmmllP(l6Yqlj85P15?9ZbpMLOaIRQcY$pTFz$sy8s2pPTdE$_;ONUT$#- z>0aiv5-SU}oCbgR@ij@wuB_4c1*@~Qy4soVrqHV-&%E}IH26phi|;}B7k9aUp`Kn@ zjKhqMXpL@~KTCt)>}8bTLfD1GU>rH7N{YuHN-ldgol8;fjN5QDay)oEFCT5EJH2kI zylAD1StcYn2`5H8qLG$%T09h%y^0^4M1^|H-FliE=CV3QHz%tBP8>vmeM&YfMy(%a z6H`;0Q-H(b&cBj^3K0_3-j;Z~?7nq3KR@5$PU1@LIziub%TkC&l^TL!T(iZ3$xAhr zHSS1u?O;tM-?;r;^>3zZ`n&SY*HWjSDbWU8f(GtU%~VvC^y@Wxgu;I?b-)-1F#H2B zrO@+~?W0*O(4c|q;uA``D`}|w8K>ha-<~!*;%l+Y`Z8_A-*xKvFt&+X<=pkU0Bq|R{G$e#H z`0#n`%c-Z|U)!Hj{Xy!H%z9u^1_lW=IxU0Q2H8l0hwd!w;V#?r&hqrZXox7!P#3@f z&pcVZP6Lg|I$QqQe#-4sVa>@ zBm{k|Hej+YJ{;b`lnIO8cU>te0IQL(w<Ij9pp`};N1rH7@G`gH)>(%u64$e$!; ztcxR(4$AhYkW%!KMoL!Qm0Bh7H3Zj-=R>d5zfNX1ts`EaR1aln?U? zefJg!?YBU?JvS(D|FQdaQ)DhjI@~}b$S@Pg< z1(zz^;pzYI$TMtgDQhZKKxPQvd@lvBT$?S#NiOGs6jXo@==UZY-xsI1Q_iSz=Xn zbusLdk|8Ux;-5ImjzmvMr#*bkVz4XMDJJG<^MXyysE&s9tbd1r_Y4LA&;v_=1xO@& z85$mOlm+sUDx%AJ6Q(V|p!;tFWlO6VY+n7zI-gG-v6a*mkes-trtu+lcO8^Y@crgk zSEAJ2H87f2ES$YUhsQ1WQZRTj^QwHQB)0kgags%@_TrN;u!Wcoz>g{N-(ZqSl!{gf zSoI4jz!|F9GG6F;v?^6UF{B!h#jJbtwS{!L*zM58x{O)!CtmC8 z-D959Ao`)Iv|R0~?~yu&5&>=C@A@-tjLKk?Us#vDi5KT#HF(kTO0~RBwtjZLQQ!wI z6zkFA_MSgi_jaG0z1V?K2BSm4>af=?Qe_dlZ+Fa=>x-Gg5W2`E-iw-{fb>S;-kbqQ zw`H7ZS-VAy1wpjKOACx#N%z4NU!3fV>v(?>g~<$d-x3M|dl3W03o|+;ACI}%B!F=c zjl8hGk?QB3Fk3a!;>-oVCjP$}0UxT`_^^!JdX)oherGd zq4#_>_q-@QUi_xZ4w@p&cGWM)P9ZQ`mv`<5oBc}Od(0Pf>^w_ESlruBcVuk+`E5&0 z;b>X7Dk5rb$NP~v9f-#(`s$`qLI?XMpt&T>y^u0Rz4QPA{4Wn&L@_ab>c$_n=tOOp zMuo49I`p9dXmzora2YV@Z9zs{r1N2u^S8{0|EA+M(HfyeeIk_!fN!|1$vast`OL}? zwP2w?3rp|1+9RKUlt;_TD%;fLg}#ihjaFGlpCH|k8!&vedUx0Gn#Yh6&?QIOLM3I1 z-s=v_tnN29*;sW%F8SDeHZGmN^jlxx8f=Bw5%MB`snTM3nF1)v2x%QY4tT?s6q>q* zhAXMz(&K+K+3%!9K8Jj4>-zXAnihRFYCl`975arSe~niSI2`w_V}D6(Mfz^F#fQCp zr`;iWJ)sZ?0?^}S4l9f`1SB{q?5Xbc+5F>ku_YXHC7v+l?3JL5sS5iyat?X#{d;ss z4W^9w$brLbUs|=-knz*NRi8$@+L3k@i;a}X@D|-4G;tJj+xIUQ4u9Nc9PZ-Uw7}Fl zDT`nDt^?caPJ2T!@vY?^LapfINMesok`3}Qc@9nqVhUeVLS&$p_+!pdavGdG_5^;EvNug4804w z5cu8LF~z<+aI2`j%*3BYpUs`K8`74b?I8|+7b{+lXuGK5aBx73IsF~>VR`9x&T08z z1eJjoIaA8U47<*NnD_})T3YGc-M$`@Rs9n~brQK0>cZx}4oj;;Z-YBG#B2cWXyBiW zaSO)|?_*OvIgp$t#C?R(2IT*+9^iu8GhA{hrojTjT=gJqa+zo2oQ^SqNisDO?C>nc zHWXHd3$6JXPgH8TqdVK`##KGvyei1yGc1L>-(NR4*~I_)73(h(92g~#HlawY`um_D zXpT()+hWB38om8=lUOO05ylx?2c|^{jyP+q815Ej!Ro6!+9W(EzlKp&9PD(nZu>&) z6S<3%=i4b)AL20&Y0O(_1yVpF{jb^t1qE#u)7grZaPaf8-EZKS)?4DWS@mSC0&d;Q z51Ja?uStX)R_r`~YNas{fW^-ooHm>bG|d`d;RO!bt})}}7EA>Nt@ppmdt=nQ5ro_( z?Z66V5~9u};trC@*t|Y?(xD>S@Er_0(DX#Q9+{Gh)jb~6+`bHO3Sb8i37@bm{Tcu? zQSX9BMz`?AGM)7RB)Z=SSkS|NvWimVM^%dddd319bcKfS9U@FOj}29}UNweyb=d{( zX(?j)61|dbYoF=|zw@R$wNTE}S&7?|={D~4H#!&;a7QZx3PUf@0ZE0Yvm8T86Y~r& zGnvH|Yx?kH1;^%CIqnj`Co$KoN@fyCr+K%hp?dsvkBz@c!?2je&)eeyS^7J>6G!YK z3Qo%(?Z%H~XVNWGJU0P|3i>uX`&HBIu3=v)jIl+9+s~(V6P{j%7qk0QalGtn39VjLb%+y>9ABGsqO zKs?ZSgdx;m#O|T2GnKQfV47Fa_>k?U-mzJ`)$qaYj~43I`Yka^s3m}bc&$aVM0`$A z%X%P&r@jHJ}4u@`rcL*?~P4P zXUa4h0gd_l>}O$%I;lZ`tG7JL|LLg`|kaWUridEVU666(T9Jch1P#a=nd6PCd>^?ez-*s**J;~ z?-u8H51Z@)MF*$o>HeO3rKf3H%@`|qG;tz|jBAK$4d0|6bEtnI=E0rQD#LAH1M93D zXL}#i8sv0?uoygMCd{sS8pRgn>6(SvD|rXwg$?)%WOSWIRz#??WDAKcjOPJTZH~Ln zxipV0C0}X;!=%w2oqVF(k7>c@h3^*yBY6@o07VqpBir2PH;|~Y?_=l);rv!sR!%Wv zXLAK73w7J4hTrepZ+w_z2VBlK2x*Y0zCU*WfQK1y0uzs<#uC>SypV}%9FNWG$&xP? zQQd5JrJKD76~hWOo#OK*Zp7by0yF%NCdd9*R2@wfDTp~Z5j6_bTp^%38y{|1> zzPUcyEytVAE9rP|QmM|#hWJzc!UbbTmoEK$Vsun0Tde4atr#Z(w-_%hH!aJ+;B~Y} zG%I+c7xIARTwU|Kg#X-sH|)VwcOv0>>58<_PL(;zANUz&&VT^_IEyg6<&ujRSRAN9 z$+hq5zxDmmscfJr8c{oGsVCaG-s6hETFQjo5Mw{OH3I6RqcTwuJ&;Uc&P>11Ds?dE za#VNTYY$u_IABhFks75ex--2G_BTEJR?Y48kF2^CbE(R&v@NL+Mv1_#`gI?#yt%7>0%@`&07C9|W1zy#sx7kRCE0+oz(>jw>! z7rwQ{irq}>lfQBD1k}?Uv-?S8i6Jl!a(_};<1M3qn!aQtQuh=N&BAcAhJckWqpw)M z0*5afOo|ANgJbE>e_q8II6F>!ijeSw&i+E8`^TN-acr;}m*;~07MIt8Ehl-DwZ@nH zG%pi^MOt5MHgd~cNd=4(KP<1KsZYQm90Lu*!D=2apctTkx%fq@&QhENRvi;G^B_YZ zsB7oKP&zjURI)U^0pvNLn`*MvS&U3%v$@Q6* z3#`|?CG35InH5gu(#F!&gh1ebwf1VdnX)C(2sl zgQ9A>GlgPc&bp_@pJ4YZZibHZu`sj{0J@gCKe0BQy5n79fTum@p!;`0yfwR%^cw*G z31&RQQ!h!8LTz@AjWUWA;vURs&#V;tD@Y(B!~ELz-_JsG8a!9?fyGTLBRlbGYC}pm z^!K=MKZPQN#bbu59C?9yIobesX#5nj+2SQtm|HPVZ)jZ%kkX*_S9y%KQ+bRu)^u7W z3~iuWCqbB4+Ej8wQI(v_)+GyZPJjc=$CULPDz8CA?x~KXF z2}(pa6l18~ILR+Q8yU{quJU+1&Jq}tRHR% zf*c-y3{Z5@|GRKbwWx{z^k;AL2w}P(w}X!unpFunOsPsVfknnty#q#FEl&>B9Pa;v z_$mBqhT?-BC=9|-t?t~>)_+;WMCbLZRVm33X$S25sAt4eh1A$Mf1G!wfa9lfDM9AjD zhzOGTUG+D8xA5m{i%Qhum4WhF)|5*#mk0Y~VT+}%_y3Fw|A3>N{q^2M*_M~|zcJ^N z;67M9?^C?!FGwKjt*Id_)8iyR*W@DDy#L1+6z>bT`dj*%y$F7SB z^`xLkml2oFD-o~bsYT5lCBX1~5rAGT-iE87fp9L#*7|q>$bj@2{(O}I{xW4l$F%@J zOJzIfqH69f>$(gii$oa^aL8^mQtODHkcp?8bFA!((@}F<7QiQ!5zVC@E9au}p8i!N z3o*ad{_Qx>(ud{kI!_OOBFt~4fv zBnqB}u?1L{D7Zbbyd39$wdU8+ zgXj3e_=KZp9l~3a&Dn%>%W}=*H2=Pex;OT{htdfKbbo^Xf6BlGYWHi<4g9L#X}FSl ztdxD(H?SUjp5X@uclfRJ6cLVsM#2+vR=M;C+pkXCr$HYFu@24y_l;m%Ev&_EEtBrB zMDE0YHG4gJe?U1GH(W2(a%LWM#>=hA*>nXc=hc2$9%Pf9B*18d1C=ipAuIlU;3gKS5Ewgg@OT*YMDL}N)^c!PDlLI$05%P?DQmF^<6en0-#g^ zw%IcaTYWZmIg^UArh&#~>Jzs?EewdepceT)N!w9f3ws=w_d z3z+n$EWk4^XCt{b+vb#FiEq z<)k(A8TpMU!}$j`@647&1W64Hc>ek*7o|T__UkT0-0dZ|#aP3f;p*=rD^O_rYs}Jx zynJ+#GjwUQ!n`)iNA)?My-iO2A#{=nId)aB{ubrSgYLPL*$U|Dm0Na=0I8a>xHbKP zUld1LoCH)m@q|0mv)h^0cl%1v<(dDP@Vxy$iBh->{z;&`mltoWC=J=4@9-dMkDn20 z1RIu51;m>A*alWMGye+LvikDmRsIEbyh0%;e}DKs_Sdq(*kx2U}*Z9=Lw4@uNy25CnH<2kL7n& zOIF8;)^M01`4|cK;Sn&AOX9O|-pbWKp6D;a}+Zk;ZHls)vqQNVR@*+_g%nATJ z;ZHD}E*}h3aL@{&r+BY;m@gPg_~Ui+W?F6w!h?bxF$ex6tl7&#%gj|Tx?Pw~@ zvQfyRzC7k|8UE-+o-@^MBdRd~;yJlVUQJ#EZn&5l1(_~=^PVA^lom~0<|I=&tP8JA zeRus()-75poY{&B>JhNMQ=MXemmoQZi%4a)R0MmF$=%K{&l2pzi@l#M-rZRbQn`Kq1Ug=5L#;VeflRe=ofd?2s(d#kq^4-)Z40-RnE!&a zRXM^$WD!b(fL>VrO=tp2W=!4mi7L%GCZ`m{N%-AfFY@Gthz&l@x|dUlK4#rVx`@{` zsPTfa4Y^_RKDwyhbt7_N3Rwh=@5OJuwk?mfBG3OS`A5O8=6_3gYSsGFwzCG56nsQpN(?Iq&Y;cy4 zye04Oo()g@C3btd{Dqn1RNa06Jo7IfB;@l%Y~z8yM5e#I=ft28sc%rhLcyHRM+3=6 zAat&x(1(~4Ok?xHmmwhZz%id)>ln}VJZ@PVSA9aZpdz4i6t(-?v={KalxbyRR5-%< zT_D~*oSUd2`@njv^`;KM82@}C+wSTpv!1#@21GHC@{FFjNMpt$l5&LdzQ(^P)3{}c znZ={dd7*S#ON*;J2A%@pc*pAtyHdzMIRyF#IBR?Kt*qx`LmL@#Jm&r5N#$oOG5aey zl!eFuQT$nii}@UL-*@eG*OmeXYB8lJv4AMk@qr#^V3a>v46aHTKd(>Zye<(CNHk|av{E&iOaGu>A#w(y?BYDQ zC-uTU7o^6Uwq3htH1U4^0imOTP@hE9)}A4*J!vTr#!edD-WO@ypAPNZ0O{UWG9pmz zY=8Nz@k()HcOE;|&<4PJ5y|eGGlwMU4jVQX8d9?|DrrGe0xUJqhyouK0(121qe6}4 zS}Zxr>94=hY-feYo5f&hL<;cM>uqCq`b}f*zMW&Ykk6{%H~mxIxMM6mddjiaM0<=G zX+;Ng)eny1dVlT8d`)4ggs?Z2r%|`OxZ-Rp;)r|K^MR@}VC9h({{!mn@y^RAL=7q@ z`Yio8&xVrLD@OAK2UYkspIdDnpF^{mE-hMi)jz3YUZM8NN)F(>P`$J7Cst!ihn!O)OVom#7i zx57FmD8uR^9hhu3m;>Iy$Im8t`f35$yxu}){zM6oVz=%`1>f(j_#CK&bcvZqFL}+u zCM%(-Ua)w5Rh&zn2z9l_R}{d`v>b0wC3l*1kl!UPIu$PAVGq2`&1r zHMMUaT|Q*%N?&UqK3^7nb`YfQ#=JgQ42NcAGqkJM@g~R6@**B2lsym9K1lvbhMKEu zG2?Iv(_Y=Nqy6B!_7qX2+3gmhTfO9&$=h3qIEEcg%e@Ac7_|ZR2Oq}@L zvr7}A&(Q}7gxw>A;d08#CDDG|yogMJV0;F=-qD4EEMKA~=Qta1N6NSft3Ny;IS>zx zud9`{X+x1Rzn^N6vty>!=aUIh^WF5o^TCCQm#sDcKBk(OnAnC3iTLLn5(L|FwV(Qq zHwo=6(~=g{ef(L1269^OVqqcR%Q+s6d`X7$F;_`r`4^I7 zNUn?Q6RMX-X9#dzhR_jE!Pn6*WpegKg=y;jhSKlRAwl?B)>89K-altvN(0*S^T@YU zu)gFIU>g+7bLIe~03TYeLmD5k_4Ix>hD!amQ;Z=}88<3~_bW%Mwfnb~ad^k-W4wrm z^3j}JHp%NxrU#S(T(a%oW^VFfYe zW};*$p&VzXgY@`AT|d<$>LNjN?K>Q(EU=yLh5#KB=4oDtPa4FHK!`}u2;i!XTltkC zxF!x?15GuVe`CLK;V|7hr6+BZ^1g&&H&BLz#&PrFvPodZZ!C$3<(_5jY?n~he_UXktKD~?Zw2F%X5<3qxW80 zykWKVWlDpla)#vGzaEG;_8W+#6xfnopkcd|4wRP{`>EmYK{3A8aw7J0CV{$+;2@N6 zu<(W4TQ@y;7W{+&!W6y1>wFk*Nk$xaT?<*BaAc3_?q!8lYxrrOR^D7_yBz5<{)N&X zDd=@tzI6=o`({={^hNP1nw-RS_fL-D{HuyY0!odO}xKU%iYD`gAJ(mvQVoO-iQ`_g( zckC=R==$w>@r%RbC?Y<&R2Hp%y4K^w&H-s-_=tdm9n6oo8`xv9Z zDoQfX3Gr=nWYy2EBrN^T*scgzsv166!A|R?tX-FEm252W9*^A9f2%%mu z|MrWqwD*hN=LjUv83oD#&aHP*c}xG3yO|VSjH)8@FjozFkF_FmP{o$-SJIUw=1JRy ze2^IIWxM=2SkMvcn2oInULlrwu|hm50{)yi>6fD1RxgIbf^ZElx_Vr7*l{e|73WEE z(*EY-$>cFDlksKr35?}vYT|xx13j&C-cOT@5zyg;5Z9TJO9xKgLPvakO7~ml$0X=5 zZf*q-HOCwJFFq9Z;$Y|tCC^NAW;xmM41Tl}!=&`FJysrFafV3hM&U6d&*@)yU8sbR z26e%%z`zr&232~|=l%1_6@6F`p>`5fz55j*JHn0OgI+`|u!pyuJ1UMnyAYr=e$Ifv zaMk~1XJ>x2ax}76Ci0TebTzyJk&TjEa5qAk=a#~O0~*15%>JQRd(-avkn6`yyxA2- zJ*BZ(4)9g8xhhRz=#k!2CO^1*{V^|D(??#|OcC(~vl7kqR^)u;vYE?|%iddEdk3Y@ z<%!B0UTjCOpp~m$?sK@@XE+<*+MPn|>^pc>RXLJ_6@ZhGuupN?`cqoIjQ9s@KcEpG z;Ie$4I+(4bc~+_Q)^zjUx+_=@$onpv{hUFFc^fh!${vDkAF^}#l@8U>iJBm`{H8Y7 zuWnPZyPGQ4Ics9tlMc$iY~}qW(}$+Fal(ZsSAZy8o5miOuZ)uRScNHDX2N09F)ADD z_Qd`x$c-*0pDxY@By?H(G}*V*!#7%Au%H)0goO(uAUEi%AIlAsI#{7 z1ndBm%+3}bNn8pQutL&BCbi(R=$_jIJHjXq29TrS~rUEcn_d94T?euj#4@wjU_EbDoq#DVDVAt*g^z2?>0 z71$6a)ixP}8CqL``6HrI2p;>I`Cw;(GJAD(g=34`y?fDF=DND3=0|^oC=;ye)_DSi zgZ8wrV3qA45mcS=)g(kHaP-pN#delDcNxPIsne-3&hNG5LI(*-4W>S~zhS+hvN zq4_gbC)ap3n#3(#TP$|Ks5yNcW%h4{XH})!n35rdB&a^h9f>XX>yIFQhP2|RU6?Rl zunJZ;S2U}LtPV!e&Vs4`9S69>5DHS z>+1>PbfXGqYOQyK%U^q+u%%Dm5!jGEOQ{Kdg{h$b_!IW(wq;f7Qvg;25di>KSMdr3 zHy@f&e_zvI4Tge&+U;30jFdy_{8ba;bqsp5g8P!!(SHwUL_&wsbD*e{gJG!dx>RMH zqA~bV`@#9zf0ge|>sOb*4?jZh{@}v1t}Q*s%~`Uf^rc&hxl?z<22SG8novYr;gns0Bcc9FuMd8>EBV0;TXb;X=6-|@lqOB_ zQE@+pN+5}{bv$4|m+wu$-QCY@23`h@&ZI!y1S8lMR6}H{4I!5)tH!M_lqpFtUr;FC zbF6LygKUF?!x<^hK~y{vJ(YT&3?ZZtGPvnJ{ExW^C%Dm$i1H5i6r+@{4iyd_|B=g! zFPY9LxB6Udsmuh`H|S5v$-IVbYuXDc@BE3_H1DN%Xd3tZ@dC|1GVawh=Ku>oTSWBb zal;Hrg^O$RM&pAStEhx_LiHb_!d1@ zsVw<6qOj>fVW)AW12dNF?=lC(>oxM5WSAjge*DK6lfOG~VrT|vy^IyY*@5zn&hcdP zicog6eG zVEd+MlkFchJGb@HUfUiCHo|UoIDf|BJi41o6t|9!0EC?`i$hm{DoJMBhz0F!K71Mngcm=`{mt2l_m-jhaA!$=}`Z5`QAAY2Os|vUUHfl>Ba8% z`by^A{wIPSGs*}O_DS^F4&v;m*ZttzcwHjgMLdY+ak2W>2*|Q5IfT%upCgyMAnQd_2+- z|FHMMm;S)DCdwZmUr+uA}UY1+}2FMQ~v-Ai0QV9tTjLG36w&f7(dCsgxdK$RGXRY zwvf^aokuyOpi%xL5k(?^lbyuv{}veREGC?V)ZzVMB*1)xEI>Bb_#KmZckByP@1fqn-fOcTnty9lb1c`RJc33i($rGgy)bXDM5qteBh64&r_1WD(y78_-@7Q+t&19y`zK}}~x<`3qAkxyT?UiU% z;JYGrMgy!$9?uFc>V>SK(*69P#=~={DYYNQUdvLI+gp~7wVzIH%%t=_X>mrj>iFD)a%(LWmeUs#5_>kKSV_0f71AuR#!3Iy5jc15zIzC`zE-M zU2Utr?k`C;uUw6|l>Am_p?NlLdumV3zpeTA*A+fHShfF(%d0C69k1G5lNvF2BphLAj}{dWjTa8%b9}Wy(Uh%0%?c! zecGRA-2y^L*u9H?SyavwNW9*^5Pu+^`Q6~zfAeax4Q81%U~%w{TS-n?>AL(2c5y#d~Rhi*1jn7W^YSZXxb=Zm;FK7O)B(wUiqxvD2Qkvr}PmOx>a+Cw*5ZP zDR%ipQl{e70IYoM(8=BZ0GS+kq!+c~UyoUQ)k>{Sz`~ZFUf31m7RcUeMA4Kxq03vU z)&Jes&$V5ULVT+@OWW}pm_7ND_6DN8_jpm_@G+H2s>w$jQravU`3j%Mg1k~IAevuC zVPV?-&}U0Xd`7D)g3(o3d)yB}5eO|k+Fl^(Dg3%RLSE>MxgS)k?uz}1@SB`1n}J5L zUtKZO(@KkcfTGlV=3Es(svfGhUPn`D_P0<+R^GfCC@ykbiMc+SU*@|#LWkB*F4yWk zS(9oAI;$HPDBMlrzHk^C>FC_nFXlm~f~a3?Z|Ph5?NlM-`1j@KQNhzG3qzWLcpi<8 zBMpteF8kLwvzDHA|42Szu0Opi*j1;(;U2nT4LtexjFr+_5~Ux4Jrif=3Y$SN%sG(kfS0_V){wglsaGYCIzU474P0 zqCK~TPZRQaOj*cUpy9H3*?P~ZUE2HcBg`K`*j93zhZwiSgKfCH^n+a;%(@ArtgKZr2JjKM(G!9nX*cGYMp=5w?FKPEisYpr){07!pbr_ zdl2Azw==oxCT-cuSCJ1NJC$JN`A+~PWC+e^tQsF&wn(2JMhy-+W{=37lq{dAQeipC zo%^fsB}Q9O6L?>eaStMXViss;nukU3-ya^)T@CP?9R@*?(q3O0)$Nwp?T}(-KYl}6 zwf8gosDNGva=jK%I;b|eEmk#h{h{FyWdfixv%j9HuR*Q@NpC^)7Hb}<2JLd*$eCYl z9`q6l1=dgja!q~FmL}=X{w8D_gGN&Y8I4PsWF4>bl=;c(zSZW#3tyw*PQ~RHoayF* z51cR-M`3NT68gz$3~7|@{!pCVx}+4Ob!yP+(`C>Z2Q7r$m8S!su!}wxGV5UWtX65f zuJq8N_VC=*N|b2~#zDymLHrZOd`e#@*AlQQ>H!oEKcFa6Gal1mM`43|^*^qW?_*dM z+T^p3*Vh$SyI12vGpA$-AgPhk73C+fg_O-A(EJzb5Mr_c+}+JPD||`{VYzx|+VMvnR*O|7rm$ zR!oIg@EQZ!ysGW4N2hpA4pum0^ zbo!SF+BWxxQcw9?%{Ch2RV}Ku&XmV?A)K#@v{8xIa1KNqo-?uUP(<_LK?h{5_nS|j z;>NEVCkv8IRroigg^9uMw$u_dd8Gv;_`ZBenkD|s-Fo*#o_M;4%1tw zlKbg6Jx#||ajjX_R_=HuqW^fS26uU&K>*cUViEe1(H}x_m#T9xP1c%zqFjd7QQ7=% zMT+Q4d=)5IgadoxzTabkh+MdO+M+H)vmx!*@jHz{8xwjamF3 zQXqos=WnLaR^*wh-7U4_`uCnRB=DrqU6tOwk?zge10GY8-L+C9^z!f#4GUv+sz`>? zrZu6CTTz34%C~#`;NGX(v8sZ{wMcHiEUDUmj6XXF#2!0D`p;!KC&0$WIh}z)W#UF} z49B@S2Uso&E!P*2Ebh~?;}QxSE?pdgY~CijnYO?v=VZU+9pw$HESiTQlCUr&sOn=J zJM({KWw4RTw)~R6IP-nqipha9-McV;Qp^goj^PlPfm-zOwL5mOqR^92yx#dvgL=;e zjZNzC^jYKe@8Crs<#yK>8xVfVw}@J<#dp@>16B)Wu%KvE6coR`>GA6l4>QqT@FW0K zgfF>TqXhX779k=2|9E=qu(rAVZ8BF0~=4EXIGu!3M)+jkfy;LxUqPy6H-gZ{tmUEczI_O`}ieSRc6>hS1*!~J>G z5$~rcbe5%BeILA1XVOLumjB)y=Kzn*j?hzM*sUMSs)7CiFVi(w(e5~vW;(`;C#Icq zK8cV~OYiaLA3`=i^&70Clw!FgvziXSV2UD?G_Ta}jx9*K8qYME*A1T_qYf?)#qI;n z-_-w}dCCNv^nZkBjqB+#y3(uO;3U(0-w=_Nu9_A`H&jBf0hS(zOD!+91rms!df)qt}( zML~O={pM5HFvrMWU1`B3QTofK%;=DODV0<6Ar>dIi&1X+u!L-RF~^wf5+H{$5>~RF z-LJBcTQs@4ozXY8CzCNYBRRG-pnx~-y!fSs$i1dI7S>qvxTT-sqjlk-MeJQgn^594 zj(I7KemBa-gM=wf>Qmz58}fWXr>rcw&+N}4>W{#qXXd7DdfhGNkH7U8Zn{#@IDhwM z9S8q9`>*XC)BHc$18fA>-#;GIL8W=cdr6Z=^6XYQPnQL>UM0H)%O&(XyhZ#4XEv3=fY8^yO@p4_nw6n2&DLKnxU}zRftRV8{-@>4=|oK% zGRvf<^o{mL3y9k6)Vh_!C(7w39kJ8~9~h%U|EQ| z9l*j0$`ol%ZV7hJYA9v6g|Y9aFA!*&+gY5bPydJry3aLboKHSq{fut{;U z^Eh3Gs=ne7> z7QmUrd?0&7g@lEu!vK!QCzTERqUEfL;H${j>3`J6M|0&Nv41)mrV7g7Fnu!eFc`d8 z;#5DWGcCV&Hf;2qRHe1IESW-DGkrWj!`FLZd3=D|ss*EkikkCANjSfY+nE+4NYmAYT-JZ!MW9u;lN? z2*B#LA#&S^^_N+(E+FkbBg*#I_J7-kiDa_ei16j#uSqQ4(se^Pe3O~4xZw*=GYYZQ zayEFdZ-7f8>%{v8_Sj7>JZyh)SJP-ux~%%5McqyBQHB)5Sa|IpQ>%lKojSuCn0pF^ zluDZy=Xp=@6inm`3YP!QnTqjh+TSABZXeO3n`ykyBPJ-Y=Ax3rhC-3IWY~V3F+AQq zQKbQY-o$S#HV))D+i3tR*{7m3RKO#j;8O$+f1!Hf5)tP5_BXfOA0RQ!qYd@CcNm2evm>KlcOLd6%9MzcBt7=R)7t(6Yzy#qj(q-4r>prR z@4tigYbD9wof{rhumI9-jUekmZleb=ONu5zcreY}=Yz;WG!2;W;Goye z=R*yxu8uhni~|<&?&FA@T8lBu04iyVotqJ-b6=asF|hvOY3v$nh;+6~ZL5z!oQuP$qMa8H<@T3f#Ei)tl|7tvQt-8qOghzgMp zsHqHJ$7zeZDLd`;gVPWCvs6`?J&?Zk*o;d56jL2k>6yW3z}t z6DJM^Ypn>leMM~rVE}y=5y`{wmYDKd<&?AvfVT8In+v88Wt|QkyW@|Acdq?$)qrGe z$_VT2y65UfhEzG~P#XFlO=UxQWm$7odHa}|a@N-J;P6?0L{XzOcJQNa?k(#5#5ND& z9-ox=Eb=)cv24a1na4wgLKhB4&|@;3hFT1LQjt(tYE~{;XHPog_wpWVu7Q@=|79j1 z5{Uf*d7R!kT9E+KU}u1e*+qM05cy{ZDEcza76N4xrrHV%?IhJke0+*Kk<^|os)f_B z=MrM3rrrLjMvo41cx%P{iLfXE;0svdPn35atZK^h7B5aPAUNsktru$IadSOy>&7#F z>Qzd8=O~!JzM|#Ff^Z~fd8wP2oGnkSz1*^zu=Jkm>bAmWqk0eq;PTqW2|F0OV@PhJ570oUR}qfYpC8 z$Fw6Pf-|w)%J#ItH`Wbc05JL!67gEF46s|@s}<%4Fd{7qQ`Tx~ckE-638r?lrJm}7 zo6rGZVD!-N>?5y-J}3LQo*|Rpuq+V|kionP31a?v2%cz6aWH}U`A#!jAC% z&RPoy?aO=YF=y6@bxufj-)TH2R{9~NRKVAJhM3GR(Z0XR;=hSb1&Q_k9qQCIm}+l~>I22w52hp!za+hIuSi22wK?k8DUHvqJ=un8|RadFOWCg`%XM16vac{D=Af;e^$-)FY ztH8MCIT(mD_t_48Pg2tDGe>PFI&A-OJ*&^&{MJdzEhFO9ScZ-7J0}vnGO@^WreG}- z8{I6ZS0?HeEQ+ zKJ29@UHG)GVR(hyeI@wpM#_aJ#q%;HjyoK$idZQ zW|ETLUqBN?;{rwH0I)&j)Lr87PDb6q2xOsWlMxtM7k)!V)!TGHhh;QZ~pXINaCK&Wke zmh5u(bgILtrP~tc)<-^%Ky;aBC*={3avEQ7pIaC2X_?c0?nJE~9DWPb(bQ_^<-EHU z6JBq1%}Ds=1`v1fN&QVc_0436=Smr8+THi9=jJ8bD?%#l_KFogtv7hAkdiS&Pvoo5 z**x+Azq+giBi~>Z8_m*7y4iLBn{Yx6<^1fPscOz5BIw^q6K*B`83~omWI}Qta$nMc zRAzxOx!dqUi|Ir(sAAnkI(yCsL(1{55E%#?wU!GG30ENWP z2c;h!Y@cOwn{ZHAO}cj~-}`ILcNRMD%#;2zeKw|%cUx()Km0`!oE(2?jtQPSEi064 z?SCi^SpI?e(QoY%d0K1!-ly8W6Ho$UF1+IuT|ylh9xkh>7RV;$ zp~j*x$^0kQTj56_cxrkYlfdc@$)m_fyn@8WTCSll?m_+8VI&5yXxr{1L?9-8sZbV*rDhk?-eMYv#0*nKJ4;cCvhE^Nwg-sVgC9zrY{pi1IW}kgU zb>+K1v6(bYsu`3z!4_$6Z-0P-1j>0~@;vkG+`MCmaFV}ql7Gt)n^|pJVb%f28ZFG& z*ZaKqiyJ+TD6Lv!0%Tu~5KH*E6#V+^4R^iTM(>)=yZ!4wb2NF@B`5V)v1=Ko;_ z!2tYX*}WGN+H=XK72%=_>N#j;OYV=mv&23t*rsjNqSXGs-aRsdGY`twnOgn%IbIZX}KrL%{hVeaQELjDKR$HW+&7DCQ8 z??6wxc!S_K%5e+fx+GngcBq~a!1&h9o9N%nRD5(~FHJxKmnN6;>)^K(PQ0cTd8WRZ zj&l6|G3g?0pXLYk9h}8)hQ#?oul3oR0%=?Ais{ z8SIg~W3SEiBKNW@m^}f+na85w!dV_3hb5ERZ~Jzg)pT?MK})e4H5JvxBCkJ(@oL=+ z6;Y1}CV@+3-s?Nq6dI!vC9GW06-OTim&>ho>@9BdV8YXHSe1y7-?7l3-ehFk=L7-E z{Fq3ZG2+7kq^(E|K0?556G?T>4Cb`+ZZCSc=jxE;2Ub=uY%oU|g-^27#@#B{;|ckF z4<}=m%cT(v)f-gj_(swg zKj@ZSPvY^-TTxCW&06_Ws?;VK86b{w+A8JrZ0V@N+)xsizUR_zKJ@MFbd>z@Ao>Ei zq~MH#S3qO_YI2#Ig{7|Pr#~EX9L~F{c@<#X7h{p9cE)zFY2c5v!u-EM*dB1I|JToI zur=4rM>o+vJd|wuNQ%3N0H>1QPXs1S1O>U+Nm@NdMi!TB{c%&}@>3(3%row}1%En@ z;gfhch$i&xuOgpr77frNHF_So*q$VUxWg#sc#&+b%mZ4BNH|;n4JnW0i-ZFE?g2&#Q?P6Z)Yw@x=q_#Z|eS_ z%J<=e?Dt7^F{XjtU-3>~16;DdX^YDdSZH$-(o9k0;7J_vTh1JqO2jUIo$5CDlzsn7 zzJAuR97=j{1`~d+CRLwBx24;5dfd#ButVJU5RmtJ4iqnShoFL|mua?WHhcn;T zJWkR7v&0$%HpY$2f;YH?!ED=LZL}|h_{jwcDqe30;v2QM0xC0|@+1AUT)xll=P3Dj zof`^vG88A#?1{s6hivCA$K~6Qp`F0HK1Bv~nyUFB(k(=A zO+j9MU6x4RlGS7~dz(c{5+AH!>~{3u^avV8$bG7hMmP`;am0|gLxh@cT#pFEEJuv0 zekbGiTZXyv?xZS|vf5mVjAz6ii~KhsfC0wTLXW#In;1gkE5dL)0zgKZaO4a;UGNMI z+^r@m6cqU0j`gpl4=OzFE~9$)zVy`-Gh1Hj040&ne3HKr4LCy_x{q{ZyH~Mm(v5ek zOjV2u3-a|*Qa_N}FU1e8!yBF{*CVVF5BAF2A?2q(xC&dS_~|5Ep4mtNCEp-FWQa3; z021X3&y-s0Z%gT0nsuqL<`f%dy=Q>|#?AjOKnjq}Ge5};K$a+_IsSu!%hyQDHL?LK z6=9A))L?!}rr-EzG84_M!($)qPcX#o%dR@TDs?DbJUu)3`wDq5s6I$4xkA}^_!oPX zx&Xt+@lywni{+X2{Zj4+fcvzq8km>p^nV6Q zsbPN)PVdE_>6Nqnds6hH23)B}Q`Xgbuk<;+smicNL|BE|$3NR<&RjZB!y&@bT0Ss| z1k%B1aBGGt$SuY^(XoOk#nBhTu0RIh;QjyM#8HT{2sx-|{*BtEw@BQTbIfe-b;YT5 z^n|R3Ihk>ZT_9h?9d|5y$iiu>U~tti#M{ryX;+vAI$g7DEz_M&ca%wwga@b^-8sou zN86ijITfDacD8hHX&*w+fkgzXez$V$$xDht|BFPjVKQEl$!LIRwat`1%(8NXZnyjB ze^46L01OPdkUl{A--*WqjQdmcUy1qQ?<95t`dm8m9PT&x-|lddsua5F7g|K4UM29H z_fZ*{nyPhPzlr@;^(W`mF+aVoCn6&1BuMD5ekMQuJm`iOE?p=A&(7DW|IY=po{CIR z(Z2Yr01iqz$GW_53kr4X{LK0tpCeM+^#Q%X`&_v6h^ zv~x3t`N=GTktVZJe-AkD4cAq|)N|roD87Z{+1%k0p<3p#NdP(|j0+D%)8_+ho!(Ok zL4UU--}e$qGT`uh{>A>UAF9Hg0qEcO0-6O)Cx3&taw@NIgihJ7|6RYv^QzWJMDV^G z-N4SXMg^OG2znc-j`)WMNo)>U;|shm*NS$S64uf?P89{>sUS>DySxYv=Pr?o9EDkV z1LNRe=7WJ5@5iI;5a`szO=X|#y$w#Sezp`?*iT{OYmE$_Gj*CXH4D_`m6jzE^XaYf zIeS^ms6)~PF?8=TGWq!|M(|c7?3zP;a51Z3lz`v^W-~w>H4*oMb`c!Dm+Oy7zpmgQ z(mYO{C=rlUrin_596Tym7hcKOf62C@`l~|d1~r;BAFl1e>e2@`3jNL>5%x;0nzw7% z55PODF=0O-UUQ+j)ePF_hs)3?&LUU`!1MfAoB!zphC#c}#@x_qtAwBKCrivLD+6m@ z-&~l>s`-K@S2|HTBOwyb$Mi+@*u?VT6Ed^Sa>a8gUJ|3U?{!F4e%Xm}4dt6u;4qS^fy&Rk zK2CQ{WSd(nZ{v?G2@|LJ#V>_!G z3!wF|0RS=m6^v3^*rfx+EH}ruk>9K@9tznTy^>g+Vk6_XIH!+1)8W}pMeZ{?&Lu7f z$yXM<*Dajr0#nzC;-FFVQb&ZaM6hytQeXT$av|zAo1Hm3-re1mU1`aq_>`FO=N)WR zPTq_$zV+7cZz>7Aq@g>rbYgI9IATZj_KNkM^u9F3VrIi{inU{<)MEkP$MIl8aPQuF zJ>QUDak%#dysy-xmX}`A^x8uQ$Td}yji(E8;Rpa}-lt24<8jzEq5%u;+u<+)#ZMmQ zoxbHy)A@g^SB1q`Ongv)aCepobkAk7FQ=;LV0@(;K+t$S9daQyWGEjCFGpMPuH)>F zz7%7l!7*LZ@2pjTHjaV&OL`{W(XK*$X=p=t073jP8qTmlA+WD1V^X z05r<2fA@OH&Cp1P?XULah5qugf7v@9H=#dyE>0?ZZz&f*}k7EmWo^K_7cVbTMAGzSuXvYJ>rhIfeeH@v8 zn@KlfP|`xCLKb4kzIu68#Bee>T{E}1uI#v4KaDk0BeGG6aL|M6Yo&`btW9N&9!Q;A zYlNwE;F<^|vL|D!gCn!eHj#?p0EDG(Y?q#weXC(%kDMPlsCm<6TA-%QIuTB`@F`cd z4)AAU4Ly(wVksrqLdF z)$cjkZX@r;Th7m;nNi?-=0}gG5Tjreeb#yYwPHmJ!QJJU@1r0*gz!MT(;U&DsmDnm zJPJsTunQyH<^pyHo$Nf95(Y-ai+$vdt8i9*w}Lxv7Cx*p{O6-GMfNoi# zY4j;74=E-U(T7}Z5A%w@+(oV1Y>LewHcBsdsG@JYpAFcS+3mF~r7oNgDw9NmbK$@D zEqi}Ks6&V_hXxI6?|{iUAA+8y-bG`6lg{et321dzlpWstXyW}?jPjn?e)H;GL&0AH zyJHlUxuZaX`SPfGSpbJ#`nsm`!!IP|c*3fg8J)2yW;qZrPD~>7b!B38+3RwlomR)` zq^~H36Un=_WQ6!zE>B%HKtF;9fd6h=p|HHAK-5Z{v-?GDD9GFuT#gJr$)2rpu9=^A zxWhd)mbo_+g8YX`xHefL{{Q+t*iySHCdM+~HR#JT=M%n&~D| zuI)a7Y02$cGMR}K9}c2*70i=A1EyI;2fISOjYM_sCohlDI1L8aB|mMpxo{t*2m$Cd zSQwY$Er6O|-a+ZY0tQ+7iRE1#_amD;Xpt}KzqY%|;5bxOfwufTVRG{F*WcJ8ZFD#@ z(xLU?@Pxrx|O0Qf02G8aQSwN!}@h7qc z48Jbxr5KF$MywTm8^uA2+-z7Q47=~tkr}T0XmZ(+UB>!3+_HX6#^(|xujfKmxntkR zH`Ib|s@Xz$vK$EhW!I;$JKWfOtjlkXn@^sk=y!cq2;-mAl>~6XrF~ipjQ@F!|7*{6~c&oZV^9~8~#NJY`t50NFV-z(h<5=L~%RcjEU zd;6}|$EI~g@)rLUAdjOPc+nachqlyYRf%f?hr=LqJiwWQE)>6~w{;@qvDD!D_PqD( zagcFbTfZV!Wv1T|AZg&T*xm!97I^dSR^{bYQQFXwHf7XMEZeJK!uri2k-=B-v> zu7(N!_+P0mxp4Z%zkW19`k8&b7X5BJtoVC`g|bqX^ng=0L#u!N5s@`!u%4M_6b4x! zov8bpXqWnShb9v;Sb_s-N(o+(5FITe**C5D+(CNxqedVLje`wQr+1hh>_y%pZO<3` z;__MF7Z3SaX$e%MOv{TZK=?Rk{eTV@f`~^nBi-|egBK3D1Tr@MUqc1Aei2uO#EWA2*kHan1{m>d zMYIqlgBqmOs2~0DE*D`pW7Y9;EGTm+Fx5jN?pHL9%c(|nJ|GibZCgg@sOGL@70y_m zTQ+lR$`#nOj(h(FJ4aU;Kq%>Gqa8R^o>aVOjLC(3bFLiHc~3dsF@>F=E(kII6%Loj zh$qqOPgJd5iuzaPGnaCQH+eTfDj77wNVv?+R4u}y?8`(Tn!D1%@hKWh^LKn)6|&kU zYz_92JAX{DOQ^Da$U`3|COOj(64>}@KDVvY2dd91;fAi($NBy{qIH1RV{Ik(-Gm}V zW*9QJxy@)55`fQXSj@Wa%3E3mfrZGX{t20lv^7iAwKCUllMeCQ&vXRkIMSbn0oPOn zu3S&;rG}+q$L-NF?-bj1P9S|Pj_n734i^KK)U~&Kx0sGFs&(20Z|(;v-$MwhdH5lk z&udI^Xb}eJOnp*K_^gU7m-jIc<2fmLp@IgboAU|)r)fJRg%%d z4*a@i-JP7lPZ{+RqO>(#6T@*GQ^P)-=fv%_Ue?211MH8lxsHI{qLJFT&H#CIFqf2- z(r7GRYtpulQi2cw1PtuuDq3G18}HqtgFGK$FNr<2V{b|FE{i? zM*Qf-M3@Mx&qEo=y?#Z$x$FF#9+#)DjAJrocR#JffNu#is3S}B~fh?5~daT{Ju2t*M8gT>T@V!cqf6em9oWDDmIlJymV%P3OrnL^U8JWTCAB{ zUKpyrrIR@atFGc_FJsNjogwN;_ih*t&fzB#&}RXZyvBf}KE3^JKIdgXCD{w=;hNMCzg$N6Ns3;0e#+jsm+qmtIt8twaY)@x42QYMiM7!)P@VtXokt)byFG zOfFp)h7p~EE|951{cWk@Gw~Jj8x>BXX&%=&ovatnHBYRS8!={1n}v$ zd4E{^8^`b&DO8rZ6pogd+T7)tyxe#gh!%u)%e>?MThwsK8-D?1@Rr?fD9*8|CahSD zmw#TZg&1kn(!VpOlKH@U7Zd$&e7tpGTxHVZ&6K?Mu+RIf9YBxL@$ot<#(vVuaP%(- zN7p6rq{L{*5YZQPM3cCwf8L%Z?xX!WwtWg5|KD}0Cj;q?Di{-kAC#feU|sot05=vY z`w^x2q}xKWCI#dF{0grakeAm5JH6H%Podr2pKXFP|J@(kw6=9Q{^-6_VrIii_b{b( z3cM9Sgj!l#lX~Bc%pA(|Zi}yYVFx|EBy$!UC+QPmrXLnE4qwUF=J6Vs^0b1r}(5zMeTD=3t$e(%cg zb#q;f&Jh!B5D8VXf)f&(`XZb)?Q9^4A9I)EFSd%{N)n1M4pd?akbHc3@5Tz6WFxe_ zuj*>@kczAoPWC+Y)d=}weJNm*z*=(DA@bLmg5u-(TKCgmldA9Hi3Oj{-l*+K#_9-m z>(?pl^eP8mp?j>|aA{XTPX{NlnM|owr!ey%!yjLxC} zZxjn)Nx5>`asarFRs9k))x5pK$7`39ONd{3mIvOzeY82w%IC3~Xonf(PzE2W4JIUE zfXB&JWAi(DLU=M}Emdj(yTyLjf&q-2E5Zwg(uC@Bu}zHkJT>vbU)lZfw@Ed&pSWU2 zBrkr#3x3V6!<<79q05W2a}%M&sG*k3O0Gwwt$IhsV#r6pn*1%uk;x671|at5NB!M+nuz-EJwEQJO%YDUB(9e@ks~yf=7`8pwwSUC$0jzBTxNrtl8THV z{Nt!Rfbp%anZvl$a7}@hQ3|#xvd?H7a3Oa5**@Ou-XH|)eZKW{uln**Yc}I<(e-$t z`15$S+ni%J9Q64%oCh`IS*yRw`@$XriP zjCCdLJn@6Ll9UOR-HJnZ5J3BxVNZRO2PhLTKb~K^j>Xg%;P01pK4u4;_f`|w22K@e z+hE&s(oZsS&MliTdvcgd=7;r9?}%u)yI!P#Us+4Y;P)pbfY?_>^UTo71Q1vI0 z*P-wl9d25FOEy(?V}^sST-NgVI~#AorHu^S#!{Cwb*{@GfOMb6Tr8&zloPfQY44Ed z)vPvQ`XLwr=**zSp#qqrK8>KY`cU$InHg>F*19J(v1M7<@ejc2gO5g=ZvHNIy!d5w z6l>^%%l)St7$xa|=s^(e^XCm9&P)IL%#VHtdt>0U>eJg2ulC1}vI(_$>IPIYqH9F) zgwsa%FCS46WF$;!VyJnSKL=2zTT%9_fUE9X1P3GWf$ssli4lqXbt5G^GT@&mP>pOfZ>QO&SxghzZpnHCu_Z=EzYY#{KOa^ zc>cLw=ia1@#eKa6>_&=X6{SWuwIC1^n_$bt{BV%O03g*?ni&R`l><{qs3(u^hC)WQ z5B}gWu@&#niGxJm8!Q#HC8Wc&!1tG z?x&)h&ayO-V2A(&W^j?{gazbE4jqBq(5{1(Gzs| zFi4dIVA@iS_y<>LLm~@i?Qs=&tcs1IVQ#@qm+3f~nI7%20KLRL_0d`Vyd7J4DB2gNA5(Eu0`#VKP#;@pDB^Cp*Oc?Vw$ zy3YX##T|2EN8Nh;83E_3Z5GpXviV*FM?o27ThwG_O{Ef(&%!urW6dYVl>QJzX zYp2udPMhy*X!MbbO3R! zjJg4o0Ca9ZNnPE<`Av4VZiy5;c;cZ^-EyMxsf))Jio4b(Z*{7x=NR5Ii3L70+g<3S zXqqRH_JhY)ZssJ0t~?zI-V|gWeiHL?U-JekwUrPl=ctJKCr%(Rk+|{S4%+pM?TqeS z^}Q#ob$8zk`9`G4Lr=W%YXa~E=a!$h0tFkARuW`TT?;BCj$)kg+ z2_s;beeOPIzcY(+(OiC9=35e40rFzGWkN3vtQJeY($mRW4ff>lz}u{e674d>W&g%X zRZ(xNIJ+8+SVRFu_#F5OYP@xgac`WP;@H1cTSU;8 zkRrx?r=U(jkG>Qg#Xz?7y&$K5P^Oa_RjTdL*clL~70~GcA)=}xuE)IGI)%bw$ite$ z?yls=>m;L{jxZl!c=0(M<=b-#A7Kt%nfWnKCymo*BrwPN&CSgg>>83$uu7fFG_F4W>m!~ zXPW%Xla*w%&bLAnnbHxsxJ@%e7VaM=QD%@q^%hI-k3X; z|90AOJ-DnhloWxJ?EM*Ef^(&zML43Dt(EisILD{{Dq1k)K;n(&QJM1Ir?eh12pUT?tg9EHpD>b&#N^4gI z#^=;D0sR?^MORRqbN3v+?w&(bu`5C?`OYf35y0*-2Dm4=UE*l1X*nG+X^wTgfgaK6 zsH0nG5eUM5*;6bG*{Y?^&TB{TA0_$Fum-;;Xxt<;!Bn~Zdj?MP8p$}d<2gB6B}X=j z)$BXf+lH}_%b0xu;nUfAz~Sq@v?DlEGr{d68Z^S!>G#3SXH;;F!z)5R=JVV)3M0&G z*O+|=@~wx^lHnqpYr%(#KN}A#=@v4PS|(#E*|{Xb3qa6jSxA@z1+Hf5hXyq0*G0IB zqM!HWN9;93c?CK&)#qqa|Pi#H4(Q6|3TL!I?ac1rm&=d;c(o zN2ZCm!xXfPjPlwYcn+)xpb^Y(2$_QA&(`m0bn-orz32%G{C0=KxBld7gd?oO;RZ_g zVN%Q1S_Mw&-&%aC`K6AeZe~n+`ZV@ox%4BTzvZy#?B^d?u8%q<Z*7T zv$K4))k4l<7wA2Tk1Y3+W}9$D1o;FVQ~JM!znFatK^}R(eS=wUqoq2qYf}T3`e1W8 z%A+rM+tVPSYansvaZoIBy*wS5wrFx#e5HTgzhcy`K!n=xwH6LkQ>8D1MS2L>b#4)g zyUlWehtDaq**Jpy7NzVJ(Ed2i8|1EW=uW%(R8As&b+?HIX6xCLbJN@W783UG=Me;8 z1Tfj~AfqAu8p~gM6>Bb)SL-*L4gh;45Th%Iq2XH>@;trP1N9&BI8e^(WZ;@J9-1tu z5%dtjG}Zw~Rz;sN!KOuI*~7LF2Hhqd5+e5px)&yw)c)<51qZ32OOu^#KvEU&UB1ew zQ&HXzeLhb^0XTeZHaR40&-Y4`a~ zd$rZOp`1h8CDlUg^Pl4^pCv?DI|vJOb9ql*t!?yRMsrECMb}^dAcOe%az{u+FD!Lk zF245-zNNk9zF_|jN90g!`;uh_V8FwC3h=qm2w)_KWg$WonLR9kQi$OPBGj4kF0#y@ zKyfz0m;zu^q0*2=FtO)k4d8n>M%KYNu1P zdq}=OLxeSRH)k_b8MiM1a~xB77Yv?}Dxu-n{jF z7itRJhM|_LFz=4BlTX>tB#*3+B7l?! zA-*$USKAA7)$?ipcM^5ZV#`N`6(9N}YeYTQG~Tho2`srZkxW-Q6#3;b#-&>MD6~#Z z1`pf4LJ0e(V5G-w?mpj80Hr}H=Y+Uh80_ETs?%ea!i&(MC@JQOW^=2Nw|GmR8L{{w zSFgxV2Q@CMWZnb`R=)cQH|D;o8=`Jyn5exaAlH}_dCW6nqYDZ>bTUp=|Ln071h<***<)zXs}eCT}(qP!iM@zK}P-^ zjiI+?ai#aGArxOzqneT0M~ibWPe)!| zM_uYpE79ZI=NL&vBnql)XmKP;v*tfZ#`^gm$M1)Znr>$3K7{yJ)1o^PLIvEpkdZV1 z{Xp7BVwy()8XHf_j76!V6V4LqC&*Ra6XiE2;dMe85uB@Eg#>dVDt@Yezx-JndL`B% zB|?$W9gO5FKVeWhs{S6AyIj_NK}s|ZjY_aax1A1Kotm7?hcBe`)e4(>o>|vx4ZI;C ziSyq728c5nkzIG2LFPXk`Q3emDfReF3@Tg$i8VgB9Cj79j( zR)DyujRJ-6M7$1w9*i#c_i_>)jG$n*)-S|xCOUa@2gE0ISij0@#;d5dAcRKb^mWgO z9V+DfJ-ednuooj)cTq6R=U!BKO;?qTe`0E^D9JH*Ga^Sph{}D=#=L}%LInQZxYkdqdUxAYj;I;ZbI89VQjvud3n>f8F^_zjJ?*ePQ5}*WbQIpA~ zdjz6oY({-AXc(@_^o{y`96o^+pq`uJ@9k@CF2D_-&(%zzf5)MljotC_ORqQ(TNW0w zIN^kKR{Gv6_HioyWlxr16$8Eq%b%0bKiSid^qMXudB33q&JmK~#A_M=*`S#w!Wc}* z7Sm1Z&_^+)uTkDR<~cyn-<}Ix(VLw0NLfruy=DR8^0Y7H=mxRKDpG5Qe-+lGyavI9 z7Y?Cjde&L<0gwKX$n*ej3vq|v$~9%9NK66pqf@5aM5pIJ zf{xT9GRgQgiXAS4sSqT181V%X$60@b&XpJ`cpVa9o+lmsk#wIY8`J8h3MM4ZuwrOe z-h%%_(Co1vi|XK|k&Ev%t^b=hT@T)^CHhP#UM#pb3-@*HUbOp(8WPi`J{*wNWy8nM zAH8n=^!4+Uq?=2*IZVtDHP0IowuKaYTqcMtE&Stcz`SFJWsKiNULE(JskI15{Q05w zU{OS^thnFiuJxw#y6tkPVnSaSrY}#HqfET_=!VZ$ z*rbGQo$>TamKur+DaE%OBmL}}&iO0H|6P?Jt2F`o<}-QiM0O=()76tNy#@#+LPcv_ z{KMV?;_K~H?lvaIy&SF%J{63M5F-6QE9K zN-Y|dkVaqDV4cp;`IL*Zr7Lr+aFgeHcB>Zazv1$@(x{)64YRDTm+jW6)4II$+h_AJ z%wEcZ0sU^Nawm{_WN@7}rPy!)uzJbVc7KF_-1oqNKj9XK;a8g;5JaE)w%74TyG)hd z;_mBU%318%H+%`yTg}MRhmvdu>7zxK6Q73(kw$3*wH*XHJN}KZuu{Hvz41lUd#Xdl znAmLIR!;a5Zv9p71Z^GR@L6UtRekD+`h(kHs{09vb1M7ShPaTz=-)xN)qpfKJ@ePdvo*m6>CQRt{2Z-|LMFV$PjE@xOEgaqO~ zeOgeeHOq@X29PBy%~W;;y#d!N3;UMBy0mDwJs0G)l8u=en3)|5fNDP#6{&uIsg0EU zQ3jC~H9Se{IP%(O9jbRWN`nJA>+xL;BsxK8ZZx(=d>2O>sbRZTCAaGfpYh}0?CflV zVnk?!0R?5LxSDU-h5@?>$OyFb!S1*@(}ew+{P>7~YcF^$@r%5f$1zZ4}pDL>?o2$pKH z`2Ey$wV8ZUo3z8k(=xOzgGhv!n^s8p>!lcRC!>NHSx;~-poBmI^}L&Ebdd>mGnUDJ z^~Sw$RMdQgx9v!@JrZ!(=HI?W)ZpVmN=nLxRv%{c&E4G+h5v$EJr)=$3mik^DZl1A zTiDk%J+FfmOeb{V}M^B!5XNfmJ~ARV`IbU<>f^(K5I+9BHY>88I)8Uv4o-O*clE7 zn)00IcQ$_*^a#{%$l0!MCeNh6IQ`6HyFiGaU`+ykoLKutFY@a4#>pStGk9_k;>`~GMc$dw7+Sd#P7A+9r22)JkQZ*_^SSLGY`-!~2vM6o%q=48h zI$x|}ZZJ$Lw_Zd9kF}Jnr-@5P+m{~hf<=UA>FEx5XN@<7noaufpLZ67mSmRa7Zwf< zwHd+N#r#wrY_LjoBL`*r=teae-z6F83^l)??4K2vsh}qxLewWX;t))AE z@>D!0Y>-3BUR)oijXN*1Q@< zY|PAvx$!SSUhwJEIH5!+y7d!RZ-N}WR^D}zU7Ux#Qsmz zzS)8v^E}#oz=Vs{2XdR0Cl|HZ?J28_Lq9C0KevAm9pHDh8PLO{^hnl1u;<>x9f&@< zIAjpj5?}so%~js92%!Lw3Qh5kuc$^F479)6y!DlG{#5{Ck5m7@!ZG|@k1VxJ74R9< zQVYKqe<~7&1ipR|{`hn0&{C`grr{M_Md9lrS78)woP@;EV3+7QH1Rp%qSbw0W!Y;e zfs{LCW6nV3F6pcsQlfP%-6kkHq-=a&!07`dX11U8#?{x?XBlvmogocv?TW+!3V!tt zl>7=D%#Cg;+)J-BE62>&{PB!FeYO@KD_PL|k>^`69Dr%>*?ESThJH!Os>nbWOH{qPy^|iaCFtZ`)<0HhLfoa8AcfPi})t ziP}L-HyjRVy6dGM*RXbf*lx8_I)QdI!48*PywZIOi`+SO_|MP@O8 zF19&jRVZ*&hUP;EGy?NrS1}%@W#>htUL77~o2=eP!{;P=e# zvtP&0*&s*K#l8AmP^qHj&VTY*@LO>(;W;YrN^YuT@VCWsFqUo__UiRhl``P-Y9na! zz`V)4)jUBcfR2Ls@aEQ4^ho?jhEQ=&I-TPz@9iiP$LR@aLx2J)n-09a-w!EaK_?ub z<}l!Jyh{ioFPPGujvKa7RgC4BJlJ+d#@#s4WuK`7HH$)-g_UTFB+y+=6&Js*bN4!p zzWM-rt|&vc{wRrX<+EIkTPrO?TgV^!BvtF~1kS(4ed$mhTQA7;^z>nvSXj=Lsz7X5 zrAT4Gy(b;mXv1ZDP_;;54V#RNj2o~gWy$|`knsa&9m`F*yWa1VxAZwgbeM>uq9tB2R+X1WR459X*>W7-D^?d!SR!Ecc*< z>FGyP&5KY$eda>3!nz_ZD>;SbDt}|i1jYs9tagx>_y3rN;yv9&kSQKxSCx1LK^~;0 zK;4J~W5HgsQ#44QEFSFMJr&_Emyw$ql5)5}0}3nPxE6?@o?&Uhj3^O2h~5&~jt_Mh zPq=SrnLlnCYy_{e=0!+ORr=g^IL%aih%;AIl+)?R{Yhy|m##y*;c^}-Dv@bV7NW21 zgDNPXo|b)J#azd#mG{RcKth74f*k(*@zjPQFafZT3`9yu&8+z%2Z2O%BW`moB2Jdt zBxs|dH(*i(Qfzek?9_*<-uE{-=QIoOzf?OZbHm+l9L7IP!XA_t{CIl=3Mjk(evc5# zg-0X7B@KlHHsmUf*>2+O!P(dO-Xpb?R|@tsMSTIk_J^>mhWRK?LUAbsa_9%1*2!`J zF09hel&=ivDuBXLXF4jqcCna1ay(KBFGNuC z0diK;@AsQ)<#|-&YXktUns9IutUgX);3aQG2qg5x_ec8u`tO+;L{a*+OOc6YhrfXS z0IOtYEF~|G1BU6OWd60-2s&N|a3~Xk*A^B%p)S`~Z z``F-~IoV2#J?1=fpIdq${$+O(&MMKRLm9BxLQE9E^ysRgk+cQbTSDp;^se9EH&flg z#(jSH-5s&Ewsv+SD094SSMt*p66G6?1mPtJK|pe81qD(MQxg+>?ai)_7aXDcKw9Mz zPG_3$k?-vj&pg2a1Wq7zdBZa?N^gABZ;Pe5m5bxk?3`q4se(gPp&X}uTVv^}cVU9a zgOQafGeuLvnLgJjc9fM;xVzA_dx!Z&x-aE?rXuco&9FO%3&PL$RfYwPV%O6Cu$p%I zM_OGM9`Zo!A{WqObRHxKSof$r@Ls|nDFV@UmbW-aFlYg6ej&X`hVlxhIX{F&J{|^l zx`iSzWcP|&NMKQ$q@xXD5Pfp*sBx9`-sKzW>FwI)Q++i*^Sj8u9K*| z9|Ndd`{f^nieG1QJlnK^BD5T1{)TGJA6Z_9<_U3`=Qjyma7@VvqF?zcsaIt>A=nd} zi-g5Q4&likHpV~3hz_iI zv)Gr9vLCOu+6`_wN6l7G)Ev}-O4X_l#BE`Ks?8wJbd;;ZAdk`D@JLZjz9gipVh& zUDDV0Jt)T#y11@r;$SrOPw@HA)h5oB24%C8`J4npl!Rf&j@#~=qmHjH^v{=ywU)Nx zU}WPfJL!Z9*%l=*i_A=ZxHhq@bBqG=e+c3rHDktQKE2Xk98oOsCx1DP#u}vQFHUdY zlV(sgei-ge8hRJFLO+nV`a3Ijd#kLIc?Sjl{E^V*7ap*kXqtj_xIS6U&Jp7FcL%+n z)2ve6C-&CI2*uWba^`l|6Lh2EKq&mF;p#hW&uRMWUC$E(V_9c?&cA1mtJ=oKq|{_^ zgZqk$hwp};VMtHG8eW!kTUcJNJTaKM-~{1wXcnn{Sc80)V2YXC&O*Wvm+!i6>Q@`W zGeev!$M%bKjsk)&Qr;1Nd#BXCZrP(ttN*XsK)FibyVVq!YPGAQ9up{8+3C+6d=s`g zM)~)l3cYviiR-iR`h=m1pm(bStP^+!KE7X65u~8%|J?tL}4diR43xID{T;E53fRWCCLZ_ zIeTgd4E^4K;(!HBBoQMTk(MU8D1Un@Zg%#>@{o@NGoKGzAYET6j-`@E9#LU}}>c7@e2 zR26u>#l_;7F)@m=M{tuzWP(;VZFf=dHSeXQid>j_W;WQ|?nmGNJ_Q9YFoK73=zm$G ztpf7Wf*ba%`WG$>-_!mWQdfU>kOomZmfhXm%?!nF#wH^#HF2YJ?s!0B6W|o@ki%t;{hhq0k4PGnQ1&c+REnZ0SyNZe>W!f?+cS zo4`Sm_V&62;&{eJ+f#^_2^^iq4b44~E}Ge3@X)E#_P5V-^YcnJYnCe3gQ%uPP#yr- zkhh16?ZYFRuBFmxk{@5sDI}fs<#gMRF$-~HNn=y}S8PfMWGT23tUz~Xb~e`n!cS8~ zhc(W4!FyaipZ<3_b;NPK|9d&KE8Q`G2|fssg`8lMDJ{Nt>6@CWj;u3^OspwZ*ccwj z(u(%q>p<659?Uaa__vla{+CAZ+^x~Ylu0PvYFN#01g|+$jY;_hJ6 z%etniTRtYQOw*l#n2n?UCnIk;NX@g3HYmEBC3tPLu-l)GQvJ%EP%+Nd z)+ZDDqbZJ(7JoTBCXV0NzIxcu{*4r4Ok&d}3)xumbfC|>D}!#{)r6u}2O>TDzUJ_G z*f+MUsFx0F&$S|$tj$$#nFDF-?M;%4sUx{vs5Q(+AGL08E`0c3aeMs;EQOd)u}S*f=enQR_%ebz>^NfjvPq7>lS>lQeHZPWbopivqT}Ge+{O~)0i;~ zS-#x-rT~C*=OgDRbzoZI=S0nc1jl8B?&h7}>mhK*nt(QcZDvRxlR-&k^PtVsB?y26R=ey%uDw4Xo(D$*Dj&#es)OOfWC~V@l0N>l00M^g{ z%!UoDH97;Je&;sme+vBvV6ZtVlfx-;9K&bltGdm+)hut@8BxQ$40e3>VDeBJ{b^&9 zi1Cff(b3Tv-P*#U*es_xh4|Q#jq~rZ@lVLLBW+9;19VmtCR98dmCmQ?PvX_=5AN*Aq}j6xmK(rt5MVxqfc+>U>c zfc%CgSjJK@Az15J=`5I1HJI6vguD?xPi=?0*~yyEIV=DwH)#?1*YV8j9EP!VV?U;q z)zw$;``jjt(ZONZZz4Y+hoSjxy-s-Vavbk2n1PY8h}pLFs8cJ~7l9EFr22CB&6!c} zaVBnd`e>X7&H1yo+jkC~#+u(pWnvMmVxS5FMpy&oF?Zxa_e6C+0mTW^PcW#fTh#HEHGxFC7l;P zJ%K|?d2A61K-ZTiG4-Fu_{aMjq;P&GY@AuCFDeT0@c36!%I`OSAo+SH|B6~2 zo|=R53;|Vh70KgraD_kj$gd@kHzNNj5Sa|(a$U620#{r#;TVQ^^` zbI*jTz5)+U)L?=2-{xRVFD5rE`z<++Klp}(1b-24ef*Ws&d;>8wEl6bn`j;2szBN+ zo29k2woKI-Cr7adpG#od!}>X30^=P}T&3#3Kf zguD23&f@W2kaPr*ddq{TT+hT&NgnQ}+2SHCZLOPpQb+ujU(_4r&WVuoiAv%Tiq1J5 zl}kR(6uPk$62}%}*W7QO0#2~}UQhax5Q((7H*jM&cdr@$=1+L-b8>mk%U#J!LQ;>Z z-=r3xzU^{b+xQmp&yj3p`05S(Kbc&0TKgy6KeVWOOyW~HU3ws)n{2_w*^iEryd zaB71&h~Z+K_UkZX(NGjHod!~0-o}Mk+@Cg|3N?qb{5yo|O4j(_H@U?%a8?s=zotH0 zZ%gA#AgQratm;AlA%NhDP>_|NY_KnAJ`%ICDqED&mr4d}(0&?1!arLsH&oMyCg%hv|Z(^1RW}(M~uu$+QOWy8Oaz7mdL4FqqB0_!v_H7Yttz-0{<9?H{)V zKzsx*Zi|pt4hoQa7y1ZA)jl5eUfi}H!8DSpaZs+`rv05C&EN_fRBb8fyQ}&0wd3QB z-{{OZEpMRT(Dvt2_dh5S6s7Y*X)N0Qe({j5@BdI0c0V)3V!}n7bz*mlxu4Z9=t7i@ z092R>n_o$!dajDM@+1@cJc;+02I&X3;u~-V`+^TOA&o8JIvM}{W|vr<@xcm@WOPPP zru)7k@w}GgkH=TvfpPJWN0|Uv1#BtAE731*&xwh+46LkpflKtNg^Q&8ng-v7=d9hS zR8Z&dcfE;2?U9j@oj3L{mZW`*qbWz08~;Jk@%$XCn!RK#4 zbKl(xIUH{}&o6^av{PSj5qS8N$@|{8QRfK&3hN#x%tqTqC8}`9kp>*v*QK}7$*K9! z8Fs%+ZVv4aI*kqO9-P$^+a~-}`*txP$fc$W+J)fgGHv4#3DSbTtSphu@~glIX%_BW zjeTmd@a~i(X(2pm(+19h{VdVMCX^VgM0!mN5<0)Bw0S=e9oBLHqS;->P3b3{txeT# zc9x+W;2Yvl$yig)`7*Kqfnzl5Y%MJ<&mXULTwhe8_a@QpA)fCOjqA$_djkoyeMw=y zFKIt!9@-!DV3nB8Me@#bS`CTD#)e;z@-?dyTfF1?ZdM&9rgFt2#)}nqXz=4wxHxpN zqCJTm85tO+^#F7uHP@%hdm~9@$sJM5rGRkpyeFywBRa3_It#2x=-yJ)KIHyyU}cP)jWa!rjs8OJ z{GV26I1|w#0|Zr>@?o-Xjr*=JG51BzL$P(^i~TlCTl=2;C&uq*b+DPwJ@L0^w!SO& z;OU8fuu<|Av#Zm?9&mJA4u#Qv6VCXvf`=CeMARRo6XM|Q?d_+}F`=~_%T+T~+iZLL zCd$8D;9?G5C0Um{hhqEms=hOMp((p3b#z4_wJs7Z`Vyz&I-8u<9c1}tCNgOm9ZVVy zZ01zlMQ#EwaKpGahf;|F3jqnnZH0q1G*QJfV^1ylZ_R;kaT2?l0E{1^#`5xGbx(K9 zQbNctCn)nUYXG_dg>)mkX*p*3sM#nYPUtAUc7CcHJMag*0b6HFdk0x|&_}oM?@)L`V_qOzAB?U0+ zR1TQgx3~Ao(nt2CWhG_?)937!{q|6sY>&P`N@|Xyi<5%;26ZbNtL)`?+Tz5R$}%Bw z$(TeXJ@1TS!~~2a(3+lJ0z~e@xL*+>VN99I;Y=u>$r1HC{&$~}w_Du6O!o$Q-{Z6_ zN2i526v7jy5NBHWKx7etDy>qkxaEfhJQYMa`j=Zq?v-Pxs7q|g!@{QY)gRK66Ue+< zToLyxp$$`|v<|o`;^I?tbMwEXvINt!Gbjj`Y| zDIN4qgQO?HIf%Hm`oLlt6Y*k#$}g}s-;gh{KP zxR)rUd?rtXnPQfSM}{#@x)d~htWoQCfC4}za=BPJFK{w>X&+U|#G+pccm4D+ZW=v@ zqji&c!DI9d2Sd$7%d@Nu5H8(95NG@r1tgS6!YqRlbrWLygFkC%sC7`d~ua7n% z?+yyI6$B>bz!PmkiLw%h1ML9d!xM8mhtlw9Vgb-ads_w?*{Wh*1d_#SF=cmS&ip*C zI`nz2{@22mq9ie{Fg}&m1z~&^8F}e~PsDJk0**XU`267#*wMLDJ2atJT|fL>VFwPm zM`;6u=sC^C3ak6}yM^8X!$n{3=mo}~zf`s#mjLe|`gMqy9Jh@95SIn6prju@DL$_| zWHa!(Hbh>5M8BH++F$orPaLq=^u@)4&R-8kQ;gFlHigLdZ}Q0ALOU0(7a$b9c0&=h zF}a?`xAb&kPCfA~^DOT~O{bs5cKYRD)1%KKE;N9!N}*6W+E)0B1XooPt(lddnv(zf zwaNH#Dbl9;Am-izYZZwd6&+9eI_zah+c2Omd54L2DmC9y2TgqORl&V;(q4M#%S`f- z@TBa5`mpE(;@tj!+5%ek>G!8=%-vst_*2w6snrAZRGwBob7~gnwInSI{3{OeQ*_nf zEsA|$E2&HaB11>kImsAp{*KG&+02L7g=mS)95COzDJAQEfr))YUoMAnkMG%_k;@c8 zJ{aSw_k9(@Rxj5SVbu7WpY^&+YgEqaeI%P3JPh%!3+eCgsBAeGfat^ibB6K~e1F`@ z{e3I+u!rw)*4!8gBq$95TPt_UTbrR0RAnBkZJSxK6cE6uL{W%;Bju4mK9n zN>}obDOc1i2z>`vTb;5(~rR(R}* zNw3S)D<}atTmS?vM5)X5tdCdotA~x(eO|Hkm>92Js@#nqAI};c2V|TOw<#WvH_&je zAP7p5%!@N5#O!pjT5O%@wrk^3l+4Y*5TPOi^mvTq;pZ?U8U#;Jr=K#VH)9M==h5Ovk$>vU$htO;AGCz{&P(tBx zK}h*A6%CrZ1$;oIVaC$pNW=>70Q%K+0LWyPe2^nqerA1sG$-A=&h*CG=~WZal&xy; zAY^mYKF@M0lAcnvD65U?4{cd-{w~6w^y0$Q1{NzTn<5DX9R(wd06aEDOe~X6&wi4h zDJU`#Yr_&o(-W?+XlIUZSPH{t6otWiJ{X3DB2X1xi!{_UQ?IZQmaEBe!7Kel*^f7N z7Vs@M<6p$_2U>s#x?Wd(T%Pwof!eR!KLD^`XmWVtAviXeOX`V;5E}6B`<1uPNxGsP z?{}3UoqtfS42UB`?Cre~M1~3h;aj*A?}aSW5mAKmGaB(x^*O8H6Of^Ev(q+geC za(sOBA5pxomrwWDmIU+al`lM*vH{^;3IP7Z4%oyScr6XV&B-lHmz)cHp5bL~^O-=z z9p;Pbni_}Y{^Q!}-2dKDeiax0=s$^Wq|X7p8eX2gs0f*;fzFrV=JQhM@%rfJAK1WKZ*%7rK&ib3n&ZUIQ0Fl8o4 zFUBZ{a-AU}ar}r%yRYO)(Kk{)k?C13?*Ns{Teko#`yQ24x?oS3-MM&Tm-`}Sqd&g> z@e%@{xO{a)85C5@TIgY@$KhBgtp>=kyt^M{Xc##@CJV>`u>SoQWjGRMOZjnw0k0W9PI2gk>auuR?-|b4 z+9LjWrilGc0F4yN7oM?J@QN*K-8u=|=Nk$StbeuS^+_UyNTomJ7mF> zz#((t7p>>pB4MG(I%J~3r(9t{dXLR-FNP|hzK;sn9Ah_?%b6C6*L_Bk`Hf;t)jT1i zr{IHa;3Cy>Rn+%2ycX4f2c#usj~q%bcO#__%9X1gmw}jq9Y{9Ov@tX57$`pt@>l~W z{P5|EXU|zERLBrz)L?3C&Fb`BtTAQGC=dVD2Fa*0;!Db1h;g@Aflh%BFu9fh)K;jB zvHIKd2Ie};83_hsV8b_>CS1q<@wtP_0$FvIg1T~D8JUqyB3Z(KXrE&XvlwY{es8-= zHWDW0qC8p!4$xLPDcVsTn{q>AON5vJQ)}XQY1JtArww%iyW1;NIzM-23{&Vd-=lEu zI;TBtnTg>~>DVbgr_wI>`CnGI^Y2P!a8*YnW=h zU%7?Q!(njOGYOBnrh*eEAEqS9a58aBaJm8l*w9mX)b=6{wuN|MRU&Pf2bn%;nYXeQ z{4UpvhPAqqOY2`{VAcO~#pJ&Lt&eF#Xm>g8^(zHITQ*H!HYiFXhz_L62Ux-HhD9I|*rg#a9u-q4( zkF<5;3u$9ntb*Q$nQ2(ZBL+A+&H-#?Q4f|5hsCrw(YEq`^E2~OdMGN<8tT(4EiO~% zZz08nB~iW9{(umoyvs!tq``i6R@3fYOD^Mm!BhcHQXK3UA?Edc z5n57kw?Th2yf~)5h}>GEZPsSoV4X6C`ezZGPI0rtO?&_h0B>p3Z1NC@eMIjEFX4|G z8KGw8CUe`w>UNQ0p81JfhLrZe?(J?N;vrIy&fy#>ALb$snk2#fWE%(B)&Nm3!#BMgu@`JTtFrlCfA)} zezD;V?w*3w6gcFPY0>44Mc(#G5Ys*yQMk+8PEP+D=ydW(Y9La#w3K-wF?K7w!(U!W zSOGdbJ*PN;uGw@l4v78V3*SRKwpEW1`klAvT)tH5D?zFI;}_QUs!oY?_3{;Y`jJ!} z$V!LT;!r5D&T!y7@4DMe`X6{tJ+G-Cn3(W6byb}Kf=^RLN!!>Uetj1crSvgX9dpf6 zcIr7ekE6qOp*qvI2&czO!+jHzslZnz?G_7`j6MKt;KJ^xZupJ>e5Dli2zh3kuz z3A?9a|5W)?gyp(_kZw`P1V#8=F9eSq92iZl1sBQ@pEz1SI+lrD>=q>H=R(7yh3*tQ zi24K1vH{c!`koa40H)lgHzz6b4=q7VazkQ%npS?k5S|G5PUv+a3`>#mK5h{;v;m7Y zK8I+8FBAn!WEUbp>@ruZm$!PE8cj*Tz<@dGBIs;tmHAaNtwb?U6l7dlI#~1W8Vg1D z_0;%VwD(R_h1&3OfjSW%{br%2O(@k)`jpzh06VywS}U&oY+X!8GZhIRCw=s|jy-NhtqXI(=OPs=xRWe9x1tykiF7))Maee33|+g5Od0&y z1Wx!pwz3bN+J8TJ@z4bIRVA7H(VroTk9OSZ2%XgfFue=<^U^>qb@1nRPYUFwv1FmF z!tqowELz2!x=6Vy6S}x37E*MXR)i}lun~7ZIv7mjPP6ITndTr+@i-dOTr|P+9-Jhf zr0X%nHeEU)M?q5=ZOZ1y7U>$Wu**uEDgqq>v-KH_?B)QFngj>}@;5(s@yd>BsL$VJ zatbmb0CTb$@uWP@rQ9{O-daP9eVIW7Y7(e~WU!sRNxYQAHbMNr-fF?1Ukne~Z~B>E zT+a)(1U~GNlOSiP-bKdW5N2D)+WuH`=Cs+Iz6K3}*O^n@=poFkd;(8!Skdn)gi7)# zylFlXm@pujbS&4*Ng%2F!$*O3T<+k z@`hoja|h{?#R%ro!{Q;%qTF=&*#JR5>M!POV~Y%Y?CWB1WcHPf&|f?7{HARH zTlbIs_fZ+7>-BDMK?D9~fWTo2Xbk$XP$nJ7U%Nq%** z7p`8RDRFbaY!LRMZB=fIp^&5%PdnMI#Yy)^h-r>C2GYp|R;6KT$-98>9qV^{edZO0 zWcFsafu%-wo5uA~ki|LXi29sud$MjJ-8VjderY?|O)a2eK`b(2w22awVdGN-?o{}k zHC^-*3xIu@YX=PMCio%)#MU9>aGoTI4e%9mUOg#WC8 z@Gh6vhtjc&M@8-Dv7kiNF>11BW;p4EI3v_E+JeX1Hbr9<0|RK|Vlp^w<1nls?6l=y zs-)P~_elATq@XUhuHC$E+QsnS3n)Z3%klw5#J-*M1CG#INfuw#f!LLhXf{ARQG9i_t%rM{Fwy$ z8B0P9p-eVsLYx>9+D_ztX*n2>;b$$HAqKFK4J{+sXiVSl_8wNre@t>{5Uj5(L!N{= zYy9S}3`#V`aKz=Eke91B#D$Ptti>*UnzPnIf85n8Hn2ZcLGAAi)7$)x{I ztLh$Esz|((W&l+bj5lMkkJ86(XSv6wq)LJZq4DAXhF2sR4?9cXtSpYQ=g~sz*(VJr z5IH0sbLA)La(SB2yaSbRH90ai-ptxlnY3D-i95 zH9m*k!6NUJlg%6m*gvrH*N3K1F-u}3iMiexO^}P%)R z)GrADtONoL83ep{F?|VWGboPOWsIyG{BPHHIp%finK3@)@7EhzixWaGq{Pcv}iMTBK3XXUg;wjC<+gnc*IQ-E; zO4J~c(1BupRPxq|gWJbn&Q2qWtzNymF?t#$@h`< zu>XU~pK-sFCWy4K@w-q)$e!)<88&2=lqzHx&5bOSpUbYiQAGGcy4@>yC0BvqonM~T z@HZ;mExC=xfS^AnLVeGI*#`KaEe!NW`rj%&pi=CQGjGq;+$5Ktw=m3r zgN$?VwRGE9nGN8wvu)X)=qAPezRFN@i6_>#%OGW=ERTi~6EXPJA@iZSJFyE-{;ab* zDBy!vk`aU)K^#xA%-6w?c0gtvdeDm8aVh^{VW}x{`mYoau^ukmd(o3GL)x!WA3*0<>m! z7Es*WoaoHm8o8}@kK4+!JP)y)@Fj=4thg+(hkHO##mEX*o`^fOFotwMFw^Dgc-KPS z#*>k|=3EZlQ<6rGP&Q9uPgPzIHG@w}WY4+c*2nVmAGP${^G9r;Qno-^kI^r;tye0( zd`BMWge6&Z2X@g+^xLyIG@&MstAII(m+E#LG6@RUsHvp87_x}br4w0{^^WB?cp*E} z<4M!Cod-xC{EgUE8+^w}fP-NIP#w>;!K#QMZj+<~cUzC-T4&MI(YcK-aFFqPs9~QV zn?k0AEX*dK*uOCAxiiB5)n z=Q3X*7?C)UDYI8VtJ!*n9Ys&rCu%doR~v#1bSPA$)!_Q7t~?ITPo1uvwj}oC_uykZ zq`py2O(iO6ZI4=i&i-bRpH0c(_E|4uR4nYIptY4Ln&12qRVbv^m8DQCA`y8U5|}bE zgj8~b{xLf?$7Vy(&AT>2K5Wb$LW?pz!ZlO%OeT`2D7x^zfU!UMI5+#PY(;ErU6(MU zUQ~j!DMvCbWnh&SY2*w%QiPpOyw2(t8e6-FgNze-a)~l%Z@jWWBfU9_8y{%ddR z@&%PcB>iy|`#{Q6@Rfm?E&^57v^L&1sHK}+Th3*;Q#c0|xx0SU$0vt-{x0R_zdPrm zQr4E&r{!!=00l4xB-v$J6Y^&xX0 zmxkC9V?N#C^?sTuJoFmnJc$&6aRpjfrBltZb#~0`O$#^W-_89A%E&ZipI1#UfwSb_ zf$Nc#r&RB!r+de1H<71=j28ofAb#iL*;fcn;OCWibVXTZ(cQ@h3C}a-=}}I-wff3D z0D)FNdZuNjwN9qBZU!5>NiHJ5vKtJEcz*w3TcpENlIs_V<5?PMp`~&r#tNUYVh&MolZpHV=DI5*mEj}gX&EERYvm~0x zN|Rko4^*0733l;a14%SbeTdr*xy$??sGB4Yux*72rXf0?_krP`_+2J4+v=M4eIbs*km>Y6-#8XzS}5lxS~Mo+(VxwY=}W61bhF z6NN&w_+7x^{gHX)r^IkHd0OK>0#S6tL*U{VM!E%M#(2Bh8b^9g+L&zugubxmE7ZsX zY}UYK$jcd-N(yhf@K;YPTg*e^vT?1&b%wcdmE)%rGsus2?{47-?fl25>|kqugp1+s zAmpPV+N}JenW7nr`S&hQYyKT+4^aRJ;4le+NhCPU(eUtkUjR~AvsWz^zjW_Yvgi}8shbqEA3G%Bp zu^9ua9BpuuXm;=~?u{aVn}=q{gXe@6JL~f;zF(uz*btHh)1oYEn`k{#4OTSjR;F~k zAUd#&nfSjFAz{a~NV*2I*rG>pnrHF!4dH=}ulVooaPO#yAIItZ7!iF)-yDm;!Ozs8 z`%xJ)Z%hCIuBG+f@|TJzZsKRleY^mi{b~?BE-7~FFrnvRy(_ws&lR7x$ME~ z`-RWLm#Y?@$Q@KAyI;3G?wfm8UrBx8t3=5F46o7bldU4*P&DeoN8@)7aCG#c{3V)kb74GCbGL&?`sfIhyF-DtSG%G6dGEVX=C=)RbbURM>c+g*_ z_EujKw$feps?~j-EIFqp{9?lsl_o$Gh!jqI=wPMO zJx#tMNZ;w4hFSp=5d-xra!HP=>?C-ij{5ES=C@l+g!KTjJ~bH2P20=8v-*Lc*DJb> zuA@$%!{UVOzca+KHMsT7tQ22sH>PcALJxUMRm|e~Lnbxya{{Y1f#l2DpUZ``^mqts zC4hqog|ZXCJ0$KGOXSJ?{FH_sExqa6-Q)Kxh=}q1#Jb@2oCc0&Y_^*;o<)KEeCyW4 z?aMbPG+$ZF@A39|+n4gHUTCJ#Y2wZ0*eiX4SoREKeI6mHn_XL#T93l745Z>(^4!Lp z5yfp^kDT3h<0KCoR(@fHK$nAkq0r&Gc`vi2jLLDd1tGeHP)>ls>?dftYr)5`>UwIO z*S~L#5CiXKGVfdEPuqTp!K5JICQw6sB(1BpE|UOOVe}O3;Th7wse_R2p#%|r@zzr7 zbKXpPNRJ*@NMS&MpbWN2Qei=c=Xr{6xc9lOJ^Bf?qob^;FTG44$Z)lxpiiCi#0QAewLw87C12)2DiW3LGp-1xq)sd9| zv7L;DiUs(17k<=q6HlX@6QLVGJ8HMCi)2<%1j-eiozWfEnU}9G0M?vSr;`=mD*?nn zdJ?BIrilfT@Q}Hj`4Rs>33-1Z-2rJCrq0EH{1t+HS^M%6`t2M93fsypSM5O)DPTQ!a*N!FX+e5?mV}X>J63 zz8lO=;McSePfE5=Svs#%&`SSppXsoIfr1eC&pzlMi7iP0>TRB?g+K^_ISYpiT8T|( zwx503_mLQw-?ZjcAZp!4Z8lVq%=mRAxdmkn-}}?Gt2aJ;!?jpql)LFtG7O!>?xGCM zLEHVZM5FMRztJ5|N0_NY`O+=S_^sHyfN-AqvNsZRfs3kW+S_T?Zr&1mZ|s56>JrEx zC#k#l`z?HR6zr0K=0ko|-_pMnCFFA?fDEhojg!M+QAs>qr5n_qLb6CjSPG?>WRB09og;Ux%M3#<%G+qQ6dNSNEf=Bu`1 zcYR5NrArPXK$Bv7q*H<0U}?r#@P7%u{f!^=tNbsZKe*wKeoqpr`u5u{gld;rCGe~6 zHU{D|mrooqi6$x-w{A9(DK3l4TH{SH$6b#yySh^V*}#qM%Y!9b59G3)q}=J~W%qf=*ITtPhcdC5?S=_fgCsg<~DlD-VOz z-4T)yDVN-^goJc~WEqrWW+x0#@t&XHZhTHXD76~pin@j+T#E#|HsZ(zx^^I_Gz*F< zr>6DkMjJ?nk8?E8j;*? z_nKiO<^D~`C{z6Rl{{)t&QLz#8?LqS(ThI#(|vALQGP_1w6Eepc0^;y@?zQa`n&{0 z|1|phf~w1vT57Ao=ewR8e>O$Y-?24#%>wCXROz1yp^-_juizC^uYSg>Jg7gIG(qFQ z%9a1MIDFQDrPI_2)OzVjPI6#z_7xpspAW zp={rZ+CQazl{>d7sPVo~QQc!FO!-6e%PBh!Vc{38qhd1G$T2O7rUyZCoeExvCuEzV zI5*LH+zvH!s5VJud~j%XYJ}tI^%&UpIeI2q{A)FBT>$!uW#=Zv!S=`1Re3;7r*1W< z>_r7KPQq1v^VaHQ0Cn-11+GGEUb7)+%jL*6Bh z^Y}Rm#HQL?gplEfv{3ir{$+kWKy?uU$w1u9&#WC&leSSnQ@>3LC{qu%F5LM%8=69F zx3_%3=uk>3g8!0h936s=O(^4yPD&XL4_|CmiAPpqMkn&P{&vi)to0~o{>pLpm+3{g zsk1|Jz=KX&{^uc}q%2>XlqqjsArPaTq7wnLt8o6q{TjRI^1iCoo5vV?_V7yTg6c+6 zDkn4<{UgFp&dUpGOv@>;#K_MDmsM9J{5o?{YiyFl7(Uye0f7G4`lNHwCDR^^2j3ZR zMa@?IFQhylib$b92Mu8Hczqb5If@gD?r65=6rzBq+|!E<+6sC{@x&s%sTZxUcfLLD z_<>xCD^=w>GK@1~c|1$sptv$S%QTpLhl)!18HcBKB^{bBB+#ifBoav*5d<#sVLuo| zpe&{9LyPV7mEX73Z8F5Q3;%)1P;>o4Dk(`uve6Oo??gex1vA8InSTUOu>AywnUwzR zihhX6^M@ky9rtV(LoI~kQ^Hysi(B3t8s21OkEhFmaCPafKwO!PHVzoxU;ZL0kSG!h z;D7ZY>)04tPx^Y_4=C>p;PBisN)?>)+5fY~tu#Q7oLmov*g#&56^}L84`PG*S}c2b z%#PpP$XMN}FN24jjA|E%BYm!szxnvIpZ_14u7R=6E=b?lwr$(CZQE>;##YK4iZ23*-TmZE+8zX~Ac0Z^55rU8HOJCGTAw~xv=yxLQ?SH&86UGQolm1LP&YVh%`?WeaH*yRr zd}@7-{NQKFz&)wnAWXF+7gtDcJPH~FK+m0tWsH&+Xq9su_R5uqXf>na8wGHapkK#u z4R#u^$fU>N1e%J+4}$>64OaR_nx(`}dVn_xf& z(gw8hiUVZ2@yEwL6jEU+%8(@UM~{LEB1bo{qOG{&CvZsQ;FtlX#8Xu6Sg^pkfxfEN zJ#GqSV;VxPcvn!%#N3j6Cfc3!R=*mhADS>P6EteR3qMV_m}shrR$d)nOAz%=u6 zR^t64lSIyb?V8lJ5yE8hB`z5m1*~DfFCO(!o|2oc&ZM3x)RoPe>bZ>tX#wAhsTVox z3u~Xn%1vD{j@rh+B~V~bO>7u5WV4Vj$411@WIdMjoXWob6^KlsE2$pO;0D=$Up@f$ zZf7YQFtKb&w_jwX*V=^>+l|e6+-|-pA(#fgnMoc90^7H^xU44TN46R8g>;g${Z0v1 zs?n;nys{zt_DEnpBZK6lpb8-@Bd1{2w?i`lp$tHR-5BZ?=!VDK8@`7A=?Rbr^X7mf^)z_KeIqM9G0VWV++ zXSa=$!+52JW*7!;s4M;{;h6-GZ*2U=)4UHNPU8y}jb__Ojd?TG@j1zy+jP&ZVIFt;% zEc}MhA>Gc%*c~sld7m~~6cMbv@PVzvQB}H%9}c&k#Msn-UB<|R1Sp8RJ>1>dgk|=k z`=dx1xUYT2Mk)&3jxZ%S)nu{9*Fi&l;^f+njDTzST)J z8Uv!+8Zr|^Vlu^)%BtRo!{v>^HN;(yQSd)rvu&@W@YM)qI^=!NhgmLIWQ?6bnPib7 ze7v(Ff>RG25rq!X(N$cMx?h2ITZ^cSS#JaK3m{PjGWo+p5lJz_P{WwdEbN>G_=UxU z=#+*IJ2i3lz7A7!%m6W{ulxyqWb>=$!1f8lShu4Jc$kjchP@G}O|8dfzz*~$ELfBvB$M9fGje=Q=9;Y3%szhW?qX5_Js%Qc3DO@)9R{Ms$SyEzNhKI z%^tbS7)%x{wIKACEWTZZd>%WavRc)C8iamZpiUqu#Jo9eJ%M7>*03=DyiKLoa-8KY zcr-oVhOx@ENM%ip{vZaK(G!(>m(BdSTUXJ*o@R9X6|lN(D7;1;q}?wQ2pTVS+K`f( zJ(k4|PtQ*n<9Rz@OrJ&GXh%%<2791|8!G?K{fPCBpyuwscJIMeRvRfqa)|% zu8FPCogjmtT?6s12NHAN_;h)06dR4>kag0^#wc`21Wa9G4^|Dq>VT zFrn&41>~Z~75$XXzL))2uxnN3ByUjBpg>p{krZe8jA zcm@-PS701RzuS}cFg{<{APU}1T)I*!G!hwAcK1eEijVv@t6*{qH#TLea^yLyPq;Eg z!^Q{ukNL6rY=tPs63lRq+HtcL2ymUtq?{=`8-L%f3`|LM{Vb2@M^7(kFu&`+kG=|FXrkGd~|drrne&gPBw`{`4pa{i~&EZRI;^u zvW}abmH<&nfT9^B)PnM{&YVG|8mkSVZFyg>UlSrO@PxgLq@0OhR^vTyGOx7HOT6H6 zSAE0m`~1BB@g*;p!AOvt-X@fNJl5LkA);7k(jXNnb#!!)iOY&?Rfyma^N#rT%5N9(0W`=>do3vayeS=@t zPQrq7m|GXEk|t~*sU9TSGE>)%?|hcPCO)l1I~8uuJLzx~L1zY*+(e@a!oS;#K+O2_ zc*$4flAzaF_H9N-Y+pNfO6$BXAi{gQCyxh9j+(J0Gb*o`CO_c#T|0WR@y7$OVYt*n zH7^Z9)DpxuORv|z+aeIiyE6wSyP^i_KpcR;3Id?9#2X?eAwZ6s{y_;_n^6ge*B-b7 zG3yC*&e6Jors4R%fZZIt9`Bybbq%`@)A<51>GXyNZAMR!iotu)z1|4K5Cy~c^I*n% zcbItjS|I3RXqAW)pe80DH-elFYv$0(07-6ek;MTuRWq+EAWU1(l#ad4chGl3xr5<= zU_yv&;a7PTOh3snOKIe}8}=AeC=obYgjs-{WLEt$c)wF{8G-%*(1hMf=l9g(&Y{QwkzZoJ<;w=8PM@merGpEd0&S*gG&*EHgiImLY}QRb*MM;T~w>3c>3YFzuWIzo;Hbfce^?&T99ZQ*Na+% zUp6yaNJU-_3K?i zo`WNhtZtkG4vW!uX}Sgaa7E=*1c;9jGBA{KZEs-8{)*V_DF5Gb{{vs$mce(GfR=CB zEEp}_ZZm2Me^&@3Qk&n~E;O<*JUmp;R52qh;Otl0qaMS@=IuF}sDR(JBLSR*FgWb$ z>G41B^19cQIwy?d{ZEN5e_pDqx{~WIFD$tqvdH#cG$OqSwAQ`bobf%I>FvMqp45)w zose0T&rb}iQM=44Q8NxEV2#eQRrjYtq(Z#w>@f5wU9G%$NXMB2|7(q170}t()Fj~| z(LDkPBamW1>mc>@lg64TO_3t_hx&1{RN8%1f5mqU1X@1MXlNNK>Nky9EeFNRo~^o; zW);dm**Fpv@DWv)6Zv_7A0A++4&*wi`S#1sk5awfqv~5Ni$m z(`z*9NS;`c)RX?VEpGyDBO<&5txbAK5^30ArZxMQ9i=I-Ke7}o6P+H7j3 zKosg6thQnP;gNpH|m00C$;Lfpq#T=WM zD5E1-)HDg#D_(>go93S| zc~}{`e3QJs$E)VNnhDuLRXnyqt!Cl`8l7Tg1d>V1+HR7gtLl z2%Bhhjl@&0b^>)qB4ByHBUxYgl+5gi+=1k~*o^pr za{B$Sve@6!5TjW3=j|Gd2QPlN`N@0(E(dkIZl*y>fY4bHuP(*8SoiV)!s; zrDmS;ID)`HNayC~<)@*x{Yk8L4E(T=^Z=;u;KDAQCPWW^-QMtt@==}TJ+8O*|5h3h z%-C8Y0hzXOY1*j{4i8^yG=>1B@%<}*t;r@Or}GljVtyD0A2aj_5>zo4bi6^Dmsw3` zpSCp-Xy|;iBHF61?a1i#fxN&!TVrvddB2%3RZeF60Z`ogcDyc#MW|a2BEuv!^t)?` zYI~yO&1mWS<(i;N4Iy`5+Ry53;My zc7NQ1l3wjW?t_pE?3F*Y*&3BSZ_JZf5B4ueuL$l6j2?Yl+%wJRnR(lyUi$kY99re< z^H7ivy&ZsqPeKzW=rMt%5c;z`-_8Ez7lih^$>4RlI9hJQ#=L`D;>Z$6SeznUfnBC+ zGM&n&%6%Os)#0QCz+#nmG6@NN1-v_y0cSkaB!;7$*PKjvGMby!U&mUOv42k+-3kwe zD0px${uxAaNy*DQNgvw)eTOni|ZH3}a3P;)bH^=s%H5-@rgC)#oGy2+$QF zw4+KaIhvrBBb=2=IBNu+m8eN8mj78C1|Tfy7C6#Of8TPen8z!D$6ZGRSCicU!Psq1 z@C)_+!^2HiL-X34>U^Wyg!hyn=|1Bf#kaWS4#1+D2Bsn7h7f|jXs-<@%&Urv#eNX_ zI;O`}$no!y+#u4`{Y&TZ#!KfGf{L~Y77ys~toM#^tkETjzy3Q0nuIS9+LhJH%r5Wc z#dGPq&K20?-DCXF2DGtrNozXQ9AMtoPI-jsV6K2}WtSZKM;ir6c`DIt+hQ{XN&Aa@ z4%c+4@L99Tx!*UN%c<7rTVW$hJGEnr;E|a+7dJHZF2VQ?vjSm+4gO8MH3*C&E`Zz- z%y?L;p<{iACkRTS+@gd+?UaqEY(O80sieC*#60?ERuf3>Ga45=TljaP8Gq50kpLiU zzoIJ$`$$WRZl_0nr*dKbDBk$bk@o*HApU6?;mR)}d^Kc6VP^xi$Uc}hPCUI`4}pB;=@ z%XM(RGQUZ*m*ysUqsO(N$(SAz`A`o*m+_Ut^AT@O4xN2k;;9g4C1M05!cz*Dx%(pl zDxCBIzogy`1Rp-t7iteypB;$?P;oFFJ*tG^Px0utNE9-+h+)D#<(YqEENB=LjVK8J zuWBh_4of+(2KJ=v zsod783kPO?Z?!7hSs%gdm%U?~@xy!se#_4sa&A0@YKAzjNcXnaXdM}K=4YN=(x~?I zMZZf@RZ{R9ik$5;yB0gmq$ImAi&*?-=OqKL>wz_~&-PM-@$daI3J~cfV(JE0w85zD zwVse?UWaLt#1O`w6Hr{9x!#2?ykW4z+;yF=k|=lOzuD3c7Od6tTPV43NU#oLK<+%m zrt=oyi%YQomAVQ?yHC`5)Q6nEqO>m?hmVSa;+34qo5(5R+|&}$@~zJ-#zzepP93!y z8}G<>dvI)~fo`>y36(>&K0r8iIK!YD5*QL91SbX@W+c{dy=SGih{a9@*5^z^#6EnQ;SXn7R|{k1R;M-=dtBrGU%Q>H9MjsrBc~no|LD1APJ+K zcJIU!o2T&2uv|lr2rmxTH4UT3YxwsB!jz5r<5Z~wu7cXbZ(0}DGFq-b)TDOR&O3KzGT9EyhtKZu6LtPxmd}QN&3MwQvU;@BN za3M*Y%lr9ETR+27eyA_pc{+fVOG9?->I!`}d@=LeZ3hvlY&QtuJT|Qr1`YE0(HYiz zXF3Kr!Mqrw=@E25=ZPrRGfr?IVbKUiw>i9rdPv4gmkadjaizAHHFNG1X^)r4$;dpO zdOrzbN#7JM90oe$%q1jk7B4$*GPwo^rJZv%S}||6rL3aa;L=D!fJ3EO^v=+XZo`L9 zZ}Lisg94+GHd`1-Lz2KMo4oF#`Apvjjv@kZCXQm|^8J3Ml%-heO~4`GgKq)ItP-z; zfW!as?W&0)-BMC?>lP-$x^Ee=WcK)LNCIBtUb~OD`91;acPX9b%dw`7LR5EX4Iv!^ z8Ci(L?9`?h@)3;2uz`8w&S63cQvSrzatFU~C9U&2 zRGduDx8RP&t7_zkgIA{XPe&bbMG*|~;3XR%i^rtlM=d^Qef#udI{tILJ;A&#;QfXJ zbWi@L?5`ioSb9DF)rEwK$|z?n>%@r1t>N)o`heI1N;u}U^rJV!cBe0pwYG#waB=M> zpfF{CDao>k?+n&9in&ft{v;;7c0CF3;MSceF#levU9`>DAs?v|ui(F(Fp_LuFTE* z4j$Eo`3no^y-0qS&xI3bL8E{uaBBedXP4j~v#LrMf6l`3%j>3`ys3QYS!otpV(4P= z&PMwokkpBZ2|b>V2Nassnl`qCbRes}FzgqmqJZORY~m;?e&l>W~GCUEi%-CtRLs(_TBk#5Xo$4Uz*U#>YGQ zw+-TWXjAbd01!DhhgwT~-^kjyCxFeyr7ru5bPi{PSM^o)PTK&Ip^)_U_qI-qP!lvd z9863mKGI)8Z-%^n`$QcmJWqj?o1^bFwnsA06)pF(?rJBMPPh@YaYuj-P&$2P{)<8` z(^d$+YN+i#m2!E`spTJSI1&(FAJ=2Cm_Q#Lp_F;g5Y41rX~(!irpxMDa|LDe(fg^-D9|>!x$wRG=xl}C{leg)YMG#x znJKRI!xTe-Fynempr6)gC>p+p0AgL(0Xneg1D*tK{D=M5ZZhgxw~tamIl}8LFfiWS z0CBAiu`x;WRcX!iIAAYm?A<7ks8jIL={u2$N)rm%yVf4UYx!Uh+LeC*5(yj43Icfz zU0wUsO|v2<(uFa`>WB!&>NAU$>cGOf+f3a6zg0Gb>a*6j#eG>tk@%=3P059WoW7TZ zw)DADIVt9(KfbSZLcexREFNNlF>rh@Y`DTGy6X^hc(;&@sZ&L9mbKPLXq%|C9XRK* z|LNZ!>3;nCyF1V6zigwVz+#UIAk4tgM>}Q6D$8If>b8IUfW6n+?r=1IHebL;jK>^p z=l^>FTy-iY%~HKDORDVktb{n)@-&ka^q&K-+^d-rK0N?Lhq#b5D%H!pMV^`>+{n{~i2VLKMl{Z`L9nK*zP@F zr*mz=kNYm=c4zm6bJ;3lAlX|+y>X^@W-y%QV)^Ccy6k;^er<1MKzX?vSP4E_UY22{ zj|h`tGAM9GI);MlN}{&XkH^r{H85C}v1Z*pC#YCba#Uv1@^NTnshcLKrOEEy4egKH z=cnemvbL7H3S0kv^6@)euG{|gwDET3H(7V>?#|)b z`#s}Z#`i0p?H9yCTR$AqpzB_CisvI<{|Y;b;d19o`IThhp6 z5p$$+VqS#WFn-v9*c4Cg0`+n2#vm!Ls}3w$sXfT7Z1Km`G%|<@a>j%cqwL^dmXsVQ z%DUfGK^Any1%IGktqvR$UvU#V+P%D8anJZUf1PvY8$QXCeExwTAn4EgUaN_CJ@~N= zC%Extlr~dI80Z7#NT@PG|7J;Rf`_>LwEZQlaTxjjh9+c>$l1NB{KL$u(_=4Wc|3y* zE!WYSS&-cMVXO$L_!0;ul&(pQx(5X()UTC*hRLR@!D9_v^4%#7nut+SCJ8wAo(7gU z{AH^|EKD_a47I@f2rdOruPXtiL-rHvRm`OhII1#YJo_0M4C;JQqtj$acC(bqPC}ed z#AR~@vLl*Vd%n@=jyK%$!((%*{&hFy^Su~;pc$?gvDW^>_2@HCFo5HEXQDvE+--Ef zKl7zGMVsklU>0Uj%e&@uElP3!WL6pd`Nh>-PMC`wn+T0SkQ^`_&voxXmsF#C&b?+X>0P9wJo$-BN3^!{&Yq+a4Ui`j}R;a12xNf|Re z$h%h7e-C?Z`>#=jWDPK)?5srI7K}gIG!b1cheqS6F~pZws5?DT2yox|i@d>e?6c_$ z{wU=0hcWt(|A76ALhh&`Piu|3ik8oXiVN2;Y+ix)%^lM$Xx}!Hm|&=g}42qY}ahqBq7A-Vub~)_tHFQ$^GGE zZs(WBL*NLJu#QfD9082_ylmPnHSoe2c~nDHK@VYscp>aiR!Ymt%o$4(?Qim|XoQq3 z`Wj2Ml(~0SK0iOR(~YhnDqa+392w0siS58_a;jF0(*(S|xy9^pP4Hg#H~Fh{U?MGA zDOR>d!ECDR=uTk5?2hSv7-GMd*U@)w@lcaLx5JhWY1Fx=LFXk}`NHp>xmb|IHjS5! zSLFY97U*e2kk{Fq)K%CRS_%$!xg+_2BGlm%avIvXr41ZJAQ)|9*_e zc?6mt0|&D9j^+Qxl5XEfjwCMp+vQ*vhq{<@3Ocxw3)~FTpCd^{lmB>=|E{tUjz4;! zrRmM-y2p_qNHzJez>4ATm{?~mApx%Mrg<2zUqaLP#FoacipQltN1u-Vuqo;)*kSzL z01gt?1oTE6Ei9Al0e9cU6-U5tf@ikzVf5Uyf?Dyvx3`^Xc?n$`W+?WWsWBim{ z-PK74w3qi$kkJ9h1gppiA)-hCCw|GFZpj*82a13 z6f)9m&{i8182SInxSo-opnk4h`Z95dGKW$ggAjs9j+#b!rAk|#)JGkkrZqKLnWT{D z6=9hAC;ep?kGANso31!rUn6)$w-1nnly5>%s#H^1eMcMmX{WzOU0HcG%xSI{2>U}a zIWa+oZf*(kwSV>aEN&{w5OUkTMrWgvrY0F*I1gewMhh3`pi@&@%;cD`GOl0yUO8EU zI}x^lp|)(c4ZSL=lJ?P!dsSfi*U+py4MN5K0$~$~oUM1faVi7=&}sJpGPZxjqyCt* zNjy4Ykvz!QH?jtig36Zl?SgfNo!nGw%ai#yJ4(P@7mUH=qpE3#X)nn$c%6F@JyZ-AX-#=x{(V`rMb!U6|=}nktw>?!}R=Fmb1i zUhOAiL-t7lqoH^PBoUAX@p9GtGNe>DBM=@Nx4m-i{|r76ELCcXDmAx87ACce{w03r zNT=Rd+fJI{6o2xf6{l{u-~U(gM9DJz~DuXneWqTQ*}lZC9J!(^Wz5motgMrH+f8 z^CSZopBe(1i+n_9)$k+!lPLO(@)-3>np4O}i zQEyL!fsV-CT6di|pTqyX5aV`31oy4Z|Dl>R8`1K2E{gBozFl66f_y}l{7R&ow5*pS>E)E8uY9h%Gv0wyi>2!S z>6j8cwH_lNspmO{A?#;Gz$;$@P%b11GAjTSgP;Ty>Eb!$v#sf3E)FMrJSlR=pN9j* z)cSlzeg`8vdPX{#c*JW9i(q#7itI7*Es$QL#t-!s$kU^#8pZ4xX#SD=6skHW_$zP3{OI!_9PHa`%0rf+ zsxt4otKB}8-IUblbFk}o;aZ2kxf6Mf!!Bi4>myQhZleC?F+Lky{P0;Ht?KIkre!u%L55`{a{7EJxFZl>&qP|3M{DB? zl=~J2ha&1Qcfxrs(E1U-BWA;^r6LqK71W_C!z|c-8asXY4pP#=VwiO{6WZ9>AqW>5 zSEU-;bL_E0N=0do()0Tc2FI`ISB{P98p>pNF+sB+3Po;5B z&f*v}Cyj=h<@drYxdYO}im*=*&D&zah5%S#h0RnUczc`|O(K;onh)WOK>LdMWo2ba z4#`wim5?)0YZ_oI6qaLmtOC64NDN{)tBUy}p9(_B2%y?dEcshe7Qvi5<5APt(`So= zq_Rj40U&6PSDSS%5+)BFKp$A6Q4YPM>rqP1*le+B(7!i)%8koBnT|oPM+*@K?jGoy zb$Y={?sdHdld&!F3UJzb&s{{IuZEAj!9X{iq5<3>(2^IXg{mR@4Z7=9isE1=%UZs~={SfiK0L{=xp`nzUo zsITGU$X>D!oQxwt{q7eDcAoE~+jJ4mw?+M2=bukK+8Ar;_PL3hJr=>;Xl^2so zTOXo*v4nv@5OQNF5{9J(=lVznGfr8$AT&o9E;kbK= zvtSMbJ_(!75Em@Evw{T^&?w0X1H?BA5^1<*$#f`tE55gNzc;FtNova*^Qd5CW)^Z= zB6xvMcPm;diz5;T6aN(6MR~?t#6EktRu&$KC(vq27S`Oz31F0k-QYc@es30?h|;E_ ztR>XOaL z9B!!O3E_dkIhM(EQ4x}_WdjBV2BnF<2IMp8N&On#_c2@eAGMdDu`uy;uYrEhu6x|J5?|Sob`#ZoV8b45mHdSPuo7Lo|LW_F{uC%LGTsk0%IemQ)1M6yb|vmQ8rhe1TyCAk>FsBjb0}W z6$LRC`Mirq7CyrM9k;Ks?WaD@R3-~qOFS#$Z`pEx{XHkPIe^EBkSB?&|Z z6Lz_KeXn=k9ava``T8DT)S;OkrPJyR5q5XQj`Dbbfx(TL9=wFfgIHAH#Z;pMxCRb9 zShswvYd$WWBw0vc-Hcuq$dH57@SBa4=)v@fn79@nJn~#0i^7d(p3BN$!pU@9JB#dq zWr)W|z>jUO@-Om#M@mhS0}qz4+zXW^pr-tKy^jeFc*vS_m84K~Cj@HDAor5|tVqfS zj6U=(at)Ie(1k%IBumg#%5vAc_fx5J`z+DJx@qTEu_FdrEHfB%{_W>~9keM1o*FEE zm{ctHvqUC^#|&3x#@^ebNrP)-AdCyV`np{}Bb-@jQ2w$gm*1;Ma;{8mA+XE#msb%i z3_6-lv=2eY?y5B*-c=l-FAd%!Cpg}=HFPUcHgWk_1gUFS@{s|r2nz3LxQ=`$mI9Jv z_BQtW71(81V%_Nk-Km$F7E(Pa?~zF1!P&zYfW?p)Ymat+tPd4da}J(`JZR>ZfJ<*tkX_U z&xb@1$!Zn22l?}UkXmRQ->x;SxX~U>({^~>^oO95GLuWtkypV44KzNjtmy9jwl?|| zNn4)OCE9z{i+&-*i{GvTffa9iv3FfoOK8qN@-t1%Ez91$L+cMz%E#y_-x379LL+Df z95YSXflXhcx&VGA*<{Vke`$Urxj>NdYF}d*Ew#S$#pef9?xFuU@2*5|?^j>H@wMTv zsCM;nz)Ze?mEQ_?g$V^4&3u_cnrX_!#SsduOe6^mkBmJA8eEjkI<#2Z>i2nn^oRVY zYN&J*ZQgc?3`ircs?nj~@k_pec$3R!9b+$$VSaQxEs{yjH@)!AzOXs5*(?TzQ*0(y z-1q3a`;}=6Q|@Qce_0(S9J0ShPLGF}PTE}yo7D_Bu+P@K#VJAH(VAJsHS;9+ojY%G z4@`>!X(*yop8DgDDC_SkQBqWT*fPV~$#6EVTr0`W;N*;Is3lPXLTZ9+KQwskB?2xt z0}Sse9V-b-sH4Aiurq%jH*nqD+)6w!jCDE}9#mJ2{(8cyDYx&ee7rrSJd~7L^Q5)< z`9=Z(q1eUG&F|L&{kc5fRL`4nbm?)&Q6&mw2_rd$@1VlhNhgRaIEE6Pk=%5Q!bCxZ zs{w78ES$5HAe%q;%%bsQY}UQny`5Fmbl{6WmR%lB#Am~5D-&!tq)z55RL-0|YJ8lr z#)U6!8Y9xoXSTRx-DjB%s3dy10=71cc~~aPD3IyVv|E|~SpWhW*kifsUrd2n3!ASn ztN@fiu^~+z$<^pz6syq=rCte-SdQ59EEUO6MFfF9v)B9+(#q0%u76==sgwBi1v=TE z^^MFWU1ga(-X*C@e{>Fw2ZKYBZ#I0$A0RxNpbJB9=@scM~>@wTyQ=G_;s!^WZ#3S{w13&131Q2}zx%{FSqmC=|uJTawwij9Ax{k)oy zlb9xOSSNT|Lj%?ndKZ?L~Y41};wDUBa_CZt#5IGuJ4+5RU<{bH>sYI0E!@lR~u zSX;yPfG5}qWtpsY?|M%6zKweEicH<&ZrS2hES~22uiv@jl1t(2aJj((9A0;+L*{8! zPh9pJ2gM%hqNeYDhZFh6pIu&+O6ef*F0TN1_IMZ#L| z407ZKZUTUW0g&LFJr#Q@+-7%6&7b*rm~P;kn+BDC0YG6K)fONx4-?HgoGYoP$VAKk zSztGK$ozT>1UFo3W#D$FUB9HFo#MOJN25Q<{6TAEfC#lHPGX|9^hXzDpXt6cR~T+h`jtC32Rs z`RlTyrQ@!;{5Vm#-fB47`0--3abTJp(!u9vyE~)FC2(|CGhFtK*)dZOf;4&`(!?u^i!>0E$ru(?Ix5~VCqA{Xuny5g z%3FC%@a_+-7lka4vEza-DSmOCO5C ziimhv?+ZqFq?@7}ronFGEOpH-S}tVy`Q}3dB{-8Lc?^_gU6`nSVV^HXbMFr4+a7$kw)hHJ}ML)-fk=AJP4;Z#l)mG zz`mti+X#NJ@KphyizBl6EAcR+_OOi?U~jKxkVSyJWB$ysz!dOkth)gzQ+;2C9KuB1 z#mUOA+q-)Whhma+NJ8(%fA|FmDr$Tt<<5B!_r7oOXqgqm|0H{)-tCx+?X)u{Vd)<{ z!LM)S)@k4W_L2L~BMvS`B-HeAS)}nT!E8k4n(iq^|MG6UL!o#`IX~Jho}&Let~fUXvnT<$|WS ztKRT`s{-NzVRL7IRE0Z>>Q`h_PEiSPSXoR=9SuF10HjTh`b7Oo38Yo%x5Gd;2_qnp zfU3c)M}j(pMB*b%X(*aub(5hx69^^yvvDE@1zqvCwH47)u@4}-2_}6UB$EZG5Kx<(!LM+2hIV2Bw5bdm~0V^UA^rNu}}mlS4_t1A=@ zP2<3LIxRh^C`nXENnvigrn1{62R{r#Ky=O+0u)VZsy7Xpj0nnVMPX{@#-sZ&f1EMH zPr@6J({|ZDSO&oh9aq!wk<$IM-Fi`~max#^_3tZi=zsFcX@!vLFJM(w&4&Uc0xiC& z{KI3IS$3SD6*jaFy%?LX`{V-ar!_$^Pv(DkOY0iE-wcV6PpvCU-#9J)M5S*8Y^>3e zCdBn6WVYMMzYQF^3lpJ0XfRn|5;#BT*83N0qb_=TkU$Y=A=MzW!rqsBf`EnlC5J;b zu}~8Dql3$w9N?mUtpAsfj~N=W;_cUp5^$HBNMy-*t<1shrum(mqvS=aRA#o|!Cdx&)$mKDVfzq zNhl&CYd#fAaM#@ZW2FHD-T*mH@Eic#phU#xByp{93=4~e50~6gbKwKa;RS#J-U`{Z(+g7+`@UMx7&F{HIyf_F^B;MP~bF*Jfx276@|4RiqH?WDY!}e~=jm`PJBByfdx<5Nx`h#l_N*oyB zq}S%Gub?(LK>v78TWHlROg|KU0}}h4rtZsDRTP*bfN4glw__LjQvGylo8U;-`K&E2aW*KsZS^~0`OX=<_Wdc}=L$9(7cZ$_TQ(Us z7PSNK>jlU_@+$PgkSTPkF0vbJ_C&-DBp{wXD!y?t%m6qd0a-BN=um@*uv4ERQ@-mY zt_h=W(7R;Dh;2%`#ZC54(Q#~4k>vlH%otSR8eAUeK9HSs z`L{NDw}==D-rHF0e!)#;7)SV-tO*xJIEp}**>tbWmdzN|K3opL{oooDv23@4nvXnoOv-^mY% zI&|Nd$!r0Z!`^fo>UR&qK1Q4n5O#R6Rjp%lMRsiZO(gL{nt~MV;DvwA(6zVPVUrjU z1ULAl5(wS>b^1DkD8h<8GgWtq3eAJ{s4EMY#3&L=awDnwC^w|9@|&Id15xk6Ow&Ia z?K*h>OZNRzIe(Q(O!!~xLb_{x<#+plG2cnTO?55*Bmbfhk-EeJoV4H9MD_#U4_ z%qcE_iAV)2=viN+-huwJpdB6z(@vY30>3S8T>ng$$IU>%dD+!v6h-&{djV{2f?$S9 z`z=obvgO_M1-J(brcToheic6oD9epzX#>O?m7i1~uz61^1`FLVl z7ImajB(;M{s#)c4Pw}QLLJ$2lazJ(-t-GFYK5e&W$vqN5nSd1ZB7NX=R9FBFDvV{) z#|S)4V|b3#sA%8^q!!ZmZ4r;$dINo2ziBZ;uZ{^=3TLb#q^Yu z_aJ_>cA`}-ex&*uG>EV%At4(F2S4!^++@0M^5-uz{>&#v-)#+iZ2bO)@&er_j?zprr#108hK zqq(`+o~jXxrXG9)3s@IXBiwRtQ-y~L)k8=HrDuPx-c|F^3Bb8sE+*Pi{bQzYjKyS#-8|#C9FL547i@uduADBMh`P|zV!z62_1_WwoDGYulS&(h zW06P#b8|!Z5)@S2@ScUqWC1ZCTp_3PIk_G%P{^FN6^Sd~mNVUO*`HcBBq%SBZxN4) zf3jY7*9L?3w)Q2j49KJ{I+!)~$OD>yWzHIzEKzJEHpr)m!^x`-6=J5^uuOS!l3C}#Ad?KygQP4EQ=3FfyFAsX3B;4ZKQbH~9KIwV;swNkzm$i7ca9fPO4a5@i*1747^MnUl3x3M4UJ zPDR6mn5gaJHbnsLJV4i8stOieO8JreaL`0cLIU1u5lRj@6y760%K=@7qm%~4MP=({`@&HjxuDCgFJcS-+x%CBP}n2%2!~OIn>$q9|5F2$k7oWJ`N|Z?GCkRVm1m2fM9P^dK=7<2co-|>h}4jLouHb zrOBu@STtjX7F3CqjJB}&RbV|sWuOm4Mj<~e*Wb*h-sD%GlRt&VZ}D1p*wHF@pjwTd zv!rJAc3mbW4E-;YFaA4T+REl$!0Bjv=l!isNX}@HC^@S&mp~A@Z^HIS^mJ|wQA1FKgHvcQB#r8tj9_!?C zspZ8G>NxIrvHEe!DENDD2+Fzv4ORJDY7004%yv|5xsy33iK%A~7`vz|K{Q69A|#p1 zNdVdNv5&X6zkTT-SemxVKfG)E?-NuCl>%pv$tpf~Ib-Ihb2Y{+M?uVYo$yY(fs<^0 zZG3}x8BMO#ZhoZBq7j<4;RwJiookkURp>Ylv7GOM^S9vy8kL+{xuO#u`XWEHKKi18 z_IJ_xg}!J|xlHO{1x|-9XpzP7id+dt-WTt7ouT57ex9#;gdEp#0rC_uXA0oWwn2JG z=qXDwD=8~B`pPK-r}WGn*t+%*{=WY2bt?3U!zyaZ7In5NVg$M?@=bx6kZ( zKn;_7D`!ztW0NrhG;Y7X9q#Y(q|}@J6&qbdEAJm&BWvn*y#w|TMpvpVMy;cjs&kKH1Lr4ovH8AdC0HtAX@c2-IMLI}wV7#`fa z&Tce~JVE04iTBDz{U?+NiISD32k^aEOHsM)TCE!8RGJVgpoRld!dtK8zY%zu%YI9nzS9NugR2t}hB zz+VJ%rnVkhDH6Y%x3p8Pc#NhJ#Bk+36mH_k9_Bfwlj3jGo`i7YM=wU_+=eVt?<)n3 z(4~t!pcQ)9UftRn7#0(s`eUFXADLx8wi`x5=7K!qzj1S5RH>rby(AT(Bb(k_6{?|K z#G~%h4)?tzZ74Eyz>qkV|!%OuJIc zVFveas^sTOjEdu4dd>G4K_#q|5E-pLYd;Fs_6uYeTLO4^86L*&jIPi`03Y)iG5ByV z^GdD-gm~jHQ24;zy>&vTxL5C64Jt*2qG-9V)B$~$&9M`tRGbwbTf8>E6RROJCECr0 z98vo5mdu!BAl`OaaAFZW3=7ALFQgPtK(FyF1?k}Wx4joznxOS*EG zU4a-El5J{^zadG;BH>q>vWB#7-F-L3>b`?rcF{8q=uL7fMIJU^H1ysNfjn-<`ms-p zuJA@EOm)I82cmzdF8gq%{u*;);>p#fs~IOw;HY)?sLvZfsqvKgkp={-d`$_?=&Hp} zzm9*qyVkW`L=_7=l0QG$wI|});$eGJ{;k}gv+PO??w}RRQSi1EvmB{?NgI#%H9e*O z!j2Xel2YvUmWHin^Hz(EWC5J+kXibd<#!p{q4l={JF2)sI=CwI8OkRTIUj^~s6VSK z9R$X_ae1p=()-(`mrULU0fP#iY(yr0EjM=-XqGl3I2`y*@|e;Ry=*tJH*bXFHVIud z6I4LmhMO32e|=Uf1`&adnRFrt4*d~N3Uyw#br-P=!h?%f3!s3hwF?%1RS0n+%9b4-V)FCy3N!! zUXv+vmmm6lMJg`Shw;-S#NkzAT)$buEuZg~g>jNghvHR)vp}QewmOpab3@g=r7AZ` z35C~v*^>nvo??;sskK3s_pOBo)*3`AD_wZI9+~j>%>K=9kby%>TT`hA|nIQV&>RHuC|gJW@H=Z*dwD}VVLGr*zgVE$JBjhWQ4HZJr- z#Sb7j&u~gvL`5%)`vs#Xe^8bwTz!(ln%Cg6?6~>W+OiRr1G4vgVXjGXrv0KJEWYb@ z%9|a#IK9u$JI6P}!N6&$w6uD)tx#4ziC}qA_>}g)m^j55n324We=QBr582MD{aKX_ z`TfS#;1}gDb*x*2-EzI^lQTMsh`|LdnF(p;`!tlcSEYyK4YabdzBTdhsnb(bHArun z%W%OBmSyk8Q*)nNydIymEg3IMlOk>W*jU|;$s4JNiIzeSaai8*VzF6gzIhN3IXsJY z^NAtn>8iXfHUwXIabrtH_~$)Cfb6?b`v^|;+6i&^bQv^oUICcRp4Kd)Mou^={oxft zIl@0rt*vGud9pC*hov>`z?R2frRYiU*Z%32LJTf}!#*tu!h*V6m@K$79{?3|5~Q|sYFen4TgH>Es05H!2I&!JMzLjrhl80KpHlf zK`k?Lb57~Oso!WpQ#XtG3>2cj5x}b0)cOk!LBpg|A%dw--Hg%bh>}j#*F)Q7xspK2xxth& zgX&RKHIi!S8Is?NC3Ze!KyzM|gyLXhbW%FvU)33zbXb-hSRY zZ+Wdt6mtdP10Q>7W2ckP=)*Dy3Mw3c#foRTjlQSW)Mr^dCj3??5f(IA5hKZdQI|G0-4Jukv@9BGY z5Z3gSd$QZ{qe+7>&S`hc?sJ4khOPI5cnbRnKhjQpS|w9%xmU7&JIGDu5pu`k%1Toz z2T{wS5+3vF{Pkhjv2%Bvg^OLEb+4A%_R4kxZ6&c;=fiO$@?!pBQIZ-N%eI1FW{VDT z_7fC}jRX270@5;xZ#@*l8}9h5>bd$w$OL7`8U4QNYOMRum4M!bA#D*Lr5#mhxw*EU zfYf$GnM8SnkM>?AO!6RWVu*FGe2U4K#sbKW|pq8wE8UnmZu=2pPY zVqm`lff~Y_dbE(36B{7usIkyuwun=khuemO$>#vbwj-!T40kGR?!l@dcwOH$NIGPk zaZuy7)Fl??u0+aqk3JJE)}Y5+BlcMJHgek=pWF5U&}SF)==VFh2ksm$rNWt@%NO^2 zSx|IB0Uj+y(b*UZDht_}$T|)h1$%n37%asyQ8)1)g(v9#*p{Q>r_Jw=%@@2{$5ALM zHP(R?#aFTBP+zmczldS27x%4Oxl(B(`M7NDt3MsSsd+}{YJPvZC7K7dPk)(6uB-c@ zEuQm!jZ|ef*1}p2ZmJ*4W6S{36Y!gyc<`1K1y}yLc~(xi;XQRiG@{gGf8?bKuhoex zfrA)xDB^CjTdm9@OjB=Now%%fx_kj%<37AeLY!tFxqtqXko>Ocx$d7r^kd;!=DN$^Mj=2lrXD$LYs`GMuAd&cm#b6crJIN)S zPJ8bum7Hi5Kj=%s=2bXu&crEpFddJ9eUOPEFVK7>11 z2+Tdcgp8%Z`rNCMb+NxrJ0EYd=|Xj+DJ6tlovj6K&^4obFQnS<#wop+*jSiw{Z9Q7 zfmrsuja96&Zio4V@r<$mUFLzedjBHfk9w4gNos1q_(!JhNIOt8kWGD3(7+o!nWV>A zlXU>Ls4gtw`};puNK%wC*TX|1jyHwR|AcIQ6F5IHPHk7%Bv8#Y=N+H0&sF=|R*!e) ztS{JW-^op%2C9qy+Kq7`O~Gzcv~fnUHElY$S;{*G(|GrOA~$Mv1==7i-17(=@Ux@e ziDP>S+d%l!o49QD(QbTLB)P0Z*(H&K@`^q_9-;1UHRPxQdYPoOD!9Io`Jw$lZf5<2 z3#M1I`+RKaxrDoN$YCLOgVDxq-_vKtR_#R<`nx-(%vS?XU9 zZ!ehF6#utOCqFXXvy*LI88aaehMw#$|G_XeYISHW1jh3lL)NRR7|H%NWUlWj7$_qL z-b%tjwzR}%%odp^=+u3p`Ou7sp!zZPcX~QD#?~cH9B$A-GS5;{p@)x+;Xl4=8;x*D zt#pwopM|DjKR3Gfi3%0mQY8W&B-Vuejl2brlZ4B-UjHt=`kQ)ukTjrxiL3SS4s_^# zO)Xxp9Jsy)QV=vx=bWZDw;=VnP1*Zd{{{auZ6P0NCBAJY%KS59O8G0^-^jH8%?tU+ zd1f4yJk{iNB2CgvGiBoLmd=(!3vh8m6>1D70L`K5@yrt%=dWjjJE*(an$kF7=Wj83KsvTKkaZbEz!Xz;NQex%(+RdJLZ6>0g;^~!&N@RW@6aE%M6ByN4HYm3wnaCA7jw>zt=p^Mhpj z`ULUEjrS(bX4Pf?d^N>2*d$SX;_qRIq+o6zrx)20k)^dYdDZT_F@CFgORp@Sh(nis z%nT3c-|3xX$JLh2_;{AazL4N*xn%*-L=T7@>aSAU-Az%|1HFvu^-bBF)w<<&_wojm zsZ&eu?CPXbxv;QsMG1~@86Dc-iSR3q30fgKmd{lGE>6mSbuaR13zCXJ4fbRd5=83y zDbl_42UD6$|1%KizT7U^cbS?*j?$w;uFF@~+OY!}7=)I`GXcO#RdMYPx=#(G@(*do zLL9prqF>S%^x@xxE@&`7=5kpO!4fzO3Rc+T*cQd4ch|EMwXFgrZNX8F=thmU%TW^xP-ZbNiLyeiJ~+{ImV+ zb%)Q|7cPZPmR>LMna-ECV_0c?ANvA399HP5(pKFaKlGFkDB2D)E+)@$Zr{Q0)n+>u zQ^pckD4R)CaHdJ{KUQApXTT1k*(UgWts&fi9mZNqYv9J)~jlc zP5A8u%A!YqqIfnK^_mY4+|OTB783f=^ooMZ(mnqCu^$-pR<01M&zOQW$PkSFHX7AR zJf;SPFTP1>jx_1UJu*>2xqbczUaqzI05u|h$39{a3+vZG+&{1$$H$9; z>&Zdq{$U~ER!mOceXK!Co*R~Vw~l_I4JC3F{5C1!i1iX5lFNH3cB84&)Z&}W0hx?6 zMX7Y21&%{4s84aJ=(yJBJJAT7Bhi`MpxH9$KD20e)}S~y=FMinGAkr~ zTiU*SXG|Jkp4C;8LonOQQ4m+}kXcP(y6wkKhwwaC)x{nlh?GF6DKkrE zJ>D@x-vla@^6gbVvOsw?)ZXx&f;hkPqfMJPV1OfORE39)BjVghgYS*=YZ@_{_CUnz zr3+gvfQ%>=0`$AE;Q~52m`Y~ViKL=f)!pW`!9(|PkxaJ#)DWOw4NRo5I~^D0Ty=Lo zn>H7}W2wvXdCzywxVWdGNQ2H+bNBL#{dJ%76Cs2C5wscBW)=dBtN=?gj5-#(&2~-d z$uGiQsw{JW$0}M)O&~6JLVs}HBP6w&^8HVkL0{F_vR`p)!80!U@q0i8K7~i5cf0bl zwO~C$tcF;5{A1=+{Wp$Y>H4-@lsW|8!Ye=Q@8CImXr$#OUR3d2-|&8yw}qx<+|2bD z`FmAqRY=vGGRbmk_YYe>x`#S{{04EDZpJi1fuUhTef?i(lsvNlCuBfHD!ibTv^1A) z^?rZc?A@V~SPKY@5yZWjse!wiS3`sG55?Y-;kzj*hg?K^VkHFqu#B}Ki; zI36Vc8wmv)p<&9P$=^`OAme!Y{YCoK>^(sodAf=YtjDrKjKsD|rfu?Tc|RvpOSm3U zQ8ZsFC*pl>LO}QN>`BJ3}-^ zP@=@*eTB>6JK0&%?F^<6NG<`#Nw#5ACKzxc9rOK!zm>G=F=8EgVVPF7;8$cZPAz(9 zTdt26pwzjCClH;hZfD1!dO(2>hcmyRDt$E{%8$>a%VYLOW-^J8;wD>^$-`sTk6})h zaYQRl2|Rc%bACEfc!u}se4W$hH#?7D+ys!I&vqVz&o=k!QX!^+v<+;ho7#~pt)l`* z2o0Z6O?Lir_|-2i44xJ*f2M&vVzD1Al1WNh z1?2OoH+c5`atw0O8kv^Wi`i>f6vK_+PM(wR*|W2nW0T3HQ$=39rAR{_At>cL3_s=* zz|r0;#E&!5-PyZ;^z0A6AgtXW^^Y+ZJ!_roM2R(EL5bBcc71LT{9R41sLF~Yk>8h! zz(M1;_mjV?%k2V!H?veR^z^6nv^)TaGJ0#4OD5~r_(4TD`v0Op(oqSt2bJ5> zN1P)(VRBWG$JTdu>MXeKOtUb&^X%LBySE^B3m2Dac7ss#Ira7HQNronX(mPqIkVC_ zjC1?{?jo0Dm@WMW1fpO3P>9p6d?Q&!uBfgkS}q8KazL+22@hv_q7740F%>$4ZP>q z(~KRiQLIEnfXqh_Rg<)eMYv`zo1xRHEIL!AnNLbg7(Cy?E*#+U+Ea&u0r)BEEW`cI z>5s_<(yA4UeokMO;Of$D4HE%NU3Pe5$v>xW4f?pHu}vn6;fEG(2?C3wTyAs}p&3&O z^P{Dn!1TV1kFasCKESZ#a0lnt+4IKs zD`B11rR23bFV?R+AL1(3#lfGdB^O8}iKppp_%-+{bVIp~g0iZ&T=$LwDX=L7J3oB) zryy>SWKvDCOS+e6TJ3sXXDTyBVgQcDVG0vD*O zy~z*NpokX`9eVEqgHQ^36!yF~-nc1G^Bbh_{d{AMUqLx!JFLR=t=)pNWLN=x^s>de zWK-0~rsE}zE|AN;48v_Xt zSLesqP@Hrh5mx<1tI-!WEO`d!LF@*L# + + + + + + + + \ No newline at end of file diff --git a/static/images/mapilio_beta.svg b/static/images/mapilio_beta.svg new file mode 100755 index 0000000..3214a6b --- /dev/null +++ b/static/images/mapilio_beta.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/static/images/mapilio_ico-modified.png b/static/images/mapilio_ico-modified.png new file mode 100755 index 0000000000000000000000000000000000000000..37d562bde77c7873b69ded748ffca33ae448518b GIT binary patch literal 91314 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 literal 0 HcmV?d00001 diff --git a/static/images/uploadIcon.svg b/static/images/uploadIcon.svg new file mode 100755 index 0000000..936e876 --- /dev/null +++ b/static/images/uploadIcon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/static/scripts/main.js b/static/scripts/main.js new file mode 100644 index 0000000..ad9008d --- /dev/null +++ b/static/scripts/main.js @@ -0,0 +1,300 @@ +document.addEventListener("DOMContentLoaded", function () { + const logoutBtn = document.getElementById('logoutBtn'); + const dropArea = document.getElementById('drop-area'); + const fileList = document.getElementById('fileList').querySelector('ul'); + + ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { + dropArea.addEventListener(eventName, preventDefaults, false); + }); + + function preventDefaults(e) { + e.preventDefault(); + e.stopPropagation(); + } + + ['dragenter', 'dragover'].forEach(eventName => { + dropArea.addEventListener(eventName, highlight, false); + }); + + ['dragleave', 'drop'].forEach(eventName => { + dropArea.addEventListener(eventName, unhighlight, false); + }); + + function highlight(e) { + dropArea.classList.add('highlight'); + } + + function unhighlight(e) { + dropArea.classList.remove('highlight'); + } + + function updateTotalImagesCount() { + const totalImagesCount = fileList.querySelectorAll('li').length; + document.getElementById('totalImagesCount').textContent = totalImagesCount; + } + + dropArea.addEventListener('drop', handleDrop, false); + + function handleDrop(e) { + e.preventDefault(); + e.stopPropagation(); + + let dt = e.dataTransfer; + console.log("Files from Drop:", dt.files); + + let droppedFiles = dt.files; + + + handleFiles(droppedFiles); + updateTotalImagesCount(); + + + let newFileList = new DataTransfer(); + + for (let i = 0; i < fileInput.files.length; i++) { + newFileList.items.add(fileInput.files[i]); + } + + for (let i = 0; i < droppedFiles.length; i++) { + newFileList.items.add(droppedFiles[i]); + } + fileInput.files = newFileList.files; + } + + + + function isDuplicate(fileName) { + let items = fileList.querySelectorAll('li'); + for (let i = 0; i < items.length; i++) { + if (items[i].innerHTML.trim() === fileName.trim()) { + return true; + } + } + return false; + } + + function handleFiles(droppedFiles) { + [...droppedFiles].forEach(file => { + if (!isDuplicate(file.name) && file.type.match('image.*')) { + uploadFile(file); + } else { + Swal.fire({ + icon: 'error', + title: 'Error', + text: `Please drop only image files, and make sure the file does not already exist in the list.`, + timer: 3000, + timerProgressBar: true, + showConfirmButton: false + }); + } + }); + updateTotalImagesCount(); + } + + const fileInput = document.getElementById('fileElem'); + + fileInput.addEventListener('change', function () { + let files = this.files; + handleFiles(files); + }, false); + + let addedFiles = []; + + function uploadFile(file) { + let li = document.createElement('li'); + li.className = 'list-group-item d-flex justify-content-between align-items-center'; + + let imageContainer = document.createElement('div'); + imageContainer.className = 'image-container'; + + let previewImg = document.createElement('img'); + previewImg.className = 'preview-image float-start rounded me-2 img-fluid'; + previewImg.src = URL.createObjectURL(file); + + imageContainer.appendChild(previewImg); + + li.appendChild(imageContainer); + + let fileInfo = document.createElement('div'); + fileInfo.className = 'text-end'; + + let fileNameSpan = document.createElement('span'); + fileNameSpan.textContent = file.name; + fileNameSpan.className = 'text-center me-auto'; + fileInfo.appendChild(fileNameSpan); + + let deleteBtn = document.createElement('button'); + deleteBtn.className = 'btn btn-danger btn-sm ms-2'; + deleteBtn.textContent = 'Delete'; + deleteBtn.addEventListener('click', function () { + let index = addedFiles.indexOf(file); + if (index !== -1) { + addedFiles.splice(index, 1); + } + li.remove(); + updateTotalImagesCount(); + removeFileFromInput(file); + }); + fileInfo.appendChild(deleteBtn); + + li.appendChild(fileInfo); + + fileList.appendChild(li); + + addedFiles.push(file); + } + + + function removeFileFromInput(fileToRemove) { + let newFileList = new DataTransfer(); + for (let i = 0; i < fileInput.files.length; i++) { + if (fileInput.files[i] !== fileToRemove) { + newFileList.items.add(fileInput.files[i]); + } + } + fileInput.files = newFileList.files; + } + + logoutBtn.addEventListener('click', function (event) { + event.preventDefault(); + + fetch('/logout', { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + Swal.fire({ + icon: 'success', + title: 'Logged Out', + text: 'You have been successfully logged out!', + timer: 2000, + timerProgressBar: true, + showConfirmButton: false + }).then(() => { + window.location.href = '/login'; + }); + } else { + Swal.fire({ + icon: 'error', + title: 'Error', + text: data.message, + timer: 2000, + timerProgressBar: true, + showConfirmButton: false + }); + } + }) + .catch(error => { + console.error('Error:', error); + }); + }); + + const uploadBtn = document.getElementById('uploadBtn'); + uploadBtn.addEventListener('click', function () { + let files = fileInput.files; + console.log("Selected files:", files); + let formData = new FormData(); + + for (let i = 0; i < files.length; i++) { + formData.append('file', files[i]); + } + if (files.length === 0) { + Swal.fire({ + icon: 'error', + title: 'Error', + text: 'Please select a file to upload.', + timer: 2000, + timerProgressBar: true, + showConfirmButton: false + }); + return; + } + + Swal.fire({ + icon: 'info', + title: 'Uploading...', + text: 'Please wait while we upload your file.', + showConfirmButton: false, + allowOutsideClick: false, + allowEscapeKey: false, + allowEnterKey: false + }); + + fetch('/upload', { + method: 'POST', + body: formData + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + fileList.innerHTML = ''; + fileInput.value = ''; + updateTotalImagesCount(); + Swal.fire({ + icon: 'success', + title: "Upload successfully done!", + html: " \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
Total images:" + data.total_images + "
Processed images:" + data.processed_images + "
Failed images:" + data.failed_images + "
", + footer: "Thanks for contributions to Mapilio 🎉", + timerProgressBar: true, + showConfirmButton: false + }) + + } else { + Swal.fire({ + icon: 'error', + title: 'Error', + text: data.message, + timer: 2000, + timerProgressBar: true, + showConfirmButton: false + }); + } + }) + .catch(data => { + Swal.fire({ + icon: 'error', + title: 'Error', + text: data.message, + timer: 2000, + timerProgressBar: true, + showConfirmButton: false + }); + }); + }); + + const removeAllBtn = document.getElementById('removeAllBtn'); + + removeAllBtn.addEventListener('click', function () { + if (fileList.innerHTML === '' && fileInput.value === '') { + Swal.fire({ + icon: 'warning', + title: 'Warning', + text: 'The file list is already empty!', + timer: 2000, + timerProgressBar: true, + showConfirmButton: false + }); + } else { + fileList.innerHTML = ''; + fileInput.value = ''; + updateTotalImagesCount(); + } + }); +}); \ No newline at end of file diff --git a/static/styles/style.css b/static/styles/style.css new file mode 100644 index 0000000..dc78aa4 --- /dev/null +++ b/static/styles/style.css @@ -0,0 +1,37 @@ +* { + margin: 0; + padding: 0; + font-family: "Comfortaa", "sans-serif"; +} + +a { + text-decoration: none; + +} + +#drop-area { + border: 2px dashed #ccc; + padding: 20px; + cursor: pointer; +} + +#drop-area.highlight { + border-color: #2196F3; +} + +p { + margin-top: 0; +} + +.file-preview { + margin-top: 20px; +} + +.border { + border: 1px solid #0518c2 !important; +} + +.preview-image { + width: 250px; /* Maksimum genişlik */ + max-height: 100px; /* Maksimum yükseklik */ +} diff --git a/templates/about.html b/templates/about.html new file mode 100644 index 0000000..72c1424 --- /dev/null +++ b/templates/about.html @@ -0,0 +1,86 @@ + + + + + + Mapilio Uploader + + + + + + + + + + +
+ + + + + + + \ No newline at end of file diff --git a/templates/login.html b/templates/login.html new file mode 100755 index 0000000..4270002 --- /dev/null +++ b/templates/login.html @@ -0,0 +1,49 @@ + + + + + Mapilio Uploader + + + + + + +
+
+
+ Image + SVG Image +

© Mapilio. 2024. All rights reserved

+
+
+
+

Mapilio Account Login

+
+
+ + +
+
+ + +
+
+ +
+

Don't have an account yet? Sign up here.

+ {% if message %} + + {% endif %} +
+
+
+
+ + + + + diff --git a/templates/support.html b/templates/support.html new file mode 100644 index 0000000..d46f101 --- /dev/null +++ b/templates/support.html @@ -0,0 +1,84 @@ + + + + + + Mapilio Uploader + + + + + + + + + + +
+
+
+ SVG Image + + + + +
+
+
+ + + + + \ No newline at end of file diff --git a/templates/upload.html b/templates/upload.html new file mode 100644 index 0000000..317dabf --- /dev/null +++ b/templates/upload.html @@ -0,0 +1,121 @@ + + + + + + Mapilio Uploader + + + + + + + + + + + {% if message %} + + {% endif %} + + + +
+
+
+ SVG Image + + + + +
+ +
+

Mapilio Kit Uploader

+
+ SVG Image +

Drag or click files here.

+ + +
+ +

Files to Upload

+

Total images of count: 0

+
+
    +
    + + +
    +
    +
    + + + + + + + + + \ No newline at end of file From c6283051c0d6158b8e71594f79a0780f6826ab71 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 3 May 2024 16:34:45 +0300 Subject: [PATCH 002/167] flask and flaskwebui libraries added. --- requirements.txt | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 7ba1673..0000000 --- a/requirements.txt +++ /dev/null @@ -1,28 +0,0 @@ -attrs==21.2.0 -certifi==2021.10.8 -chardet==3.0.4 -charset-normalizer==2.0.7 -construct==2.8.8 -ExifRead==2.3.2 -gpxpy==0.9.8 -pip>22.3.0 -idna==2.7 -jsonschema==4.1.1 -key==0.4 -piexif==1.1.3 -pymp4==1.1.0 -pynmea2==1.19.0 -pyrsistent==0.18.0 -python-dateutil==2.8.1 -requests==2.20.0 -Shapely -colorama==0.4.6 -six==1.16.0 -tqdm==4.62.3 -pandas -typing-extensions>=4.7.1 -tzwhere==3.0.3 -simple-term-menu -gps-anomaly -calculation-mapilio -windows-curses; platform_system == "Windows" From 75acd81c0f45f83c816e01e0a4fc4c4a1a19d65d Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 3 May 2024 16:35:26 +0300 Subject: [PATCH 003/167] FlaskWeb GUI was configured. --- flask_app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flask_app.py b/flask_app.py index f9a2ef7..e0a5d96 100755 --- a/flask_app.py +++ b/flask_app.py @@ -161,5 +161,5 @@ def mapilio_about_page(): if __name__ == "__main__": # webbrowser.open("http://127.0.0.1:8080/") - app.run(host="0.0.0.0", port=8080, debug=True) - # FlaskUI(app=app, server="flask", port=5050, width=1200, height=800).run() + # app.run(host="0.0.0.0", port=8080, debug=True) + FlaskUI(app=app, server="flask", port=8080, width=1200, height=800).run() From 3a81f908a95c8de2d89a22d33e2bdb183d03aaec Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 8 May 2024 09:45:27 +0300 Subject: [PATCH 004/167] Add requirements.txt --- requirements.txt | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..8ac483c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,30 @@ +attrs==21.2.0 +certifi==2021.10.8 +chardet==3.0.4 +charset-normalizer==2.0.7 +construct==2.8.8 +ExifRead==2.3.2 +gpxpy==0.9.8 +pip>22.3.0 +idna==2.7 +jsonschema==4.1.1 +key==0.4 +piexif==1.1.3 +pymp4==1.1.0 +pynmea2==1.19.0 +pyrsistent==0.18.0 +python-dateutil==2.8.1 +requests==2.20.0 +Shapely +colorama==0.4.6 +six==1.16.0 +tqdm==4.62.3 +pandas +typing-extensions>=4.7.1 +tzwhere==3.0.3 +simple-term-menu +gps-anomaly +calculation-mapilio +windows-curses; platform_system == "Windows" +flask +flaskwebgui From b10d96abc860b97db7dc8582ab86ebadad36d327 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 9 May 2024 16:30:08 +0300 Subject: [PATCH 005/167] Added missing return statement. --- docker/Dockerfile | 2 ++ docker/docker-compose.yml | 7 +++---- flask_app.py | 8 +++++--- templates/about.html | 2 +- templates/support.html | 2 +- templates/upload.html | 2 +- 6 files changed, 13 insertions(+), 10 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index bc86a1a..69799f9 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -38,3 +38,5 @@ RUN make -C /app/dependencies/max2sphere-batch -j4 RUN cp /app/dependencies/max2sphere-batch/MAX2spherebatch /app/bin/ RUN python3 -m pip install --upgrade git+https://github.com/mapilio/mapilio-kit + +RUN git clone -b flask_app https://github.com/mapilio/mapilio-kit.git diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 8aae482..fada74d 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,10 +1,9 @@ -version: '3' services: - kit: + mapiliokit: image: kit container_name: kit build: . stdin_open: true tty: true - volumes: - - :/app/data/ \ No newline at end of file + ports: + - "8080:8080" \ No newline at end of file diff --git a/flask_app.py b/flask_app.py index e0a5d96..840fd91 100755 --- a/flask_app.py +++ b/flask_app.py @@ -136,10 +136,12 @@ def mapilio_upload_page(): return jsonify(success=True, message="Images uploaded successfully", total_images=total_images, processed_images=processed_images, failed_images=failed_images), 200 except OSError as err: print(f"Error: {UPLOAD_FOLDER} could not be deleted. - {err}") + return jsonify(success=False, message=f"{err}") except subprocess.CalledProcessError as e: return jsonify(success=False, message="Error occurred while running command"), 500 else: return jsonify(success=False, message="Method Not Allowed"), 500 + return jsonify(success=False, message="Error occurred while running command"), 500 @app.route('/support', methods=['GET', 'POST']) def mapilio_support_page(): @@ -160,6 +162,6 @@ def mapilio_about_page(): return redirect(url_for("mapilio_login")) if __name__ == "__main__": - # webbrowser.open("http://127.0.0.1:8080/") - # app.run(host="0.0.0.0", port=8080, debug=True) - FlaskUI(app=app, server="flask", port=8080, width=1200, height=800).run() + webbrowser.open("http://127.0.0.1:8081/") + app.run(host="0.0.0.0", port=8081, debug=True) + # FlaskUI(app=app, server="flask", width=1200, height=800, port=8080).run() diff --git a/templates/about.html b/templates/about.html index 72c1424..1eaaa72 100644 --- a/templates/about.html +++ b/templates/about.html @@ -28,7 +28,7 @@
  • diff --git a/templates/support.html b/templates/support.html index d46f101..7625c78 100644 --- a/templates/support.html +++ b/templates/support.html @@ -28,7 +28,7 @@
  • diff --git a/templates/upload.html b/templates/upload.html index 317dabf..a9696dd 100644 --- a/templates/upload.html +++ b/templates/upload.html @@ -43,7 +43,7 @@
  • From b8677240fc73da4e94daa8d6c028783d05e6fed6 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 10 May 2024 11:04:40 +0300 Subject: [PATCH 006/167] About and support pages have been deprecated and replaced with Video upload page, but it is not active. --- flask_app.py | 21 ++--- static/scripts/main.js | 2 +- templates/about.html | 86 ------------------- templates/{upload.html => image-upload.html} | 14 +-- templates/{support.html => video-upload.html} | 19 ++-- 5 files changed, 20 insertions(+), 122 deletions(-) delete mode 100644 templates/about.html rename templates/{upload.html => image-upload.html} (90%) rename templates/{support.html => video-upload.html} (87%) diff --git a/flask_app.py b/flask_app.py index 840fd91..181c408 100755 --- a/flask_app.py +++ b/flask_app.py @@ -40,7 +40,7 @@ def check_authenticate(): def index(): token, authentication_status = check_authenticate() if authentication_status: - return render_template("upload.html", token=token) + return render_template("image-upload.html", token=token) else: return render_template('login.html') @@ -64,7 +64,7 @@ def mapilio_login(): if check_authenticate['status']: message = check_authenticate['message'] token = check_authenticate['token'] - return render_template("upload.html", message=message, token=token) + return render_template("image-upload.html", message=message, token=token) else: message = check_authenticate['message'] return render_template('login.html', message=message) @@ -91,13 +91,13 @@ def remove_accounts(): return jsonify(success=True, message="Account successfully removed!"), 200 -@app.route('/upload', methods=['GET', 'POST']) +@app.route('/image-upload', methods=['GET', 'POST']) def mapilio_upload_page(): if request.method == 'GET': token, authentication_status = check_authenticate() if authentication_status: - return render_template('upload.html', token=token) + return render_template('image-upload.html', token=token) else: return redirect(url_for("mapilio_login")) elif request.method == 'POST': @@ -143,21 +143,12 @@ def mapilio_upload_page(): return jsonify(success=False, message="Method Not Allowed"), 500 return jsonify(success=False, message="Error occurred while running command"), 500 -@app.route('/support', methods=['GET', 'POST']) +@app.route('/video-upload', methods=['GET', 'POST']) def mapilio_support_page(): token, authentication_status = check_authenticate() if authentication_status: - return render_template('support.html', token=token) - else: - return redirect(url_for("mapilio_login")) - -@app.route('/about', methods=['GET', 'POST']) -def mapilio_about_page(): - token, authentication_status = check_authenticate() - - if authentication_status: - return render_template('about.html', token=token) + return render_template('video-upload.html', token=token) else: return redirect(url_for("mapilio_login")) diff --git a/static/scripts/main.js b/static/scripts/main.js index ad9008d..2129303 100644 --- a/static/scripts/main.js +++ b/static/scripts/main.js @@ -224,7 +224,7 @@ document.addEventListener("DOMContentLoaded", function () { allowEnterKey: false }); - fetch('/upload', { + fetch('/image-upload', { method: 'POST', body: formData }) diff --git a/templates/about.html b/templates/about.html deleted file mode 100644 index 1eaaa72..0000000 --- a/templates/about.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - Mapilio Uploader - - - - - - - - - - -
    -
    - -
    -
    - - - - - - - \ No newline at end of file diff --git a/templates/upload.html b/templates/image-upload.html similarity index 90% rename from templates/upload.html rename to templates/image-upload.html index a9696dd..4abc4c8 100644 --- a/templates/upload.html +++ b/templates/image-upload.html @@ -41,21 +41,15 @@ diff --git a/templates/support.html b/templates/video-upload.html similarity index 87% rename from templates/support.html rename to templates/video-upload.html index 7625c78..77f32ca 100644 --- a/templates/support.html +++ b/templates/video-upload.html @@ -26,21 +26,15 @@ @@ -57,8 +51,12 @@
  • +
    +

    Coming Soon

    +
    + + From e1f79491a17f186c29ae98a1c024c889d20280c9 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 10 May 2024 14:09:39 +0300 Subject: [PATCH 007/167] If there is more than one mapilo account, active accounts will be removed and you will be asked to log in again. --- flask_app.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/flask_app.py b/flask_app.py index 181c408..3b7af89 100755 --- a/flask_app.py +++ b/flask_app.py @@ -14,6 +14,17 @@ UPLOAD_FOLDER = os.path.join(os.path.expanduser("~"), ".cache", "mapilio", "MapilioKit", "images/") +MAPILIO_CONFIG_PATH = os.getenv( + "MAPILIO_CONFIG_PATH", + os.path.join( + os.path.expanduser("~"), + ".config", + "mapilio", + "configs", + "CLIENT_USERS", + ), + ) + def get_args_mapilio(func): arg_names = func.__code__.co_varnames[:func.__code__.co_argcount] @@ -28,7 +39,7 @@ def check_authenticate(): elif len(list_all_users()) >= 2: token = None authentication_status = False - username = input("Found multiple Mapilio accounts. Please specify your username.\n") + remove_accounts() else: token = list_all_users()[0]['user_upload_token'] authentication_status = True @@ -74,21 +85,11 @@ def mapilio_login(): @app.route('/logout', methods=['GET']) def remove_accounts(): - MAPILIO_CONFIG_PATH = os.getenv( - "MAPILIO_CONFIG_PATH", - os.path.join( - os.path.expanduser("~"), - ".config", - "mapilio", - "configs", - "CLIENT_USERS", - ), - ) if os.path.exists(MAPILIO_CONFIG_PATH): os.remove(MAPILIO_CONFIG_PATH) + return jsonify(success=True, message="Account successfully removed!"), 200 else: return jsonify(success=True, message="No accounts found!"), 200 - return jsonify(success=True, message="Account successfully removed!"), 200 @app.route('/image-upload', methods=['GET', 'POST']) @@ -144,7 +145,7 @@ def mapilio_upload_page(): return jsonify(success=False, message="Error occurred while running command"), 500 @app.route('/video-upload', methods=['GET', 'POST']) -def mapilio_support_page(): +def mapilio_video_upload_page(): token, authentication_status = check_authenticate() if authentication_status: From ad14712140595daa6a4ddc714c58628c6cf8a68c Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 15 May 2024 12:23:56 +0300 Subject: [PATCH 008/167] Added error to return to provide information to the user in case of error. --- flask_app.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/flask_app.py b/flask_app.py index 3b7af89..072440c 100755 --- a/flask_app.py +++ b/flask_app.py @@ -104,7 +104,6 @@ def mapilio_upload_page(): elif request.method == 'POST': if 'file' not in request.files: return jsonify(success=False, message="No file part") - for file in request.files.getlist('file'): if file.filename == '': continue @@ -139,10 +138,16 @@ def mapilio_upload_page(): print(f"Error: {UPLOAD_FOLDER} could not be deleted. - {err}") return jsonify(success=False, message=f"{err}") except subprocess.CalledProcessError as e: - return jsonify(success=False, message="Error occurred while running command"), 500 + return jsonify(success=False, message={e}), 500 else: return jsonify(success=False, message="Method Not Allowed"), 500 - return jsonify(success=False, message="Error occurred while running command"), 500 + error_message = result.stderr.split() + error_message = ' '.join(error_message) + error_index = error_message.find("error:") + if error_index != -1: + error_message = error_message[error_index:] + return jsonify(success=False, message=f"Error: {error_message}"), 500 + @app.route('/video-upload', methods=['GET', 'POST']) def mapilio_video_upload_page(): From 584e7af3d501a01bfc2f90a06dd67e447ba28c97 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 15 May 2024 14:17:40 +0300 Subject: [PATCH 009/167] spec file added --- flask_app.spec | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 flask_app.spec diff --git a/flask_app.spec b/flask_app.spec new file mode 100644 index 0000000..2e778e7 --- /dev/null +++ b/flask_app.spec @@ -0,0 +1,56 @@ +# -*- mode: python ; coding: utf-8 -*- + +block_cipher = None +options = [("u", None, "OPTION")] + +a = Analysis( + ['flask_app.py'], + pathex=[SPECPATH], + binaries=[], + datas=[ + ('templates', 'templates'), + ('static', 'static'), + ('mapilio_kit', 'mapilio_kit'), + ('testkit-env/lib/python3.10/site-packages/piexif', 'piexif'), + ('testkit-env/lib/python3.10/site-packages/calculation', 'calculation'), + ('testkit-env/lib/python3.10/site-packages/gps_anomaly', 'gps_anomaly'), + ('testkit-env/lib/python3.10/site-packages/exifread', 'exifread'), + ('testkit-env/lib/python3.10/site-packages/gpxpy', 'gpxpy'), + ('testkit-env/lib/python3.10/site-packages/pynmea2', 'pynmea2'), + ('testkit-env/lib/python3.10/site-packages/flask', 'flask'), + ('testkit-env/lib/python3.10/site-packages/flaskwebgui.py','.'), + ('testkit-env/lib/python3.10/site-packages/psutil', 'psutil'), + ('testkit-env/lib/python3.10/site-packages/requests', 'requests'), + ('testkit-env/lib/python3.10/site-packages/urllib3', 'urllib3'), + ('testkit-env/lib/python3.10/site-packages/chardet', 'chardet'), + ('testkit-env/lib/python3.10/site-packages/certifi', 'certifi'), + ('testkit-env/lib/python3.10/site-packages/jsonschema','jsonschema'), + ('testkit-env/lib/python3.10/site-packages/attr', 'attr'), + ('testkit-env/lib/python3.10/site-packages/pyrsistent','pyrsistent') + ], + hiddenimports=[], + hookspath=[], + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, +) +pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) + +exe = EXE( + pyz, + a.scripts, + options, + a.binaries, + a.zipfiles, + a.datas, + name="MapilioKit-Flask", + debug=False, + strip=False, + upx=True, + runtime_tmpdir=None, + console=True, +) + +app = BUNDLE(exe, name="kit-gui.app", icon=None, bundle_identifier=None) \ No newline at end of file From 21909b90009dd71f44708a90cba8defba8fdb260 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 15 May 2024 17:45:04 +0300 Subject: [PATCH 010/167] pytest library added to requirements file. --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 8ac483c..8590681 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,3 +28,4 @@ calculation-mapilio windows-curses; platform_system == "Windows" flask flaskwebgui +pytest \ No newline at end of file From 610d49f2a39639c142485069b123dbb89fb6c264 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 15 May 2024 17:45:45 +0300 Subject: [PATCH 011/167] Added python-package.yml workflow. --- .github/workflows/python-package.yml | 68 ++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 .github/workflows/python-package.yml diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml new file mode 100644 index 0000000..f202a29 --- /dev/null +++ b/.github/workflows/python-package.yml @@ -0,0 +1,68 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Python package + +on: + pull_request: + branches: [flask_app] + push: + branches: [flask_app] + +jobs: + build: + strategy: + matrix: + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + platform: ["ubuntu-latest", "macos-latest", "windows-latest"] + # Optional - x64 or x86 architecture, defaults to x64 + # architecture: "x64" + + runs-on: ${{ matrix.platform }} + + defaults: + run: + working-directory: ./flask_app + + steps: + # https://github.com/actions/checkout#Checkout-multiple-repos-side-by-side + # pull + - uses: actions/checkout@v4 + with: + path: main + + # https://github.com/actions/checkout#Checkout-multiple-repos-side-by-side + - name: Setup ExifTool + uses: actions/checkout@v4 + with: + repository: "exiftool/exiftool" + path: exiftool + + - name: Check ExifTool version + # https://exiftool.org/install.html + run: | + mv ${{ github.workspace }}/exiftool/exiftool ${{ github.workspace }}/exiftool/exiftool.pl + perl ${{ github.workspace }}/exiftool/exiftool.pl -ver + + - name: Setup FFmpeg + uses: FedericoCarboni/setup-ffmpeg@v2 + if: matrix.platform != 'macos-latest' + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install . + python -m pip install -r requirements-dev.txt + + - name: Test with pytest + run: | + mapilio_kit --version + pytest -s -vv tests + env: + MAPILIO_KIT__TESTS_EXECUTABLE: mapilio-kit + MAPILLARY_TOOLS__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl \ No newline at end of file From 29921b8252716e59c67aa551d0e9e3573373755c Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 09:42:15 +0300 Subject: [PATCH 012/167] working-directory changed to main --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index f202a29..700d079 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -22,7 +22,7 @@ jobs: defaults: run: - working-directory: ./flask_app + working-directory: ./main steps: # https://github.com/actions/checkout#Checkout-multiple-repos-side-by-side From caafc7b641a3e45f883bc6e5c89d11cde4f30744 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 09:49:45 +0300 Subject: [PATCH 013/167] python3.12 support removed from the workflow. --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 700d079..a830f1c 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -13,7 +13,7 @@ jobs: build: strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.8", "3.9", "3.10", "3.11"] platform: ["ubuntu-latest", "macos-latest", "windows-latest"] # Optional - x64 or x86 architecture, defaults to x64 # architecture: "x64" From 4dfec6948b62e7ed7198c7544fe0d71d20114217 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 10:16:45 +0300 Subject: [PATCH 014/167] Refactor python-package.yml --- .github/workflows/python-package.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index a830f1c..f98e2e7 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -57,7 +57,6 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install . - python -m pip install -r requirements-dev.txt - name: Test with pytest run: | @@ -65,4 +64,4 @@ jobs: pytest -s -vv tests env: MAPILIO_KIT__TESTS_EXECUTABLE: mapilio-kit - MAPILLARY_TOOLS__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl \ No newline at end of file + MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl \ No newline at end of file From ace64f9ce08f975d95595c943187113de6538223 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 11:20:31 +0300 Subject: [PATCH 015/167] Refactor python-package.yml --- .github/workflows/python-package.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index f98e2e7..28e2d73 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -1,6 +1,3 @@ -# This workflow will install Python dependencies, run tests and lint with a variety of Python versions -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions - name: Python package on: @@ -13,10 +10,8 @@ jobs: build: strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python_version: ["3.8", "3.9", "3.10", "3.11"] platform: ["ubuntu-latest", "macos-latest", "windows-latest"] - # Optional - x64 or x86 architecture, defaults to x64 - # architecture: "x64" runs-on: ${{ matrix.platform }} @@ -25,13 +20,11 @@ jobs: working-directory: ./main steps: - # https://github.com/actions/checkout#Checkout-multiple-repos-side-by-side - # pull - - uses: actions/checkout@v4 + - name: Checkout main repository + uses: actions/checkout@v4 with: path: main - # https://github.com/actions/checkout#Checkout-multiple-repos-side-by-side - name: Setup ExifTool uses: actions/checkout@v4 with: @@ -39,7 +32,6 @@ jobs: path: exiftool - name: Check ExifTool version - # https://exiftool.org/install.html run: | mv ${{ github.workspace }}/exiftool/exiftool ${{ github.workspace }}/exiftool/exiftool.pl perl ${{ github.workspace }}/exiftool/exiftool.pl -ver @@ -48,15 +40,23 @@ jobs: uses: FedericoCarboni/setup-ffmpeg@v2 if: matrix.platform != 'macos-latest' - - name: Set up Python ${{ matrix.python-version }} + - name: Set up Python ${{ matrix.python_version }} uses: actions/setup-python@v5 with: - python-version: ${{ matrix.python-version }} + python-version: ${{ matrix.python_version }} - name: Install dependencies run: | python -m pip install --upgrade pip + echo "Installing package..." python -m pip install . + echo "Package installation complete." + + - name: Create directory if not exists + run: mkdir -p build/bdist.macosx-10.9-universal2/wheel/mapilio_kit + + - name: Copy __init__.py file + run: cp build/lib/mapilio_kit/__init__.py build/bdist.macosx-10.9-universal2/wheel/mapilio_kit - name: Test with pytest run: | @@ -64,4 +64,4 @@ jobs: pytest -s -vv tests env: MAPILIO_KIT__TESTS_EXECUTABLE: mapilio-kit - MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl \ No newline at end of file + MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl From e573b296bb05b9976c2a857d09c1f81d4921dcf9 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 11:31:16 +0300 Subject: [PATCH 016/167] Refactor python-package.yml --- .github/workflows/python-package.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 28e2d73..7064259 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -45,18 +45,21 @@ jobs: with: python-version: ${{ matrix.python_version }} + - name: Upgrade pip + run: python -m pip install --upgrade pip + - name: Install dependencies run: | - python -m pip install --upgrade pip - echo "Installing package..." - python -m pip install . - echo "Package installation complete." + python -m pip install . || true + + - name: Verify installed packages + run: python -m pip freeze - name: Create directory if not exists run: mkdir -p build/bdist.macosx-10.9-universal2/wheel/mapilio_kit - name: Copy __init__.py file - run: cp build/lib/mapilio_kit/__init__.py build/bdist.macosx-10.9-universal2/wheel/mapilio_kit + run: cp build/lib/mapilio_kit/__init__.py build/bdist.macosx-10.9-universal2/wheel/mapilio_kit || true - name: Test with pytest run: | From 8574d4bc79f0a8a7d58fa13869c4fe39978c713e Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 11:58:20 +0300 Subject: [PATCH 017/167] Refactor python-package.yml --- .github/workflows/python-package.yml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 7064259..33d7119 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -49,17 +49,8 @@ jobs: run: python -m pip install --upgrade pip - name: Install dependencies - run: | - python -m pip install . || true - - - name: Verify installed packages - run: python -m pip freeze - - - name: Create directory if not exists - run: mkdir -p build/bdist.macosx-10.9-universal2/wheel/mapilio_kit + run: python -m pip install -e . - - name: Copy __init__.py file - run: cp build/lib/mapilio_kit/__init__.py build/bdist.macosx-10.9-universal2/wheel/mapilio_kit || true - name: Test with pytest run: | From da5d64d6fe9447940513960ad72c487050ccb167 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 14:09:43 +0300 Subject: [PATCH 018/167] Fixed an error due to multiple setup. --- setup.py | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/setup.py b/setup.py index f1fe42b..3594c47 100644 --- a/setup.py +++ b/setup.py @@ -22,31 +22,6 @@ def read_requirements_macos(): with open('requirements.txt') as fp: return [row.strip() for row in fp if row.strip()] -if platform.system() == "Darwin": - about = {} - with open(os.path.join(here, 'mapilio_kit', 'components', 'version.py'), 'r') as f: - exec(f.read(), about) - - setup(name='mapilio-kit', - version=about['VERSION'], - description='MAPILIO Image/Video Upload Pipeline', - long_description=open('README.md').read(), - long_description_content_type='text/markdown', - url='https://github.com/mapilio/mapilio-kit-v2', - author='Mapilio', - license='MIT License', - python_requires=">=3.6, !=3.12.*", - packages=['mapilio_kit', 'mapilio_kit.base', 'mapilio_kit.components'], - entry_points=''' - [console_scripts] - mapilio_kit=mapilio_kit.__main__:main - ''', - install_requires=read_requirements_macos(), - - ) - print("Installed") - - class MakeExtension(Extension): def __init__(self, name, sourcedir=''): Extension.__init__(self, name, sources=[]) From f7df6b01d66e97d8ee040b29937fc1fdb9ab7a0b Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 14:32:36 +0300 Subject: [PATCH 019/167] Fixed an error due to multiple setup. --- setup.py | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/setup.py b/setup.py index f1fe42b..65ce1bf 100644 --- a/setup.py +++ b/setup.py @@ -22,31 +22,6 @@ def read_requirements_macos(): with open('requirements.txt') as fp: return [row.strip() for row in fp if row.strip()] -if platform.system() == "Darwin": - about = {} - with open(os.path.join(here, 'mapilio_kit', 'components', 'version.py'), 'r') as f: - exec(f.read(), about) - - setup(name='mapilio-kit', - version=about['VERSION'], - description='MAPILIO Image/Video Upload Pipeline', - long_description=open('README.md').read(), - long_description_content_type='text/markdown', - url='https://github.com/mapilio/mapilio-kit-v2', - author='Mapilio', - license='MIT License', - python_requires=">=3.6, !=3.12.*", - packages=['mapilio_kit', 'mapilio_kit.base', 'mapilio_kit.components'], - entry_points=''' - [console_scripts] - mapilio_kit=mapilio_kit.__main__:main - ''', - install_requires=read_requirements_macos(), - - ) - print("Installed") - - class MakeExtension(Extension): def __init__(self, name, sourcedir=''): Extension.__init__(self, name, sources=[]) @@ -127,4 +102,4 @@ def win_read_requirements(): mapilio_kit=mapilio_kit.__main__:main ''', install_requires=requires - ) + ) \ No newline at end of file From 7bb849f3894c216811eef2c8fc1c38b8e884d8e2 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 3 May 2024 15:35:48 +0300 Subject: [PATCH 020/167] Flask web interface added. --- flask_app.py | 165 ++++++++++++++ static/images/mapilio.png | Bin 0 -> 74572 bytes static/images/mapilioWatermark.svg | 17 ++ static/images/mapilio_beta.svg | 13 ++ static/images/mapilio_ico-modified.png | Bin 0 -> 91314 bytes static/images/uploadIcon.svg | 8 + static/scripts/main.js | 300 +++++++++++++++++++++++++ static/styles/style.css | 37 +++ templates/about.html | 86 +++++++ templates/login.html | 49 ++++ templates/support.html | 84 +++++++ templates/upload.html | 121 ++++++++++ 12 files changed, 880 insertions(+) create mode 100755 flask_app.py create mode 100755 static/images/mapilio.png create mode 100755 static/images/mapilioWatermark.svg create mode 100755 static/images/mapilio_beta.svg create mode 100755 static/images/mapilio_ico-modified.png create mode 100755 static/images/uploadIcon.svg create mode 100644 static/scripts/main.js create mode 100644 static/styles/style.css create mode 100644 templates/about.html create mode 100755 templates/login.html create mode 100644 templates/support.html create mode 100644 templates/upload.html diff --git a/flask_app.py b/flask_app.py new file mode 100755 index 0000000..f9a2ef7 --- /dev/null +++ b/flask_app.py @@ -0,0 +1,165 @@ +from flask import Flask, render_template, request, redirect, url_for, jsonify +from flaskwebgui import FlaskUI +import webbrowser +import os +import subprocess +import shutil +import re + +from mapilio_kit.base import authenticator +from mapilio_kit.components.edit_config import edit_config +from mapilio_kit.components.login import list_all_users + +app = Flask(__name__) + +UPLOAD_FOLDER = os.path.join(os.path.expanduser("~"), ".cache", "mapilio", "MapilioKit", "images/") + + +def get_args_mapilio(func): + arg_names = func.__code__.co_varnames[:func.__code__.co_argcount] + return {arg: None for arg in arg_names} + + +def check_authenticate(): + global authentication_status, token + if len(list_all_users()) == 0: + authentication_status = False + token = None + elif len(list_all_users()) >= 2: + token = None + authentication_status = False + username = input("Found multiple Mapilio accounts. Please specify your username.\n") + else: + token = list_all_users()[0]['user_upload_token'] + authentication_status = True + + return token, authentication_status + + +@app.route("/", methods=["GET", "POST"]) +def index(): + token, authentication_status = check_authenticate() + if authentication_status: + return render_template("upload.html", token=token) + else: + return render_template('login.html') + + +@app.route('/login', methods=['GET','POST']) +def mapilio_login(): + + if request.method == 'POST': + args = get_args_mapilio(edit_config) + email = request.form['email'].strip() + password = request.form['password'].strip() + + username = email.split('@')[0] + + args["user_name"] = username + args["user_email"] = email + args["user_password"] = str(password) + args["gui"] = True + check_authenticate = authenticator().perform_task(args) + + if check_authenticate['status']: + message = check_authenticate['message'] + token = check_authenticate['token'] + return render_template("upload.html", message=message, token=token) + else: + message = check_authenticate['message'] + return render_template('login.html', message=message) + else: + return render_template('login.html') + + +@app.route('/logout', methods=['GET']) +def remove_accounts(): + MAPILIO_CONFIG_PATH = os.getenv( + "MAPILIO_CONFIG_PATH", + os.path.join( + os.path.expanduser("~"), + ".config", + "mapilio", + "configs", + "CLIENT_USERS", + ), + ) + if os.path.exists(MAPILIO_CONFIG_PATH): + os.remove(MAPILIO_CONFIG_PATH) + else: + return jsonify(success=True, message="No accounts found!"), 200 + return jsonify(success=True, message="Account successfully removed!"), 200 + + +@app.route('/upload', methods=['GET', 'POST']) +def mapilio_upload_page(): + if request.method == 'GET': + token, authentication_status = check_authenticate() + + if authentication_status: + return render_template('upload.html', token=token) + else: + return redirect(url_for("mapilio_login")) + elif request.method == 'POST': + if 'file' not in request.files: + return jsonify(success=False, message="No file part") + + for file in request.files.getlist('file'): + if file.filename == '': + continue + if not os.path.exists(UPLOAD_FOLDER): + os.makedirs(UPLOAD_FOLDER, exist_ok=True) + file.save(UPLOAD_FOLDER + file.filename) + + command = f"mapilio_kit upload {UPLOAD_FOLDER} --dry_run" + try: + result = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + + if result.returncode == 0: + try: + shutil.rmtree(UPLOAD_FOLDER) + output_lines = result.stderr.split('\n') + output_text = '\n'.join(output_lines) + + total_images_pattern = r'"total_images": (\d+)' + processed_images_pattern = r'"processed_images": (\d+)' + failed_images_pattern = r'"failed_images": (\d+)' + + total_images_match = re.search(total_images_pattern, output_text) + processed_images_match = re.search(processed_images_pattern, output_text) + failed_images_match = re.search(failed_images_pattern, output_text) + + total_images = int(total_images_match.group(1)) if total_images_match else 0 + processed_images = int(processed_images_match.group(1)) if processed_images_match else 0 + failed_images = int(failed_images_match.group(1)) if failed_images_match else 0 + + return jsonify(success=True, message="Images uploaded successfully", total_images=total_images, processed_images=processed_images, failed_images=failed_images), 200 + except OSError as err: + print(f"Error: {UPLOAD_FOLDER} could not be deleted. - {err}") + except subprocess.CalledProcessError as e: + return jsonify(success=False, message="Error occurred while running command"), 500 + else: + return jsonify(success=False, message="Method Not Allowed"), 500 + +@app.route('/support', methods=['GET', 'POST']) +def mapilio_support_page(): + token, authentication_status = check_authenticate() + + if authentication_status: + return render_template('support.html', token=token) + else: + return redirect(url_for("mapilio_login")) + +@app.route('/about', methods=['GET', 'POST']) +def mapilio_about_page(): + token, authentication_status = check_authenticate() + + if authentication_status: + return render_template('about.html', token=token) + else: + return redirect(url_for("mapilio_login")) + +if __name__ == "__main__": + # webbrowser.open("http://127.0.0.1:8080/") + app.run(host="0.0.0.0", port=8080, debug=True) + # FlaskUI(app=app, server="flask", port=5050, width=1200, height=800).run() diff --git a/static/images/mapilio.png b/static/images/mapilio.png new file mode 100755 index 0000000000000000000000000000000000000000..07c14718bbf3f3e9de33c0596014ac88fad14665 GIT binary patch literal 74572 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 + + + + + + + + \ No newline at end of file diff --git a/static/images/mapilio_beta.svg b/static/images/mapilio_beta.svg new file mode 100755 index 0000000..3214a6b --- /dev/null +++ b/static/images/mapilio_beta.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/static/images/mapilio_ico-modified.png b/static/images/mapilio_ico-modified.png new file mode 100755 index 0000000000000000000000000000000000000000..37d562bde77c7873b69ded748ffca33ae448518b GIT binary patch literal 91314 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 literal 0 HcmV?d00001 diff --git a/static/images/uploadIcon.svg b/static/images/uploadIcon.svg new file mode 100755 index 0000000..936e876 --- /dev/null +++ b/static/images/uploadIcon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/static/scripts/main.js b/static/scripts/main.js new file mode 100644 index 0000000..ad9008d --- /dev/null +++ b/static/scripts/main.js @@ -0,0 +1,300 @@ +document.addEventListener("DOMContentLoaded", function () { + const logoutBtn = document.getElementById('logoutBtn'); + const dropArea = document.getElementById('drop-area'); + const fileList = document.getElementById('fileList').querySelector('ul'); + + ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { + dropArea.addEventListener(eventName, preventDefaults, false); + }); + + function preventDefaults(e) { + e.preventDefault(); + e.stopPropagation(); + } + + ['dragenter', 'dragover'].forEach(eventName => { + dropArea.addEventListener(eventName, highlight, false); + }); + + ['dragleave', 'drop'].forEach(eventName => { + dropArea.addEventListener(eventName, unhighlight, false); + }); + + function highlight(e) { + dropArea.classList.add('highlight'); + } + + function unhighlight(e) { + dropArea.classList.remove('highlight'); + } + + function updateTotalImagesCount() { + const totalImagesCount = fileList.querySelectorAll('li').length; + document.getElementById('totalImagesCount').textContent = totalImagesCount; + } + + dropArea.addEventListener('drop', handleDrop, false); + + function handleDrop(e) { + e.preventDefault(); + e.stopPropagation(); + + let dt = e.dataTransfer; + console.log("Files from Drop:", dt.files); + + let droppedFiles = dt.files; + + + handleFiles(droppedFiles); + updateTotalImagesCount(); + + + let newFileList = new DataTransfer(); + + for (let i = 0; i < fileInput.files.length; i++) { + newFileList.items.add(fileInput.files[i]); + } + + for (let i = 0; i < droppedFiles.length; i++) { + newFileList.items.add(droppedFiles[i]); + } + fileInput.files = newFileList.files; + } + + + + function isDuplicate(fileName) { + let items = fileList.querySelectorAll('li'); + for (let i = 0; i < items.length; i++) { + if (items[i].innerHTML.trim() === fileName.trim()) { + return true; + } + } + return false; + } + + function handleFiles(droppedFiles) { + [...droppedFiles].forEach(file => { + if (!isDuplicate(file.name) && file.type.match('image.*')) { + uploadFile(file); + } else { + Swal.fire({ + icon: 'error', + title: 'Error', + text: `Please drop only image files, and make sure the file does not already exist in the list.`, + timer: 3000, + timerProgressBar: true, + showConfirmButton: false + }); + } + }); + updateTotalImagesCount(); + } + + const fileInput = document.getElementById('fileElem'); + + fileInput.addEventListener('change', function () { + let files = this.files; + handleFiles(files); + }, false); + + let addedFiles = []; + + function uploadFile(file) { + let li = document.createElement('li'); + li.className = 'list-group-item d-flex justify-content-between align-items-center'; + + let imageContainer = document.createElement('div'); + imageContainer.className = 'image-container'; + + let previewImg = document.createElement('img'); + previewImg.className = 'preview-image float-start rounded me-2 img-fluid'; + previewImg.src = URL.createObjectURL(file); + + imageContainer.appendChild(previewImg); + + li.appendChild(imageContainer); + + let fileInfo = document.createElement('div'); + fileInfo.className = 'text-end'; + + let fileNameSpan = document.createElement('span'); + fileNameSpan.textContent = file.name; + fileNameSpan.className = 'text-center me-auto'; + fileInfo.appendChild(fileNameSpan); + + let deleteBtn = document.createElement('button'); + deleteBtn.className = 'btn btn-danger btn-sm ms-2'; + deleteBtn.textContent = 'Delete'; + deleteBtn.addEventListener('click', function () { + let index = addedFiles.indexOf(file); + if (index !== -1) { + addedFiles.splice(index, 1); + } + li.remove(); + updateTotalImagesCount(); + removeFileFromInput(file); + }); + fileInfo.appendChild(deleteBtn); + + li.appendChild(fileInfo); + + fileList.appendChild(li); + + addedFiles.push(file); + } + + + function removeFileFromInput(fileToRemove) { + let newFileList = new DataTransfer(); + for (let i = 0; i < fileInput.files.length; i++) { + if (fileInput.files[i] !== fileToRemove) { + newFileList.items.add(fileInput.files[i]); + } + } + fileInput.files = newFileList.files; + } + + logoutBtn.addEventListener('click', function (event) { + event.preventDefault(); + + fetch('/logout', { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + Swal.fire({ + icon: 'success', + title: 'Logged Out', + text: 'You have been successfully logged out!', + timer: 2000, + timerProgressBar: true, + showConfirmButton: false + }).then(() => { + window.location.href = '/login'; + }); + } else { + Swal.fire({ + icon: 'error', + title: 'Error', + text: data.message, + timer: 2000, + timerProgressBar: true, + showConfirmButton: false + }); + } + }) + .catch(error => { + console.error('Error:', error); + }); + }); + + const uploadBtn = document.getElementById('uploadBtn'); + uploadBtn.addEventListener('click', function () { + let files = fileInput.files; + console.log("Selected files:", files); + let formData = new FormData(); + + for (let i = 0; i < files.length; i++) { + formData.append('file', files[i]); + } + if (files.length === 0) { + Swal.fire({ + icon: 'error', + title: 'Error', + text: 'Please select a file to upload.', + timer: 2000, + timerProgressBar: true, + showConfirmButton: false + }); + return; + } + + Swal.fire({ + icon: 'info', + title: 'Uploading...', + text: 'Please wait while we upload your file.', + showConfirmButton: false, + allowOutsideClick: false, + allowEscapeKey: false, + allowEnterKey: false + }); + + fetch('/upload', { + method: 'POST', + body: formData + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + fileList.innerHTML = ''; + fileInput.value = ''; + updateTotalImagesCount(); + Swal.fire({ + icon: 'success', + title: "Upload successfully done!", + html: " \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
    Total images:" + data.total_images + "
    Processed images:" + data.processed_images + "
    Failed images:" + data.failed_images + "
    ", + footer: "Thanks for contributions to Mapilio 🎉", + timerProgressBar: true, + showConfirmButton: false + }) + + } else { + Swal.fire({ + icon: 'error', + title: 'Error', + text: data.message, + timer: 2000, + timerProgressBar: true, + showConfirmButton: false + }); + } + }) + .catch(data => { + Swal.fire({ + icon: 'error', + title: 'Error', + text: data.message, + timer: 2000, + timerProgressBar: true, + showConfirmButton: false + }); + }); + }); + + const removeAllBtn = document.getElementById('removeAllBtn'); + + removeAllBtn.addEventListener('click', function () { + if (fileList.innerHTML === '' && fileInput.value === '') { + Swal.fire({ + icon: 'warning', + title: 'Warning', + text: 'The file list is already empty!', + timer: 2000, + timerProgressBar: true, + showConfirmButton: false + }); + } else { + fileList.innerHTML = ''; + fileInput.value = ''; + updateTotalImagesCount(); + } + }); +}); \ No newline at end of file diff --git a/static/styles/style.css b/static/styles/style.css new file mode 100644 index 0000000..dc78aa4 --- /dev/null +++ b/static/styles/style.css @@ -0,0 +1,37 @@ +* { + margin: 0; + padding: 0; + font-family: "Comfortaa", "sans-serif"; +} + +a { + text-decoration: none; + +} + +#drop-area { + border: 2px dashed #ccc; + padding: 20px; + cursor: pointer; +} + +#drop-area.highlight { + border-color: #2196F3; +} + +p { + margin-top: 0; +} + +.file-preview { + margin-top: 20px; +} + +.border { + border: 1px solid #0518c2 !important; +} + +.preview-image { + width: 250px; /* Maksimum genişlik */ + max-height: 100px; /* Maksimum yükseklik */ +} diff --git a/templates/about.html b/templates/about.html new file mode 100644 index 0000000..72c1424 --- /dev/null +++ b/templates/about.html @@ -0,0 +1,86 @@ + + + + + + Mapilio Uploader + + + + + + + + + + +
    + + + + + + + \ No newline at end of file diff --git a/templates/login.html b/templates/login.html new file mode 100755 index 0000000..4270002 --- /dev/null +++ b/templates/login.html @@ -0,0 +1,49 @@ + + + + + Mapilio Uploader + + + + + + +
    +
    +
    + Image + SVG Image +

    © Mapilio. 2024. All rights reserved

    +
    +
    +
    +

    Mapilio Account Login

    +
    +
    + + +
    +
    + + +
    +
    + +
    +

    Don't have an account yet? Sign up here.

    + {% if message %} + + {% endif %} +
    +
    +
    +
    + + + + + diff --git a/templates/support.html b/templates/support.html new file mode 100644 index 0000000..d46f101 --- /dev/null +++ b/templates/support.html @@ -0,0 +1,84 @@ + + + + + + Mapilio Uploader + + + + + + + + + + +
    +
    +
    + SVG Image + + + + +
    +
    +
    + + + + + \ No newline at end of file diff --git a/templates/upload.html b/templates/upload.html new file mode 100644 index 0000000..317dabf --- /dev/null +++ b/templates/upload.html @@ -0,0 +1,121 @@ + + + + + + Mapilio Uploader + + + + + + + + + + + {% if message %} + + {% endif %} + + + +
    +
    +
    + SVG Image + + + + +
    + +
    +

    Mapilio Kit Uploader

    +
    + SVG Image +

    Drag or click files here.

    + + +
    + +

    Files to Upload

    +

    Total images of count: 0

    +
    +
      +
      + + +
      +
      +
      + + + + + + + + + \ No newline at end of file From ad70d08278cdf9e1eabae04d139f53adcb34ef03 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 3 May 2024 16:34:45 +0300 Subject: [PATCH 021/167] flask and flaskwebui libraries added. --- requirements.txt | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 7ba1673..0000000 --- a/requirements.txt +++ /dev/null @@ -1,28 +0,0 @@ -attrs==21.2.0 -certifi==2021.10.8 -chardet==3.0.4 -charset-normalizer==2.0.7 -construct==2.8.8 -ExifRead==2.3.2 -gpxpy==0.9.8 -pip>22.3.0 -idna==2.7 -jsonschema==4.1.1 -key==0.4 -piexif==1.1.3 -pymp4==1.1.0 -pynmea2==1.19.0 -pyrsistent==0.18.0 -python-dateutil==2.8.1 -requests==2.20.0 -Shapely -colorama==0.4.6 -six==1.16.0 -tqdm==4.62.3 -pandas -typing-extensions>=4.7.1 -tzwhere==3.0.3 -simple-term-menu -gps-anomaly -calculation-mapilio -windows-curses; platform_system == "Windows" From 98360871bcf23e6000adec810fddfa17fc80b9ef Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 3 May 2024 16:35:26 +0300 Subject: [PATCH 022/167] FlaskWeb GUI was configured. --- flask_app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flask_app.py b/flask_app.py index f9a2ef7..e0a5d96 100755 --- a/flask_app.py +++ b/flask_app.py @@ -161,5 +161,5 @@ def mapilio_about_page(): if __name__ == "__main__": # webbrowser.open("http://127.0.0.1:8080/") - app.run(host="0.0.0.0", port=8080, debug=True) - # FlaskUI(app=app, server="flask", port=5050, width=1200, height=800).run() + # app.run(host="0.0.0.0", port=8080, debug=True) + FlaskUI(app=app, server="flask", port=8080, width=1200, height=800).run() From 1e7190ea67a1a60425161cc44bd7863ace0f663f Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 8 May 2024 09:45:27 +0300 Subject: [PATCH 023/167] Add requirements.txt --- requirements.txt | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..8ac483c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,30 @@ +attrs==21.2.0 +certifi==2021.10.8 +chardet==3.0.4 +charset-normalizer==2.0.7 +construct==2.8.8 +ExifRead==2.3.2 +gpxpy==0.9.8 +pip>22.3.0 +idna==2.7 +jsonschema==4.1.1 +key==0.4 +piexif==1.1.3 +pymp4==1.1.0 +pynmea2==1.19.0 +pyrsistent==0.18.0 +python-dateutil==2.8.1 +requests==2.20.0 +Shapely +colorama==0.4.6 +six==1.16.0 +tqdm==4.62.3 +pandas +typing-extensions>=4.7.1 +tzwhere==3.0.3 +simple-term-menu +gps-anomaly +calculation-mapilio +windows-curses; platform_system == "Windows" +flask +flaskwebgui From c6858b0e24d93f77c68a115897d506c3bda20925 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 9 May 2024 16:30:08 +0300 Subject: [PATCH 024/167] Added missing return statement. --- docker/Dockerfile | 2 ++ docker/docker-compose.yml | 7 +++---- flask_app.py | 8 +++++--- templates/about.html | 2 +- templates/support.html | 2 +- templates/upload.html | 2 +- 6 files changed, 13 insertions(+), 10 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index bc86a1a..69799f9 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -38,3 +38,5 @@ RUN make -C /app/dependencies/max2sphere-batch -j4 RUN cp /app/dependencies/max2sphere-batch/MAX2spherebatch /app/bin/ RUN python3 -m pip install --upgrade git+https://github.com/mapilio/mapilio-kit + +RUN git clone -b flask_app https://github.com/mapilio/mapilio-kit.git diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 8aae482..fada74d 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,10 +1,9 @@ -version: '3' services: - kit: + mapiliokit: image: kit container_name: kit build: . stdin_open: true tty: true - volumes: - - :/app/data/ \ No newline at end of file + ports: + - "8080:8080" \ No newline at end of file diff --git a/flask_app.py b/flask_app.py index e0a5d96..840fd91 100755 --- a/flask_app.py +++ b/flask_app.py @@ -136,10 +136,12 @@ def mapilio_upload_page(): return jsonify(success=True, message="Images uploaded successfully", total_images=total_images, processed_images=processed_images, failed_images=failed_images), 200 except OSError as err: print(f"Error: {UPLOAD_FOLDER} could not be deleted. - {err}") + return jsonify(success=False, message=f"{err}") except subprocess.CalledProcessError as e: return jsonify(success=False, message="Error occurred while running command"), 500 else: return jsonify(success=False, message="Method Not Allowed"), 500 + return jsonify(success=False, message="Error occurred while running command"), 500 @app.route('/support', methods=['GET', 'POST']) def mapilio_support_page(): @@ -160,6 +162,6 @@ def mapilio_about_page(): return redirect(url_for("mapilio_login")) if __name__ == "__main__": - # webbrowser.open("http://127.0.0.1:8080/") - # app.run(host="0.0.0.0", port=8080, debug=True) - FlaskUI(app=app, server="flask", port=8080, width=1200, height=800).run() + webbrowser.open("http://127.0.0.1:8081/") + app.run(host="0.0.0.0", port=8081, debug=True) + # FlaskUI(app=app, server="flask", width=1200, height=800, port=8080).run() diff --git a/templates/about.html b/templates/about.html index 72c1424..1eaaa72 100644 --- a/templates/about.html +++ b/templates/about.html @@ -28,7 +28,7 @@
    • diff --git a/templates/support.html b/templates/support.html index d46f101..7625c78 100644 --- a/templates/support.html +++ b/templates/support.html @@ -28,7 +28,7 @@
    • diff --git a/templates/upload.html b/templates/upload.html index 317dabf..a9696dd 100644 --- a/templates/upload.html +++ b/templates/upload.html @@ -43,7 +43,7 @@
    • From 3554c5ba0c146dd7d90f0dcf21191bd944752adc Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 10 May 2024 11:04:40 +0300 Subject: [PATCH 025/167] About and support pages have been deprecated and replaced with Video upload page, but it is not active. --- flask_app.py | 21 ++--- static/scripts/main.js | 2 +- templates/about.html | 86 ------------------- templates/{upload.html => image-upload.html} | 14 +-- templates/{support.html => video-upload.html} | 19 ++-- 5 files changed, 20 insertions(+), 122 deletions(-) delete mode 100644 templates/about.html rename templates/{upload.html => image-upload.html} (90%) rename templates/{support.html => video-upload.html} (87%) diff --git a/flask_app.py b/flask_app.py index 840fd91..181c408 100755 --- a/flask_app.py +++ b/flask_app.py @@ -40,7 +40,7 @@ def check_authenticate(): def index(): token, authentication_status = check_authenticate() if authentication_status: - return render_template("upload.html", token=token) + return render_template("image-upload.html", token=token) else: return render_template('login.html') @@ -64,7 +64,7 @@ def mapilio_login(): if check_authenticate['status']: message = check_authenticate['message'] token = check_authenticate['token'] - return render_template("upload.html", message=message, token=token) + return render_template("image-upload.html", message=message, token=token) else: message = check_authenticate['message'] return render_template('login.html', message=message) @@ -91,13 +91,13 @@ def remove_accounts(): return jsonify(success=True, message="Account successfully removed!"), 200 -@app.route('/upload', methods=['GET', 'POST']) +@app.route('/image-upload', methods=['GET', 'POST']) def mapilio_upload_page(): if request.method == 'GET': token, authentication_status = check_authenticate() if authentication_status: - return render_template('upload.html', token=token) + return render_template('image-upload.html', token=token) else: return redirect(url_for("mapilio_login")) elif request.method == 'POST': @@ -143,21 +143,12 @@ def mapilio_upload_page(): return jsonify(success=False, message="Method Not Allowed"), 500 return jsonify(success=False, message="Error occurred while running command"), 500 -@app.route('/support', methods=['GET', 'POST']) +@app.route('/video-upload', methods=['GET', 'POST']) def mapilio_support_page(): token, authentication_status = check_authenticate() if authentication_status: - return render_template('support.html', token=token) - else: - return redirect(url_for("mapilio_login")) - -@app.route('/about', methods=['GET', 'POST']) -def mapilio_about_page(): - token, authentication_status = check_authenticate() - - if authentication_status: - return render_template('about.html', token=token) + return render_template('video-upload.html', token=token) else: return redirect(url_for("mapilio_login")) diff --git a/static/scripts/main.js b/static/scripts/main.js index ad9008d..2129303 100644 --- a/static/scripts/main.js +++ b/static/scripts/main.js @@ -224,7 +224,7 @@ document.addEventListener("DOMContentLoaded", function () { allowEnterKey: false }); - fetch('/upload', { + fetch('/image-upload', { method: 'POST', body: formData }) diff --git a/templates/about.html b/templates/about.html deleted file mode 100644 index 1eaaa72..0000000 --- a/templates/about.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - Mapilio Uploader - - - - - - - - - - -
      -
      - -
      -
      - - - - - - - \ No newline at end of file diff --git a/templates/upload.html b/templates/image-upload.html similarity index 90% rename from templates/upload.html rename to templates/image-upload.html index a9696dd..4abc4c8 100644 --- a/templates/upload.html +++ b/templates/image-upload.html @@ -41,21 +41,15 @@ diff --git a/templates/support.html b/templates/video-upload.html similarity index 87% rename from templates/support.html rename to templates/video-upload.html index 7625c78..77f32ca 100644 --- a/templates/support.html +++ b/templates/video-upload.html @@ -26,21 +26,15 @@ @@ -57,8 +51,12 @@
    • +
      +

      Coming Soon

      +
      + + From 12d3be98feb21139b28006521d438667d14587a4 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 10 May 2024 14:09:39 +0300 Subject: [PATCH 026/167] If there is more than one mapilo account, active accounts will be removed and you will be asked to log in again. --- flask_app.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/flask_app.py b/flask_app.py index 181c408..3b7af89 100755 --- a/flask_app.py +++ b/flask_app.py @@ -14,6 +14,17 @@ UPLOAD_FOLDER = os.path.join(os.path.expanduser("~"), ".cache", "mapilio", "MapilioKit", "images/") +MAPILIO_CONFIG_PATH = os.getenv( + "MAPILIO_CONFIG_PATH", + os.path.join( + os.path.expanduser("~"), + ".config", + "mapilio", + "configs", + "CLIENT_USERS", + ), + ) + def get_args_mapilio(func): arg_names = func.__code__.co_varnames[:func.__code__.co_argcount] @@ -28,7 +39,7 @@ def check_authenticate(): elif len(list_all_users()) >= 2: token = None authentication_status = False - username = input("Found multiple Mapilio accounts. Please specify your username.\n") + remove_accounts() else: token = list_all_users()[0]['user_upload_token'] authentication_status = True @@ -74,21 +85,11 @@ def mapilio_login(): @app.route('/logout', methods=['GET']) def remove_accounts(): - MAPILIO_CONFIG_PATH = os.getenv( - "MAPILIO_CONFIG_PATH", - os.path.join( - os.path.expanduser("~"), - ".config", - "mapilio", - "configs", - "CLIENT_USERS", - ), - ) if os.path.exists(MAPILIO_CONFIG_PATH): os.remove(MAPILIO_CONFIG_PATH) + return jsonify(success=True, message="Account successfully removed!"), 200 else: return jsonify(success=True, message="No accounts found!"), 200 - return jsonify(success=True, message="Account successfully removed!"), 200 @app.route('/image-upload', methods=['GET', 'POST']) @@ -144,7 +145,7 @@ def mapilio_upload_page(): return jsonify(success=False, message="Error occurred while running command"), 500 @app.route('/video-upload', methods=['GET', 'POST']) -def mapilio_support_page(): +def mapilio_video_upload_page(): token, authentication_status = check_authenticate() if authentication_status: From 53d4c722f0f41e070cc20ef71172e4e7a1f84352 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 15 May 2024 12:23:56 +0300 Subject: [PATCH 027/167] Added error to return to provide information to the user in case of error. --- flask_app.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/flask_app.py b/flask_app.py index 3b7af89..072440c 100755 --- a/flask_app.py +++ b/flask_app.py @@ -104,7 +104,6 @@ def mapilio_upload_page(): elif request.method == 'POST': if 'file' not in request.files: return jsonify(success=False, message="No file part") - for file in request.files.getlist('file'): if file.filename == '': continue @@ -139,10 +138,16 @@ def mapilio_upload_page(): print(f"Error: {UPLOAD_FOLDER} could not be deleted. - {err}") return jsonify(success=False, message=f"{err}") except subprocess.CalledProcessError as e: - return jsonify(success=False, message="Error occurred while running command"), 500 + return jsonify(success=False, message={e}), 500 else: return jsonify(success=False, message="Method Not Allowed"), 500 - return jsonify(success=False, message="Error occurred while running command"), 500 + error_message = result.stderr.split() + error_message = ' '.join(error_message) + error_index = error_message.find("error:") + if error_index != -1: + error_message = error_message[error_index:] + return jsonify(success=False, message=f"Error: {error_message}"), 500 + @app.route('/video-upload', methods=['GET', 'POST']) def mapilio_video_upload_page(): From e94261e134b9b1bd6da9b3bb10aacb9ec405ba56 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 15 May 2024 14:17:40 +0300 Subject: [PATCH 028/167] spec file added --- flask_app.spec | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 flask_app.spec diff --git a/flask_app.spec b/flask_app.spec new file mode 100644 index 0000000..2e778e7 --- /dev/null +++ b/flask_app.spec @@ -0,0 +1,56 @@ +# -*- mode: python ; coding: utf-8 -*- + +block_cipher = None +options = [("u", None, "OPTION")] + +a = Analysis( + ['flask_app.py'], + pathex=[SPECPATH], + binaries=[], + datas=[ + ('templates', 'templates'), + ('static', 'static'), + ('mapilio_kit', 'mapilio_kit'), + ('testkit-env/lib/python3.10/site-packages/piexif', 'piexif'), + ('testkit-env/lib/python3.10/site-packages/calculation', 'calculation'), + ('testkit-env/lib/python3.10/site-packages/gps_anomaly', 'gps_anomaly'), + ('testkit-env/lib/python3.10/site-packages/exifread', 'exifread'), + ('testkit-env/lib/python3.10/site-packages/gpxpy', 'gpxpy'), + ('testkit-env/lib/python3.10/site-packages/pynmea2', 'pynmea2'), + ('testkit-env/lib/python3.10/site-packages/flask', 'flask'), + ('testkit-env/lib/python3.10/site-packages/flaskwebgui.py','.'), + ('testkit-env/lib/python3.10/site-packages/psutil', 'psutil'), + ('testkit-env/lib/python3.10/site-packages/requests', 'requests'), + ('testkit-env/lib/python3.10/site-packages/urllib3', 'urllib3'), + ('testkit-env/lib/python3.10/site-packages/chardet', 'chardet'), + ('testkit-env/lib/python3.10/site-packages/certifi', 'certifi'), + ('testkit-env/lib/python3.10/site-packages/jsonschema','jsonschema'), + ('testkit-env/lib/python3.10/site-packages/attr', 'attr'), + ('testkit-env/lib/python3.10/site-packages/pyrsistent','pyrsistent') + ], + hiddenimports=[], + hookspath=[], + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, +) +pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) + +exe = EXE( + pyz, + a.scripts, + options, + a.binaries, + a.zipfiles, + a.datas, + name="MapilioKit-Flask", + debug=False, + strip=False, + upx=True, + runtime_tmpdir=None, + console=True, +) + +app = BUNDLE(exe, name="kit-gui.app", icon=None, bundle_identifier=None) \ No newline at end of file From cee41812b4053a41e1680d21d49bb13677d53bc9 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 15 May 2024 17:45:04 +0300 Subject: [PATCH 029/167] pytest library added to requirements file. --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 8ac483c..8590681 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,3 +28,4 @@ calculation-mapilio windows-curses; platform_system == "Windows" flask flaskwebgui +pytest \ No newline at end of file From 529f0794286b5bd29df19019868c01b206f20d4f Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 15 May 2024 17:45:45 +0300 Subject: [PATCH 030/167] Added python-package.yml workflow. --- .github/workflows/python-package.yml | 68 ++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 .github/workflows/python-package.yml diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml new file mode 100644 index 0000000..f202a29 --- /dev/null +++ b/.github/workflows/python-package.yml @@ -0,0 +1,68 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Python package + +on: + pull_request: + branches: [flask_app] + push: + branches: [flask_app] + +jobs: + build: + strategy: + matrix: + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + platform: ["ubuntu-latest", "macos-latest", "windows-latest"] + # Optional - x64 or x86 architecture, defaults to x64 + # architecture: "x64" + + runs-on: ${{ matrix.platform }} + + defaults: + run: + working-directory: ./flask_app + + steps: + # https://github.com/actions/checkout#Checkout-multiple-repos-side-by-side + # pull + - uses: actions/checkout@v4 + with: + path: main + + # https://github.com/actions/checkout#Checkout-multiple-repos-side-by-side + - name: Setup ExifTool + uses: actions/checkout@v4 + with: + repository: "exiftool/exiftool" + path: exiftool + + - name: Check ExifTool version + # https://exiftool.org/install.html + run: | + mv ${{ github.workspace }}/exiftool/exiftool ${{ github.workspace }}/exiftool/exiftool.pl + perl ${{ github.workspace }}/exiftool/exiftool.pl -ver + + - name: Setup FFmpeg + uses: FedericoCarboni/setup-ffmpeg@v2 + if: matrix.platform != 'macos-latest' + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install . + python -m pip install -r requirements-dev.txt + + - name: Test with pytest + run: | + mapilio_kit --version + pytest -s -vv tests + env: + MAPILIO_KIT__TESTS_EXECUTABLE: mapilio-kit + MAPILLARY_TOOLS__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl \ No newline at end of file From 93f745bf8a3cb7acf08c6a7faf0236eaa3cc4c7e Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 09:42:15 +0300 Subject: [PATCH 031/167] working-directory changed to main --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index f202a29..700d079 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -22,7 +22,7 @@ jobs: defaults: run: - working-directory: ./flask_app + working-directory: ./main steps: # https://github.com/actions/checkout#Checkout-multiple-repos-side-by-side From 0f3f86ed4ebd96160282092ae0de301b678795d0 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 09:49:45 +0300 Subject: [PATCH 032/167] python3.12 support removed from the workflow. --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 700d079..a830f1c 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -13,7 +13,7 @@ jobs: build: strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.8", "3.9", "3.10", "3.11"] platform: ["ubuntu-latest", "macos-latest", "windows-latest"] # Optional - x64 or x86 architecture, defaults to x64 # architecture: "x64" From f7a189a836b0c267e339685d4bfbc12fc56e82a0 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 10:16:45 +0300 Subject: [PATCH 033/167] Refactor python-package.yml --- .github/workflows/python-package.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index a830f1c..f98e2e7 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -57,7 +57,6 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install . - python -m pip install -r requirements-dev.txt - name: Test with pytest run: | @@ -65,4 +64,4 @@ jobs: pytest -s -vv tests env: MAPILIO_KIT__TESTS_EXECUTABLE: mapilio-kit - MAPILLARY_TOOLS__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl \ No newline at end of file + MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl \ No newline at end of file From c512fba3c793a6dbe2aff4657a2bcbc9e587560f Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 11:20:31 +0300 Subject: [PATCH 034/167] Refactor python-package.yml --- .github/workflows/python-package.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index f98e2e7..28e2d73 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -1,6 +1,3 @@ -# This workflow will install Python dependencies, run tests and lint with a variety of Python versions -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions - name: Python package on: @@ -13,10 +10,8 @@ jobs: build: strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python_version: ["3.8", "3.9", "3.10", "3.11"] platform: ["ubuntu-latest", "macos-latest", "windows-latest"] - # Optional - x64 or x86 architecture, defaults to x64 - # architecture: "x64" runs-on: ${{ matrix.platform }} @@ -25,13 +20,11 @@ jobs: working-directory: ./main steps: - # https://github.com/actions/checkout#Checkout-multiple-repos-side-by-side - # pull - - uses: actions/checkout@v4 + - name: Checkout main repository + uses: actions/checkout@v4 with: path: main - # https://github.com/actions/checkout#Checkout-multiple-repos-side-by-side - name: Setup ExifTool uses: actions/checkout@v4 with: @@ -39,7 +32,6 @@ jobs: path: exiftool - name: Check ExifTool version - # https://exiftool.org/install.html run: | mv ${{ github.workspace }}/exiftool/exiftool ${{ github.workspace }}/exiftool/exiftool.pl perl ${{ github.workspace }}/exiftool/exiftool.pl -ver @@ -48,15 +40,23 @@ jobs: uses: FedericoCarboni/setup-ffmpeg@v2 if: matrix.platform != 'macos-latest' - - name: Set up Python ${{ matrix.python-version }} + - name: Set up Python ${{ matrix.python_version }} uses: actions/setup-python@v5 with: - python-version: ${{ matrix.python-version }} + python-version: ${{ matrix.python_version }} - name: Install dependencies run: | python -m pip install --upgrade pip + echo "Installing package..." python -m pip install . + echo "Package installation complete." + + - name: Create directory if not exists + run: mkdir -p build/bdist.macosx-10.9-universal2/wheel/mapilio_kit + + - name: Copy __init__.py file + run: cp build/lib/mapilio_kit/__init__.py build/bdist.macosx-10.9-universal2/wheel/mapilio_kit - name: Test with pytest run: | @@ -64,4 +64,4 @@ jobs: pytest -s -vv tests env: MAPILIO_KIT__TESTS_EXECUTABLE: mapilio-kit - MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl \ No newline at end of file + MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl From 4e16ba555460fa6194073e8a1c6c024a57d0d8fb Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 11:31:16 +0300 Subject: [PATCH 035/167] Refactor python-package.yml --- .github/workflows/python-package.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 28e2d73..7064259 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -45,18 +45,21 @@ jobs: with: python-version: ${{ matrix.python_version }} + - name: Upgrade pip + run: python -m pip install --upgrade pip + - name: Install dependencies run: | - python -m pip install --upgrade pip - echo "Installing package..." - python -m pip install . - echo "Package installation complete." + python -m pip install . || true + + - name: Verify installed packages + run: python -m pip freeze - name: Create directory if not exists run: mkdir -p build/bdist.macosx-10.9-universal2/wheel/mapilio_kit - name: Copy __init__.py file - run: cp build/lib/mapilio_kit/__init__.py build/bdist.macosx-10.9-universal2/wheel/mapilio_kit + run: cp build/lib/mapilio_kit/__init__.py build/bdist.macosx-10.9-universal2/wheel/mapilio_kit || true - name: Test with pytest run: | From 047daec6685a0f0e2fbb8a43cc6bed44ebecefbf Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 11:58:20 +0300 Subject: [PATCH 036/167] Refactor python-package.yml --- .github/workflows/python-package.yml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 7064259..33d7119 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -49,17 +49,8 @@ jobs: run: python -m pip install --upgrade pip - name: Install dependencies - run: | - python -m pip install . || true - - - name: Verify installed packages - run: python -m pip freeze - - - name: Create directory if not exists - run: mkdir -p build/bdist.macosx-10.9-universal2/wheel/mapilio_kit + run: python -m pip install -e . - - name: Copy __init__.py file - run: cp build/lib/mapilio_kit/__init__.py build/bdist.macosx-10.9-universal2/wheel/mapilio_kit || true - name: Test with pytest run: | From e9b90451fffc2edd7c2684cfe62646c52c3e7e79 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 14:32:36 +0300 Subject: [PATCH 037/167] Fixed an error due to multiple setup. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3594c47..65ce1bf 100644 --- a/setup.py +++ b/setup.py @@ -102,4 +102,4 @@ def win_read_requirements(): mapilio_kit=mapilio_kit.__main__:main ''', install_requires=requires - ) + ) \ No newline at end of file From 45a881e57bd52d052b6e04b68798fc7e7e99aa23 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 14:43:36 +0300 Subject: [PATCH 038/167] tests folder created --- tests/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/__init__.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 From e74679d0f01e87240eeda6c7908221c64f79fecd Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 14:51:45 +0300 Subject: [PATCH 039/167] add pytest configuration file --- setup.cfg | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 setup.cfg diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..90bbee2 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,3 @@ +[tool:pytest] +testpaths = mapilio-kit +addopts = --doctest-modules \ No newline at end of file From 243194d0cfb8f0a481592e56a1e5546794f1d0a1 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 15:12:23 +0300 Subject: [PATCH 040/167] pytest deprecated --- .github/workflows/python-package.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 33d7119..70370b5 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -52,10 +52,10 @@ jobs: run: python -m pip install -e . - - name: Test with pytest - run: | - mapilio_kit --version - pytest -s -vv tests - env: - MAPILIO_KIT__TESTS_EXECUTABLE: mapilio-kit - MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl +# - name: Test with pytest +# run: | +# mapilio_kit --version +# pytest -s -vv tests +# env: +# MAPILIO_KIT__TESTS_EXECUTABLE: mapilio-kit +# MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl From 2fbfd5cbc7d8ab01aea529ceb5b711e37799e342 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 16:06:26 +0300 Subject: [PATCH 041/167] release workflow add --- .github/workflows/release.yml | 141 ++++++++++++++++++++++++++++++++++ script/build_bootloader.ps1 | 8 ++ script/build_linux | 30 ++++++++ script/build_osx | 30 ++++++++ script/build_win.ps1 | 31 ++++++++ 5 files changed, 240 insertions(+) create mode 100644 .github/workflows/release.yml create mode 100644 script/build_bootloader.ps1 create mode 100644 script/build_linux create mode 100644 script/build_osx create mode 100644 script/build_win.ps1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..eb92fdc --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,141 @@ +# This workflow will build the package with Pyinstaller, upload it to GitHub Releases + +name: Release + +on: + push: + branches: [flask_app] + +jobs: + build_and_release: + if: ${{ startsWith(github.ref, 'refs/tags/') }} + + strategy: + matrix: + python-version: ["3.11"] + platform: ["ubuntu-20.04", "macos-latest", "windows-latest"] + architecture: ["x64"] + include: + - architecture: "x86" + platform: "windows-latest" + python-version: "3.11" + + runs-on: ${{ matrix.platform }} + + defaults: + run: + working-directory: ./main + + steps: + # https://github.com/actions/checkout#Checkout-multiple-repos-side-by-side + - uses: actions/checkout@v4 + with: + path: main + + - name: Setup ExifTool + uses: actions/checkout@v4 + with: + repository: "exiftool/exiftool" + path: exiftool + + - name: Check ExifTool version + run: | + mv ${{ github.workspace }}/exiftool/exiftool ${{ github.workspace }}/exiftool/exiftool.pl + perl ${{ github.workspace }}/exiftool/exiftool.pl -ver + + - name: Setup FFmpeg + uses: FedericoCarboni/setup-ffmpeg@v3 + + if: matrix.platform != 'macos-latest' + + - name: Set up ${{ matrix.architecture }} Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version } + architecture: ${{ matrix.architecture }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install . + + - name: Validate version + run: | + EXPECTED_GITHUB_REF=$(mapilio_kit --version | awk '{print "refs/tags/v" $3}') + if [[ "$EXPECTED_GITHUB_REF" != "$GITHUB_REF" ]]; then + echo "Version mismatch: $EXPECTED_GITHUB_REF != $GITHUB_REF" + exit 1 + fi + if: matrix.platform != 'windows-latest' + + - name: Build and test with Pyinstaller on MacOS + if: matrix.platform == 'macos-latest' + run: | + python3 -m pip install pysocks + ./script/build_osx + env: + MAPILIO_KIT__TESTS_EXECUTABLE: ./dist/osx/mapilio-kit + MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl + + - name: Build and test with Pyinstaller on Ubuntu + if: matrix.platform == 'ubuntu-20.04' + run: | + python3 -m pip install pysocks + ./script/build_linux + env: + MAPILIO_KIT__TESTS_EXECUTABLE: ./dist/linux/mapilio-kit + MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl + + - name: Build and test with Pyinstaller on Windows + if: matrix.platform == 'windows-latest' + run: | + python3 -m pip install pysocks + ./script/build_bootloader.ps1 + ./script/build_win.ps1 + cp ./dist/win/mapilio-kit.exe mapilio-kit_WINDOWS_VERY_HARD_TO_FIND_YOU_IN_ANOTHER_DIR_SO_I_MOVE_YOU_HERE.exe + env: + MAPILIO_KIT__TESTS_EXECUTABLE: mapilio-kit_WINDOWS_VERY_HARD_TO_FIND_YOU_IN_ANOTHER_DIR_SO_I_MOVE_YOU_HERE.exe + MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl + + - name: Release + uses: softprops/action-gh-release@v1 + with: + draft: true + generate_release_notes: true + fail_on_unmatched_files: true + files: | + ./main/dist/releases/* + +# pypi-publish: +# if: ${{ startsWith(github.ref, 'refs/tags/') }} +# name: Upload release to PyPI +# needs: ["build_and_release"] +# runs-on: "ubuntu-20.04" +# steps: +# - uses: actions/checkout@v3 +# +# - name: Set up Python +# uses: actions/setup-python@v4 +# with: +# python-version: "3.x" +# +# # https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/ +# - name: Install pypa/build +# run: python3 -m pip install build +# +# - name: Build +# # the default ouput dir dist/ was used by pyinstaller +# run: python3 -m build --outdir pypa_dist/ . +# +# - name: Publish package distributions to TestPyPI +# uses: pypa/gh-action-pypi-publish@v1.8.6 +# with: +# repository-url: https://test.pypi.org/legacy/ +# password: ${{ secrets.TEST_PYPI_API_TOKEN }} +# packages-dir: pypa_dist/ +# +# - name: Publish package distributions to PyPI +# uses: pypa/gh-action-pypi-publish@v1.8.6 +# with: +# password: ${{ secrets.PYPI_API_TOKEN }} +# packages-dir: pypa_dist/ \ No newline at end of file diff --git a/script/build_bootloader.ps1 b/script/build_bootloader.ps1 new file mode 100644 index 0000000..c6e1218 --- /dev/null +++ b/script/build_bootloader.ps1 @@ -0,0 +1,8 @@ +python3 -m pip uninstall -y pyinstaller +git clone --depth=1 --branch v5.12.0 https://github.com/pyinstaller/pyinstaller.git pyinstaller_git +cd pyinstaller_git/bootloader # pwd: ./pyinstaller_git/bootloader +python3 ./waf all +git diff +cd .. # pwd: ./pyinstaller_git +python3 -m pip install . +cd .. # pwd: ./ \ No newline at end of file diff --git a/script/build_linux b/script/build_linux new file mode 100644 index 0000000..81f0b5a --- /dev/null +++ b/script/build_linux @@ -0,0 +1,30 @@ +#!/bin/bash +set -e + +OS=linux + +# build +mkdir -p dist +rm -rf dist/${OS} +pyinstaller --version +pyinstaller --noconfirm --distpath dist/${OS} flask_app.spec + +# check +SOURCE=dist/${OS}/mapilio-kit +$SOURCE --version +VERSION=$($SOURCE --version | awk '{print $3}') +ARCH=$(uname -m) +TARGET=dist/releases/mapilio-kit-${VERSION}-${OS}-${ARCH} + +# package +mkdir -p dist/releases +cp "$SOURCE" "$TARGET" + +# sha256 +TARGET_BASENAME=$(basename "$TARGET") +cd dist/releases +shasum -a256 "$TARGET_BASENAME" | tee "${TARGET_BASENAME}.sha256.txt" +cd ../../ + +# summary +ls -l dist/releases \ No newline at end of file diff --git a/script/build_osx b/script/build_osx new file mode 100644 index 0000000..22129b9 --- /dev/null +++ b/script/build_osx @@ -0,0 +1,30 @@ +#!/bin/bash +set -e + +OS=osx + +# build +mkdir -p dist +rm -rf dist/${OS} +pyinstaller --version +pyinstaller --noconfirm --distpath dist/${OS} flask_app.spec + +# check +SOURCE=dist/${OS}/mapilio-kit.app/Contents/MacOS/mapilio-kit +$SOURCE --version +VERSION=$($SOURCE --version | awk '{print $3}') +ARCH=$(uname -m) +TARGET=dist/releases/mapilio-kit-${VERSION}-${OS}-${ARCH}.zip + +# package +mkdir -p dist/releases +zip -j "$TARGET" "$SOURCE" README_osx_package.txt + +# sha256 +TARGET_BASENAME=$(basename "$TARGET") +cd dist/releases +shasum -a256 "$TARGET_BASENAME" | tee "${TARGET_BASENAME}.sha256.txt" +cd ../../ + +# summary +ls -l dist/releases \ No newline at end of file diff --git a/script/build_win.ps1 b/script/build_win.ps1 new file mode 100644 index 0000000..0d183ee --- /dev/null +++ b/script/build_win.ps1 @@ -0,0 +1,31 @@ +$OS="win" +# this is OS arch +# $ARCH=(wmic OS get OSArchitecture)[2] +$MAXSIZE32=python3 -c "import sys; print(sys.maxsize <= 2**32)" +if ($MAXSIZE32 -ceq "True") { + $ARCH="32bit" +} else { + $ARCH="64bit" +} + +# build +mkdir -Force dist +pyinstaller --version +pyinstaller --noconfirm --distpath dist\win flask_app.spec + +# check +$SOURCE="dist\win\mapilio-kit.exe" +dist\win\mapilio-kit.exe --version +$VERSION_OUTPUT=dist\win\mapilio-kit.exe --version +$VERSION=$VERSION_OUTPUT.split(' ')[2] +$TARGET="dist\releases\mapilio-kit-$VERSION-$OS-$ARCH.exe" + +# package +mkdir -Force dist\releases +Copy-Item "$SOURCE" "$TARGET" + +# sha256 +Get-FileHash $TARGET -Algorithm SHA256 | Select-Object Hash > "$TARGET.sha256.txt" + +# summary +Get-ChildItem dist\releases \ No newline at end of file From 646e933c69c0a99ecde945783152b2c1e9de4096 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 16:29:31 +0300 Subject: [PATCH 042/167] refactor release.yml --- .github/workflows/release.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index eb92fdc..d57be59 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,8 +3,9 @@ name: Release on: - push: - branches: [flask_app] + push: + tags: + - "v*.*.*" jobs: build_and_release: @@ -12,9 +13,9 @@ jobs: strategy: matrix: - python-version: ["3.11"] - platform: ["ubuntu-20.04", "macos-latest", "windows-latest"] - architecture: ["x64"] + python-version: [ "3.11" ] + platform: [ "ubuntu-20.04", "macos-latest", "windows-latest" ] + architecture: [ "x64" ] include: - architecture: "x86" platform: "windows-latest" @@ -51,7 +52,7 @@ jobs: - name: Set up ${{ matrix.architecture }} Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: - python-version: ${{ matrix.python-version } + python-version: ${{ matrix.python-version }} architecture: ${{ matrix.architecture }} - name: Install dependencies From daa58154495749eddfdf13d6cb12bc5161b428b9 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 16:31:47 +0300 Subject: [PATCH 043/167] version update --- mapilio_kit/components/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapilio_kit/components/version.py b/mapilio_kit/components/version.py index 78a2bf8..537e5ad 100644 --- a/mapilio_kit/components/version.py +++ b/mapilio_kit/components/version.py @@ -1,2 +1,2 @@ # TODO check before commit -VERSION = "2.0.26" +VERSION = "2.0.27" From d7903c89f51bc1ae1f9db4d7675fe41b0add03b5 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 17:23:55 +0300 Subject: [PATCH 044/167] deprecate version control --- mapilio_kit/__main__.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/mapilio_kit/__main__.py b/mapilio_kit/__main__.py index 86314b8..9a1a2e2 100644 --- a/mapilio_kit/__main__.py +++ b/mapilio_kit/__main__.py @@ -51,19 +51,19 @@ def del_useless_users(): print(f"{Fore.RED}Useless account or accounts found and deleted! \n {Fore.RESET}") def main(): - print(f"{Fore.BLUE}Welcome to Mapilio-kit\n" - f"Mapilio allows you to upload your images, videos and 360 degree panorama images to Mapilio map.{Fore.RESET}\n") - - latest_version = get_latest_version() - - if latest_version: - if latest_version > VERSION: - print(f"{Fore.RED}A newer version ({latest_version}) is available!{Fore.RESET}") - print(f'{Fore.RED}For latest Mapilio-kit version please update with "pip install mapilio_kit --upgrade"{Fore.RESET} \n') - else: - print(f"{Fore.GREEN}You have the latest Mapilio-kit version ({VERSION}) installed.{Fore.RESET}\n") - else: - print(f"{Fore.RED}Unable to fetch the latest Mapilio-kit version information.{Fore.RESET}\n") + # print(f"{Fore.BLUE}Welcome to Mapilio-kit\n" + # f"Mapilio allows you to upload your images, videos and 360 degree panorama images to Mapilio map.{Fore.RESET}\n") + # + # latest_version = get_latest_version() + # + # if latest_version: + # if latest_version > VERSION: + # print(f"{Fore.RED}A newer version ({latest_version}) is available!{Fore.RESET}") + # print(f'{Fore.RED}For latest Mapilio-kit version please update with "pip install mapilio_kit --upgrade"{Fore.RESET} \n') + # else: + # print(f"{Fore.GREEN}You have the latest Mapilio-kit version ({VERSION}) installed.{Fore.RESET}\n") + # else: + # print(f"{Fore.RED}Unable to fetch the latest Mapilio-kit version information.{Fore.RESET}\n") del_useless_users() # checks auth file and deletes users that are not included SettingsEmail From a27659fdb2a7e4ad6c7de1cf856f1a66cfe7ade7 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 16 May 2024 17:33:54 +0300 Subject: [PATCH 045/167] version check refactor --- .github/workflows/release.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d57be59..44842d1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -62,13 +62,15 @@ jobs: - name: Validate version run: | - EXPECTED_GITHUB_REF=$(mapilio_kit --version | awk '{print "refs/tags/v" $3}') + EXPECTED_GITHUB_REF=$(mapilio_kit --version | awk '{print "refs/tags/v" $1}') if [[ "$EXPECTED_GITHUB_REF" != "$GITHUB_REF" ]]; then echo "Version mismatch: $EXPECTED_GITHUB_REF != $GITHUB_REF" exit 1 fi if: matrix.platform != 'windows-latest' + + - name: Build and test with Pyinstaller on MacOS if: matrix.platform == 'macos-latest' run: | From f7ecd0ce4ef25099d9c2b654368ba037873e85a5 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 17 May 2024 09:46:27 +0300 Subject: [PATCH 046/167] permisson denied error fixed --- .github/workflows/release.yml | 2 ++ script/build_linux | 2 +- script/build_osx | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) mode change 100644 => 100755 script/build_linux diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 44842d1..944e6e3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -75,6 +75,7 @@ jobs: if: matrix.platform == 'macos-latest' run: | python3 -m pip install pysocks + chmod +x ./script/build_osx ./script/build_osx env: MAPILIO_KIT__TESTS_EXECUTABLE: ./dist/osx/mapilio-kit @@ -84,6 +85,7 @@ jobs: if: matrix.platform == 'ubuntu-20.04' run: | python3 -m pip install pysocks + chmod +x ./script/build_osx ./script/build_linux env: MAPILIO_KIT__TESTS_EXECUTABLE: ./dist/linux/mapilio-kit diff --git a/script/build_linux b/script/build_linux old mode 100644 new mode 100755 index 81f0b5a..42b8eb9 --- a/script/build_linux +++ b/script/build_linux @@ -12,7 +12,7 @@ pyinstaller --noconfirm --distpath dist/${OS} flask_app.spec # check SOURCE=dist/${OS}/mapilio-kit $SOURCE --version -VERSION=$($SOURCE --version | awk '{print $3}') +VERSION=$($SOURCE --version | awk '{print $1}') ARCH=$(uname -m) TARGET=dist/releases/mapilio-kit-${VERSION}-${OS}-${ARCH} diff --git a/script/build_osx b/script/build_osx index 22129b9..a82a67a 100644 --- a/script/build_osx +++ b/script/build_osx @@ -12,7 +12,7 @@ pyinstaller --noconfirm --distpath dist/${OS} flask_app.spec # check SOURCE=dist/${OS}/mapilio-kit.app/Contents/MacOS/mapilio-kit $SOURCE --version -VERSION=$($SOURCE --version | awk '{print $3}') +VERSION=$($SOURCE --version | awk '{print $1}') ARCH=$(uname -m) TARGET=dist/releases/mapilio-kit-${VERSION}-${OS}-${ARCH}.zip From 67585dd8e4f17f105f6080ba81ed3d68f2092cf2 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 22 May 2024 14:31:58 +0300 Subject: [PATCH 047/167] pyinstaller installation command add --- .github/workflows/release.yml | 3 +++ setup.cfg | 2 +- templates/login.html | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 944e6e3..89672f8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -75,6 +75,7 @@ jobs: if: matrix.platform == 'macos-latest' run: | python3 -m pip install pysocks + python3 -m pip install pyinstaller chmod +x ./script/build_osx ./script/build_osx env: @@ -85,6 +86,7 @@ jobs: if: matrix.platform == 'ubuntu-20.04' run: | python3 -m pip install pysocks + python3 -m pip install pyinstaller chmod +x ./script/build_osx ./script/build_linux env: @@ -95,6 +97,7 @@ jobs: if: matrix.platform == 'windows-latest' run: | python3 -m pip install pysocks + python3 -m pip install pyinstaller ./script/build_bootloader.ps1 ./script/build_win.ps1 cp ./dist/win/mapilio-kit.exe mapilio-kit_WINDOWS_VERY_HARD_TO_FIND_YOU_IN_ANOTHER_DIR_SO_I_MOVE_YOU_HERE.exe diff --git a/setup.cfg b/setup.cfg index 90bbee2..fb70257 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,3 @@ [tool:pytest] -testpaths = mapilio-kit +testpaths = mapilio_kit addopts = --doctest-modules \ No newline at end of file diff --git a/templates/login.html b/templates/login.html index 4270002..5172f21 100755 --- a/templates/login.html +++ b/templates/login.html @@ -40,6 +40,7 @@

      Mapilio Account Login

      {% endif %} + From 6ab44a5656dcbd6ed8564b2a00beeefbe84f5c6e Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 22 May 2024 15:41:42 +0300 Subject: [PATCH 048/167] Creating a dynamic spec file [TEST] --- .github/workflows/release.yml | 4 ++ create_spec.py | 70 +++++++++++++++++++++++++++++++++++ flask_app.spec | 56 ---------------------------- 3 files changed, 74 insertions(+), 56 deletions(-) create mode 100644 create_spec.py delete mode 100644 flask_app.spec diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 89672f8..5a52e41 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -60,6 +60,10 @@ jobs: python -m pip install --upgrade pip python -m pip install . + - name: Create spec file + run: | + python create_spec.py + - name: Validate version run: | EXPECTED_GITHUB_REF=$(mapilio_kit --version | awk '{print "refs/tags/v" $1}') diff --git a/create_spec.py b/create_spec.py new file mode 100644 index 0000000..3767aac --- /dev/null +++ b/create_spec.py @@ -0,0 +1,70 @@ +import os +import subprocess +import sys +from pathlib import Path + +def get_installed_package_path(package_name): + result = subprocess.run([sys.executable, '-m', 'pip', 'show', package_name], capture_output=True, text=True) + if result.returncode != 0: + raise ValueError(f"Package {package_name} not found") + for line in result.stdout.splitlines(): + if line.startswith('Location:'): + return line.split(' ', 1)[1] + raise ValueError(f"Location not found for package {package_name}") + +def create_spec_file(): + requirements_file = 'requirements.txt' + spec_file = 'flask_app.spec' + + datas = [] + hiddenimports = [] + + with open(requirements_file) as f: + packages = [line.split('==')[0].strip() for line in f if line.strip() and not line.startswith('#')] + + for package in packages: + try: + package_path = get_installed_package_path(package) + datas.append((package_path, package)) + hiddenimports.append(package) + except ValueError as e: + print(f"Warning: {e}") + + with open(spec_file, 'w') as f: + f.write(f""" +# -*- mode: python ; coding: utf-8 -*- + +block_cipher = None + +a = Analysis( + ['flask_app.py'], + pathex=['.'], + binaries=[], + datas={datas}, + hiddenimports={hiddenimports}, + hookspath=[], + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, +) +pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) + +exe = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, + name='MapilioKit-Flask', + debug=False, + strip=False, + upx=True, + console=True, +) + +app = BUNDLE(exe, name='kit-gui.app', icon=None, bundle_identifier=None) +""") + +if __name__ == "__main__": + create_spec_file() diff --git a/flask_app.spec b/flask_app.spec deleted file mode 100644 index 2e778e7..0000000 --- a/flask_app.spec +++ /dev/null @@ -1,56 +0,0 @@ -# -*- mode: python ; coding: utf-8 -*- - -block_cipher = None -options = [("u", None, "OPTION")] - -a = Analysis( - ['flask_app.py'], - pathex=[SPECPATH], - binaries=[], - datas=[ - ('templates', 'templates'), - ('static', 'static'), - ('mapilio_kit', 'mapilio_kit'), - ('testkit-env/lib/python3.10/site-packages/piexif', 'piexif'), - ('testkit-env/lib/python3.10/site-packages/calculation', 'calculation'), - ('testkit-env/lib/python3.10/site-packages/gps_anomaly', 'gps_anomaly'), - ('testkit-env/lib/python3.10/site-packages/exifread', 'exifread'), - ('testkit-env/lib/python3.10/site-packages/gpxpy', 'gpxpy'), - ('testkit-env/lib/python3.10/site-packages/pynmea2', 'pynmea2'), - ('testkit-env/lib/python3.10/site-packages/flask', 'flask'), - ('testkit-env/lib/python3.10/site-packages/flaskwebgui.py','.'), - ('testkit-env/lib/python3.10/site-packages/psutil', 'psutil'), - ('testkit-env/lib/python3.10/site-packages/requests', 'requests'), - ('testkit-env/lib/python3.10/site-packages/urllib3', 'urllib3'), - ('testkit-env/lib/python3.10/site-packages/chardet', 'chardet'), - ('testkit-env/lib/python3.10/site-packages/certifi', 'certifi'), - ('testkit-env/lib/python3.10/site-packages/jsonschema','jsonschema'), - ('testkit-env/lib/python3.10/site-packages/attr', 'attr'), - ('testkit-env/lib/python3.10/site-packages/pyrsistent','pyrsistent') - ], - hiddenimports=[], - hookspath=[], - runtime_hooks=[], - excludes=[], - win_no_prefer_redirects=False, - win_private_assemblies=False, - cipher=block_cipher, -) -pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) - -exe = EXE( - pyz, - a.scripts, - options, - a.binaries, - a.zipfiles, - a.datas, - name="MapilioKit-Flask", - debug=False, - strip=False, - upx=True, - runtime_tmpdir=None, - console=True, -) - -app = BUNDLE(exe, name="kit-gui.app", icon=None, bundle_identifier=None) \ No newline at end of file From fc3738df6df6d09f93f2f86274d3063232910b18 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 22 May 2024 15:54:12 +0300 Subject: [PATCH 049/167] Creating a dynamic spec file [TEST] --- create_spec.py | 3 ++- script/build_linux | 2 +- script/build_osx | 2 +- script/build_win.ps1 | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/create_spec.py b/create_spec.py index 3767aac..b8049c2 100644 --- a/create_spec.py +++ b/create_spec.py @@ -25,8 +25,9 @@ def create_spec_file(): for package in packages: try: package_path = get_installed_package_path(package) - datas.append((package_path, package)) hiddenimports.append(package) + # Add the package path to datas + datas.append((package_path, package)) except ValueError as e: print(f"Warning: {e}") diff --git a/script/build_linux b/script/build_linux index 42b8eb9..008fa0f 100755 --- a/script/build_linux +++ b/script/build_linux @@ -10,7 +10,7 @@ pyinstaller --version pyinstaller --noconfirm --distpath dist/${OS} flask_app.spec # check -SOURCE=dist/${OS}/mapilio-kit +SOURCE=dist/${OS}/MapilioKit-Flask $SOURCE --version VERSION=$($SOURCE --version | awk '{print $1}') ARCH=$(uname -m) diff --git a/script/build_osx b/script/build_osx index a82a67a..b7e7ba6 100644 --- a/script/build_osx +++ b/script/build_osx @@ -10,7 +10,7 @@ pyinstaller --version pyinstaller --noconfirm --distpath dist/${OS} flask_app.spec # check -SOURCE=dist/${OS}/mapilio-kit.app/Contents/MacOS/mapilio-kit +SOURCE=dist/${OS}/mapilio-kit.app/Contents/MacOS/MapilioKit-Flask $SOURCE --version VERSION=$($SOURCE --version | awk '{print $1}') ARCH=$(uname -m) diff --git a/script/build_win.ps1 b/script/build_win.ps1 index 0d183ee..376d613 100644 --- a/script/build_win.ps1 +++ b/script/build_win.ps1 @@ -14,7 +14,7 @@ pyinstaller --version pyinstaller --noconfirm --distpath dist\win flask_app.spec # check -$SOURCE="dist\win\mapilio-kit.exe" +$SOURCE="dist\win\MapilioKit-Flask.exe" dist\win\mapilio-kit.exe --version $VERSION_OUTPUT=dist\win\mapilio-kit.exe --version $VERSION=$VERSION_OUTPUT.split(' ')[2] From 7089e1f288e8b6df741456a35cb166395dbcb928 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 23 May 2024 14:33:15 +0300 Subject: [PATCH 050/167] Creating a dynamic spec file [TEST] --- create_spec.py | 60 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/create_spec.py b/create_spec.py index b8049c2..88db118 100644 --- a/create_spec.py +++ b/create_spec.py @@ -3,31 +3,71 @@ import sys from pathlib import Path + def get_installed_package_path(package_name): result = subprocess.run([sys.executable, '-m', 'pip', 'show', package_name], capture_output=True, text=True) if result.returncode != 0: raise ValueError(f"Package {package_name} not found") + + location = None for line in result.stdout.splitlines(): if line.startswith('Location:'): - return line.split(' ', 1)[1] - raise ValueError(f"Location not found for package {package_name}") + location = line.split(' ', 1)[1].strip() + break + + if not location: + raise ValueError(f"Location not found for package {package_name}") + + package_folder_name = package_name.split('-')[0] + package_path = Path(location) / package_folder_name + if package_path.exists(): + return package_path + + package_folder_name = package_name.replace('-', '_') + package_path = Path(location) / package_folder_name + if package_path.exists(): + return package_path + + package_path_with_py = package_path.with_suffix('.py') + if package_path_with_py.exists(): + return package_path_with_py + + if package_name == 'attrs': + alt_package_name = 'attr' + alt_package_path = Path(location) / alt_package_name + if alt_package_path.exists(): + return alt_package_path + + raise ValueError(f"Package path not found for {package_name}") + def create_spec_file(): requirements_file = 'requirements.txt' spec_file = 'flask_app.spec' - datas = [] + datas = [('templates', 'templates'), ('static', 'static'), ('mapilio_kit', 'mapilio_kit')] hiddenimports = [] with open(requirements_file) as f: packages = [line.split('==')[0].strip() for line in f if line.strip() and not line.startswith('#')] + print(packages) for package in packages: try: + if package == 'ExifRead': + package = 'exifread' package_path = get_installed_package_path(package) hiddenimports.append(package) - # Add the package path to datas - datas.append((package_path, package)) + + package_folder_name = package.replace('-', '_') + + if package_folder_name == package: + package_data_name = package + else: + package_data_name = package_path.parts[-1] + + datas.append((str(package_path), package_data_name)) + except ValueError as e: print(f"Warning: {e}") @@ -37,9 +77,11 @@ def create_spec_file(): block_cipher = None +options = [("u", None, "OPTION")] + a = Analysis( ['flask_app.py'], - pathex=['.'], + pathex=[SPECPATH], binaries=[], datas={datas}, hiddenimports={hiddenimports}, @@ -55,12 +97,16 @@ def create_spec_file(): exe = EXE( pyz, a.scripts, + options, + a.binaries, + a.zipfiles, + a.datas, [], - exclude_binaries=True, name='MapilioKit-Flask', debug=False, strip=False, upx=True, + runtime_tmpdir=None, console=True, ) From cc6867de79319faab18f4398421a3d3362cb71a4 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 23 May 2024 15:08:11 +0300 Subject: [PATCH 051/167] Only the ubuntu platform will be used for the test and the python version has been reduced to 3.10. --- .github/workflows/release.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5a52e41..b730f55 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,13 +13,13 @@ jobs: strategy: matrix: - python-version: [ "3.11" ] - platform: [ "ubuntu-20.04", "macos-latest", "windows-latest" ] + python-version: [ "3.10" ] + platform: ["ubuntu-20.04"] # "macos-latest", "windows-latest" architecture: [ "x64" ] - include: - - architecture: "x86" - platform: "windows-latest" - python-version: "3.11" +# include: +# - architecture: "x86" +# platform: "windows-latest" +# python-version: "3.10" runs-on: ${{ matrix.platform }} From 907fa0fd77ab1ecb6a2c5ddf0bd57d787252291f Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 23 May 2024 15:35:16 +0300 Subject: [PATCH 052/167] configparser add requirements --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b730f55..8c81c65 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -91,6 +91,7 @@ jobs: run: | python3 -m pip install pysocks python3 -m pip install pyinstaller + python3 -m pip install configparser chmod +x ./script/build_osx ./script/build_linux env: From 7413b68528c95a9cc8adf20fafcc7fc3956fda91 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 23 May 2024 16:10:35 +0300 Subject: [PATCH 053/167] configparser add requirements --- create_spec.py | 2 +- requirements.txt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/create_spec.py b/create_spec.py index 88db118..1842ba7 100644 --- a/create_spec.py +++ b/create_spec.py @@ -46,7 +46,7 @@ def create_spec_file(): spec_file = 'flask_app.spec' datas = [('templates', 'templates'), ('static', 'static'), ('mapilio_kit', 'mapilio_kit')] - hiddenimports = [] + hiddenimports = ['configparser'] with open(requirements_file) as f: packages = [line.split('==')[0].strip() for line in f if line.strip() and not line.startswith('#')] diff --git a/requirements.txt b/requirements.txt index 8590681..b315c5b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,4 +28,5 @@ calculation-mapilio windows-curses; platform_system == "Windows" flask flaskwebgui -pytest \ No newline at end of file +pytest +configparser \ No newline at end of file From 8fa16eb58b3dc78eefc354418b7c513b634a1d34 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 23 May 2024 16:37:23 +0300 Subject: [PATCH 054/167] version update --- mapilio_kit/components/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapilio_kit/components/version.py b/mapilio_kit/components/version.py index 537e5ad..a0880df 100644 --- a/mapilio_kit/components/version.py +++ b/mapilio_kit/components/version.py @@ -1,2 +1,2 @@ # TODO check before commit -VERSION = "2.0.27" +VERSION = "2.0.29" From 70a0dcddfed2926f51b150b7bd1526af7ac5dfee Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 23 May 2024 16:53:30 +0300 Subject: [PATCH 055/167] build_linux file refactor --- script/build_linux | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/script/build_linux b/script/build_linux index 008fa0f..deb9d79 100755 --- a/script/build_linux +++ b/script/build_linux @@ -11,7 +11,12 @@ pyinstaller --noconfirm --distpath dist/${OS} flask_app.spec # check SOURCE=dist/${OS}/MapilioKit-Flask -$SOURCE --version + +nohup $SOURCE --version > /dev/null 2>&1 & +PID=$! + +sleep 5 + VERSION=$($SOURCE --version | awk '{print $1}') ARCH=$(uname -m) TARGET=dist/releases/mapilio-kit-${VERSION}-${OS}-${ARCH} @@ -23,8 +28,10 @@ cp "$SOURCE" "$TARGET" # sha256 TARGET_BASENAME=$(basename "$TARGET") cd dist/releases -shasum -a256 "$TARGET_BASENAME" | tee "${TARGET_BASENAME}.sha256.txt" +shasum -a 256 "$TARGET_BASENAME" | tee "${TARGET_BASENAME}.sha256.txt" cd ../../ +kill $PID + # summary -ls -l dist/releases \ No newline at end of file +ls -l dist/releases From e683653c4c81b41cbee22f669c3fa02832b4ddbf Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 23 May 2024 17:13:36 +0300 Subject: [PATCH 056/167] gh_token add --- .github/workflows/release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8c81c65..a786b8b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -112,6 +112,8 @@ jobs: - name: Release uses: softprops/action-gh-release@v1 + env: + GH_TOKEN: ${{ secrets.GH_TOKEN }} with: draft: true generate_release_notes: true From 1f030daee8e1b8968a4d61e15c6c7cb1b46f0589 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 23 May 2024 17:19:27 +0300 Subject: [PATCH 057/167] gh_token refactor --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a786b8b..154ed22 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -113,7 +113,7 @@ jobs: - name: Release uses: softprops/action-gh-release@v1 env: - GH_TOKEN: ${{ secrets.GH_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} with: draft: true generate_release_notes: true From 3a3c3388cbd729ff30beedbb83d4d37b7c102850 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 23 May 2024 17:36:13 +0300 Subject: [PATCH 058/167] release step refactor --- .github/workflows/release.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 154ed22..7fc00aa 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -110,14 +110,16 @@ jobs: MAPILIO_KIT__TESTS_EXECUTABLE: mapilio-kit_WINDOWS_VERY_HARD_TO_FIND_YOU_IN_ANOTHER_DIR_SO_I_MOVE_YOU_HERE.exe MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl - - name: Release - uses: softprops/action-gh-release@v1 + + - name: Create Release + uses: ncipollo/release-action@v1 env: GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} with: + tag: ${{ github.ref }} + name: Release ${{ github.ref }} + body: "Description of the release" draft: true - generate_release_notes: true - fail_on_unmatched_files: true files: | ./main/dist/releases/* From c9079eef1dd5849fe3f368ecbc1c1d4b630de5c0 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 24 May 2024 09:33:06 +0300 Subject: [PATCH 059/167] release.yml refactor --- .github/workflows/release.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7fc00aa..154ed22 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -110,16 +110,14 @@ jobs: MAPILIO_KIT__TESTS_EXECUTABLE: mapilio-kit_WINDOWS_VERY_HARD_TO_FIND_YOU_IN_ANOTHER_DIR_SO_I_MOVE_YOU_HERE.exe MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl - - - name: Create Release - uses: ncipollo/release-action@v1 + - name: Release + uses: softprops/action-gh-release@v1 env: GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} with: - tag: ${{ github.ref }} - name: Release ${{ github.ref }} - body: "Description of the release" draft: true + generate_release_notes: true + fail_on_unmatched_files: true files: | ./main/dist/releases/* From eeb16fe662e50c53fda017531ce454751b4c1437 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 24 May 2024 10:18:09 +0300 Subject: [PATCH 060/167] release.yml refactor --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 154ed22..9a7952f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -117,7 +117,7 @@ jobs: with: draft: true generate_release_notes: true - fail_on_unmatched_files: true + fail_on_unmatched_files: false files: | ./main/dist/releases/* From b93c4a928f1c005ac7a2d44c90033517e4615729 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 24 May 2024 11:10:06 +0300 Subject: [PATCH 061/167] release.yml refactor --- .github/workflows/release.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9a7952f..ab71d65 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -74,7 +74,6 @@ jobs: if: matrix.platform != 'windows-latest' - - name: Build and test with Pyinstaller on MacOS if: matrix.platform == 'macos-latest' run: | @@ -110,16 +109,17 @@ jobs: MAPILIO_KIT__TESTS_EXECUTABLE: mapilio-kit_WINDOWS_VERY_HARD_TO_FIND_YOU_IN_ANOTHER_DIR_SO_I_MOVE_YOU_HERE.exe MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl - - name: Release - uses: softprops/action-gh-release@v1 + - name: Create Release + uses: ncipollo/release-action@v1 env: GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} with: + tag: ${{ github.ref }} + name: Release ${{ github.ref }} + body: "Description of the release" draft: true - generate_release_notes: true - fail_on_unmatched_files: false files: | - ./main/dist/releases/* + dist/releases/* # pypi-publish: # if: ${{ startsWith(github.ref, 'refs/tags/') }} From 94b9e21bb43e20d38e20cf441216b58f3160268e Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 24 May 2024 12:09:32 +0300 Subject: [PATCH 062/167] release.yml refactor --- .github/workflows/release.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ab71d65..a0cb595 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -109,17 +109,16 @@ jobs: MAPILIO_KIT__TESTS_EXECUTABLE: mapilio-kit_WINDOWS_VERY_HARD_TO_FIND_YOU_IN_ANOTHER_DIR_SO_I_MOVE_YOU_HERE.exe MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl - - name: Create Release - uses: ncipollo/release-action@v1 + - name: Release + uses: softprops/action-gh-release@v1 env: GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} with: - tag: ${{ github.ref }} - name: Release ${{ github.ref }} - body: "Description of the release" draft: true + generate_release_notes: true + fail_on_unmatched_files: true files: | - dist/releases/* + ./main/dist/releases/* # pypi-publish: # if: ${{ startsWith(github.ref, 'refs/tags/') }} From c11fed6efd27682a045387c66a72c55850d42129 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 24 May 2024 14:12:44 +0300 Subject: [PATCH 063/167] release.yml refactor --- .github/workflows/release.yml | 49 +++++++++-------------------------- 1 file changed, 12 insertions(+), 37 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a0cb595..e0bada4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -91,7 +91,7 @@ jobs: python3 -m pip install pysocks python3 -m pip install pyinstaller python3 -m pip install configparser - chmod +x ./script/build_osx + chmod +x ./script/build_linux ./script/build_linux env: MAPILIO_KIT__TESTS_EXECUTABLE: ./dist/linux/mapilio-kit @@ -110,6 +110,7 @@ jobs: MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl - name: Release + id: create_release uses: softprops/action-gh-release@v1 env: GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} @@ -117,39 +118,13 @@ jobs: draft: true generate_release_notes: true fail_on_unmatched_files: true - files: | - ./main/dist/releases/* - -# pypi-publish: -# if: ${{ startsWith(github.ref, 'refs/tags/') }} -# name: Upload release to PyPI -# needs: ["build_and_release"] -# runs-on: "ubuntu-20.04" -# steps: -# - uses: actions/checkout@v3 -# -# - name: Set up Python -# uses: actions/setup-python@v4 -# with: -# python-version: "3.x" -# -# # https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/ -# - name: Install pypa/build -# run: python3 -m pip install build -# -# - name: Build -# # the default ouput dir dist/ was used by pyinstaller -# run: python3 -m build --outdir pypa_dist/ . -# -# - name: Publish package distributions to TestPyPI -# uses: pypa/gh-action-pypi-publish@v1.8.6 -# with: -# repository-url: https://test.pypi.org/legacy/ -# password: ${{ secrets.TEST_PYPI_API_TOKEN }} -# packages-dir: pypa_dist/ -# -# - name: Publish package distributions to PyPI -# uses: pypa/gh-action-pypi-publish@v1.8.6 -# with: -# password: ${{ secrets.PYPI_API_TOKEN }} -# packages-dir: pypa_dist/ \ No newline at end of file + files: ./main/dist/releases/* + + - name: Check for 502 and retry + if: steps.create_release.outcome == 'failure' + run: | + echo "Release failed, retrying..." + sleep 30 + gh release create ${{ github.ref }} ./main/dist/releases/* + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} From e96fb007226ff25a8cb198d1fe5b7df345bad930 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 24 May 2024 14:22:45 +0300 Subject: [PATCH 064/167] release.yml refactor --- .github/workflows/release.yml | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e0bada4..d5c77da 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,5 +1,3 @@ -# This workflow will build the package with Pyinstaller, upload it to GitHub Releases - name: Release on: @@ -109,22 +107,20 @@ jobs: MAPILIO_KIT__TESTS_EXECUTABLE: mapilio-kit_WINDOWS_VERY_HARD_TO_FIND_YOU_IN_ANOTHER_DIR_SO_I_MOVE_YOU_HERE.exe MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl - - name: Release - id: create_release - uses: softprops/action-gh-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + - name: Install GitHub CLI + uses: actions/setup-node@v4 with: - draft: true - generate_release_notes: true - fail_on_unmatched_files: true - files: ./main/dist/releases/* + node-version: '20' + - run: npm install -g @actions/github-script + + - name: Authenticate GitHub CLI + run: gh auth login --with-token <<< "${{ secrets.GH_TOKEN }}" - - name: Check for 502 and retry - if: steps.create_release.outcome == 'failure' + - name: Create Release + run: gh release create ${{ github.ref }} --draft --notes "Automated release by GitHub Actions" + + - name: Upload Assets run: | - echo "Release failed, retrying..." - sleep 30 - gh release create ${{ github.ref }} ./main/dist/releases/* - env: - GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + for file in ./main/dist/releases/*; do + gh release upload ${{ github.ref }} "$file" + done From cc53c58c9c2869e00ed60e814c74941b012b1378 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 24 May 2024 14:31:46 +0300 Subject: [PATCH 065/167] release.yml refactor --- .github/workflows/release.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d5c77da..86dd9dc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -111,8 +111,6 @@ jobs: uses: actions/setup-node@v4 with: node-version: '20' - - run: npm install -g @actions/github-script - - name: Authenticate GitHub CLI run: gh auth login --with-token <<< "${{ secrets.GH_TOKEN }}" From 82c0b0a700dd5b56cc3d7e8323bb33f0c7c742e2 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 24 May 2024 14:58:18 +0300 Subject: [PATCH 066/167] release.yml refactor --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 86dd9dc..d6287dd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -119,6 +119,6 @@ jobs: - name: Upload Assets run: | - for file in ./main/dist/releases/*; do + for file in ./dist/releases/*; do gh release upload ${{ github.ref }} "$file" done From ab9a67c599dd239dc3cfee713858b8552267ccba Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 24 May 2024 15:44:55 +0300 Subject: [PATCH 067/167] build_linux refactor --- script/build_linux | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/script/build_linux b/script/build_linux index deb9d79..37f480d 100755 --- a/script/build_linux +++ b/script/build_linux @@ -29,6 +29,11 @@ cp "$SOURCE" "$TARGET" TARGET_BASENAME=$(basename "$TARGET") cd dist/releases shasum -a 256 "$TARGET_BASENAME" | tee "${TARGET_BASENAME}.sha256.txt" +ZIP_FILE=mapilio-kit-${VERSION}-${OS}-${ARCH}.zip +zip -r "$ZIP_FILE" . +find . -mindepth 1 ! -name '*.zip' -delete + + cd ../../ kill $PID From 6eec496896adaeeff8666180322c3d3457b8c8fb Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 24 May 2024 16:08:04 +0300 Subject: [PATCH 068/167] release.yml refactor --- .github/workflows/release.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d6287dd..37448b2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -118,7 +118,4 @@ jobs: run: gh release create ${{ github.ref }} --draft --notes "Automated release by GitHub Actions" - name: Upload Assets - run: | - for file in ./dist/releases/*; do - gh release upload ${{ github.ref }} "$file" - done + run: gh release upload ${{ github.ref }} ./dist/releases/* From 9728e974b83037f88cdeb69548222145e0fefcc8 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 24 May 2024 16:52:44 +0300 Subject: [PATCH 069/167] release.yml refactor --- .github/workflows/release.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 37448b2..76e4666 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -89,6 +89,10 @@ jobs: python3 -m pip install pysocks python3 -m pip install pyinstaller python3 -m pip install configparser + python3 -m pip install types-requests + python3 -m pip install types-appdirs + python3 -m pip install usort + python3 -m pip install pyre-check chmod +x ./script/build_linux ./script/build_linux env: From b69beddcaca407eeb429f6471548b8a04e146952 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 24 May 2024 17:18:15 +0300 Subject: [PATCH 070/167] release.yml refactor --- .github/workflows/release.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 76e4666..1f01615 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -89,12 +89,13 @@ jobs: python3 -m pip install pysocks python3 -m pip install pyinstaller python3 -m pip install configparser - python3 -m pip install types-requests - python3 -m pip install types-appdirs - python3 -m pip install usort - python3 -m pip install pyre-check - chmod +x ./script/build_linux - ./script/build_linux +# python3 -m pip install types-requests +# python3 -m pip install types-appdirs +# python3 -m pip install usort +# python3 -m pip install pyre-check +# chmod +x ./script/build_linux +# ./script/build_linux + git clone https://github.com/mapilio/mapilio-kit.git env: MAPILIO_KIT__TESTS_EXECUTABLE: ./dist/linux/mapilio-kit MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl From d1e3e73db6363ff70121b1265ab702cca9b506ba Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 24 May 2024 17:33:41 +0300 Subject: [PATCH 071/167] release.yml refactor --- .github/workflows/release.yml | 13 ++++++------- script/build_linux | 6 +++--- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1f01615..76e4666 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -89,13 +89,12 @@ jobs: python3 -m pip install pysocks python3 -m pip install pyinstaller python3 -m pip install configparser -# python3 -m pip install types-requests -# python3 -m pip install types-appdirs -# python3 -m pip install usort -# python3 -m pip install pyre-check -# chmod +x ./script/build_linux -# ./script/build_linux - git clone https://github.com/mapilio/mapilio-kit.git + python3 -m pip install types-requests + python3 -m pip install types-appdirs + python3 -m pip install usort + python3 -m pip install pyre-check + chmod +x ./script/build_linux + ./script/build_linux env: MAPILIO_KIT__TESTS_EXECUTABLE: ./dist/linux/mapilio-kit MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl diff --git a/script/build_linux b/script/build_linux index 37f480d..f93f4b3 100755 --- a/script/build_linux +++ b/script/build_linux @@ -29,9 +29,9 @@ cp "$SOURCE" "$TARGET" TARGET_BASENAME=$(basename "$TARGET") cd dist/releases shasum -a 256 "$TARGET_BASENAME" | tee "${TARGET_BASENAME}.sha256.txt" -ZIP_FILE=mapilio-kit-${VERSION}-${OS}-${ARCH}.zip -zip -r "$ZIP_FILE" . -find . -mindepth 1 ! -name '*.zip' -delete +#ZIP_FILE=mapilio-kit-${VERSION}-${OS}-${ARCH}.zip +#zip -r "$ZIP_FILE" . +find . -mindepth 1 ! -name '*.txt' -delete cd ../../ From 8078830bbc538eb6e1446c9b1cd8f6b83c4ce232 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 29 May 2024 10:12:52 +0300 Subject: [PATCH 072/167] release.yml refactor --- .github/workflows/release.yml | 32 +++++++++++++++++++++----------- script/build_linux | 6 +++--- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 76e4666..ec76b55 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -111,15 +111,25 @@ jobs: MAPILIO_KIT__TESTS_EXECUTABLE: mapilio-kit_WINDOWS_VERY_HARD_TO_FIND_YOU_IN_ANOTHER_DIR_SO_I_MOVE_YOU_HERE.exe MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl - - name: Install GitHub CLI - uses: actions/setup-node@v4 - with: - node-version: '20' - - name: Authenticate GitHub CLI - run: gh auth login --with-token <<< "${{ secrets.GH_TOKEN }}" - - - name: Create Release - run: gh release create ${{ github.ref }} --draft --notes "Automated release by GitHub Actions" - - name: Upload Assets - run: gh release upload ${{ github.ref }} ./dist/releases/* + - name: Release + uses: softprops/action-gh-release@v1 + with: + draft: true + generate_release_notes: true + fail_on_unmatched_files: true + files: | + ./dist/releases/* + +# - name: Install GitHub CLI +# uses: actions/setup-node@v4 +# with: +# node-version: '20' +# - name: Authenticate GitHub CLI +# run: gh auth login --with-token <<< "${{ secrets.GH_TOKEN }}" +# +# - name: Create Release +# run: gh release create ${{ github.ref }} --draft --notes "Automated release by GitHub Actions" +# +# - name: Upload Assets +# run: gh release upload ${{ github.ref }} ./dist/releases/* diff --git a/script/build_linux b/script/build_linux index f93f4b3..37f480d 100755 --- a/script/build_linux +++ b/script/build_linux @@ -29,9 +29,9 @@ cp "$SOURCE" "$TARGET" TARGET_BASENAME=$(basename "$TARGET") cd dist/releases shasum -a 256 "$TARGET_BASENAME" | tee "${TARGET_BASENAME}.sha256.txt" -#ZIP_FILE=mapilio-kit-${VERSION}-${OS}-${ARCH}.zip -#zip -r "$ZIP_FILE" . -find . -mindepth 1 ! -name '*.txt' -delete +ZIP_FILE=mapilio-kit-${VERSION}-${OS}-${ARCH}.zip +zip -r "$ZIP_FILE" . +find . -mindepth 1 ! -name '*.zip' -delete cd ../../ From c55de0a209408ab9f2a774a0cbe2c703348fed28 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 29 May 2024 10:40:59 +0300 Subject: [PATCH 073/167] release.yml refactor --- .github/workflows/release.yml | 2 +- script/build_linux | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ec76b55..27ce9c2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -119,7 +119,7 @@ jobs: generate_release_notes: true fail_on_unmatched_files: true files: | - ./dist/releases/* + ./main/dist/releases/* # - name: Install GitHub CLI # uses: actions/setup-node@v4 diff --git a/script/build_linux b/script/build_linux index 37f480d..0f14b28 100755 --- a/script/build_linux +++ b/script/build_linux @@ -29,9 +29,9 @@ cp "$SOURCE" "$TARGET" TARGET_BASENAME=$(basename "$TARGET") cd dist/releases shasum -a 256 "$TARGET_BASENAME" | tee "${TARGET_BASENAME}.sha256.txt" -ZIP_FILE=mapilio-kit-${VERSION}-${OS}-${ARCH}.zip -zip -r "$ZIP_FILE" . -find . -mindepth 1 ! -name '*.zip' -delete +#ZIP_FILE=mapilio-kit-${VERSION}-${OS}-${ARCH}.zip +#zip -r "$ZIP_FILE" . +#find . -mindepth 1 ! -name '*.zip' -delete cd ../../ From 537a1701e68e237ee4337d78f736a8bc747afc05 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 29 May 2024 10:47:33 +0300 Subject: [PATCH 074/167] release.yml refactor --- .github/workflows/release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 27ce9c2..0d2f4be 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -114,6 +114,8 @@ jobs: - name: Release uses: softprops/action-gh-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} with: draft: true generate_release_notes: true From 02c99b07ad70ff775ed391889cbb1f587606e06f Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 29 May 2024 14:45:12 +0300 Subject: [PATCH 075/167] release.yml refactor --- .github/workflows/release.yml | 26 +++++++++++++++++++------- create_spec.py | 4 +--- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0d2f4be..b055d00 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -112,16 +112,28 @@ jobs: MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl - - name: Release - uses: softprops/action-gh-release@v1 + - name: Upload Release Asset + id: upload-release-asset + uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} with: - draft: true - generate_release_notes: true - fail_on_unmatched_files: true - files: | - ./main/dist/releases/* + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./dist/linux/* + asset_name: test.zip + asset_content_type: application/zip + + +# - name: Release +# uses: softprops/action-gh-release@v1 +# env: +# GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} +# with: +# draft: true +# generate_release_notes: true +# fail_on_unmatched_files: true +# files: | +# ./main/dist/releases/* # - name: Install GitHub CLI # uses: actions/setup-node@v4 diff --git a/create_spec.py b/create_spec.py index 1842ba7..01e4d9a 100644 --- a/create_spec.py +++ b/create_spec.py @@ -46,7 +46,6 @@ def create_spec_file(): spec_file = 'flask_app.spec' datas = [('templates', 'templates'), ('static', 'static'), ('mapilio_kit', 'mapilio_kit')] - hiddenimports = ['configparser'] with open(requirements_file) as f: packages = [line.split('==')[0].strip() for line in f if line.strip() and not line.startswith('#')] @@ -57,7 +56,6 @@ def create_spec_file(): if package == 'ExifRead': package = 'exifread' package_path = get_installed_package_path(package) - hiddenimports.append(package) package_folder_name = package.replace('-', '_') @@ -84,7 +82,7 @@ def create_spec_file(): pathex=[SPECPATH], binaries=[], datas={datas}, - hiddenimports={hiddenimports}, + hiddenimports=[], hookspath=[], runtime_hooks=[], excludes=[], From 8ff63e5edfcec962e9fc6606b064bc832b9ed14b Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 29 May 2024 14:51:08 +0300 Subject: [PATCH 076/167] create_spec.py refactor --- create_spec.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/create_spec.py b/create_spec.py index 01e4d9a..1842ba7 100644 --- a/create_spec.py +++ b/create_spec.py @@ -46,6 +46,7 @@ def create_spec_file(): spec_file = 'flask_app.spec' datas = [('templates', 'templates'), ('static', 'static'), ('mapilio_kit', 'mapilio_kit')] + hiddenimports = ['configparser'] with open(requirements_file) as f: packages = [line.split('==')[0].strip() for line in f if line.strip() and not line.startswith('#')] @@ -56,6 +57,7 @@ def create_spec_file(): if package == 'ExifRead': package = 'exifread' package_path = get_installed_package_path(package) + hiddenimports.append(package) package_folder_name = package.replace('-', '_') @@ -82,7 +84,7 @@ def create_spec_file(): pathex=[SPECPATH], binaries=[], datas={datas}, - hiddenimports=[], + hiddenimports={hiddenimports}, hookspath=[], runtime_hooks=[], excludes=[], From 0f924ebcf286acf6baee109c99d542ac56a11828 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 31 May 2024 11:31:36 +0300 Subject: [PATCH 077/167] release.yml refactor --- .github/workflows/release.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b055d00..458ce48 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -135,15 +135,15 @@ jobs: # files: | # ./main/dist/releases/* -# - name: Install GitHub CLI -# uses: actions/setup-node@v4 -# with: -# node-version: '20' -# - name: Authenticate GitHub CLI -# run: gh auth login --with-token <<< "${{ secrets.GH_TOKEN }}" -# -# - name: Create Release -# run: gh release create ${{ github.ref }} --draft --notes "Automated release by GitHub Actions" -# -# - name: Upload Assets -# run: gh release upload ${{ github.ref }} ./dist/releases/* + - name: Install GitHub CLI + uses: actions/setup-node@v4 + with: + node-version: '20' + - name: Authenticate GitHub CLI + run: gh auth login --with-token <<< "${{ secrets.GH_TOKEN }}" + + - name: Create Release + run: gh release create ${{ github.ref }} --draft --notes "Automated release by GitHub Actions" + + - name: Upload Assets + run: gh release upload ${{ github.ref }} ./dist/releases/* From 00e25931c17b4b222a88e2d5ef7d5e98200f3099 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 31 May 2024 11:37:41 +0300 Subject: [PATCH 078/167] release.yml refactor --- .github/workflows/release.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 458ce48..a749526 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -112,16 +112,16 @@ jobs: MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl - - name: Upload Release Asset - id: upload-release-asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./dist/linux/* - asset_name: test.zip - asset_content_type: application/zip +# - name: Upload Release Asset +# id: upload-release-asset +# uses: actions/upload-release-asset@v1 +# env: +# GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} +# with: +# upload_url: ${{ steps.create_release.outputs.upload_url }} +# asset_path: ./dist/linux/* +# asset_name: test.zip +# asset_content_type: application/zip # - name: Release From a96610d74112581b57f59fced54f7b85fbcc743b Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 31 May 2024 11:56:18 +0300 Subject: [PATCH 079/167] build_linux file refactor --- script/build_linux | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/script/build_linux b/script/build_linux index 0f14b28..bb630b5 100755 --- a/script/build_linux +++ b/script/build_linux @@ -12,12 +12,9 @@ pyinstaller --noconfirm --distpath dist/${OS} flask_app.spec # check SOURCE=dist/${OS}/MapilioKit-Flask -nohup $SOURCE --version > /dev/null 2>&1 & -PID=$! +#nohup $SOURCE --version > /dev/null 2>&1 & -sleep 5 - -VERSION=$($SOURCE --version | awk '{print $1}') +#VERSION=$($SOURCE --version | awk '{print $1}') ARCH=$(uname -m) TARGET=dist/releases/mapilio-kit-${VERSION}-${OS}-${ARCH} @@ -36,7 +33,5 @@ shasum -a 256 "$TARGET_BASENAME" | tee "${TARGET_BASENAME}.sha256.txt" cd ../../ -kill $PID - # summary ls -l dist/releases From ca876748ecf687f55b8237e229ccf903e0394481 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 31 May 2024 12:02:01 +0300 Subject: [PATCH 080/167] release.yml refactor --- .github/workflows/release.yml | 44 +++++++++++++++++------------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a749526..1802281 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -124,26 +124,26 @@ jobs: # asset_content_type: application/zip -# - name: Release -# uses: softprops/action-gh-release@v1 -# env: -# GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} -# with: -# draft: true -# generate_release_notes: true -# fail_on_unmatched_files: true -# files: | -# ./main/dist/releases/* - - - name: Install GitHub CLI - uses: actions/setup-node@v4 + - name: Release + uses: softprops/action-gh-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} with: - node-version: '20' - - name: Authenticate GitHub CLI - run: gh auth login --with-token <<< "${{ secrets.GH_TOKEN }}" - - - name: Create Release - run: gh release create ${{ github.ref }} --draft --notes "Automated release by GitHub Actions" - - - name: Upload Assets - run: gh release upload ${{ github.ref }} ./dist/releases/* + draft: true + generate_release_notes: true + fail_on_unmatched_files: true + files: | + ./main/dist/releases/* + +# - name: Install GitHub CLI +# uses: actions/setup-node@v4 +# with: +# node-version: '20' +# - name: Authenticate GitHub CLI +# run: gh auth login --with-token <<< "${{ secrets.GH_TOKEN }}" +# +# - name: Create Release +# run: gh release create ${{ github.ref }} --draft --notes "Automated release by GitHub Actions" +# +# - name: Upload Assets +# run: gh release upload ${{ github.ref }} ./dist/releases/* From 8703915413e290a5aa416c7c57e29763a95d1c63 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 31 May 2024 12:31:22 +0300 Subject: [PATCH 081/167] release.yml refactor --- .github/workflows/release.yml | 5 ----- script/build_linux | 1 + 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1802281..1c959f7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -88,11 +88,6 @@ jobs: run: | python3 -m pip install pysocks python3 -m pip install pyinstaller - python3 -m pip install configparser - python3 -m pip install types-requests - python3 -m pip install types-appdirs - python3 -m pip install usort - python3 -m pip install pyre-check chmod +x ./script/build_linux ./script/build_linux env: diff --git a/script/build_linux b/script/build_linux index bb630b5..2e7f2d4 100755 --- a/script/build_linux +++ b/script/build_linux @@ -12,6 +12,7 @@ pyinstaller --noconfirm --distpath dist/${OS} flask_app.spec # check SOURCE=dist/${OS}/MapilioKit-Flask +chmod +x "$SOURCE" #nohup $SOURCE --version > /dev/null 2>&1 & #VERSION=$($SOURCE --version | awk '{print $1}') From a514756c714cab29dc1289403f3ef6894f3423b5 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 31 May 2024 12:40:21 +0300 Subject: [PATCH 082/167] build_linux refactor --- script/build_linux | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/script/build_linux b/script/build_linux index 2e7f2d4..b8dbf2d 100755 --- a/script/build_linux +++ b/script/build_linux @@ -12,7 +12,6 @@ pyinstaller --noconfirm --distpath dist/${OS} flask_app.spec # check SOURCE=dist/${OS}/MapilioKit-Flask -chmod +x "$SOURCE" #nohup $SOURCE --version > /dev/null 2>&1 & #VERSION=$($SOURCE --version | awk '{print $1}') @@ -22,7 +21,7 @@ TARGET=dist/releases/mapilio-kit-${VERSION}-${OS}-${ARCH} # package mkdir -p dist/releases cp "$SOURCE" "$TARGET" - +chmod +x "$TARGET" # sha256 TARGET_BASENAME=$(basename "$TARGET") cd dist/releases From c6080df56bef4644e59910813821440316e0cc1f Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 31 May 2024 12:54:43 +0300 Subject: [PATCH 083/167] release.yml refactor --- .github/workflows/release.yml | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1c959f7..8a71cac 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -90,6 +90,7 @@ jobs: python3 -m pip install pyinstaller chmod +x ./script/build_linux ./script/build_linux + chmod +x ./dist/releases/mapilio-kit--linux-x86_64 env: MAPILIO_KIT__TESTS_EXECUTABLE: ./dist/linux/mapilio-kit MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl @@ -106,19 +107,6 @@ jobs: MAPILIO_KIT__TESTS_EXECUTABLE: mapilio-kit_WINDOWS_VERY_HARD_TO_FIND_YOU_IN_ANOTHER_DIR_SO_I_MOVE_YOU_HERE.exe MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl - -# - name: Upload Release Asset -# id: upload-release-asset -# uses: actions/upload-release-asset@v1 -# env: -# GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} -# with: -# upload_url: ${{ steps.create_release.outputs.upload_url }} -# asset_path: ./dist/linux/* -# asset_name: test.zip -# asset_content_type: application/zip - - - name: Release uses: softprops/action-gh-release@v1 env: @@ -129,16 +117,3 @@ jobs: fail_on_unmatched_files: true files: | ./main/dist/releases/* - -# - name: Install GitHub CLI -# uses: actions/setup-node@v4 -# with: -# node-version: '20' -# - name: Authenticate GitHub CLI -# run: gh auth login --with-token <<< "${{ secrets.GH_TOKEN }}" -# -# - name: Create Release -# run: gh release create ${{ github.ref }} --draft --notes "Automated release by GitHub Actions" -# -# - name: Upload Assets -# run: gh release upload ${{ github.ref }} ./dist/releases/* From 3d80268843c4bc09a60e755f18b23aa8c33d5067 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 31 May 2024 15:19:57 +0300 Subject: [PATCH 084/167] create_spec.py file refactor --- create_spec.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/create_spec.py b/create_spec.py index 1842ba7..815c8a8 100644 --- a/create_spec.py +++ b/create_spec.py @@ -3,6 +3,17 @@ import sys from pathlib import Path +def install_exiftool(): + try: + subprocess.run(['sudo', 'apt', 'install', '-y', 'exiftool'], check=True) + except subprocess.CalledProcessError as e: + print(f"Error installing exiftool: {e}") + +def get_exiftool_path(): + result = subprocess.run(['which', 'exiftool'], capture_output=True, text=True) + if result.returncode != 0: + raise ValueError("ExifTool not found") + return result.stdout.strip() def get_installed_package_path(package_name): result = subprocess.run([sys.executable, '-m', 'pip', 'show', package_name], capture_output=True, text=True) @@ -40,7 +51,6 @@ def get_installed_package_path(package_name): raise ValueError(f"Package path not found for {package_name}") - def create_spec_file(): requirements_file = 'requirements.txt' spec_file = 'flask_app.spec' @@ -48,6 +58,14 @@ def create_spec_file(): datas = [('templates', 'templates'), ('static', 'static'), ('mapilio_kit', 'mapilio_kit')] hiddenimports = ['configparser'] + # ExifTool'u kur ve yolunu bul + install_exiftool() + try: + exiftool_path = get_exiftool_path() + datas.append((exiftool_path, 'exiftool')) + except ValueError as e: + print(f"Warning: {e}") + with open(requirements_file) as f: packages = [line.split('==')[0].strip() for line in f if line.strip() and not line.startswith('#')] print(packages) From 795ed48b22ca59a051903e7ec3734811dca7ee9a Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 31 May 2024 16:27:35 +0300 Subject: [PATCH 085/167] flask_app.py FlaskUI integration --- flask_app.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flask_app.py b/flask_app.py index 072440c..8bc6d2c 100755 --- a/flask_app.py +++ b/flask_app.py @@ -159,6 +159,6 @@ def mapilio_video_upload_page(): return redirect(url_for("mapilio_login")) if __name__ == "__main__": - webbrowser.open("http://127.0.0.1:8081/") - app.run(host="0.0.0.0", port=8081, debug=True) - # FlaskUI(app=app, server="flask", width=1200, height=800, port=8080).run() + # webbrowser.open("http://127.0.0.1:8081/") + # app.run(host="0.0.0.0", port=8081, debug=True) + FlaskUI(app=app, server="flask", width=1200, height=800, port=8080).run() From 67e4eb29d59a5861dcd790afb9eff7b08562dbea Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 7 Jun 2024 12:11:02 +0300 Subject: [PATCH 086/167] exiftool path has been edited to be found dynamically. --- flask_app.py | 12 ++++++++---- mapilio_kit/components/version.py | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/flask_app.py b/flask_app.py index 8bc6d2c..6dab142 100755 --- a/flask_app.py +++ b/flask_app.py @@ -5,6 +5,7 @@ import subprocess import shutil import re +import sys from mapilio_kit.base import authenticator from mapilio_kit.components.edit_config import edit_config @@ -111,7 +112,10 @@ def mapilio_upload_page(): os.makedirs(UPLOAD_FOLDER, exist_ok=True) file.save(UPLOAD_FOLDER + file.filename) - command = f"mapilio_kit upload {UPLOAD_FOLDER} --dry_run" + bundle_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__))) + path_to_exiftool = os.path.abspath(os.path.join(bundle_dir, 'exiftool/exiftool')) + print(path_to_exiftool) + command = f"mapilio_kit upload {UPLOAD_FOLDER} --dry_run --exiftool_path {path_to_exiftool}" try: result = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) @@ -159,6 +163,6 @@ def mapilio_video_upload_page(): return redirect(url_for("mapilio_login")) if __name__ == "__main__": - # webbrowser.open("http://127.0.0.1:8081/") - # app.run(host="0.0.0.0", port=8081, debug=True) - FlaskUI(app=app, server="flask", width=1200, height=800, port=8080).run() + webbrowser.open("http://127.0.0.1:8081/") + app.run(host="0.0.0.0", port=8081, debug=True) + # FlaskUI(app=app, server="flask", width=1200, height=800, port=8080).run() diff --git a/mapilio_kit/components/version.py b/mapilio_kit/components/version.py index 1b89f18..a0880df 100644 --- a/mapilio_kit/components/version.py +++ b/mapilio_kit/components/version.py @@ -1,2 +1,2 @@ # TODO check before commit -VERSION = "2.0.28" +VERSION = "2.0.29" From afba136d047c320db7b44990f5428e7d8783a2dd Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 7 Jun 2024 12:13:02 +0300 Subject: [PATCH 087/167] pandas removed dependencies --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e933264..5b77bf9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,7 +19,6 @@ Shapely colorama==0.4.6 six==1.16.0 tqdm==4.62.3 -pandas==2.2.2 typing-extensions>=4.7.1 tzwhere==3.0.3 simple-term-menu From 48ef8ccb1c5720afd66a946acff92029ec0151cb Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 7 Jun 2024 12:20:06 +0300 Subject: [PATCH 088/167] pandas removed export.py --- mapilio_kit/components/export.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/mapilio_kit/components/export.py b/mapilio_kit/components/export.py index 693e82a..e86fb92 100644 --- a/mapilio_kit/components/export.py +++ b/mapilio_kit/components/export.py @@ -7,7 +7,6 @@ import string import hashlib import json -import pandas as pd def photo_uuid_creater(time: str, image_name: str) -> str: """ @@ -38,15 +37,22 @@ def unique_sequence_id_generator(letter_count: int = 8, digit_count: int = 4) -> def gps_file_reader(gps_file_path: str) -> tuple: """ - :param gps_file_path: The csv file that obtain gps values - :return: Latitude, longitude and time values as type list + Reads a CSV file containing GPS values and returns the latitude, longitude, and time values. + + :param gps_file_path: The path to the CSV file that contains GPS values + :return: Latitude, longitude, and time values as lists """ + lats = [] + lons = [] + times = [] + + with open(gps_file_path, mode='r', newline='') as csv_file: + csv_reader = csv.DictReader(csv_file) + for row in csv_reader: + lats.append(float(row['lat'])) + lons.append(float(row['lon'])) + times.append(row['captureTime']) - with open(gps_file_path) as csv_file: - csv_reader = pd.read_csv(csv_file, delimiter=',') - lats = csv_reader['lat'].tolist() - lons = csv_reader['lon'].tolist() - times = csv_reader['captureTime'].tolist() return lats, lons, times def geojson_add_feature(lat: float, lon: float, time: str, order: float, color: str, heading: float) -> dict: @@ -216,6 +222,4 @@ def export(csv_path, images_dir, output_geojson_name="out.geojson", output_csv_n geoFormat = geojson_type(features_geo) geoExportPath = output_geojson_name + ".geojson" - save_(geoFormat, geoExportPath) - - + save_(geoFormat, geoExportPath) \ No newline at end of file From f83ef2f8240d1f03fd33e32aaf2492234f6214ed Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 7 Jun 2024 12:35:49 +0300 Subject: [PATCH 089/167] FlaskUI integration --- flask_app.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flask_app.py b/flask_app.py index 6dab142..fbdcea5 100755 --- a/flask_app.py +++ b/flask_app.py @@ -163,6 +163,6 @@ def mapilio_video_upload_page(): return redirect(url_for("mapilio_login")) if __name__ == "__main__": - webbrowser.open("http://127.0.0.1:8081/") - app.run(host="0.0.0.0", port=8081, debug=True) - # FlaskUI(app=app, server="flask", width=1200, height=800, port=8080).run() + # webbrowser.open("http://127.0.0.1:8081/") + # app.run(host="0.0.0.0", port=8081, debug=True) + FlaskUI(app=app, server="flask", width=1200, height=800, port=8080).run() From c68bbe23b3cfc50011637c4d2c66fb5f06173fc5 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 7 Jun 2024 14:18:01 +0300 Subject: [PATCH 090/167] update version --- mapilio_kit/components/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapilio_kit/components/version.py b/mapilio_kit/components/version.py index a0880df..5585633 100644 --- a/mapilio_kit/components/version.py +++ b/mapilio_kit/components/version.py @@ -1,2 +1,2 @@ # TODO check before commit -VERSION = "2.0.29" +VERSION = "2.0.31" From 9f4c46eec420f9e0ca257b2b321a8e9a28c168f8 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 7 Jun 2024 14:39:35 +0300 Subject: [PATCH 091/167] flaskui deprecated --- flask_app.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flask_app.py b/flask_app.py index fbdcea5..6dab142 100755 --- a/flask_app.py +++ b/flask_app.py @@ -163,6 +163,6 @@ def mapilio_video_upload_page(): return redirect(url_for("mapilio_login")) if __name__ == "__main__": - # webbrowser.open("http://127.0.0.1:8081/") - # app.run(host="0.0.0.0", port=8081, debug=True) - FlaskUI(app=app, server="flask", width=1200, height=800, port=8080).run() + webbrowser.open("http://127.0.0.1:8081/") + app.run(host="0.0.0.0", port=8081, debug=True) + # FlaskUI(app=app, server="flask", width=1200, height=800, port=8080).run() From 89cab058c8885495d2bc162a10691a1c709ef45d Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 7 Jun 2024 16:41:55 +0300 Subject: [PATCH 092/167] exiftool installation command added on windows and macos --- create_spec.py | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/create_spec.py b/create_spec.py index 815c8a8..6470982 100644 --- a/create_spec.py +++ b/create_spec.py @@ -4,16 +4,38 @@ from pathlib import Path def install_exiftool(): - try: + if sys.platform == 'linux': subprocess.run(['sudo', 'apt', 'install', '-y', 'exiftool'], check=True) - except subprocess.CalledProcessError as e: - print(f"Error installing exiftool: {e}") + elif sys.platform == 'darwin': + subprocess.run(['brew', 'install', 'exiftool'], check=True) + elif sys.platform == 'win32': + subprocess.run(['powershell', '-Command', + 'Set-ExecutionPolicy Bypass -Scope Process -Force; ' + '[System.Net.ServicePointManager]::SecurityProtocol = ' + '[System.Net.ServicePointManager]::SecurityProtocol -bor 3072; ' + 'iex ((New-Object System.Net.WebClient).DownloadString(\'https://community.chocolatey.org/install.ps1\'))'], + check=True) + subprocess.run(['choco', 'install', 'exiftool', '-y'], check=True) + else: + raise ValueError("Unsupported platform") + def get_exiftool_path(): - result = subprocess.run(['which', 'exiftool'], capture_output=True, text=True) - if result.returncode != 0: - raise ValueError("ExifTool not found") - return result.stdout.strip() + try: + if sys.platform == 'linux' or sys.platform == 'darwin': + # Unix-based systems (Linux, macOS) + result = subprocess.run(['which', 'exiftool'], capture_output=True, text=True) + elif sys.platform == 'win32': + # Windows + result = subprocess.run(['where', 'exiftool'], capture_output=True, text=True) + else: + raise ValueError("Unsupported operating system") + + if result.returncode != 0: + raise ValueError("ExifTool not found") + return result.stdout.strip() + except subprocess.CalledProcessError as e: + raise ValueError(f"Error finding ExifTool: {e}") def get_installed_package_path(package_name): result = subprocess.run([sys.executable, '-m', 'pip', 'show', package_name], capture_output=True, text=True) From cbce12ae4c273a731f87c9b5b192f4022af4c36c Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 7 Jun 2024 16:47:04 +0300 Subject: [PATCH 093/167] release.yml refactor --- .github/workflows/release.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8a71cac..90894ce 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,12 +12,12 @@ jobs: strategy: matrix: python-version: [ "3.10" ] - platform: ["ubuntu-20.04"] # "macos-latest", "windows-latest" + platform: ["ubuntu-20.04", "windows-latest"] # "macos-latest", architecture: [ "x64" ] -# include: -# - architecture: "x86" -# platform: "windows-latest" -# python-version: "3.10" + include: + - architecture: "x86" + platform: "windows-latest" + python-version: "3.10" runs-on: ${{ matrix.platform }} From 29ceb50395cf2468ef38baeb43af6291ecf36db6 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 7 Jun 2024 17:19:08 +0300 Subject: [PATCH 094/167] build_win.ps1 refactor --- script/build_win.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/script/build_win.ps1 b/script/build_win.ps1 index 376d613..fb184dd 100644 --- a/script/build_win.ps1 +++ b/script/build_win.ps1 @@ -15,9 +15,9 @@ pyinstaller --noconfirm --distpath dist\win flask_app.spec # check $SOURCE="dist\win\MapilioKit-Flask.exe" -dist\win\mapilio-kit.exe --version -$VERSION_OUTPUT=dist\win\mapilio-kit.exe --version -$VERSION=$VERSION_OUTPUT.split(' ')[2] +# dist\win\mapilio-kit.exe --version +# $VERSION_OUTPUT=dist\win\mapilio-kit.exe --version +# $VERSION=$VERSION_OUTPUT.split(' ')[2] $TARGET="dist\releases\mapilio-kit-$VERSION-$OS-$ARCH.exe" # package From 5f400903dd60c0de0128b57a214e1a0860976698 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 7 Jun 2024 17:29:13 +0300 Subject: [PATCH 095/167] release.yml refactor --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 90894ce..8154ecd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -102,7 +102,7 @@ jobs: python3 -m pip install pyinstaller ./script/build_bootloader.ps1 ./script/build_win.ps1 - cp ./dist/win/mapilio-kit.exe mapilio-kit_WINDOWS_VERY_HARD_TO_FIND_YOU_IN_ANOTHER_DIR_SO_I_MOVE_YOU_HERE.exe + cp ./dist/win/mapilio-kit--win-64bit.exe mapilio-kit--win-64bit.exe env: MAPILIO_KIT__TESTS_EXECUTABLE: mapilio-kit_WINDOWS_VERY_HARD_TO_FIND_YOU_IN_ANOTHER_DIR_SO_I_MOVE_YOU_HERE.exe MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl From 412759420dc66fad72cbf45cb10748540e92c99e Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 7 Jun 2024 17:43:18 +0300 Subject: [PATCH 096/167] release.yml refactor --- .github/workflows/release.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8154ecd..6ef65bb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -102,7 +102,14 @@ jobs: python3 -m pip install pyinstaller ./script/build_bootloader.ps1 ./script/build_win.ps1 - cp ./dist/win/mapilio-kit--win-64bit.exe mapilio-kit--win-64bit.exe + if Test-Path ./dist/win/mapilio-kit--win-64bit.exe; then + cp ./dist/win/mapilio-kit--win-64bit.exe mapilio-kit.exe + elif Test-Path ./dist/win/mapilio-kit--win-32bit.exe; then + cp ./dist/win/mapilio-kit--win-32bit.exe mapilio-kit.exe + else + echo "Unable to find a suitable Windows executable." + exit 1 + fi env: MAPILIO_KIT__TESTS_EXECUTABLE: mapilio-kit_WINDOWS_VERY_HARD_TO_FIND_YOU_IN_ANOTHER_DIR_SO_I_MOVE_YOU_HERE.exe MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl From c9d7892dee0803f105f4eb8cb5061b4328e00805 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 7 Jun 2024 17:44:18 +0300 Subject: [PATCH 097/167] release.yml refactor --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6ef65bb..0a6b8a3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -103,9 +103,9 @@ jobs: ./script/build_bootloader.ps1 ./script/build_win.ps1 if Test-Path ./dist/win/mapilio-kit--win-64bit.exe; then - cp ./dist/win/mapilio-kit--win-64bit.exe mapilio-kit.exe + cp ./dist/win/mapilio-kit--win-64bit.exe mapilio-kit--win-64bit.exe elif Test-Path ./dist/win/mapilio-kit--win-32bit.exe; then - cp ./dist/win/mapilio-kit--win-32bit.exe mapilio-kit.exe + cp ./dist/win/mapilio-kit--win-32bit.exe mapilio-kit--win-32bit.exe else echo "Unable to find a suitable Windows executable." exit 1 From 55821450670c43efdd9732913172f4d8e4d27efa Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Fri, 7 Jun 2024 18:36:50 +0300 Subject: [PATCH 098/167] release.yml refactor --- .github/workflows/release.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0a6b8a3..da5ef5f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,12 +12,12 @@ jobs: strategy: matrix: python-version: [ "3.10" ] - platform: ["ubuntu-20.04", "windows-latest"] # "macos-latest", + platform: ["ubuntu-20.04"] # "macos-latest", "windows-latest" architecture: [ "x64" ] - include: - - architecture: "x86" - platform: "windows-latest" - python-version: "3.10" +# include: +# - architecture: "x86" +# platform: "windows-latest" +# python-version: "3.10" runs-on: ${{ matrix.platform }} @@ -102,14 +102,14 @@ jobs: python3 -m pip install pyinstaller ./script/build_bootloader.ps1 ./script/build_win.ps1 - if Test-Path ./dist/win/mapilio-kit--win-64bit.exe; then - cp ./dist/win/mapilio-kit--win-64bit.exe mapilio-kit--win-64bit.exe - elif Test-Path ./dist/win/mapilio-kit--win-32bit.exe; then - cp ./dist/win/mapilio-kit--win-32bit.exe mapilio-kit--win-32bit.exe - else - echo "Unable to find a suitable Windows executable." - exit 1 - fi + if (Test-Path ./dist/win/mapilio-kit--win-64bit.exe) { + cp ./dist/win/mapilio-kit--win-64bit.exe mapilio-kit--win-32bit.exe + } elseif (Test-Path ./dist/win/mapilio-kit--win-32bit.exe) { + cp ./dist/win/mapilio-kit--win-32bit.exe mapilio-kit--win-32bit.exe + } else { + echo "mapilio-kit.exe not found" + exit 1 + } env: MAPILIO_KIT__TESTS_EXECUTABLE: mapilio-kit_WINDOWS_VERY_HARD_TO_FIND_YOU_IN_ANOTHER_DIR_SO_I_MOVE_YOU_HERE.exe MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl From fbbd6c852afe381dedbd790cbbc12f716e36bfac Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 12 Jun 2024 10:00:13 +0300 Subject: [PATCH 099/167] release.yml refactor --- .github/workflows/release.yml | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index da5ef5f..53d37e4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,12 +12,12 @@ jobs: strategy: matrix: python-version: [ "3.10" ] - platform: ["ubuntu-20.04"] # "macos-latest", "windows-latest" + platform: ["ubuntu-20.04", "windows-latest"] # "macos-latest", "windows-latest" architecture: [ "x64" ] -# include: -# - architecture: "x86" -# platform: "windows-latest" -# python-version: "3.10" + include: + - architecture: "x86" + platform: "windows-latest" + python-version: "3.10" runs-on: ${{ matrix.platform }} @@ -102,13 +102,11 @@ jobs: python3 -m pip install pyinstaller ./script/build_bootloader.ps1 ./script/build_win.ps1 + if (Test-Path ./dist/win/mapilio-kit--win-32bit.exe) { + cp ./dist/win/mapilio-kit--win-32bit.exe mapilio-kit--win-32bit.exe + } if (Test-Path ./dist/win/mapilio-kit--win-64bit.exe) { - cp ./dist/win/mapilio-kit--win-64bit.exe mapilio-kit--win-32bit.exe - } elseif (Test-Path ./dist/win/mapilio-kit--win-32bit.exe) { - cp ./dist/win/mapilio-kit--win-32bit.exe mapilio-kit--win-32bit.exe - } else { - echo "mapilio-kit.exe not found" - exit 1 + cp ./dist/win/mapilio-kit--win-64bit.exe mapilio-kit--win-64bit.exe } env: MAPILIO_KIT__TESTS_EXECUTABLE: mapilio-kit_WINDOWS_VERY_HARD_TO_FIND_YOU_IN_ANOTHER_DIR_SO_I_MOVE_YOU_HERE.exe From 2eeadb84e14ad384c171507201119f85ed4b3472 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 12 Jun 2024 10:16:07 +0300 Subject: [PATCH 100/167] create_spec.py refactor --- create_spec.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/create_spec.py b/create_spec.py index 6470982..d0d7258 100644 --- a/create_spec.py +++ b/create_spec.py @@ -97,7 +97,8 @@ def create_spec_file(): if package == 'ExifRead': package = 'exifread' package_path = get_installed_package_path(package) - hiddenimports.append(package) + if not package == "charset-normalizer": + hiddenimports.append(package) package_folder_name = package.replace('-', '_') From b2065f78dfea23068ca8d2aa9446997241a99f49 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 12 Jun 2024 10:24:16 +0300 Subject: [PATCH 101/167] hidden_imports deprecated --- create_spec.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/create_spec.py b/create_spec.py index d0d7258..27f63da 100644 --- a/create_spec.py +++ b/create_spec.py @@ -97,8 +97,7 @@ def create_spec_file(): if package == 'ExifRead': package = 'exifread' package_path = get_installed_package_path(package) - if not package == "charset-normalizer": - hiddenimports.append(package) + # hiddenimports.append(package) package_folder_name = package.replace('-', '_') From e3a89e3a3cf1d7105f2f1d72624d48c2fb935ccb Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 12 Jun 2024 11:16:48 +0300 Subject: [PATCH 102/167] hidden_imports refactor --- create_spec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_spec.py b/create_spec.py index 27f63da..6470982 100644 --- a/create_spec.py +++ b/create_spec.py @@ -97,7 +97,7 @@ def create_spec_file(): if package == 'ExifRead': package = 'exifread' package_path = get_installed_package_path(package) - # hiddenimports.append(package) + hiddenimports.append(package) package_folder_name = package.replace('-', '_') From 28e9632923284524c83edd1d68e051904625f645 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 12 Jun 2024 14:40:44 +0300 Subject: [PATCH 103/167] create_spec.py refactor --- create_spec.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/create_spec.py b/create_spec.py index 6470982..62adb96 100644 --- a/create_spec.py +++ b/create_spec.py @@ -78,7 +78,7 @@ def create_spec_file(): spec_file = 'flask_app.spec' datas = [('templates', 'templates'), ('static', 'static'), ('mapilio_kit', 'mapilio_kit')] - hiddenimports = ['configparser'] + hiddenimports = [] # ExifTool'u kur ve yolunu bul install_exiftool() @@ -97,7 +97,7 @@ def create_spec_file(): if package == 'ExifRead': package = 'exifread' package_path = get_installed_package_path(package) - hiddenimports.append(package) + hiddenimports.append(str(package_path)) package_folder_name = package.replace('-', '_') From 04a77783b3a6bbe9b7cae3fb5b72c0ce502a75f0 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 12 Jun 2024 15:02:48 +0300 Subject: [PATCH 104/167] create_spec.py refactor --- create_spec.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/create_spec.py b/create_spec.py index 62adb96..25a945c 100644 --- a/create_spec.py +++ b/create_spec.py @@ -78,7 +78,7 @@ def create_spec_file(): spec_file = 'flask_app.spec' datas = [('templates', 'templates'), ('static', 'static'), ('mapilio_kit', 'mapilio_kit')] - hiddenimports = [] + hiddenimports = ['attrs'] # ExifTool'u kur ve yolunu bul install_exiftool() @@ -97,7 +97,7 @@ def create_spec_file(): if package == 'ExifRead': package = 'exifread' package_path = get_installed_package_path(package) - hiddenimports.append(str(package_path)) + # hiddenimports.append(package) package_folder_name = package.replace('-', '_') From beacf9506aa29b77ab86c00f750e6e631e4377f2 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 12 Jun 2024 15:25:04 +0300 Subject: [PATCH 105/167] create_spec.py refactor --- create_spec.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/create_spec.py b/create_spec.py index 25a945c..0c300ce 100644 --- a/create_spec.py +++ b/create_spec.py @@ -78,7 +78,7 @@ def create_spec_file(): spec_file = 'flask_app.spec' datas = [('templates', 'templates'), ('static', 'static'), ('mapilio_kit', 'mapilio_kit')] - hiddenimports = ['attrs'] + hiddenimports = [] # ExifTool'u kur ve yolunu bul install_exiftool() @@ -97,7 +97,7 @@ def create_spec_file(): if package == 'ExifRead': package = 'exifread' package_path = get_installed_package_path(package) - # hiddenimports.append(package) + hiddenimports.append(package) package_folder_name = package.replace('-', '_') From 1f6323e2b42cbb0e9e4dc2d275652ff5a659fe6e Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 12 Jun 2024 15:33:19 +0300 Subject: [PATCH 106/167] create_spec.py refactor --- create_spec.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/create_spec.py b/create_spec.py index 0c300ce..0a7c073 100644 --- a/create_spec.py +++ b/create_spec.py @@ -97,6 +97,8 @@ def create_spec_file(): if package == 'ExifRead': package = 'exifread' package_path = get_installed_package_path(package) + if package == 'charset-normalizer': + package = 'charset_normalizer' hiddenimports.append(package) package_folder_name = package.replace('-', '_') From f3bd5197e918c82467c0c528a0ccb9d8854cc4f8 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 12 Jun 2024 15:41:21 +0300 Subject: [PATCH 107/167] create_spec.py refactor --- create_spec.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/create_spec.py b/create_spec.py index 0a7c073..459cc65 100644 --- a/create_spec.py +++ b/create_spec.py @@ -97,11 +97,9 @@ def create_spec_file(): if package == 'ExifRead': package = 'exifread' package_path = get_installed_package_path(package) - if package == 'charset-normalizer': - package = 'charset_normalizer' - hiddenimports.append(package) package_folder_name = package.replace('-', '_') + hiddenimports.append(package_folder_name) if package_folder_name == package: package_data_name = package From bde9a7a209273490090a769510c61ced8ee85031 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 12 Jun 2024 15:52:22 +0300 Subject: [PATCH 108/167] create_spec.py refactor --- create_spec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_spec.py b/create_spec.py index 459cc65..4bf39b2 100644 --- a/create_spec.py +++ b/create_spec.py @@ -78,7 +78,7 @@ def create_spec_file(): spec_file = 'flask_app.spec' datas = [('templates', 'templates'), ('static', 'static'), ('mapilio_kit', 'mapilio_kit')] - hiddenimports = [] + hiddenimports = ['configparser'] # ExifTool'u kur ve yolunu bul install_exiftool() From 348e830b35a40bb62efc5675b58d30652e39cda1 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 12 Jun 2024 16:24:00 +0300 Subject: [PATCH 109/167] mapilio-kit dynamic installation --- create_spec.py | 2 +- flask_app.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/create_spec.py b/create_spec.py index 4bf39b2..66b66a7 100644 --- a/create_spec.py +++ b/create_spec.py @@ -77,7 +77,7 @@ def create_spec_file(): requirements_file = 'requirements.txt' spec_file = 'flask_app.spec' - datas = [('templates', 'templates'), ('static', 'static'), ('mapilio_kit', 'mapilio_kit')] + datas = [('templates', 'templates'), ('static', 'static'), ('.', 'mapilio_kit')] hiddenimports = ['configparser'] # ExifTool'u kur ve yolunu bul diff --git a/flask_app.py b/flask_app.py index 6dab142..ae97dfc 100755 --- a/flask_app.py +++ b/flask_app.py @@ -163,6 +163,10 @@ def mapilio_video_upload_page(): return redirect(url_for("mapilio_login")) if __name__ == "__main__": + bundle_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__))) + path_to_mapilio_kit = os.path.abspath(os.path.join(bundle_dir, 'mapilio_kit')) + command = f"pip install ." + subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) webbrowser.open("http://127.0.0.1:8081/") app.run(host="0.0.0.0", port=8081, debug=True) # FlaskUI(app=app, server="flask", width=1200, height=800, port=8080).run() From 32ed4ab7a567b5a3d51a55d32293e66f7d50764c Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 12 Jun 2024 17:17:07 +0300 Subject: [PATCH 110/167] mapilio-kit dynamic installation --- flask_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask_app.py b/flask_app.py index ae97dfc..28ed8db 100755 --- a/flask_app.py +++ b/flask_app.py @@ -165,7 +165,7 @@ def mapilio_video_upload_page(): if __name__ == "__main__": bundle_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__))) path_to_mapilio_kit = os.path.abspath(os.path.join(bundle_dir, 'mapilio_kit')) - command = f"pip install ." + command = f"pip install {path_to_mapilio_kit}" subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) webbrowser.open("http://127.0.0.1:8081/") app.run(host="0.0.0.0", port=8081, debug=True) From 0f3187a4ea9d72082d359d673f7dc14b858213ee Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 12 Jun 2024 17:39:44 +0300 Subject: [PATCH 111/167] mapilio-kit dynamic installation --- create_spec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_spec.py b/create_spec.py index 66b66a7..8e64822 100644 --- a/create_spec.py +++ b/create_spec.py @@ -110,7 +110,7 @@ def create_spec_file(): except ValueError as e: print(f"Warning: {e}") - + print(datas) with open(spec_file, 'w') as f: f.write(f""" # -*- mode: python ; coding: utf-8 -*- From 5ee36292f2f5cc972b88207c9b56c79f3b78160c Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Wed, 12 Jun 2024 18:04:32 +0300 Subject: [PATCH 112/167] create_spec.py refactor --- create_spec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_spec.py b/create_spec.py index 8e64822..e4ec432 100644 --- a/create_spec.py +++ b/create_spec.py @@ -77,7 +77,7 @@ def create_spec_file(): requirements_file = 'requirements.txt' spec_file = 'flask_app.spec' - datas = [('templates', 'templates'), ('static', 'static'), ('.', 'mapilio_kit')] + datas = [('templates', 'templates'), ('static', 'static'), ('mapilio_kit', 'mapilio_kit')] hiddenimports = ['configparser'] # ExifTool'u kur ve yolunu bul From 17761481c68abffc06cfd05dabbcabd258f300b0 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 13 Jun 2024 09:41:33 +0300 Subject: [PATCH 113/167] release.yml refactor --- .github/workflows/release.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 53d37e4..2e8adca 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -60,6 +60,8 @@ jobs: - name: Create spec file run: | + ls + pwd python create_spec.py - name: Validate version @@ -86,6 +88,8 @@ jobs: - name: Build and test with Pyinstaller on Ubuntu if: matrix.platform == 'ubuntu-20.04' run: | + ls + pwd python3 -m pip install pysocks python3 -m pip install pyinstaller chmod +x ./script/build_linux @@ -98,6 +102,8 @@ jobs: - name: Build and test with Pyinstaller on Windows if: matrix.platform == 'windows-latest' run: | + ls + pwd python3 -m pip install pysocks python3 -m pip install pyinstaller ./script/build_bootloader.ps1 From b1edfdd0337833de2e8fd71026886bbc72c6c5fc Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 13 Jun 2024 09:51:11 +0300 Subject: [PATCH 114/167] mapilio-kit path changed to working directory --- create_spec.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/create_spec.py b/create_spec.py index e4ec432..fcc6546 100644 --- a/create_spec.py +++ b/create_spec.py @@ -77,7 +77,8 @@ def create_spec_file(): requirements_file = 'requirements.txt' spec_file = 'flask_app.spec' - datas = [('templates', 'templates'), ('static', 'static'), ('mapilio_kit', 'mapilio_kit')] + current_directory = os.getcwd() + datas = [('templates', 'templates'), ('static', 'static'), ({current_directory}, 'mapilio_kit')] hiddenimports = ['configparser'] # ExifTool'u kur ve yolunu bul From 2435ffe4f517fc953b7ccf9830b37b05958a5725 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 13 Jun 2024 09:55:38 +0300 Subject: [PATCH 115/167] mapilio-kit path changed to working directory --- create_spec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_spec.py b/create_spec.py index fcc6546..dc15548 100644 --- a/create_spec.py +++ b/create_spec.py @@ -78,7 +78,7 @@ def create_spec_file(): spec_file = 'flask_app.spec' current_directory = os.getcwd() - datas = [('templates', 'templates'), ('static', 'static'), ({current_directory}, 'mapilio_kit')] + datas = [('templates', 'templates'), ('static', 'static'), (current_directory, 'mapilio_kit')] hiddenimports = ['configparser'] # ExifTool'u kur ve yolunu bul From 04b1ffc6eea42bd6d87067bf33e8e62ad7b3cc35 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 13 Jun 2024 10:02:47 +0300 Subject: [PATCH 116/167] create_spec.py refactor --- create_spec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_spec.py b/create_spec.py index dc15548..dee73aa 100644 --- a/create_spec.py +++ b/create_spec.py @@ -78,7 +78,7 @@ def create_spec_file(): spec_file = 'flask_app.spec' current_directory = os.getcwd() - datas = [('templates', 'templates'), ('static', 'static'), (current_directory, 'mapilio_kit')] + datas = [('templates', 'templates'), ('static', 'static'), (current_directory, 'mapilio_kit_setup'), ('mapilio_kit', 'mapilio_kit')] hiddenimports = ['configparser'] # ExifTool'u kur ve yolunu bul From 1b04a2d316733b3746a591ab048186c12e5c5103 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 13 Jun 2024 11:33:19 +0300 Subject: [PATCH 117/167] subprocess deprecated for mapilio_kit upload command usage --- flask_app.py | 90 ++++++++++++++++++++++++---------------------------- 1 file changed, 42 insertions(+), 48 deletions(-) diff --git a/flask_app.py b/flask_app.py index 28ed8db..00cc781 100755 --- a/flask_app.py +++ b/flask_app.py @@ -1,30 +1,34 @@ -from flask import Flask, render_template, request, redirect, url_for, jsonify -from flaskwebgui import FlaskUI -import webbrowser +import json import os -import subprocess import shutil -import re import sys +import webbrowser + +from flask import Flask, render_template, request, redirect, url_for, jsonify from mapilio_kit.base import authenticator from mapilio_kit.components.edit_config import edit_config +from mapilio_kit.components.geotag_property_handler import geotag_property_handler +from mapilio_kit.components.insert_MAPJson import insert_MAPJson from mapilio_kit.components.login import list_all_users +from mapilio_kit.components.metadata_property_handler import metadata_property_handler +from mapilio_kit.components.sequence_property_handler import sequence_property_handler +from mapilio_kit.components.upload import upload app = Flask(__name__) UPLOAD_FOLDER = os.path.join(os.path.expanduser("~"), ".cache", "mapilio", "MapilioKit", "images/") MAPILIO_CONFIG_PATH = os.getenv( - "MAPILIO_CONFIG_PATH", - os.path.join( - os.path.expanduser("~"), - ".config", - "mapilio", - "configs", - "CLIENT_USERS", - ), - ) + "MAPILIO_CONFIG_PATH", + os.path.join( + os.path.expanduser("~"), + ".config", + "mapilio", + "configs", + "CLIENT_USERS", + ), +) def get_args_mapilio(func): @@ -48,6 +52,13 @@ def check_authenticate(): return token, authentication_status +def decompose(import_path, exiftool_path): + metadata_property_handler(import_path=import_path, exiftool_path=exiftool_path) + geotag_property_handler(import_path=import_path) + sequence_property_handler(import_path=import_path) + insert_MAPJson(import_path=import_path) + + @app.route("/", methods=["GET", "POST"]) def index(): token, authentication_status = check_authenticate() @@ -57,9 +68,8 @@ def index(): return render_template('login.html') -@app.route('/login', methods=['GET','POST']) +@app.route('/login', methods=['GET', 'POST']) def mapilio_login(): - if request.method == 'POST': args = get_args_mapilio(edit_config) email = request.form['email'].strip() @@ -114,43 +124,30 @@ def mapilio_upload_page(): bundle_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__))) path_to_exiftool = os.path.abspath(os.path.join(bundle_dir, 'exiftool/exiftool')) - print(path_to_exiftool) - command = f"mapilio_kit upload {UPLOAD_FOLDER} --dry_run --exiftool_path {path_to_exiftool}" - try: - result = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - if result.returncode == 0: + try: + decompose(import_path=UPLOAD_FOLDER, exiftool_path=path_to_exiftool) + upload_status = upload(import_path=UPLOAD_FOLDER, dry_run=True) + if upload_status.get("Success"): try: + jsonPath = os.path.join(UPLOAD_FOLDER, "mapilio_image_description.json") + with open(jsonPath, 'r') as f: + data = json.load(f) + total_images = data[-1]["Information"]["total_images"] + processed_images = data[-1]["Information"]["processed_images"] + failed_images = data[-1]["Information"]["failed_images"] shutil.rmtree(UPLOAD_FOLDER) - output_lines = result.stderr.split('\n') - output_text = '\n'.join(output_lines) - - total_images_pattern = r'"total_images": (\d+)' - processed_images_pattern = r'"processed_images": (\d+)' - failed_images_pattern = r'"failed_images": (\d+)' - total_images_match = re.search(total_images_pattern, output_text) - processed_images_match = re.search(processed_images_pattern, output_text) - failed_images_match = re.search(failed_images_pattern, output_text) - - total_images = int(total_images_match.group(1)) if total_images_match else 0 - processed_images = int(processed_images_match.group(1)) if processed_images_match else 0 - failed_images = int(failed_images_match.group(1)) if failed_images_match else 0 - - return jsonify(success=True, message="Images uploaded successfully", total_images=total_images, processed_images=processed_images, failed_images=failed_images), 200 + return jsonify(success=True, message="Images uploaded successfully", total_images=total_images, + processed_images=processed_images, failed_images=failed_images), 200 except OSError as err: print(f"Error: {UPLOAD_FOLDER} could not be deleted. - {err}") return jsonify(success=False, message=f"{err}") - except subprocess.CalledProcessError as e: - return jsonify(success=False, message={e}), 500 + except: + e = upload_status.get("Error") + return jsonify(success=False, message=f"Error: {e}"), 500 else: return jsonify(success=False, message="Method Not Allowed"), 500 - error_message = result.stderr.split() - error_message = ' '.join(error_message) - error_index = error_message.find("error:") - if error_index != -1: - error_message = error_message[error_index:] - return jsonify(success=False, message=f"Error: {error_message}"), 500 @app.route('/video-upload', methods=['GET', 'POST']) @@ -162,11 +159,8 @@ def mapilio_video_upload_page(): else: return redirect(url_for("mapilio_login")) + if __name__ == "__main__": - bundle_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__))) - path_to_mapilio_kit = os.path.abspath(os.path.join(bundle_dir, 'mapilio_kit')) - command = f"pip install {path_to_mapilio_kit}" - subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) webbrowser.open("http://127.0.0.1:8081/") app.run(host="0.0.0.0", port=8081, debug=True) # FlaskUI(app=app, server="flask", width=1200, height=800, port=8080).run() From 4b2254a22f538d5fd68577e6c11a5566947cdca6 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 13 Jun 2024 11:42:56 +0300 Subject: [PATCH 118/167] subprocess deprecated for mapilio_kit upload command usage --- flask_app.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/flask_app.py b/flask_app.py index 00cc781..bac9c4d 100755 --- a/flask_app.py +++ b/flask_app.py @@ -143,6 +143,9 @@ def mapilio_upload_page(): except OSError as err: print(f"Error: {UPLOAD_FOLDER} could not be deleted. - {err}") return jsonify(success=False, message=f"{err}") + else: + e = upload_status.get("Error") + return jsonify(success=False, message=f"Error: {e}"), 500 except: e = upload_status.get("Error") return jsonify(success=False, message=f"Error: {e}"), 500 From bbeed7d3235709792b7f97a48046e97af3936c19 Mon Sep 17 00:00:00 2001 From: Yusuf Esat Date: Thu, 13 Jun 2024 11:51:08 +0300 Subject: [PATCH 119/167] subprocess deprecated for mapilio_kit upload command usage --- .github/workflows/release.yml | 6 ------ create_spec.py | 3 +-- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2e8adca..53d37e4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -60,8 +60,6 @@ jobs: - name: Create spec file run: | - ls - pwd python create_spec.py - name: Validate version @@ -88,8 +86,6 @@ jobs: - name: Build and test with Pyinstaller on Ubuntu if: matrix.platform == 'ubuntu-20.04' run: | - ls - pwd python3 -m pip install pysocks python3 -m pip install pyinstaller chmod +x ./script/build_linux @@ -102,8 +98,6 @@ jobs: - name: Build and test with Pyinstaller on Windows if: matrix.platform == 'windows-latest' run: | - ls - pwd python3 -m pip install pysocks python3 -m pip install pyinstaller ./script/build_bootloader.ps1 diff --git a/create_spec.py b/create_spec.py index dee73aa..983231b 100644 --- a/create_spec.py +++ b/create_spec.py @@ -78,10 +78,9 @@ def create_spec_file(): spec_file = 'flask_app.spec' current_directory = os.getcwd() - datas = [('templates', 'templates'), ('static', 'static'), (current_directory, 'mapilio_kit_setup'), ('mapilio_kit', 'mapilio_kit')] + datas = [('templates', 'templates'), ('static', 'static'), ('mapilio_kit', 'mapilio_kit')] hiddenimports = ['configparser'] - # ExifTool'u kur ve yolunu bul install_exiftool() try: exiftool_path = get_exiftool_path() From 0e74d89b9cd70219d9df834eaa70408a8bef65eb Mon Sep 17 00:00:00 2001 From: gorkemgul Date: Tue, 25 Jun 2024 12:55:39 +0100 Subject: [PATCH 120/167] fix FakeUploadManager Class problem --- mapilio_kit/components/upload_manager.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/mapilio_kit/components/upload_manager.py b/mapilio_kit/components/upload_manager.py index 978fcb7..90b55bb 100644 --- a/mapilio_kit/components/upload_manager.py +++ b/mapilio_kit/components/upload_manager.py @@ -130,7 +130,10 @@ class FakeUploadManager(UploadManager): def upload( self, + user_items: types.User, data: T.IO[bytes], + organization_key: str = None, + project_key: str = None, offset: T.Optional[int] = None, chunk_size: int = DEFAULT_CHUNK_SIZE, ) -> str: @@ -154,13 +157,18 @@ def upload( # ) -> int: # return 1 - def fetch_offset(self) -> int: - try: - with open(self.session_key, "rb") as fp: - fp.seek(0, io.SEEK_END) - return fp.tell() - except FileNotFoundError: - return 0 + def fetch_offset(self, email) -> int: + headers = { + "Authorization": f"OAuth {self.user_access_token}" + } + resp = requests.get( + f"{MAPILIO_UPLOAD_ENDPOINT_ZIP}?fileName={self.session_key}&email={email}", + headers=headers + ) + resp.raise_for_status() + data = resp.json() + + return data["totalChunkUploaded"] def _file_stats(fp: T.IO[bytes]): From 41a6fa51eb1b859eafd01cd9b9d5c62add33b941 Mon Sep 17 00:00:00 2001 From: gorkemgul Date: Wed, 26 Jun 2024 09:33:12 +0100 Subject: [PATCH 121/167] update: fix exiftool path problem --- mapilio_kit/components/utilities.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mapilio_kit/components/utilities.py b/mapilio_kit/components/utilities.py index 9e672d5..a03e38b 100644 --- a/mapilio_kit/components/utilities.py +++ b/mapilio_kit/components/utilities.py @@ -91,16 +91,16 @@ def get_exiftool_specific_feature(video_or_image_path: str, exiftool_path=None) Returns: """ - + exiftool_path = exiftool_path if exiftool_path is not None else "exiftool" os_name = platform.system() if os_name == "Windows": - process = subprocess.Popen(["exiftool", video_or_image_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE, creationflags=subprocess.CREATE_NO_WINDOW) + process = subprocess.Popen([exiftool_path, video_or_image_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE, creationflags=subprocess.CREATE_NO_WINDOW) else: if exiftool_path is not None: process = subprocess.Popen([exiftool_path, video_or_image_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE) else: - process = subprocess.Popen(["exiftool", video_or_image_path], stdout=subprocess.PIPE, + process = subprocess.Popen([exiftool_path, video_or_image_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE) dict_object = { From e38e90baca20c05ec16c4ff310117d204588d627 Mon Sep 17 00:00:00 2001 From: gorkemgul Date: Wed, 26 Jun 2024 10:09:12 +0100 Subject: [PATCH 122/167] update: close dry run method --- flask_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask_app.py b/flask_app.py index bac9c4d..1f13f8a 100755 --- a/flask_app.py +++ b/flask_app.py @@ -127,7 +127,7 @@ def mapilio_upload_page(): try: decompose(import_path=UPLOAD_FOLDER, exiftool_path=path_to_exiftool) - upload_status = upload(import_path=UPLOAD_FOLDER, dry_run=True) + upload_status = upload(import_path=UPLOAD_FOLDER, dry_run=False) if upload_status.get("Success"): try: jsonPath = os.path.join(UPLOAD_FOLDER, "mapilio_image_description.json") From a6acf7ee72c7748df817d3bedef73be48f70c257 Mon Sep 17 00:00:00 2001 From: gorkemgul Date: Wed, 26 Jun 2024 10:57:24 +0100 Subject: [PATCH 123/167] update: fix typo --- templates/image-upload.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/image-upload.html b/templates/image-upload.html index 4abc4c8..17b86ac 100644 --- a/templates/image-upload.html +++ b/templates/image-upload.html @@ -71,13 +71,13 @@

      Mapilio Kit Uploader

      SVG Image -

      Drag or click files here.

      +

      Drag files or click here to choose.

      Files to Upload

      -

      Total images of count: 0

      +

      Total number of images: 0

        From 3e7a9cd3aca33c12e6b36717773bfb5f019b8394 Mon Sep 17 00:00:00 2001 From: gorkemgul Date: Wed, 26 Jun 2024 14:14:10 +0100 Subject: [PATCH 124/167] update: add app icon --- create_spec.py | 1 + docs/assets/icon/mapilio_ico.ico | Bin 0 -> 15750 bytes 2 files changed, 1 insertion(+) create mode 100644 docs/assets/icon/mapilio_ico.ico diff --git a/create_spec.py b/create_spec.py index 983231b..fe5c5d5 100644 --- a/create_spec.py +++ b/create_spec.py @@ -148,6 +148,7 @@ def create_spec_file(): upx=True, runtime_tmpdir=None, console=True, + icon='/home/ai/Desktop/mapilio-kit/docs/assets/icon/mapilio_ico.ico' ) app = BUNDLE(exe, name='kit-gui.app', icon=None, bundle_identifier=None) diff --git a/docs/assets/icon/mapilio_ico.ico b/docs/assets/icon/mapilio_ico.ico new file mode 100644 index 0000000000000000000000000000000000000000..d41a8e64eef8145ad57156c99732d9376ee2bc5a GIT binary patch literal 15750 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

        ySE}=vZKok#-XN!=T#zl5~!*n3L zDukKoKZn>U9Pu~tyQZCq*dStPyGmw9u7c0Lk$9pZ9`9T-i)RoR3tN3m(~Zzf$7uvQ zS6tR!=GRS13vs*H4{>+T{!ESD)>Aogkw1etp~u0$G^*&vwf&{eHp&-6aun1wr0^|} zf{ExtTKf9YXn?iDjWl?CQRS~mKiVN7eEPSwXZxetD0!466hkI@RZ0tT$_Lu9pP*?( z8CsU`7oI@}{6wp*B4Xiav&y4T|I)6!j2~Z>FUmf+Qik4IEA%A8Qg)W07A(%m4yzx~ z7XFh|pJ>Ff=1tdra7Yo)E>kzEcxB@Ysj0`w67Qiz))gxW1WIo4!bQ&vl2^LJ>`mLD zj0r?Fd2`8`U9<6}k)@ojL=BH;pj-MP66e`HL?0+y0e}5&!H?Mbpqig`LMUgG+(Mj@ zo*^myMI4PfKRzBeS)&kIk0{(;)_i`d`FImI)N~qs^Y<<^Gfrz-ldEVDhzNM(IxHP3 zq2Yd&LF+2!cg{BFdNcUSqNApqE>V&Hls-K^QMo1Go-FqvmW-cR`R6%WCuvjA3t;y4 zVQ3euvTWO3Yc7-&;y0K7+b2kR;!)tG$b@}5l>dqY0hd%#b|a8u-nm&n3ku{az!?g7 z=?HHQA28ews<)cxC9l_qIBdmUb{pUgxyBW(@tME_j-^iQdVXey;Pt+sNXqX^LQ&PMJv{|T)~|aWPc-w&dOsZrPgmrzJ)fJh8I07$(uwQO$=CK2Nlv^nKC8mWsLoeFTFu0RxAZd zN(>V4im@5v6Y~;Cplxu|w?}8g)I$E7-_GTI&AvBYBZ_JVa(NO(JF(QQ>j~K~}&4v{g}7D#_yON~wKC z4M6UoT{>#xYWFy0I}BAI4VsxzN4PbDL^F%=T*yxxaBbF91?pd9K%r=$E3yH;fW-*N z1f>e(;k4Qmhse5pm#C;1c_%80Fc3VPoScPFT3JRJaglE&H!RBGCO};U01si%4n`B5 z%^)ve%{2A_tmX!Fre~`|WSsEk*4DE42Spj_kB4I1<%|)DxFuD+Z zYBtEPa|hE_gU7XQo(I%w%c6QwS`h~0DVTS!@=tl9^c^xH2go}JDGyAH75P@c65=qa zVeJL&987C=+U$Bp3oUd0=4@rO~tGZ65@=Ne}|Hm>?oEjwq!yl<~{F{m&(ehr@L!jO~^9F!Bcy@a4h zz1ard0nZop;mzIU+&>hwVKJckdQ5s#h-EhK-Xi@h7RPH^%90OlJeYum@De(k74j=< zw++kKhsa2nvb`gl?NvfC8&NIdlA5i{PmR*|0EK=D!y;W+ zpSm@AX3#m3$+c*;JOq?|FRpDAg(aN_`J=mK5_~;^C8b@}Cvj4!Yqb=k>#9I~M?u#S z4(o?wTqs+N&k@%qRTKX1Cq6$q>Ls^eEs7^hYz8@_#v_LuPk%UMS5I3>TVz1os#Ic% zc@eX~MwBiPtOrqDP+e$RjN>&dOhrZN#E}Gw3UaRB7$KQl(d7q zcsn9YAo&>V;Tp&@2&SJ^4oI3Q4a+SI=DK1gBm2vhY!^NWkZ52__UX^s_YVHq4Wg6p zdb5aYDp*r@aoxac<(tpMM4S}Bkx%r$i@>>vo4y>hvPZ;;GQowzo-D>%_lJzR#{a^4 z%(AeyY2n%&?*BmH8K}cho2#U?+ymdBZ0KIEDqhVBc|E~WI^rU16olT5C_ofWOM69> VHuTdf6LFIo@Ih7uQYCE~@_!ecKxY5| literal 0 HcmV?d00001 From 32aac87237f3b5ffdaa87b45f49d62177ebf6294 Mon Sep 17 00:00:00 2001 From: gorkemgul Date: Wed, 26 Jun 2024 14:25:59 +0100 Subject: [PATCH 125/167] update: change icon dir, and test it --- create_spec.py | 3 ++- docs/assets/icon/mapilio_ico.ico => mapilio_ico.ico | Bin 2 files changed, 2 insertions(+), 1 deletion(-) rename docs/assets/icon/mapilio_ico.ico => mapilio_ico.ico (100%) diff --git a/create_spec.py b/create_spec.py index fe5c5d5..a579374 100644 --- a/create_spec.py +++ b/create_spec.py @@ -76,6 +76,7 @@ def get_installed_package_path(package_name): def create_spec_file(): requirements_file = 'requirements.txt' spec_file = 'flask_app.spec' + icon_file = 'mapilio_ico.ico' current_directory = os.getcwd() datas = [('templates', 'templates'), ('static', 'static'), ('mapilio_kit', 'mapilio_kit')] @@ -148,7 +149,7 @@ def create_spec_file(): upx=True, runtime_tmpdir=None, console=True, - icon='/home/ai/Desktop/mapilio-kit/docs/assets/icon/mapilio_ico.ico' + icon='{icon_file}' ) app = BUNDLE(exe, name='kit-gui.app', icon=None, bundle_identifier=None) diff --git a/docs/assets/icon/mapilio_ico.ico b/mapilio_ico.ico similarity index 100% rename from docs/assets/icon/mapilio_ico.ico rename to mapilio_ico.ico From 708601364f9dea36bb7bf06b01b4961ff349bb81 Mon Sep 17 00:00:00 2001 From: gorkemgul Date: Wed, 26 Jun 2024 14:33:37 +0100 Subject: [PATCH 126/167] update: change icon dir, and test again --- create_spec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_spec.py b/create_spec.py index a579374..7362583 100644 --- a/create_spec.py +++ b/create_spec.py @@ -79,7 +79,7 @@ def create_spec_file(): icon_file = 'mapilio_ico.ico' current_directory = os.getcwd() - datas = [('templates', 'templates'), ('static', 'static'), ('mapilio_kit', 'mapilio_kit')] + datas = [('templates', 'templates'), ('static', 'static'), ('mapilio_kit', 'mapilio_kit'), ('mapilio_ico.ico', 'mapilio_ico.ico')] hiddenimports = ['configparser'] install_exiftool() From b4da092780d9f96b76d93d98c2d7564b697dc0c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Wed, 26 Jun 2024 15:46:31 +0100 Subject: [PATCH 127/167] Update create_spec.py --- create_spec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_spec.py b/create_spec.py index 7362583..b5af4d0 100644 --- a/create_spec.py +++ b/create_spec.py @@ -152,7 +152,7 @@ def create_spec_file(): icon='{icon_file}' ) -app = BUNDLE(exe, name='kit-gui.app', icon=None, bundle_identifier=None) +app = BUNDLE(exe, name='kit-gui.app', icon='{icon_file}', bundle_identifier=None) """) if __name__ == "__main__": From 680c37260ceb148d2c0b297bee0048245d9060bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Wed, 26 Jun 2024 15:50:49 +0100 Subject: [PATCH 128/167] Update create_spec.py --- create_spec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_spec.py b/create_spec.py index b5af4d0..e21f19b 100644 --- a/create_spec.py +++ b/create_spec.py @@ -152,7 +152,7 @@ def create_spec_file(): icon='{icon_file}' ) -app = BUNDLE(exe, name='kit-gui.app', icon='{icon_file}', bundle_identifier=None) +app = BUNDLE(exe, name='kit-gui.app', icon='mapilio_ico.ico', bundle_identifier=None) """) if __name__ == "__main__": From 41de0a88df51060e2a363beb4a0ae4630b5dbb84 Mon Sep 17 00:00:00 2001 From: visio-ai Date: Wed, 3 Jul 2024 15:47:55 +0300 Subject: [PATCH 129/167] - add mapper.py --- mapilio_kit/components/mapper.py | 99 ++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 mapilio_kit/components/mapper.py diff --git a/mapilio_kit/components/mapper.py b/mapilio_kit/components/mapper.py new file mode 100644 index 0000000..8ef1b1e --- /dev/null +++ b/mapilio_kit/components/mapper.py @@ -0,0 +1,99 @@ +import json +import os +import webbrowser + +import folium + + +def create_map(json_path, output_html='map.html', tile_type='cartodbdark_matter'): + # Load JSON data + with open(json_path) as f: + data = json.load(f) + + # Create a map centered at the first location + latitude = data[0]['latitude'] + longitude = data[0]['longitude'] + mymap = folium.Map(location=[latitude, longitude], zoom_start=14, tiles=tile_type) + + import_path = '/'.join(json_path.split('/')[:-1]) + + # Add markers to the map + for entry in data[:-1]: + lat = entry['latitude'] + lon = entry['longitude'] + anomaly = entry['anomaly'] + filename = entry['filename'] + + # Extract additional features + capture_time = entry.get('captureTime', 'N/A') + altitude = entry.get('altitude', 'N/A') + heading = entry.get('heading', 'N/A') + source = entry.get('source', 'N/A') + orientation = entry.get('orientation', 'N/A') + roll = entry.get('roll', 'N/A') + pitch = entry.get('pitch', 'N/A') + yaw = entry.get('yaw', 'N/A') + car_speed = entry.get('carSpeed', 'N/A') + device_make = entry.get('deviceMake', 'N/A') + device_model = entry.get('deviceModel', 'N/A') + image_size = entry.get('imageSize', 'N/A') + fov = entry.get('fov', 'N/A') + megapixels = entry.get('megapixels', 'N/A') + vfov = entry.get('vfov', 'N/A') + path = entry.get('path', 'N/A') + + # Define marker color based on anomaly + color = 'green' if anomaly == 0 else 'red' + + # Format popup content + popup_content = f""" +
        + Anomaly: {anomaly}
        + Filename: {filename}
        + Capture Time: {capture_time}
        + Latitude: {lat}
        + Longitude: {lon}
        + Altitude: {altitude}
        + Heading: {heading}
        + Source: {source}
        + Orientation: {orientation}
        + Roll: {roll}
        + Pitch: {pitch}
        + Yaw: {yaw}
        + Car Speed: {car_speed}
        + Device Make: {device_make}
        + Device Model: {device_model}
        + Image Size: {image_size}
        + FOV: {fov}
        + Megapixels: {megapixels}
        + VFOV: {vfov}
        + """ + + # Add a marker + folium.Marker( + location=[lat, lon], + popup=folium.Popup(popup_content, max_width=300), + icon=folium.Icon(color=color) + ).add_to(mymap) + + logo_url = 'https://end.mapilio.com/app/default/assets/images/mapilio_white.png?v=1719386247' + logo_html = f""" +

        + +
        + """ + logo_element = folium.Element(logo_html) + mymap.get_root().html.add_child(logo_element) + # Save the map to an HTML file + mymap.save(output_html) + + # Open the map in a web browser + webbrowser.open('file://' + os.path.realpath(output_html)) + +if __name__ == '__main__': + # Call the function with the path to the JSON file + create_map( + '/home/visio-ai/PycharmProjects/mapilio-kit/videos/mapilio_sampled_video_frames/mapilio_image_description.json') From 0cd4dcc2ca87935ce623ccd3bf0dd23cb50f146b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Fri, 5 Jul 2024 12:40:24 +0100 Subject: [PATCH 130/167] Update build_linux --- script/build_linux | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/build_linux b/script/build_linux index b8dbf2d..7e7814a 100755 --- a/script/build_linux +++ b/script/build_linux @@ -25,7 +25,7 @@ chmod +x "$TARGET" # sha256 TARGET_BASENAME=$(basename "$TARGET") cd dist/releases -shasum -a 256 "$TARGET_BASENAME" | tee "${TARGET_BASENAME}.sha256.txt" +#shasum -a 256 "$TARGET_BASENAME" | tee "${TARGET_BASENAME}.sha256.txt" #ZIP_FILE=mapilio-kit-${VERSION}-${OS}-${ARCH}.zip #zip -r "$ZIP_FILE" . #find . -mindepth 1 ! -name '*.zip' -delete From ba952eebd294f42775e44e72ea85913c4b573274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Fri, 5 Jul 2024 13:07:15 +0100 Subject: [PATCH 131/167] Update build_linux --- script/build_linux | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/build_linux b/script/build_linux index 7e7814a..b8dbf2d 100755 --- a/script/build_linux +++ b/script/build_linux @@ -25,7 +25,7 @@ chmod +x "$TARGET" # sha256 TARGET_BASENAME=$(basename "$TARGET") cd dist/releases -#shasum -a 256 "$TARGET_BASENAME" | tee "${TARGET_BASENAME}.sha256.txt" +shasum -a 256 "$TARGET_BASENAME" | tee "${TARGET_BASENAME}.sha256.txt" #ZIP_FILE=mapilio-kit-${VERSION}-${OS}-${ARCH}.zip #zip -r "$ZIP_FILE" . #find . -mindepth 1 ! -name '*.zip' -delete From e999898c69622131476da6fbfb4e636d8b3e4d34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Mon, 8 Jul 2024 07:30:21 +0100 Subject: [PATCH 132/167] Update version.py --- mapilio_kit/components/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapilio_kit/components/version.py b/mapilio_kit/components/version.py index 5585633..9caee0a 100644 --- a/mapilio_kit/components/version.py +++ b/mapilio_kit/components/version.py @@ -1,2 +1,2 @@ # TODO check before commit -VERSION = "2.0.31" +VERSION = "2.0.33" From 5c8f87606bd471ba581d4b1cf2610d6a96e9bf2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Mon, 8 Jul 2024 08:04:58 +0100 Subject: [PATCH 133/167] Update release.yml From 3d8f12905e1e3205a0ee5e37cf0d28d0116f7383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Mon, 8 Jul 2024 09:10:14 +0100 Subject: [PATCH 134/167] Update release.yml From d931c664afd39fef927e0483ea9f58b30dfb5714 Mon Sep 17 00:00:00 2001 From: gorkemgul Date: Mon, 8 Jul 2024 10:12:32 +0100 Subject: [PATCH 135/167] update version, and make configurations for macOS release --- .github/workflows/release.yml | 9 ++++++--- mapilio_kit/components/version.py | 2 +- script/build_osx | 9 ++++++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 53d37e4..4f24ccd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,13 +11,13 @@ jobs: strategy: matrix: - python-version: [ "3.10" ] - platform: ["ubuntu-20.04", "windows-latest"] # "macos-latest", "windows-latest" + python-version: [ "3.11" ] + platform: ["ubuntu-20.04", "windows-latest", "macos-latest"] architecture: [ "x64" ] include: - architecture: "x86" platform: "windows-latest" - python-version: "3.10" + python-version: "3.11" runs-on: ${{ matrix.platform }} @@ -77,8 +77,10 @@ jobs: run: | python3 -m pip install pysocks python3 -m pip install pyinstaller + python3 -m pip install pillow chmod +x ./script/build_osx ./script/build_osx + ./dist/releases/mapilio-kit--osx-arm64 env: MAPILIO_KIT__TESTS_EXECUTABLE: ./dist/osx/mapilio-kit MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl @@ -88,6 +90,7 @@ jobs: run: | python3 -m pip install pysocks python3 -m pip install pyinstaller + python3 -m pip install pillow chmod +x ./script/build_linux ./script/build_linux chmod +x ./dist/releases/mapilio-kit--linux-x86_64 diff --git a/mapilio_kit/components/version.py b/mapilio_kit/components/version.py index 9caee0a..62cc886 100644 --- a/mapilio_kit/components/version.py +++ b/mapilio_kit/components/version.py @@ -1,2 +1,2 @@ # TODO check before commit -VERSION = "2.0.33" +VERSION = "2.0.34" diff --git a/script/build_osx b/script/build_osx index b7e7ba6..4e1d604 100644 --- a/script/build_osx +++ b/script/build_osx @@ -12,13 +12,16 @@ pyinstaller --noconfirm --distpath dist/${OS} flask_app.spec # check SOURCE=dist/${OS}/mapilio-kit.app/Contents/MacOS/MapilioKit-Flask $SOURCE --version -VERSION=$($SOURCE --version | awk '{print $1}') +# VERSION=$($SOURCE --version | awk '{print $1}') + ARCH=$(uname -m) -TARGET=dist/releases/mapilio-kit-${VERSION}-${OS}-${ARCH}.zip +TARGET=dist/releases/mapilio-kit-${VERSION}-${OS}-${ARCH} # package mkdir -p dist/releases -zip -j "$TARGET" "$SOURCE" README_osx_package.txt +# zip -j "$TARGET" "$SOURCE" README_osx_package.txt +cp "$SOURCE" "$TARGET" +chmod +x "$TARGET" # sha256 TARGET_BASENAME=$(basename "$TARGET") From b2bdc988fad383c05dc6263f23902908510e5a45 Mon Sep 17 00:00:00 2001 From: gorkemgul Date: Mon, 8 Jul 2024 10:16:19 +0100 Subject: [PATCH 136/167] hotfix --- script/build_osx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/build_osx b/script/build_osx index 4e1d604..f2ddd6a 100644 --- a/script/build_osx +++ b/script/build_osx @@ -10,7 +10,7 @@ pyinstaller --version pyinstaller --noconfirm --distpath dist/${OS} flask_app.spec # check -SOURCE=dist/${OS}/mapilio-kit.app/Contents/MacOS/MapilioKit-Flask +SOURCE=dist/${OS}/MapilioKit-Flask $SOURCE --version # VERSION=$($SOURCE --version | awk '{print $1}') From 26beee8587554b0c59970d1e6881b1605c79e6ef Mon Sep 17 00:00:00 2001 From: gorkemgul Date: Mon, 8 Jul 2024 10:20:29 +0100 Subject: [PATCH 137/167] hotfix --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4f24ccd..3461a66 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: python-version: [ "3.11" ] - platform: ["ubuntu-20.04", "windows-latest", "macos-latest"] + platform: ["ubuntu-22.04", "windows-latest", "macos-latest"] architecture: [ "x64" ] include: - architecture: "x86" From 7dc11146a66778be80bd955207e13a6e4090fb79 Mon Sep 17 00:00:00 2001 From: gorkemgul Date: Mon, 8 Jul 2024 10:26:08 +0100 Subject: [PATCH 138/167] hotfix --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3461a66..4f24ccd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: python-version: [ "3.11" ] - platform: ["ubuntu-22.04", "windows-latest", "macos-latest"] + platform: ["ubuntu-20.04", "windows-latest", "macos-latest"] architecture: [ "x64" ] include: - architecture: "x86" From 8da12871f0d17a5ec3d3bd4d3ba72c0b193c3f03 Mon Sep 17 00:00:00 2001 From: gorkemgul Date: Wed, 9 Oct 2024 12:15:40 +0100 Subject: [PATCH 139/167] update: modify decompose process, and modify the try except structure --- flask_app.py | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/flask_app.py b/flask_app.py index 1f13f8a..f3294b2 100755 --- a/flask_app.py +++ b/flask_app.py @@ -7,13 +7,13 @@ from flask import Flask, render_template, request, redirect, url_for, jsonify from mapilio_kit.base import authenticator -from mapilio_kit.components.edit_config import edit_config -from mapilio_kit.components.geotag_property_handler import geotag_property_handler -from mapilio_kit.components.insert_MAPJson import insert_MAPJson -from mapilio_kit.components.login import list_all_users -from mapilio_kit.components.metadata_property_handler import metadata_property_handler -from mapilio_kit.components.sequence_property_handler import sequence_property_handler -from mapilio_kit.components.upload import upload +from mapilio_kit.components.utilities.edit_config import edit_config +from mapilio_kit.components.geotagging.geotag_property_handler import geotag_property_handler +from mapilio_kit.components.utilities.insert_MAPJson import insert_MAPJson +from mapilio_kit.components.auth.login import list_all_users +from mapilio_kit.components.metadata.metadata_property_handler import metadata_property_handler +from mapilio_kit.components.processing.sequence_property_handler import sequence_property_handler +from mapilio_kit.components.upload.upload import upload app = Flask(__name__) @@ -123,21 +123,27 @@ def mapilio_upload_page(): file.save(UPLOAD_FOLDER + file.filename) bundle_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__))) - path_to_exiftool = os.path.abspath(os.path.join(bundle_dir, 'exiftool/exiftool')) + if app.debug: + path_to_exiftool = "/usr/bin/exiftool" + else: + path_to_exiftool = os.path.abspath(os.path.join(bundle_dir, 'exiftool/exiftool')) try: decompose(import_path=UPLOAD_FOLDER, exiftool_path=path_to_exiftool) + jsonPath = os.path.join(UPLOAD_FOLDER, "mapilio_image_description.json") + with open(jsonPath, 'r') as f: + data = json.load(f) + total_images = data[-1]["Information"]["total_images"] + processed_images = data[-1]["Information"]["processed_images"] + failed_images = data[-1]["Information"]["failed_images"] + except: + return jsonify(success=False, message="An error occurred during metadata properties extraction.") + + try: upload_status = upload(import_path=UPLOAD_FOLDER, dry_run=False) if upload_status.get("Success"): try: - jsonPath = os.path.join(UPLOAD_FOLDER, "mapilio_image_description.json") - with open(jsonPath, 'r') as f: - data = json.load(f) - total_images = data[-1]["Information"]["total_images"] - processed_images = data[-1]["Information"]["processed_images"] - failed_images = data[-1]["Information"]["failed_images"] shutil.rmtree(UPLOAD_FOLDER) - return jsonify(success=True, message="Images uploaded successfully", total_images=total_images, processed_images=processed_images, failed_images=failed_images), 200 except OSError as err: @@ -165,5 +171,5 @@ def mapilio_video_upload_page(): if __name__ == "__main__": webbrowser.open("http://127.0.0.1:8081/") - app.run(host="0.0.0.0", port=8081, debug=True) + app.run(host="0.0.0.0", port=8081, debug=False) # FlaskUI(app=app, server="flask", width=1200, height=800, port=8080).run() From a5531cdef1044b87894027536bb80f4f653498e8 Mon Sep 17 00:00:00 2001 From: gorkemgul Date: Wed, 9 Oct 2024 12:20:48 +0100 Subject: [PATCH 140/167] update version.py --- mapilio_kit/components/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapilio_kit/components/version.py b/mapilio_kit/components/version.py index 334eda3..c0f9606 100644 --- a/mapilio_kit/components/version.py +++ b/mapilio_kit/components/version.py @@ -1,2 +1,2 @@ # TODO check before commit -VERSION = "3.0.1" +VERSION = "3.0.2" From 1be3512090a787543b2a065c9668deba4d357017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Wed, 9 Oct 2024 12:36:47 +0100 Subject: [PATCH 141/167] Update version.py --- mapilio_kit/components/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapilio_kit/components/version.py b/mapilio_kit/components/version.py index c0f9606..334eda3 100644 --- a/mapilio_kit/components/version.py +++ b/mapilio_kit/components/version.py @@ -1,2 +1,2 @@ # TODO check before commit -VERSION = "3.0.2" +VERSION = "3.0.1" From 8e4d5f182585336c0a05dfe17e283aa8b7ae0f04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Wed, 9 Oct 2024 12:44:47 +0100 Subject: [PATCH 142/167] Update version.py --- mapilio_kit/components/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapilio_kit/components/version.py b/mapilio_kit/components/version.py index 334eda3..c0f9606 100644 --- a/mapilio_kit/components/version.py +++ b/mapilio_kit/components/version.py @@ -1,2 +1,2 @@ # TODO check before commit -VERSION = "3.0.1" +VERSION = "3.0.2" From 07b64f3c14eafcc57af51fe9227d808fce232e1c Mon Sep 17 00:00:00 2001 From: gorkemgul Date: Wed, 9 Oct 2024 13:10:27 +0100 Subject: [PATCH 143/167] update version.py --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4f24ccd..a697e34 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: python-version: [ "3.11" ] - platform: ["ubuntu-20.04", "windows-latest", "macos-latest"] + platform: ["ubuntu-20.04", "windows-latest"] # "macos-latest" architecture: [ "x64" ] include: - architecture: "x86" From 847385a1ed67e240aac64363d2e5837c057840dd Mon Sep 17 00:00:00 2001 From: gorkemgul Date: Wed, 9 Oct 2024 15:24:25 +0100 Subject: [PATCH 144/167] update release.yml --- .github/workflows/release.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a697e34..2235eac 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -62,13 +62,13 @@ jobs: run: | python create_spec.py - - name: Validate version - run: | - EXPECTED_GITHUB_REF=$(mapilio_kit --version | awk '{print "refs/tags/v" $1}') - if [[ "$EXPECTED_GITHUB_REF" != "$GITHUB_REF" ]]; then - echo "Version mismatch: $EXPECTED_GITHUB_REF != $GITHUB_REF" - exit 1 - fi +# - name: Validate version +# run: | +# EXPECTED_GITHUB_REF=$(mapilio_kit --version | awk '{print "refs/tags/v" $1}') +# if [[ "$EXPECTED_GITHUB_REF" != "$GITHUB_REF" ]]; then +# echo "Version mismatch: $EXPECTED_GITHUB_REF != $GITHUB_REF" +# exit 1 +# fi if: matrix.platform != 'windows-latest' From d529764a1ce867db44a8039ff7bc4f68aacde62b Mon Sep 17 00:00:00 2001 From: gorkemgul Date: Wed, 9 Oct 2024 15:50:50 +0100 Subject: [PATCH 145/167] update release.yml --- .github/workflows/release.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2235eac..544eb3b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: python-version: [ "3.11" ] - platform: ["ubuntu-20.04", "windows-latest"] # "macos-latest" + platform: ["ubuntu-20.04", "windows-latest",] # "macos-latest" architecture: [ "x64" ] include: - architecture: "x86" @@ -62,13 +62,13 @@ jobs: run: | python create_spec.py -# - name: Validate version -# run: | -# EXPECTED_GITHUB_REF=$(mapilio_kit --version | awk '{print "refs/tags/v" $1}') -# if [[ "$EXPECTED_GITHUB_REF" != "$GITHUB_REF" ]]; then -# echo "Version mismatch: $EXPECTED_GITHUB_REF != $GITHUB_REF" -# exit 1 -# fi + - name: Validate version + run: | + EXPECTED_GITHUB_REF=$(mapilio_kit --version | awk '{print "refs/tags/v" $1}') + if [[ "$EXPECTED_GITHUB_REF" != "$GITHUB_REF" ]]; then + echo "Version mismatch: $EXPECTED_GITHUB_REF != $GITHUB_REF" + exit 1 + fi if: matrix.platform != 'windows-latest' @@ -124,4 +124,4 @@ jobs: generate_release_notes: true fail_on_unmatched_files: true files: | - ./main/dist/releases/* + ./main/dist/releases/* \ No newline at end of file From 2962e3c0a2c38d34a89eb833fe1133572003cb4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Thu, 10 Oct 2024 07:58:49 +0100 Subject: [PATCH 146/167] Update version.py --- mapilio_kit/components/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapilio_kit/components/version.py b/mapilio_kit/components/version.py index c0f9606..3b6411b 100644 --- a/mapilio_kit/components/version.py +++ b/mapilio_kit/components/version.py @@ -1,2 +1,2 @@ # TODO check before commit -VERSION = "3.0.2" +VERSION = "3.0.4" From 5aacacbbb646b032bddfc6b17be8907738d8469d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Thu, 10 Oct 2024 08:10:13 +0100 Subject: [PATCH 147/167] Update release.yml --- .github/workflows/release.yml | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 544eb3b..1a6b51b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: python-version: [ "3.11" ] - platform: ["ubuntu-20.04", "windows-latest",] # "macos-latest" + platform: ["ubuntu-20.04",] # "macos-latest","windows-latest" architecture: [ "x64" ] include: - architecture: "x86" @@ -62,14 +62,15 @@ jobs: run: | python create_spec.py - - name: Validate version - run: | - EXPECTED_GITHUB_REF=$(mapilio_kit --version | awk '{print "refs/tags/v" $1}') - if [[ "$EXPECTED_GITHUB_REF" != "$GITHUB_REF" ]]; then - echo "Version mismatch: $EXPECTED_GITHUB_REF != $GITHUB_REF" - exit 1 - fi - if: matrix.platform != 'windows-latest' +# - name: Validate version +# run: | +# EXPECTED_GITHUB_REF=$(mapilio_kit --version | awk '{print "refs/tags/v" $1}') +# if [[ "$EXPECTED_GITHUB_REF" != "$GITHUB_REF" ]]; then +# echo "Version mismatch: $EXPECTED_GITHUB_REF != $GITHUB_REF" +# exit 1 +# fi +# if: matrix.platform != 'windows-latest' + - name: Build and test with Pyinstaller on MacOS @@ -124,4 +125,4 @@ jobs: generate_release_notes: true fail_on_unmatched_files: true files: | - ./main/dist/releases/* \ No newline at end of file + ./main/dist/releases/* From 531c2ac4035f1f26246b2d11536d175aae5ac29b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Thu, 10 Oct 2024 09:18:02 +0100 Subject: [PATCH 148/167] add duplicated image parameter for users --- static/scripts/main.js | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/static/scripts/main.js b/static/scripts/main.js index 2129303..1635240 100644 --- a/static/scripts/main.js +++ b/static/scripts/main.js @@ -237,20 +237,24 @@ document.addEventListener("DOMContentLoaded", function () { Swal.fire({ icon: 'success', title: "Upload successfully done!", - html: " \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
        Total images:" + data.total_images + "
        Processed images:" + data.processed_images + "
        Failed images:" + data.failed_images + "
        ", + html: ` + + + + + + + + + + + + + + + + +
        Total images:${data.total_images}
        Processed images:${data.processed_images}
        Failed images:${data.failed_images}
        Duplicated images:${data.duplicated_images}
        `, footer: "Thanks for contributions to Mapilio 🎉", timerProgressBar: true, showConfirmButton: false @@ -297,4 +301,4 @@ document.addEventListener("DOMContentLoaded", function () { updateTotalImagesCount(); } }); -}); \ No newline at end of file +}); From e59542e12b3871a043f66e4a485e90a04a76546d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Thu, 10 Oct 2024 09:19:25 +0100 Subject: [PATCH 149/167] add duplicated parameter --- flask_app.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flask_app.py b/flask_app.py index f3294b2..6ea0b86 100755 --- a/flask_app.py +++ b/flask_app.py @@ -136,6 +136,7 @@ def mapilio_upload_page(): total_images = data[-1]["Information"]["total_images"] processed_images = data[-1]["Information"]["processed_images"] failed_images = data[-1]["Information"]["failed_images"] + duplicated_images = data[-1]["Information"]["duplicated_images"] except: return jsonify(success=False, message="An error occurred during metadata properties extraction.") @@ -145,7 +146,7 @@ def mapilio_upload_page(): try: shutil.rmtree(UPLOAD_FOLDER) return jsonify(success=True, message="Images uploaded successfully", total_images=total_images, - processed_images=processed_images, failed_images=failed_images), 200 + processed_images=processed_images, failed_images=failed_images, duplicated_images=duplicated_images), 200 except OSError as err: print(f"Error: {UPLOAD_FOLDER} could not be deleted. - {err}") return jsonify(success=False, message=f"{err}") From dac64dd7b92bd7cec7bf2c96067f404dc134a437 Mon Sep 17 00:00:00 2001 From: gorkemgul Date: Mon, 25 Nov 2024 16:00:47 +0300 Subject: [PATCH 150/167] update --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1a6b51b..74290bc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: python-version: [ "3.11" ] - platform: ["ubuntu-20.04",] # "macos-latest","windows-latest" + platform: ["macos-latest"] # "ubuntu-20.04","windows-latest" architecture: [ "x64" ] include: - architecture: "x86" From ad39b6433520b44d23d25fae8e1ddcc1f5ea2e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Mon, 25 Nov 2024 16:03:21 +0300 Subject: [PATCH 151/167] Update version.py --- mapilio_kit/components/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapilio_kit/components/version.py b/mapilio_kit/components/version.py index 3b6411b..1f3edff 100644 --- a/mapilio_kit/components/version.py +++ b/mapilio_kit/components/version.py @@ -1,2 +1,2 @@ # TODO check before commit -VERSION = "3.0.4" +VERSION = "3.0.5" From 589cab38dbae8947fcf967c8b2450a9f4135ddfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Mon, 25 Nov 2024 16:04:04 +0300 Subject: [PATCH 152/167] Update version.py --- mapilio_kit/components/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapilio_kit/components/version.py b/mapilio_kit/components/version.py index 1f3edff..dc14186 100644 --- a/mapilio_kit/components/version.py +++ b/mapilio_kit/components/version.py @@ -1,2 +1,2 @@ # TODO check before commit -VERSION = "3.0.5" +VERSION = "3.0.6" From 3aa8b16b6ceb4ab7b937541e4bc5cb722284310c Mon Sep 17 00:00:00 2001 From: gorkemgul Date: Mon, 25 Nov 2024 16:47:17 +0300 Subject: [PATCH 153/167] update --- .github/workflows/release.yml | 2 +- script/build_osx | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 74290bc..e2545db 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: python-version: [ "3.11" ] - platform: ["macos-latest"] # "ubuntu-20.04","windows-latest" + platform: ["macos-13"] # "ubuntu-20.04","windows-latest" architecture: [ "x64" ] include: - architecture: "x86" diff --git a/script/build_osx b/script/build_osx index f2ddd6a..7e31041 100644 --- a/script/build_osx +++ b/script/build_osx @@ -15,16 +15,17 @@ $SOURCE --version # VERSION=$($SOURCE --version | awk '{print $1}') ARCH=$(uname -m) -TARGET=dist/releases/mapilio-kit-${VERSION}-${OS}-${ARCH} +TARGET=dist/releases/mapilio-kit-${OS}-${ARCH}.zip # package mkdir -p dist/releases -# zip -j "$TARGET" "$SOURCE" README_osx_package.txt -cp "$SOURCE" "$TARGET" -chmod +x "$TARGET" +zip -j "$TARGET" "$SOURCE" +#cp "$SOURCE" "$TARGET" +#chmod +x "$TARGET" # sha256 TARGET_BASENAME=$(basename "$TARGET") + cd dist/releases shasum -a256 "$TARGET_BASENAME" | tee "${TARGET_BASENAME}.sha256.txt" cd ../../ From 2ba911fc6ef934b09c7a985853ea5e3756922b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Mon, 25 Nov 2024 16:58:44 +0300 Subject: [PATCH 154/167] Update release.yml --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e2545db..c1b33b7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -74,7 +74,7 @@ jobs: - name: Build and test with Pyinstaller on MacOS - if: matrix.platform == 'macos-latest' + if: matrix.platform == 'macos-13' run: | python3 -m pip install pysocks python3 -m pip install pyinstaller From 4db02a790b44847dcd1ed612c694f8a1bc76f749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Thu, 28 Nov 2024 16:45:35 +0300 Subject: [PATCH 155/167] Update release.yml --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c1b33b7..ba93beb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -81,7 +81,7 @@ jobs: python3 -m pip install pillow chmod +x ./script/build_osx ./script/build_osx - ./dist/releases/mapilio-kit--osx-arm64 +# ./dist/releases/mapilio-kit--osx-arm64 env: MAPILIO_KIT__TESTS_EXECUTABLE: ./dist/osx/mapilio-kit MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl From be54235f797bb57b31e76e506e06743b8e0a68e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Thu, 28 Nov 2024 16:46:41 +0300 Subject: [PATCH 156/167] Update version.py --- mapilio_kit/components/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapilio_kit/components/version.py b/mapilio_kit/components/version.py index dc14186..c3e7c1a 100644 --- a/mapilio_kit/components/version.py +++ b/mapilio_kit/components/version.py @@ -1,2 +1,2 @@ # TODO check before commit -VERSION = "3.0.6" +VERSION = "3.0.7" From 58cad3429fa7fe6ac49901a14d37282355105d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Thu, 28 Nov 2024 17:06:24 +0300 Subject: [PATCH 157/167] Update release.yml --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ba93beb..4ce1249 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -45,7 +45,7 @@ jobs: - name: Setup FFmpeg uses: FedericoCarboni/setup-ffmpeg@v3 - if: matrix.platform != 'macos-latest' + if: matrix.platform != 'macos-13' - name: Set up ${{ matrix.architecture }} Python ${{ matrix.python-version }} uses: actions/setup-python@v5 From da3491be46580f1aed2e0d9f2c4054a67ce6fe2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Fri, 29 Nov 2024 10:52:40 +0300 Subject: [PATCH 158/167] Update release.yml --- .github/workflows/release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4ce1249..d09a049 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -45,7 +45,7 @@ jobs: - name: Setup FFmpeg uses: FedericoCarboni/setup-ffmpeg@v3 - if: matrix.platform != 'macos-13' + if: matrix.platform != 'macos-latest' - name: Set up ${{ matrix.architecture }} Python ${{ matrix.python-version }} uses: actions/setup-python@v5 @@ -74,14 +74,14 @@ jobs: - name: Build and test with Pyinstaller on MacOS - if: matrix.platform == 'macos-13' + if: matrix.platform == 'macos-latest' run: | python3 -m pip install pysocks python3 -m pip install pyinstaller python3 -m pip install pillow chmod +x ./script/build_osx ./script/build_osx -# ./dist/releases/mapilio-kit--osx-arm64 + ./dist/releases/MapilioKit-Flask-osx-arm64 env: MAPILIO_KIT__TESTS_EXECUTABLE: ./dist/osx/mapilio-kit MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl From ea3ccadd559e7df8a408796a5bc2b307db96dc0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Fri, 29 Nov 2024 10:53:27 +0300 Subject: [PATCH 159/167] Update build_osx --- script/build_osx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/script/build_osx b/script/build_osx index 7e31041..279d8b1 100644 --- a/script/build_osx +++ b/script/build_osx @@ -10,12 +10,13 @@ pyinstaller --version pyinstaller --noconfirm --distpath dist/${OS} flask_app.spec # check -SOURCE=dist/${OS}/MapilioKit-Flask +SOURCE=dist/${OS}/MapilioKit-Flask.app/Contents/MacOS/MapilioKit-Flask $SOURCE --version # VERSION=$($SOURCE --version | awk '{print $1}') +echo "HELLOOOOO" ARCH=$(uname -m) -TARGET=dist/releases/mapilio-kit-${OS}-${ARCH}.zip +TARGET=dist/releases/MapilioKit-Flask-${OS}-${ARCH}.zip # package mkdir -p dist/releases @@ -31,4 +32,4 @@ shasum -a256 "$TARGET_BASENAME" | tee "${TARGET_BASENAME}.sha256.txt" cd ../../ # summary -ls -l dist/releases \ No newline at end of file +ls -l dist/releases From 0a3da2165a26ad6feafb1782a5f593c470337606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Fri, 29 Nov 2024 10:54:00 +0300 Subject: [PATCH 160/167] Update flask_app.py --- flask_app.py | 310 ++++++++++++++++++++++++--------------------------- 1 file changed, 146 insertions(+), 164 deletions(-) diff --git a/flask_app.py b/flask_app.py index 6ea0b86..530c8e6 100755 --- a/flask_app.py +++ b/flask_app.py @@ -1,176 +1,158 @@ -import json import os -import shutil +import subprocess import sys -import webbrowser - -from flask import Flask, render_template, request, redirect, url_for, jsonify - -from mapilio_kit.base import authenticator -from mapilio_kit.components.utilities.edit_config import edit_config -from mapilio_kit.components.geotagging.geotag_property_handler import geotag_property_handler -from mapilio_kit.components.utilities.insert_MAPJson import insert_MAPJson -from mapilio_kit.components.auth.login import list_all_users -from mapilio_kit.components.metadata.metadata_property_handler import metadata_property_handler -from mapilio_kit.components.processing.sequence_property_handler import sequence_property_handler -from mapilio_kit.components.upload.upload import upload - -app = Flask(__name__) - -UPLOAD_FOLDER = os.path.join(os.path.expanduser("~"), ".cache", "mapilio", "MapilioKit", "images/") - -MAPILIO_CONFIG_PATH = os.getenv( - "MAPILIO_CONFIG_PATH", - os.path.join( - os.path.expanduser("~"), - ".config", - "mapilio", - "configs", - "CLIENT_USERS", - ), -) - - -def get_args_mapilio(func): - arg_names = func.__code__.co_varnames[:func.__code__.co_argcount] - return {arg: None for arg in arg_names} - - -def check_authenticate(): - global authentication_status, token - if len(list_all_users()) == 0: - authentication_status = False - token = None - elif len(list_all_users()) >= 2: - token = None - authentication_status = False - remove_accounts() +from pathlib import Path + +def install_exiftool(): + if sys.platform == 'linux': + subprocess.run(['sudo', 'apt', 'install', '-y', 'exiftool'], check=True) + elif sys.platform == 'darwin': + subprocess.run(['brew', 'install', 'exiftool'], check=True) + elif sys.platform == 'win32': + subprocess.run(['powershell', '-Command', + 'Set-ExecutionPolicy Bypass -Scope Process -Force; ' + '[System.Net.ServicePointManager]::SecurityProtocol = ' + '[System.Net.ServicePointManager]::SecurityProtocol -bor 3072; ' + 'iex ((New-Object System.Net.WebClient).DownloadString(\'https://community.chocolatey.org/install.ps1\'))'], + check=True) + subprocess.run(['choco', 'install', 'exiftool', '-y'], check=True) else: - token = list_all_users()[0]['user_upload_token'] - authentication_status = True + raise ValueError("Unsupported platform") - return token, authentication_status - -def decompose(import_path, exiftool_path): - metadata_property_handler(import_path=import_path, exiftool_path=exiftool_path) - geotag_property_handler(import_path=import_path) - sequence_property_handler(import_path=import_path) - insert_MAPJson(import_path=import_path) - - -@app.route("/", methods=["GET", "POST"]) -def index(): - token, authentication_status = check_authenticate() - if authentication_status: - return render_template("image-upload.html", token=token) - else: - return render_template('login.html') - - -@app.route('/login', methods=['GET', 'POST']) -def mapilio_login(): - if request.method == 'POST': - args = get_args_mapilio(edit_config) - email = request.form['email'].strip() - password = request.form['password'].strip() - - username = email.split('@')[0] - - args["user_name"] = username - args["user_email"] = email - args["user_password"] = str(password) - args["gui"] = True - check_authenticate = authenticator().perform_task(args) - - if check_authenticate['status']: - message = check_authenticate['message'] - token = check_authenticate['token'] - return render_template("image-upload.html", message=message, token=token) +def get_exiftool_path(): + try: + if sys.platform == 'linux' or sys.platform == 'darwin': + # Unix-based systems (Linux, macOS) + result = subprocess.run(['which', 'exiftool'], capture_output=True, text=True) + elif sys.platform == 'win32': + # Windows + result = subprocess.run(['where', 'exiftool'], capture_output=True, text=True) else: - message = check_authenticate['message'] - return render_template('login.html', message=message) - else: - return render_template('login.html') - - -@app.route('/logout', methods=['GET']) -def remove_accounts(): - if os.path.exists(MAPILIO_CONFIG_PATH): - os.remove(MAPILIO_CONFIG_PATH) - return jsonify(success=True, message="Account successfully removed!"), 200 - else: - return jsonify(success=True, message="No accounts found!"), 200 - - -@app.route('/image-upload', methods=['GET', 'POST']) -def mapilio_upload_page(): - if request.method == 'GET': - token, authentication_status = check_authenticate() - - if authentication_status: - return render_template('image-upload.html', token=token) - else: - return redirect(url_for("mapilio_login")) - elif request.method == 'POST': - if 'file' not in request.files: - return jsonify(success=False, message="No file part") - for file in request.files.getlist('file'): - if file.filename == '': - continue - if not os.path.exists(UPLOAD_FOLDER): - os.makedirs(UPLOAD_FOLDER, exist_ok=True) - file.save(UPLOAD_FOLDER + file.filename) - - bundle_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__))) - if app.debug: - path_to_exiftool = "/usr/bin/exiftool" - else: - path_to_exiftool = os.path.abspath(os.path.join(bundle_dir, 'exiftool/exiftool')) - - try: - decompose(import_path=UPLOAD_FOLDER, exiftool_path=path_to_exiftool) - jsonPath = os.path.join(UPLOAD_FOLDER, "mapilio_image_description.json") - with open(jsonPath, 'r') as f: - data = json.load(f) - total_images = data[-1]["Information"]["total_images"] - processed_images = data[-1]["Information"]["processed_images"] - failed_images = data[-1]["Information"]["failed_images"] - duplicated_images = data[-1]["Information"]["duplicated_images"] - except: - return jsonify(success=False, message="An error occurred during metadata properties extraction.") - + raise ValueError("Unsupported operating system") + + if result.returncode != 0: + raise ValueError("ExifTool not found") + return result.stdout.strip() + except subprocess.CalledProcessError as e: + raise ValueError(f"Error finding ExifTool: {e}") + +def get_installed_package_path(package_name): + result = subprocess.run([sys.executable, '-m', 'pip', 'show', package_name], capture_output=True, text=True) + if result.returncode != 0: + raise ValueError(f"Package {package_name} not found") + + location = None + for line in result.stdout.splitlines(): + if line.startswith('Location:'): + location = line.split(' ', 1)[1].strip() + break + + if not location: + raise ValueError(f"Location not found for package {package_name}") + + package_folder_name = package_name.split('-')[0] + package_path = Path(location) / package_folder_name + if package_path.exists(): + return package_path + + package_folder_name = package_name.replace('-', '_') + package_path = Path(location) / package_folder_name + if package_path.exists(): + return package_path + + package_path_with_py = package_path.with_suffix('.py') + if package_path_with_py.exists(): + return package_path_with_py + + if package_name == 'attrs': + alt_package_name = 'attr' + alt_package_path = Path(location) / alt_package_name + if alt_package_path.exists(): + return alt_package_path + + raise ValueError(f"Package path not found for {package_name}") + +def create_spec_file(): + requirements_file = 'requirements.txt' + spec_file = 'flask_app.spec' + icon_file = 'mapilio_ico.ico' + + current_directory = os.getcwd() + datas = [('templates', 'templates'), ('static', 'static'), ('mapilio_kit', 'mapilio_kit'), ('mapilio_ico.ico', 'mapilio_ico.ico')] + hiddenimports = ['configparser'] + + install_exiftool() + try: + exiftool_path = get_exiftool_path() + datas.append((exiftool_path, 'exiftool')) + except ValueError as e: + print(f"Warning: {e}") + + with open(requirements_file) as f: + packages = [line.split('==')[0].strip() for line in f if line.strip() and not line.startswith('#')] + print(packages) + + for package in packages: try: - upload_status = upload(import_path=UPLOAD_FOLDER, dry_run=False) - if upload_status.get("Success"): - try: - shutil.rmtree(UPLOAD_FOLDER) - return jsonify(success=True, message="Images uploaded successfully", total_images=total_images, - processed_images=processed_images, failed_images=failed_images, duplicated_images=duplicated_images), 200 - except OSError as err: - print(f"Error: {UPLOAD_FOLDER} could not be deleted. - {err}") - return jsonify(success=False, message=f"{err}") - else: - e = upload_status.get("Error") - return jsonify(success=False, message=f"Error: {e}"), 500 - except: - e = upload_status.get("Error") - return jsonify(success=False, message=f"Error: {e}"), 500 - else: - return jsonify(success=False, message="Method Not Allowed"), 500 - + if package == 'ExifRead': + package = 'exifread' + package_path = get_installed_package_path(package) -@app.route('/video-upload', methods=['GET', 'POST']) -def mapilio_video_upload_page(): - token, authentication_status = check_authenticate() + package_folder_name = package.replace('-', '_') + hiddenimports.append(package_folder_name) - if authentication_status: - return render_template('video-upload.html', token=token) - else: - return redirect(url_for("mapilio_login")) + if package_folder_name == package: + package_data_name = package + else: + package_data_name = package_path.parts[-1] + + datas.append((str(package_path), package_data_name)) + + except ValueError as e: + print(f"Warning: {e}") + print(datas) + with open(spec_file, 'w') as f: + f.write(f""" +# -*- mode: python ; coding: utf-8 -*- + +block_cipher = None + +options = [("u", None, "OPTION")] + +a = Analysis( + ['flask_app.py'], + pathex=[SPECPATH], + binaries=[], + datas={datas}, + hiddenimports={hiddenimports}, + hookspath=[], + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, +) +pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) + +exe = EXE( + pyz, + a.scripts, + options, + a.binaries, + a.zipfiles, + a.datas, + [], + name='MapilioKit-Flask', + debug=False, + strip=False, + upx=True, + runtime_tmpdir=None, + console=True, +) +app = BUNDLE(exe, name='MapilioKit-Flask.app', icon='mapilio_ico.ico', bundle_identifier=None) +""") if __name__ == "__main__": - webbrowser.open("http://127.0.0.1:8081/") - app.run(host="0.0.0.0", port=8081, debug=False) - # FlaskUI(app=app, server="flask", width=1200, height=800, port=8080).run() + create_spec_file() From 05c26a7e98cd0713267c2207d8bf548eb623d46e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Fri, 29 Nov 2024 10:55:34 +0300 Subject: [PATCH 161/167] Update flask_app.py --- flask_app.py | 310 +++++++++++++++++++++++++++------------------------ 1 file changed, 164 insertions(+), 146 deletions(-) diff --git a/flask_app.py b/flask_app.py index 530c8e6..c98f847 100755 --- a/flask_app.py +++ b/flask_app.py @@ -1,158 +1,176 @@ +import json import os -import subprocess +import shutil import sys -from pathlib import Path - -def install_exiftool(): - if sys.platform == 'linux': - subprocess.run(['sudo', 'apt', 'install', '-y', 'exiftool'], check=True) - elif sys.platform == 'darwin': - subprocess.run(['brew', 'install', 'exiftool'], check=True) - elif sys.platform == 'win32': - subprocess.run(['powershell', '-Command', - 'Set-ExecutionPolicy Bypass -Scope Process -Force; ' - '[System.Net.ServicePointManager]::SecurityProtocol = ' - '[System.Net.ServicePointManager]::SecurityProtocol -bor 3072; ' - 'iex ((New-Object System.Net.WebClient).DownloadString(\'https://community.chocolatey.org/install.ps1\'))'], - check=True) - subprocess.run(['choco', 'install', 'exiftool', '-y'], check=True) +import webbrowser + +from flask import Flask, render_template, request, redirect, url_for, jsonify + +from mapilio_kit.base import authenticator +from mapilio_kit.components.utilities.edit_config import edit_config +from mapilio_kit.components.geotagging.geotag_property_handler import geotag_property_handler +from mapilio_kit.components.utilities.insert_MAPJson import insert_MAPJson +from mapilio_kit.components.auth.login import list_all_users +from mapilio_kit.components.metadata.metadata_property_handler import metadata_property_handler +from mapilio_kit.components.processing.sequence_property_handler import sequence_property_handler +from mapilio_kit.components.upload.upload import upload + +app = Flask(__name__) + +UPLOAD_FOLDER = os.path.join(os.path.expanduser("~"), ".cache", "mapilio", "MapilioKit", "images/") + +MAPILIO_CONFIG_PATH = os.getenv( + "MAPILIO_CONFIG_PATH", + os.path.join( + os.path.expanduser("~"), + ".config", + "mapilio", + "configs", + "CLIENT_USERS", + ), +) + + +def get_args_mapilio(func): + arg_names = func.__code__.co_varnames[:func.__code__.co_argcount] + return {arg: None for arg in arg_names} + + +def check_authenticate(): + global authentication_status, token + if len(list_all_users()) == 0: + authentication_status = False + token = None + elif len(list_all_users()) >= 2: + token = None + authentication_status = False + remove_accounts() else: - raise ValueError("Unsupported platform") + token = list_all_users()[0]['user_upload_token'] + authentication_status = True + return token, authentication_status -def get_exiftool_path(): - try: - if sys.platform == 'linux' or sys.platform == 'darwin': - # Unix-based systems (Linux, macOS) - result = subprocess.run(['which', 'exiftool'], capture_output=True, text=True) - elif sys.platform == 'win32': - # Windows - result = subprocess.run(['where', 'exiftool'], capture_output=True, text=True) + +def decompose(import_path, exiftool_path): + metadata_property_handler(import_path=import_path, exiftool_path=exiftool_path) + geotag_property_handler(import_path=import_path) + sequence_property_handler(import_path=import_path) + insert_MAPJson(import_path=import_path) + + +@app.route("/", methods=["GET", "POST"]) +def index(): + token, authentication_status = check_authenticate() + if authentication_status: + return render_template("image-upload.html", token=token) + else: + return render_template('login.html') + + +@app.route('/login', methods=['GET', 'POST']) +def mapilio_login(): + if request.method == 'POST': + args = get_args_mapilio(edit_config) + email = request.form['email'].strip() + password = request.form['password'].strip() + + username = email.split('@')[0] + + args["user_name"] = username + args["user_email"] = email + args["user_password"] = str(password) + args["gui"] = True + check_authenticate = authenticator().perform_task(args) + + if check_authenticate['status']: + message = check_authenticate['message'] + token = check_authenticate['token'] + return render_template("image-upload.html", message=message, token=token) else: - raise ValueError("Unsupported operating system") - - if result.returncode != 0: - raise ValueError("ExifTool not found") - return result.stdout.strip() - except subprocess.CalledProcessError as e: - raise ValueError(f"Error finding ExifTool: {e}") - -def get_installed_package_path(package_name): - result = subprocess.run([sys.executable, '-m', 'pip', 'show', package_name], capture_output=True, text=True) - if result.returncode != 0: - raise ValueError(f"Package {package_name} not found") - - location = None - for line in result.stdout.splitlines(): - if line.startswith('Location:'): - location = line.split(' ', 1)[1].strip() - break - - if not location: - raise ValueError(f"Location not found for package {package_name}") - - package_folder_name = package_name.split('-')[0] - package_path = Path(location) / package_folder_name - if package_path.exists(): - return package_path - - package_folder_name = package_name.replace('-', '_') - package_path = Path(location) / package_folder_name - if package_path.exists(): - return package_path - - package_path_with_py = package_path.with_suffix('.py') - if package_path_with_py.exists(): - return package_path_with_py - - if package_name == 'attrs': - alt_package_name = 'attr' - alt_package_path = Path(location) / alt_package_name - if alt_package_path.exists(): - return alt_package_path - - raise ValueError(f"Package path not found for {package_name}") - -def create_spec_file(): - requirements_file = 'requirements.txt' - spec_file = 'flask_app.spec' - icon_file = 'mapilio_ico.ico' - - current_directory = os.getcwd() - datas = [('templates', 'templates'), ('static', 'static'), ('mapilio_kit', 'mapilio_kit'), ('mapilio_ico.ico', 'mapilio_ico.ico')] - hiddenimports = ['configparser'] - - install_exiftool() - try: - exiftool_path = get_exiftool_path() - datas.append((exiftool_path, 'exiftool')) - except ValueError as e: - print(f"Warning: {e}") - - with open(requirements_file) as f: - packages = [line.split('==')[0].strip() for line in f if line.strip() and not line.startswith('#')] - print(packages) - - for package in packages: - try: - if package == 'ExifRead': - package = 'exifread' - package_path = get_installed_package_path(package) + message = check_authenticate['message'] + return render_template('login.html', message=message) + else: + return render_template('login.html') - package_folder_name = package.replace('-', '_') - hiddenimports.append(package_folder_name) - if package_folder_name == package: - package_data_name = package +@app.route('/logout', methods=['GET']) +def remove_accounts(): + if os.path.exists(MAPILIO_CONFIG_PATH): + os.remove(MAPILIO_CONFIG_PATH) + return jsonify(success=True, message="Account successfully removed!"), 200 + else: + return jsonify(success=True, message="No accounts found!"), 200 + + +@app.route('/image-upload', methods=['GET', 'POST']) +def mapilio_upload_page(): + if request.method == 'GET': + token, authentication_status = check_authenticate() + + if authentication_status: + return render_template('image-upload.html', token=token) + else: + return redirect(url_for("mapilio_login")) + elif request.method == 'POST': + if 'file' not in request.files: + return jsonify(success=False, message="No file part") + for file in request.files.getlist('file'): + if file.filename == '': + continue + if not os.path.exists(UPLOAD_FOLDER): + os.makedirs(UPLOAD_FOLDER, exist_ok=True) + file.save(UPLOAD_FOLDER + file.filename) + + bundle_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__))) + if app.debug: + path_to_exiftool = "/usr/bin/exiftool" + else: + path_to_exiftool = os.path.abspath(os.path.join(bundle_dir, 'exiftool/exiftool')) + + try: + decompose(import_path=UPLOAD_FOLDER, exiftool_path=path_to_exiftool) + jsonPath = os.path.join(UPLOAD_FOLDER, "mapilio_image_description.json") + with open(jsonPath, 'r') as f: + data = json.load(f) + total_images = data[-1]["Information"]["total_images"] + processed_images = data[-1]["Information"]["processed_images"] + failed_images = data[-1]["Information"]["failed_images"] + duplicated_images = data[-1]["Information"]["duplicated_images"] + except: + return jsonify(success=False, message="An error occurred during metadata properties extraction.") + + try: + upload_status = upload(import_path=UPLOAD_FOLDER, dry_run=False) + if upload_status.get("Success"): + try: + shutil.rmtree(UPLOAD_FOLDER) + return jsonify(success=True, message="Images uploaded successfully", total_images=total_images, + processed_images=processed_images, failed_images=failed_images, duplicated_images=duplicated_images), 200 + except OSError as err: + print(f"Error: {UPLOAD_FOLDER} could not be deleted. - {err}") + return jsonify(success=False, message=f"{err}") else: - package_data_name = package_path.parts[-1] - - datas.append((str(package_path), package_data_name)) - - except ValueError as e: - print(f"Warning: {e}") - print(datas) - with open(spec_file, 'w') as f: - f.write(f""" -# -*- mode: python ; coding: utf-8 -*- - -block_cipher = None - -options = [("u", None, "OPTION")] - -a = Analysis( - ['flask_app.py'], - pathex=[SPECPATH], - binaries=[], - datas={datas}, - hiddenimports={hiddenimports}, - hookspath=[], - runtime_hooks=[], - excludes=[], - win_no_prefer_redirects=False, - win_private_assemblies=False, - cipher=block_cipher, -) -pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) - -exe = EXE( - pyz, - a.scripts, - options, - a.binaries, - a.zipfiles, - a.datas, - [], - name='MapilioKit-Flask', - debug=False, - strip=False, - upx=True, - runtime_tmpdir=None, - console=True, -) + e = upload_status.get("Error") + return jsonify(success=False, message=f"Error: {e}"), 500 + except: + e = upload_status.get("Error") + return jsonify(success=False, message=f"Error: {e}"), 500 + else: + return jsonify(success=False, message="Method Not Allowed"), 500 + + +@app.route('/video-upload', methods=['GET', 'POST']) +def mapilio_video_upload_page(): + token, authentication_status = check_authenticate() + + if authentication_status: + return render_template('video-upload.html', token=token) + else: + return redirect(url_for("mapilio_login")) -app = BUNDLE(exe, name='MapilioKit-Flask.app', icon='mapilio_ico.ico', bundle_identifier=None) -""") if __name__ == "__main__": - create_spec_file() + webbrowser.open("http://127.0.0.1:8081/") + app.run(host="0.0.0.0", port=8081, debug=True) + # FlaskUI(app=app, server="flask", width=1200, height=800, port=8080).run() From e9521a86c4ded9d42514f38375c50605a71c1813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Fri, 29 Nov 2024 11:03:42 +0300 Subject: [PATCH 162/167] Update release.yml --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d09a049..2945d00 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -81,7 +81,7 @@ jobs: python3 -m pip install pillow chmod +x ./script/build_osx ./script/build_osx - ./dist/releases/MapilioKit-Flask-osx-arm64 + ./dist/releases/MapilioKit-Flask-osx-arm64 env: MAPILIO_KIT__TESTS_EXECUTABLE: ./dist/osx/mapilio-kit MAPILIO_KIT__TESTS_EXIFTOOL_EXECUTABLE: perl ${{ github.workspace }}/exiftool/exiftool.pl From 7cd74ffa93b9aa74a121268a630cb0aa7a746a19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6rkem=20G=C3=BCl?= <104680758+gorkemgul@users.noreply.github.com> Date: Fri, 29 Nov 2024 11:09:19 +0300 Subject: [PATCH 163/167] Update release.yml --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2945d00..f898a12 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -45,7 +45,7 @@ jobs: - name: Setup FFmpeg uses: FedericoCarboni/setup-ffmpeg@v3 - if: matrix.platform != 'macos-latest' + if: matrix.platform != 'macos-13' - name: Set up ${{ matrix.architecture }} Python ${{ matrix.python-version }} uses: actions/setup-python@v5 From b65d4813b9abda9679b6ed45f39f294111ee985f Mon Sep 17 00:00:00 2001 From: Ozcan Durak Date: Fri, 29 Nov 2024 17:00:00 +0300 Subject: [PATCH 164/167] Update release.yml --- .github/workflows/release.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f898a12..52e8956 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -58,10 +58,6 @@ jobs: python -m pip install --upgrade pip python -m pip install . - - name: Create spec file - run: | - python create_spec.py - # - name: Validate version # run: | # EXPECTED_GITHUB_REF=$(mapilio_kit --version | awk '{print "refs/tags/v" $1}') From d41a7719b51391ca66bec1e90040130259a815fc Mon Sep 17 00:00:00 2001 From: Mehmet Ali Han <132016552+mehmetalihn@users.noreply.github.com> Date: Wed, 9 Apr 2025 14:28:53 +0300 Subject: [PATCH 165/167] Update release.yml --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 52e8956..402d783 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: python-version: [ "3.11" ] - platform: ["macos-13"] # "ubuntu-20.04","windows-latest" + platform: ["macos-latest"] # "ubuntu-20.04","windows-latest" architecture: [ "x64" ] include: - architecture: "x86" @@ -45,7 +45,7 @@ jobs: - name: Setup FFmpeg uses: FedericoCarboni/setup-ffmpeg@v3 - if: matrix.platform != 'macos-13' + if: matrix.platform != 'macos-latest' - name: Set up ${{ matrix.architecture }} Python ${{ matrix.python-version }} uses: actions/setup-python@v5 From a922d76802d2530095addb71746b2dc4971e7291 Mon Sep 17 00:00:00 2001 From: Mehmet Ali Han <132016552+mehmetalihn@users.noreply.github.com> Date: Wed, 9 Apr 2025 14:47:46 +0300 Subject: [PATCH 166/167] Update release.yml --- .github/workflows/release.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 402d783..f16a9c7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,9 +1,7 @@ name: Release on: - push: - tags: - - "v*.*.*" + workflow_dispatch: jobs: build_and_release: From 050e4313fd31077da6dc75266346a86985b8acf3 Mon Sep 17 00:00:00 2001 From: Mehmet Ali Han <132016552+mehmetalihn@users.noreply.github.com> Date: Wed, 9 Apr 2025 14:52:10 +0300 Subject: [PATCH 167/167] Update release.yml --- .github/workflows/release.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f16a9c7..402d783 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,7 +1,9 @@ name: Release on: - workflow_dispatch: + push: + tags: + - "v*.*.*" jobs: build_and_release: