From 008e2bc274594179d5f0aeb651dff7e9166c1205 Mon Sep 17 00:00:00 2001 From: mkrieger Date: Wed, 13 Nov 2024 18:40:55 +0100 Subject: [PATCH] webcrawler v0.1 --- Dockerfile | 18 +++ app/__init__.py | 52 +++++++ app/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1635 bytes app/__pycache__/models.cpython-310.pyc | Bin 0 -> 1092 bytes app/__pycache__/routes.cpython-310.pyc | Bin 0 -> 4637 bytes app/__pycache__/webcrawler.cpython-310.pyc | Bin 0 -> 3691 bytes app/models.py | 20 +++ app/routes.py | 148 ++++++++++++++++++ app/static/styles.css | 166 +++++++++++++++++++++ app/templates/base.html | 27 ++++ app/templates/jobs.html | 61 ++++++++ app/templates/login.html | 16 ++ app/templates/signup.html | 16 ++ app/templates/upload.html | 11 ++ app/webcrawler.py | 128 ++++++++++++++++ docker-compose.yml | 11 ++ instance/users.db | Bin 0 -> 16384 bytes requirements.txt | 7 + 18 files changed, 681 insertions(+) create mode 100644 Dockerfile create mode 100644 app/__init__.py create mode 100644 app/__pycache__/__init__.cpython-310.pyc create mode 100644 app/__pycache__/models.cpython-310.pyc create mode 100644 app/__pycache__/routes.cpython-310.pyc create mode 100644 app/__pycache__/webcrawler.cpython-310.pyc create mode 100644 app/models.py create mode 100644 app/routes.py create mode 100644 app/static/styles.css create mode 100644 app/templates/base.html create mode 100644 app/templates/jobs.html create mode 100644 app/templates/login.html create mode 100644 app/templates/signup.html create mode 100644 app/templates/upload.html create mode 100644 app/webcrawler.py create mode 100644 docker-compose.yml create mode 100644 instance/users.db create mode 100644 requirements.txt diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2dc2c9d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +# Python Image +FROM python:3.10-slim + +# Arbeitsverzeichnis setzen +WORKDIR /app + +# Abhängigkeiten installieren +COPY requirements.txt requirements.txt +RUN pip install --no-cache-dir -r requirements.txt + +# App-Dateien kopieren +COPY . . + +# Flask Umgebungsvariable setzen +ENV FLASK_APP=app + +# Flask starten +CMD ["flask", "run", "--host=0.0.0.0", "--port=5000"] diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..23efef7 --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,52 @@ +import os +from flask import Flask, redirect, url_for, request +from flask_sqlalchemy import SQLAlchemy +from flask_login import LoginManager, current_user +from .models import db, User + +# Konfiguration für Upload- und Ergebnis-Ordner +UPLOAD_FOLDER = '/app/uploads' +RESULT_FOLDER = '/app/results' + +def create_app(): + app = Flask(__name__) + app.config['SECRET_KEY'] = '008e7369b075886d5f494c8813efdfb17155da6af12b3fe8ee' + app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db' + app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER + app.config['RESULT_FOLDER'] = RESULT_FOLDER + + db.init_app(app) + + # Flask-Login Setup + login_manager = LoginManager() + login_manager.login_view = 'auth.login' + login_manager.init_app(app) + + @login_manager.user_loader + def load_user(user_id): + return User.query.get(int(user_id)) + + # Umleitung nicht authentifizierter Benutzer, statische Dateien und bestimmte Routen ausnehmen + @app.before_request + def require_login(): + allowed_routes = ['auth.login', 'auth.signup'] + + # Prüfen, ob der Benutzer authentifiziert ist oder eine erlaubte Route anfragt + if (not current_user.is_authenticated + and request.endpoint not in allowed_routes + and not request.path.startswith('/static/')): + return redirect(url_for('auth.login')) + + # Erstellen Sie die Ordner, falls sie noch nicht existieren + os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) + os.makedirs(app.config['RESULT_FOLDER'], exist_ok=True) + + # Registrieren der Routen + from . import routes + app.register_blueprint(routes.bp) + + # Erstellen der Tabellen in der Datenbank + with app.app_context(): + db.create_all() + + return app diff --git a/app/__pycache__/__init__.cpython-310.pyc b/app/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..41c64c991cb710c8b058301b4dfd03936b0857d5 GIT binary patch literal 1635 zcmZux&2Aev5GMCetJO+Y92ZT3v}jL-E|wktDTNW#jTN-0;xv|x!h2y?A||92u?lZ2!?wE5nlw>Rze}iFLSYpwRat^^T3G4A_ zR3DGhJf`A$#F-eUQgn3;^Unpv2YC!4{(?>-i7)UuATdP)i(1=oXQMgWNX|4DG{@LH z(A-OJimot5vT%XUF)d6n@uzrho#H`}`n32O!LAl~1vH=~GqPoA5So45yNHIg{Hq1K z%x&HW8?DfVw47DW9gL>va$)KWs`^CGZ(X6^0Bwk3@d{z|6XeK&c46z;Hd;kua0)AR zI|9+RT6Hgau(Hx%TWgySJ1biojm9S3=&e6o?=~8%Yph3moz<=IzJ$xI)V|l!s>fCm98t0$Z9gsGNs6r>v0AE@<2N(EC zvk3!{cnM~d3yk4s%I-6%>|_Ysi3&O{lF;7~zYR;?E*U~-QV^016G_PO=u8zgKMaNL z>SOe4s9KZHx}3$55m>#vo#x#*6^|go>y;nzM@MOcm?*bFp_p2I>)b>u?nr9@@xH|T z9%#;`OYJKx?n_S=D6s4TZb89yE;VP5FTfs?lEFkI{o!~t@#{jyGU?W9KG#%L)kF}z z0N9^&Ax_FQi(4)==+vu2ROji`_OXU`FQf>|AF+8?*TkvpS?4$1n$wjxJGFM3 zYp|71$S}?rA6TPYx15ViNy*=(9Ltx6B@19B+{aYU#H6vRg(P{U;}D<^i+O})FRcUL97mTRvZ zIKi=3evYr4`WHAc<5Z;;2(SF)d7kl%pJ&TXCn7K&{dh7TxrF>g`crblI zg>&H~!P@>73imq&4l28V>~~mC1mM{cAymYShsnp6)U=m6%eh{q$8vS`1yT$LjtQC~ zJ87h0MWYm-D0xXWsyG@%hNWr2bD5?lO4Ga)jly-DrY8+o*E^1wnBYaJn!GUK>!Ox( zsg08f6CBn$E9QnZkRF{4Tk@(pEw#8LdIwTi_|<>NmrFY#gH+Yta-~s-5N5>y80pa+ zx=Stlmu3NY?4#>ffd6Z@fcLxB?XGGq5rCG+IZ1S_B&Y`*6D{D{Yi<$C&SJwd-5W2A zq&Bh#+@`?zzGwp%UpS%z(OY5z-l1#UH|1okvEH)~HQCF=P`AO{>76Md* zHVK8C@kZ75+QwUylL}Z*@iXkGaKfj@TFyp;Z5`to9fS>pE= zOU3J~EUKeysUKjRhj;w*&g<1Z*wMQHSaXF%;_L3xZTR{y?>lyPG-#RLO!4YCtxgnQ z|3}&Zchg?es+?zqhM%0+D($6>e}Eli1+&NeE4yJ&*PZ-~VhLDBOkx_-z}a?o{NFh3 B1@8a= literal 0 HcmV?d00001 diff --git a/app/__pycache__/routes.cpython-310.pyc b/app/__pycache__/routes.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..38d5d360ba7bbc7faccf35154c5e1ed149ca04da GIT binary patch literal 4637 zcma)9OLH5?5#ARTi#J446shORmTbe8Kqr=ca5;*tN1Qk^rI?aawHFkC83GILE|A$- zGeSWp<*HxcxC%V_5BTJpQvMNp^~rz1r+hsFf*?~#1y$&snVy|TcYpo$j%&5DfnV$A zJA>!v4dZ9(tp1AVe2RZMH4TFq%#4gcKh3~YeJil=v?4op0w*p6h1d<;xEK^;FYw}0 zP>RbzIj#g1listVYFrCyaXqNVji3=XgQgmHqI28m}zYWas{2yflNilxA&3v(7e@=52msev_R)GI@h-vI{hZ`R}OFEj7B$ zE-pq}YL!bTtKgl>?8-v>uF_s*wSh@{LwAk&3*CE4cOB%&rfLeCClS#|T%h7lcCY>zhBCnu5&ZO?twSqRoCM>TWzeiJBX5L6SMz~ru z>BhWe<(mVZaDn|glWv+Gjs@!s={W2CA@3iY9{dH7Wwh+vV!hmXM!VZzjC)wOIuYYO zPgAvUP|POLxXV(yb(%#o&7E#0hf{Y)9WS>(fA} zG_zisrXkC+!U_}_dxoqsSB=$Jk@_ufS8QXi+#_p|d2;XACM{E3g6h`>U8Fr4_d2QU z$}G)3!{{UOhQH5;(SWD@Vbq05+y3i+_PCgc*FWy_#NP|Ke~%}boa&YREMfi=ZE)G1 zYKOLmGLBkxK{wA`ZI|3Zh{R%@h6)HF@jNdfBqRc*_Z*=Uid2dwwBj-eN)f^#Q6S-x zpv2I!)%n$=+@bq@Z-^UE{JOS-m{Na%Fyd)@f@PJT&Ox27L6ohsSvJ?qrs{F;YV=XRh*_}ov5-;F>ve*pNsu*G!Q)O57AB~k0N

{>kiptfq@W;nX#C{x%8KO^NRwKfeS^;a0eY~V&SM*t&ysX))s zpwp`y6{_IYIm(_HBj=_0!kpng%sw*HtGE$HVCACpmbs`jD$gsjIx~*|fSEC`EyCYj zynV%h+UI(VcRWy>=Y z{pp2MiC}q-wjf|lEBYE5;8#unP6;^Tsidg&L18>dhqb8+)2CNa2>o9vf?)+2$6!YB;FE#Gvy`9VFqIO<<-BdC~UFHM?qgmiQbKr<*%$e2CoHWjmjs9{QNz7P7m2N&&|| z8x6{P3Q#|xL4cYA3`c;Ev>6~Ya=t|Yi)R5(Te{T$PLVi`QUAaeZ^_~UU>-PQLH5v> z(+{B0l)s{pl~HwGll4)96}Q1bBb1`xAe(tJYY8Np( z<+G53Li~pa>0T11{*a3-K|wIh6qt0#W^coQ54tPU!c_VRFy|-WGwCDR0FyzlE82PO ztA~5f9z9*e;`HX%Ai@mw-z|Ru>eD;;-;5Jn<--N0|BjVIE~G;7VJMhC&0_z-!rwls zARbCW6j5uNYQ9=G?R2H=_J=Ww+Uyf`RLUOuXal~F@`!4v<2&N8Q(|bk?ifX~|70I9 zUldpE>6PPoOy!_6tF+cmNFgs1GRC5F=lwf^YBvouN->Hm&Y~7YIaRRBf?3d6#qUVm zC86L34o~(&0Fjd^QsF^^D%sURp4)_|%3>kH*;!aMj z+L)gQ71AlFy{V|mB`lvlSIgbaKEz%A55Ocu2#8ESdi?{6bVQd*?b#Myf2O`y77PYX zQZwYoeny`Ir(U%bn&vlVa%#IHyWC+Yurcq@# zm4re0koQm~97bGdrc?%AA~`R1d#R%5BEHY{$6rUmzSB{r&b>~DhC7|y>P_;3`dU)o zL8`u1MXstA72zplDif_(TiIQi-1`KF%p!i5_%EeQ;u-W^^^UnkMmIMg9qX!Dwws{U XW(6j%SQzshlFb6i`ZXmlS(yKC=sJ+h literal 0 HcmV?d00001 diff --git a/app/__pycache__/webcrawler.cpython-310.pyc b/app/__pycache__/webcrawler.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b2349e94e3e41642bcbc215d0b7468c43e21fedd GIT binary patch literal 3691 zcmb7HOK%*<5uVr1&OW((h*%c|pIb;W{sqU_> zuCA*2s@N`-@)~|0eEvrBFUK_Pb2T{nGcfo8{-T>8L?e2j)zY_K)044LGl)UVz-(DH ztCgu`bhT~;b}L)Ug3r_(V%KscTgwxN^#`SGOn8G3!$$Vn=Y+-Mk#NIL3&xPH$_O)J?XGm5U(Bp_K^jHscT@%^9 zL2^xP-&oK-yxDVvgE8*CZ}f5rU)F-D5t^>Wzv~@Z*WbV!}@(d`aX;VaH@2i{jv&>DwYF3Zf`V zSk>vJj9~=(Xs9d3|~8upU6?jv^>r!se6PV-J+_&DiBXd z{MP?rJE7e3gD5dPo;uXq*=mQh9(G$BlwI7V8t zy)rx8Y)R`n?jp1EsmU_4qRx=n^;CO&_A1>9D04Tc-*V?SSa*|#F2TgD)#W)i9&xAL zHSY0l)Myhr=XTYXG9PX-uSqx4otvNifw|Qjj#yc$Ut7E_9Yzs}h)2?FQZ5TovcIkr zNK3^^W;)CdxwP>G(~!tKz1N@}?jszv%4)iKaX3{Smv+P%g(j!r^SyfP4Mpaq_VXbA7=&=zcpuEV&KrpL(zyNFtb<$YWp{Q0K4$`!#RfglCT7c6) zT&b5j{jJ8TP;)Se%W*n)&A^JG+s`VQJS2*EDwtPu>T$j_?i+uzUODllWsN*1bweUPJ6|Uo3HbthM$l1o%64QX}KQ z(LaNqIOO}sl5>+Y5-!nyXxX7v33p7LP9`5`6zU4AKPg5M{QdQvTSspfI+wKQue*r{ zbnQ0$07OQ;dSdh{gBTtc6@~iA=mbB$uRqeG95?zW6`#Z%vqOK!H-z;_XV(z3QF0Pd zJB6rO4^JggJ0TOK8DS}1FIbklgv^RwKWZC%cvg%K&mvPQ zPecXu>|wTqMM#}fF(vMcd9<#D=TTdx9_f2AD$6)3%Q48lBF3L<{I4BJjT!%FJS?HY z;H@gmQ7cuxzC2Fset6VwB~?9ty?X{94-k-X>aMVM4A^%Q#6vSOy|e&`oCW9Zb{V0r zx6z~dEPTI9@5>B6Su!smT9222RQ+HBuGtD4rgk0Hmfq;;`8c;Bp-0 z!g(FVP{!RZLc-?UTJD{8s}oRE_jv6ZMF3nz1A5oPa`@a>bPI8}<*p5Ohb|(#<-zaa zjQgI)c6`bxif8C5=X*iqUbu=nPsxRunw@fS^=Q3aYiz-yjX}kI!QeT!dQKKlR8Tiz z!#&Q<;quaGN7Cwed<&Jd?T4~-Yi0Sy{6hWejpc>KRc0v%aN+au|;yd6q>7Nxq^nMz$**j4YX^q^caVC~&Vj-(k<-&EPF z`jy;a{hyG|^J#m#y`g$BQ}zDIwDLt-`%SFXvbc?sRvl7$n?!$}x49S86F%c3VE3en z0XvTyuvbB9BZH2jKFC$33TLfw#4b4;B<{CU%Z%vy?Jzx+pI@oT?lAHt++ ziM@>%Q#P{GAkuE3OAFGBNwP_1HUlr(VXC2K-&15skyDDOW+Tya!~uyb5-&L4g|2Qu Zzpws', methods=['GET']) +@login_required +def download_result(job_id): + job = Job.query.get_or_404(job_id) + print(f"Job ID: {job.id} - User ID: {job.user_id} - Current User ID: {current_user.id}") + + # Überprüfen, ob der Job dem aktuellen Benutzer gehört + if job.user_id != current_user.id: + flash("Sie haben keine Berechtigung, dieses Ergebnis herunterzuladen.") + return redirect(url_for('auth.job_status')) + + # Überprüfen, ob das Ergebnis vorhanden ist + if not job.result_filename: + flash("Das Ergebnis ist noch nicht verfügbar.") + return redirect(url_for('auth.job_status')) + + # Überprüfen, ob die Datei im angegebenen Pfad existiert + result_path = os.path.join(current_app.config['RESULT_FOLDER'], job.result_filename) + print(f"Versuche, Datei herunterzuladen von: {result_path}") + + if os.path.exists(result_path): + print("Datei existiert und wird zum Download bereitgestellt.") + return send_file(result_path, as_attachment=True) + else: + print("Datei nicht gefunden. Ergebnisverzeichnis oder Pfad prüfen.") + flash("Ergebnisdatei nicht gefunden.") + return redirect(url_for('auth.job_status')) + + +@bp.route('/delete_job/', methods=['POST']) +@login_required +def delete_job(job_id): + job = Job.query.get_or_404(job_id) + if job.user_id != current_user.id: + flash("Sie haben keine Berechtigung, diesen Job zu löschen.") + return redirect(url_for('auth.job_status')) + + # Löschen der Upload-Datei + upload_path = os.path.join(UPLOAD_FOLDER, job.filename) + if os.path.exists(upload_path): + os.remove(upload_path) + print(f"Upload-Datei gelöscht: {upload_path}") + else: + print(f"Upload-Datei nicht gefunden: {upload_path}") + + # Löschen der Results-Datei, falls vorhanden + if job.result_filename: + result_path = os.path.join(RESULT_FOLDER, job.result_filename) + if os.path.exists(result_path): + try: + os.remove(result_path) + print(f"Ergebnisdatei gelöscht: {result_path}") + except Exception as e: + print(f"Fehler beim Löschen der Ergebnisdatei: {e}") + else: + print(f"Ergebnisdatei nicht gefunden: {result_path}") + + # Job aus der Datenbank löschen + db.session.delete(job) + db.session.commit() + flash("Job erfolgreich gelöscht.") + return redirect(url_for('auth.job_status')) diff --git a/app/static/styles.css b/app/static/styles.css new file mode 100644 index 0000000..20a6174 --- /dev/null +++ b/app/static/styles.css @@ -0,0 +1,166 @@ +* { + box-sizing: border-box; + margin: 0; + padding: 0; + font-family: 'Roboto', sans-serif; +} + +body { + display: flex; + flex-direction: column; + min-height: 100vh; + background-color: #f5f5f7; + color: #333; +} + +header { + background-color: #007aff; + color: white; + padding: 1em; +} + +nav ul { + display: flex; + justify-content: space-around; + list-style: none; +} + +nav ul li a { + color: white; + text-decoration: none; + font-weight: 500; + padding: 0.5em 1em; + transition: background-color 0.3s; +} + +nav ul li a:hover { + background-color: #005bb5; + border-radius: 4px; +} + +/* Vollbild-Layout */ +.container { + width: 90%; + margin: 2em auto; + flex: 1; +} + +/* Boxed-Design für Login und Signup */ +.form-container { + max-width: 400px; + margin: auto; + padding: 2em; + background: white; + border-radius: 10px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + text-align: center; +} + +h2 { + font-weight: 500; + color: #1d1d1f; + margin-bottom: 1.5em; +} + +label { + display: block; + font-size: 0.9em; + color: #6e6e73; + margin-bottom: 0.5em; +} + +input { + width: 100%; + padding: 0.75em; + margin-bottom: 1em; + border: 1px solid #d1d1d6; + border-radius: 8px; + font-size: 1em; + color: #333; +} + +input:focus { + border-color: #007aff; + outline: none; + box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.2); +} + +button { + width: 100%; + padding: 0.75em; + font-size: 1em; + font-weight: 500; + color: white; + background-color: #007aff; + border: none; + border-radius: 8px; + cursor: pointer; + transition: background-color 0.2s ease-in-out; +} + +button:hover { + background-color: #005bb5; +} + +p { + text-align: center; + margin-top: 1em; +} + +a { + color: #007aff; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +.table-container { + width: 90%; + margin: 2em auto; +} + +table { + width: 100%; + border-collapse: collapse; + margin-top: 1em; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); +} + +table, th, td { + border: 1px solid #d1d1d6; +} + +th, td { + padding: 0.75em; + text-align: left; +} + +th { + background-color: #f1f1f1; + color: #333; +} + +td { + background-color: white; +} + +tr:nth-child(even) td { + background-color: #f9f9f9; +} + +.delete-btn { + background-color: #ff4d4d; + color: white; + padding: 0.5em; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 0.9em; + transition: background-color 0.2s ease; +} + +.delete-btn:hover { + background-color: #e60000; +} diff --git a/app/templates/base.html b/app/templates/base.html new file mode 100644 index 0000000..1f06c06 --- /dev/null +++ b/app/templates/base.html @@ -0,0 +1,27 @@ + + + + + + {{ title }} + + + + + {% if current_user.is_authenticated %} +

+ +
+ {% endif %} + +
+ {% block content %}{% endblock %} +
+ + diff --git a/app/templates/jobs.html b/app/templates/jobs.html new file mode 100644 index 0000000..34c8bd0 --- /dev/null +++ b/app/templates/jobs.html @@ -0,0 +1,61 @@ +{% extends "base.html" %} + +{% block content %} +
+

Ihre Aufträge

+ + + + + + + + + + + + {% for job in jobs %} + + + + + + + + {% endfor %} + +
DateinameStatusErstellt amErgebnisAktionen
{{ job.filename }}{{ job.status }}{{ job.created_at.strftime('%Y-%m-%d %H:%M:%S') }} + {% if job.status == "Completed" %} + Download + {% else %} + Noch nicht verfügbar + {% endif %} + +
+ +
+
+
+ + +{% endblock %} diff --git a/app/templates/login.html b/app/templates/login.html new file mode 100644 index 0000000..905a1f2 --- /dev/null +++ b/app/templates/login.html @@ -0,0 +1,16 @@ +{% extends "base.html" %} +{% block content %} +
+

Anmelden

+
+ + + + + + + +
+

Noch keinen Account? Registrieren

+
+{% endblock %} diff --git a/app/templates/signup.html b/app/templates/signup.html new file mode 100644 index 0000000..58f11cb --- /dev/null +++ b/app/templates/signup.html @@ -0,0 +1,16 @@ +{% extends "base.html" %} +{% block content %} +
+

Registrieren

+
+ + + + + + + +
+

Bereits registriert? Login

+
+{% endblock %} diff --git a/app/templates/upload.html b/app/templates/upload.html new file mode 100644 index 0000000..279c7af --- /dev/null +++ b/app/templates/upload.html @@ -0,0 +1,11 @@ +{% extends "base.html" %} +{% block content %} +
+

Datei hochladen

+
+ + + +
+
+{% endblock %} diff --git a/app/webcrawler.py b/app/webcrawler.py new file mode 100644 index 0000000..c4b4aff --- /dev/null +++ b/app/webcrawler.py @@ -0,0 +1,128 @@ +import csv +import os +import requests +from .models import db, Job +from flask import current_app + +UPLOAD_FOLDER = 'uploads' +RESULT_FOLDER = 'results' +API_KEY = 'AIzaSyAIf0yXJTwo87VMWLBtq2m2LqE-OaPGbzw' + +def get_place_details(street, city_zip): + address = f"{street}, {city_zip}" + url = f"https://maps.googleapis.com/maps/api/place/textsearch/json" + params = {'query': address, 'key': API_KEY} + + results = [] + try: + response = requests.get(url, params=params, timeout=5) + if response.status_code == 200: + data = response.json() + print(f"API Response Data for {address}: {data}") + + for place in data.get('results', []): + name = place.get('name', 'N/A') + place_id = place.get('place_id') + formatted_address = place.get('formatted_address', 'N/A') + + # Zweite Anfrage für detailliertere Informationen + phone, website = 'N/A', 'N/A' + if place_id: + details_url = f"https://maps.googleapis.com/maps/api/place/details/json" + details_params = { + 'place_id': place_id, + 'fields': 'formatted_phone_number,website', + 'key': API_KEY + } + details_response = requests.get(details_url, params=details_params, timeout=5) + if details_response.status_code == 200: + details_data = details_response.json().get('result', {}) + phone = details_data.get('formatted_phone_number', 'N/A') + website = details_data.get('website', 'N/A') + + # Speichern nur, wenn Name und Telefonnummer vorhanden sind + if name != 'N/A' and phone != 'N/A': + results.append({ + 'Name': name, + 'Address': formatted_address, + 'Phone': phone, + 'Website': website + }) + else: + print(f"Fehler beim Abrufen der URL: {url} - Statuscode: {response.status_code}") + except requests.exceptions.RequestException as e: + print(f"Anfragefehler für {url}: {e}") + + return results + +def process_file(filename, job_id, app): + with app.app_context(): + print(f"Starte Prozess für Job-ID: {job_id}") + filepath = os.path.join(UPLOAD_FOLDER, filename) + results = [] + + job = Job.query.get(job_id) + if not job: + print("Job wurde abgebrochen, bevor er starten konnte.") + return + job.status = "In Progress" + db.session.commit() + + with open(filepath, newline='', encoding='ISO-8859-1') as csvfile: + reader = csv.DictReader(csvfile, delimiter=';') + rows = list(reader) + total_rows = len(rows) + print(f"Insgesamt zu verarbeitende Zeilen: {total_rows}") + + for index, row in enumerate(rows): + # Job-Verfügbarkeit erneut prüfen + job = Job.query.get(job_id) + if not job: + print("Job wurde abgebrochen.") + return + + # Vollständige Adresse erstellen + street = f"{row.get('Straße', '')} {row.get('Hausnummer', '')}".strip() + city_zip = f"{row.get('PLZ', '')} {row.get('Stadt', '')}".strip() + print(f"Verarbeite Adresse: {street}, {city_zip}") + address_results = get_place_details(street, city_zip) + + for result in address_results: + # Ergebnisse nur speichern, wenn Name und Telefonnummer vorhanden sind + if result['Name'] != 'N/A' and result['Phone'] != 'N/A': + result.update({ + 'PLZ': row.get('PLZ', ''), + 'Stadt': row.get('Stadt', ''), + 'Straße': row.get('Straße', ''), + 'Hausnummer': row.get('Hausnummer', ''), + 'Zusatz': row.get('Zusatz', '') + }) + results.append(result) + + # Results-Dateiname basierend auf dem Upload-Dateinamen + result_file = f"results_{filename}" + result_path = os.path.join(RESULT_FOLDER, result_file) + + # Prüfen und erstellen des Ergebnisverzeichnisses + if not os.path.exists(RESULT_FOLDER): + os.makedirs(RESULT_FOLDER) + print(f"Erstelle Ergebnisverzeichnis: {RESULT_FOLDER}") + + try: + if results: # Nur speichern, wenn Ergebnisse vorhanden sind + with open(result_path, 'w', newline='', encoding='utf-8-sig') as csvfile: + writer = csv.DictWriter(csvfile, fieldnames=['Name', 'Address', 'Phone', 'Website', 'PLZ', 'Stadt', 'Straße', 'Hausnummer', 'Zusatz']) + writer.writeheader() + writer.writerows(results) + print(f"Ergebnisdatei erfolgreich gespeichert unter: {result_path}") + job.status = "Completed" + job.result_filename = result_file + db.session.commit() + else: + print("Keine relevanten Ergebnisse zum Speichern vorhanden. Markiere den Job als 'Failed'.") + job.status = "Failed" + db.session.commit() + except Exception as e: + print(f"Fehler beim Schreiben der Ergebnisdatei: {e}") + job.status = "Failed" + db.session.commit() diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e11dd79 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,11 @@ +version: '3' +services: + web: + build: . + ports: + - "5000:5000" + environment: + - FLASK_APP=app + command: flask run --host=0.0.0.0 --port=5000 + volumes: + - .:/app diff --git a/instance/users.db b/instance/users.db new file mode 100644 index 0000000000000000000000000000000000000000..5f9b0347167514d476389843ca9299275c96a68d GIT binary patch literal 16384 zcmeI&Z)?*)90%~rI^9~C8I00LgL^Qrg&|GTzYM|HU2Cz;T-r|dC|sJ0M*C+=Qs&c5 z_6Ec!z7gMsZ^j2c^iu6`I7K;xZhsIS2%gglo&k)P1>+UaQ06 z=|F_jVPxO9Y8-!VwK|4%OrNo1n)Th;QjG(PwVGx;m;JHT9JN@3Sm#NMuKY00bZa0SG_<0uX=z z1Rwwb2teRp3kc;7**pjq8q9^2(Je_sS_*m!f_lvZFx& z0uX=z1Rwwb2tWV=5P$##AaIKW?j^R9^WPQX|NrGn|3v+uj&D(kC