From b5a85c610acbeedb0e4c0a1129e68fc5c82d5b53 Mon Sep 17 00:00:00 2001 From: Florian Uhlig Date: Tue, 7 Oct 2025 08:44:02 +0200 Subject: [PATCH] Dashboard Finished --- config/__pycache__/database.cpython-312.pyc | Bin 3097 -> 3510 bytes config/database.py | 11 +++- .../__pycache__/sqlite_db.cpython-312.pyc | Bin 10999 -> 12327 bytes database/sqlite_db.py | 49 ++++++++++++------ .../__pycache__/user_service.cpython-312.pyc | Bin 4843 -> 4847 bytes services/user_service.py | 12 +++-- 6 files changed, 53 insertions(+), 19 deletions(-) diff --git a/config/__pycache__/database.cpython-312.pyc b/config/__pycache__/database.cpython-312.pyc index 760518b43a557a178e18bd628818bed8759521c8..71bd015262284b729609df815890c2477948d7d5 100644 GIT binary patch literal 3510 zcmbVPUrZZE8lPRS*9QNA#1tHC0!g_f+oYHaY5tX?g5VaKgm9R&jT>oYm^Ik9_Bylc zL`Fo4s#Hp(tI|FZ6e|4Chg5lSrK+M&_j;#$v9l+E=2G>fODpw_mPnQQbThji8&K}x z7|Gwx%=gWF-}jsO=KK72pRW;+cIlhvvww2~@NYU`O{jJ@`blR2Kma)v#Oaq;xeOoY zIofxqj*Ji&GS0Y@13Y*KK;bTc&L_ORIxZGCN&JZp#v~34;|XI{;;~~eTfo9_YTB5# zzOsvi2GZaTEb_e+AKuBb_!n%ebSUu0IxD3N4-R!z-Ba!5-n(}o@yzb0RpO&CL(5wVv=U=@U( zTmidfIh9QrvOL$e6`LN$p#JFHgxmqo+YT%gJ}VRtJRULI-nb|H*}K--zS?@U(t6Zv zmF@|jcs8h~SVW4EN7*g#l3QiN`y+v10T7_5=(d)o2e7R%@F4f6V*sF!4JQ*Y*fjx9 zs)v=PTDG;9%|U+71IV4F^fqS@YPYnP_3B?J7)@+6&oNEx@Ne^Vd**q@P48nX>R;xE z-d10c-e2dl9+R85Y`o1w=O+C%4Jj_R7Ys!U!<##J?1FCQpYaN_Ylk%ru#v4{DD1Gp z1HEj;4w63T-=$;|Y-W;hAKbqr?g^#9u25>(14^&_-zc^28l|>fqZHhP(t$mr)NY}a z-{I^+dq61+JJ>1i@-7|RHA*mD^IC*{#7W&_ zh^AHCz+PN^u5p^Y#Z#Km1E;Vj_Qy-3lTmqmaB>6-Ig$~vGgBam05@K~MtJhbmDnV1 zP_^lVI;-ghn;yS1F^Pq~zTVT=Zwn3&P7V$Y#-i9&-JXeFjYTK0fHciuqT_?H*!x!| zhB3gdypcScnL0%hqhReX8HBizejrU6L31 zhzxeB+AReknz(2(O{P>uma&*iASG)cnpqG{D%hbL2zyexoS4!zHE$@`F?|bSM=DFs z^}U3eSE2}MNOB;WJFpij$wXc?Wb0aZ?CQW8=u}l#R~MqW1NjM=3vEx`n~DXhT>pXO z$2(xn)AXSKe*dbcqvGi>J)Om~Pd&$l;bm!2DqjA&uQYG=4V&%Jdlz4bpe6XwV+Oic1HF|%?>_=3pB+5BJi9piDE;`& z%E8m0dj8eYzI37JF~cXyf!E94*Vlae9$deFebv`l@pYQML&f)>`c6C#wl6mrZ{ZX9^=F;?7%jg<9#R86KchOyRkeb7nZo6h=ztH(PUV}yJhdYsD&K&mb#0UrdC;dl63xC-9=urS_w4m8#4_qgtQN9o}{yUm|mg?n7$}axI$z59aUBKb&0t zaPh-()3LJqnB~Tp)JP*Lo5;|JYLw-S2J=>Qd1d)VKB3wi6rgs|q7V&GLdr`!EU?qa z=Trr~O(wns)|Ph6(25pOLhDDJ{;RnQe&HR?*VY@4I4^P!VX^PAqj+{Py8-BUeVpT* zQt|5JzLHRit-Me%reNG)5S-oYjMbtUEZ4`zL7l4g|jk;a|lFC;b8lu~z;E^<~~4 delta 1384 zcmbVMO=}ZD7@pb9ZnF8b-K33a+NN4fVnGW<5lM?51P%RAN%W8&Byoai-Fz_FL@JSZ z=s{1Si`YP*ITfW|q~N7Lp_hP{vL2*IZ=oIpPwu;6B~l@=b9iRnnfH0#*=J|w^GMz6 z+;cdrNNZcW2ib)AwbL7!)}nWaAc7^7gr1Rbj!7~(Hpybdpqq%W>xh`P8Q>w5G_Bz< z_Z@~ypDJ4z^U5f58{3r}GZ*v{M&@7%+A81ayOFSW9o|9jx++k>ILJs9rt!253xPU;?%d)V$fh6f|WZ~TCM&%tQl9*mK3L%he<16>|W zG`KjDZN?!Wr>e?~d2;&n5{1L9nu;kotJ>ms7h;L%gFDv}x1PF5N=~IyrA+C;05e&O|9K zQ0mfVOK?X5bO5p(%B~RLA-D8aIS_oq`)*(3`ev*iYvx)P=fC(DTH<2MzPOXNW-%6( zJ=?t@C`FwB-D=7udn7OgLLLpCqkWh7s`|3Jk=R_WFSlHwmL)_*<&AwRJqT2j1}a;U zDP$=?ikDJd|-t+-4T_S*NQ9oN1cLDU)Q8JVDzWhFH0m>AY~~tN2A&TSfzhZo5dJ^ SMZ!1a|H%h&_z-EJ(|!Z^oxy?t diff --git a/config/database.py b/config/database.py index 2f39957..7b6dc6e 100644 --- a/config/database.py +++ b/config/database.py @@ -1,4 +1,5 @@ import os +from pathlib import Path from typing import Any, Dict @@ -10,7 +11,15 @@ class DatabaseConfig: def _load_config(self) -> Dict[str, Any]: if self.db_type.lower() == "sqlite": - return {"path": os.getenv("SQLITE_PATH", "databases/chatbot.db")} + # Standardpfad relativ zum Projektroot + default_path = Path(__file__).parent.parent / "databases" / "chatbot.db" + sqlite_path = os.getenv("SQLITE_PATH", str(default_path)) + + # Stelle sicher, dass wir einen absoluten Pfad haben + if not Path(sqlite_path).is_absolute(): + sqlite_path = Path.cwd() / sqlite_path + + return {"path": str(sqlite_path)} elif self.db_type.lower() == "mysql": return { "host": os.getenv("MYSQL_HOST", "localhost"), diff --git a/database/__pycache__/sqlite_db.cpython-312.pyc b/database/__pycache__/sqlite_db.cpython-312.pyc index 8044406fd9ca17b71756b588520e4479df63efc2..01339283e31e739e5565b92bfaa5ffa287bf22c5 100644 GIT binary patch delta 3414 zcmZ`*Z%iD=6`$GN+uh^tVEK302970Q;J|iGOfa@%$2PVxu7g9se}Y|IkJ|+fA9rMS zDF#E%sjSwPECsqB+{(4&^owf|DYb~&NNp;oQd6XU=pkPP-8zw_`a`NpEs9+=a#g8s zX77)w=xX=Py!U3_d-LY~X6Ik0?|!@R_dZ_%f=B!LlaY~{`NB>3d-IJgNsKh42{Ca@ z8j!~10eQ?l;0BpX6JrHqo&gU=9MaL~COUQfqx%-HBN-^&Mg31VA#BW#X9>xv~J;aHHj`9tw3d;XKx@ZabE6Y!O5;#FZl)C64`RV)>kCnezB zTDc}eTL7&aS`V}Z(0VnGE@)o(eDDdnZ%Eb(L0-sYf?lL6x}X;iaht(A{e}ds2=pav z-o|r(8k5^(7wlzXASkq8U7cLeO)b z8Dt$StO{Xdy0W5$oGO&b7(!Dhh&SVdLUNnr-xf@+%KZlT*kwq7v$F4DwV?j3FtPohiwTWAsorRnxQ zQ;)?=)rdw$bh1s=!iGVKdL*;{?Pqi{t%EimHB|jl)G(tuG5dp@EsckX9ybkJX5vud z;&fFf(Zfko*T966J~fxVz3O!Rv2Zj7N;9EGV4$gQFid^U?Nxh%ZbGx;?1|_&vPI^{ zuw7#p!Ky9B62rqfv4v=SC}B&d`i>HkAhs*1(>BNx1uWR2Hb}S7#=1c*s;Wwgsb!kF z>0n>)$*8Gk*Sh2XZ@wRf8#6$zpeM!UbK1?=eC%Oy-AZxua&hxDakZ%Yz1i!tHr972) ztJT%g)`tLId9P}ALcYyFE^n%inriVRDfqk_fRFnh@cFEOcmVfU7sT7R*?KNE2>_8M z3m4QMSHq@C= zV)aO`b`Tn(?b7tYiDBEtKtgJ$?F>TX@SqVIhaUy01!*27)=#B2iHXi(>n~C*)OLx_ z|D>|!j&RrezV~6}_La(4mMdR*Sh;&neiYpKVa>goM`aa1l&#lS8(LPY)JL^VE43ZV zwH?3Oy|S-&8Q$97XKq(>$(%sZkX9O=c~SfRU!1>x-jS>*4a-VHT6y`gQbtEO{ED(; zS=o{LM)G)C>0aGPb0V#Lb3;W}S~z7xG zwk{WKP03rYnOXKMMG%XSK{ibuyA38^$)p$gR{NDeHLRY;jRTb?=I!>JJ`DGzPi5+ zrn7w{)wIWcRqmsgBKo))O~l!D-s9MT=WHTHPWGg0w#Ex8tM-7zcoWDKv?jRRC97U# z#j7rR)wK7e#d`%`VO(eZ*n4VUBekC^HQN=5B@Eqh-AYyMKx}C;Oybe_Fxd^9k#%Nm z_ci0gi|2d?g?<{aEk-7YksxHJ!z9MWqUH;$pr%;pga!wHV0K^&w1CNes$<1Rj{`Wh z;n=qRR@@<;1lG82h3nPD?vnd_l4XXCre4SaN@&45iK2!{5Qtsa9XIu15;dnB1ni|k znIvMd!EodvTb6?g$w}iOI`c3kos_&rNj@lgl+vJ7fdw|}-zI7xG9I(Q{@`EZLhA#w zo{#L?XIj zIP1!7N2ccOvWaoHf#{(LtGT`vFDzcD=lRw%)aHgcCg-SiR&MJSG}pKdKd?S&{6Z>& z0X@MA>nBaylca@?roT6Ki*V|zpwl^bDI8e_o2h;uB?p1n!eAm1BVClI(Gu#1(0eKA zp`@3RK1$xAj}k8e7#g2L?<)F}&R3XXi0KDN n6j%(OLDRIxV~qcd3gP`6ZL)sbtd<<%ab>E!;h%_hSTg<#rlT9# delta 2383 zcmZuyU2GIp6rP!#ot@pC{ptR+h0=Druq{)TpV9`XDG0U&E2U}>jeW3`o!NHlcBkB# zO|5`M(}dPYuwD~Pj0u_;f{n&zNeI5^gNY^@ZDW&NMjm(|CO&8y3&EI(-g9TSU9^*# zbMN^-_q+F=xq9-!3Ga6vkBcGCpSiokZ`aIvtJzm)o3{0j3jM;Uqu;?Y3a{AH;;6IV z$ubT1yP3WzOVvHJwCm{12xd z1fcW)A?KMwRoyiImUOqW1`kd17u_pFO5BiYWKkwj+z5~{4)Q_Fl^-}fZ`*=@6xug$ zHb6?B5wSE|8_zN$E_}rsiYqolD2Yd;(rj%k(|Yl|qsqq5@p!+usd8;?WhQ?7w_`u+ z#PdQO{!I*r!)t*`GsTW)oo(Dm7;9n=8^$-qo`&_z#L`(~8KV+^yP+1Jbh`2T&NwH# zaKd!}f8_dr-HH#ox5j0U=@=f=2Mq%u56Oez?3xb!bUJ0It)?TPWio2Qpy1?)Dos^EH+vFGUxn=#sSK zj^qami_c5Vi&FE#wyq_qyRe?(@RIcWk##klOH$XmitZ)p@NFq@sduJ#VFOs*@dYoP znK_g9#TI?B1u=#y$m06&Lr({Hj>R3`7i^rt;fMZOe5<-be1lPJ=eXB6{JnRoJ4!53 zgb3Vr~yg=_-B4j?o*PiACIB>X#7`-FDF=`;9reI)l%)x2$z0!57>2igfF0)!@jun5|X z>FvoF>JUm96WxFu+6F=qY3cN-!NkjJmB4Ln+-DJE4TF|1`&lUGvYde9;PT&r-v5gBAd&x7P1thjZ!rFStDbk6d)7OIUSA`ym5GwA`5p%@BS)8p31I7(0Q}^)2|D z=rH?uZrA2K+x}?j=rM?~PQ9puDD?8U_+acQ;Z2ZUpqur1%)jnt!5)k!v}~sQT&!gP zFwh=CO8Y_)=>qBM%1Fn_CK9TyTdP~$o6^>VvSSL}5&XE^!oHU~E!%7@hau*=D5EDK z`u~8!AIIaur$nWH0t>49@~THTM_Rzy+U2&y;` z_>GzTDNReGy}$!*$PNU~IywO45RgtFJwT2Ua*d%4#Jb!rkOF}}n_gfD8dJ*sEi|_A@1Q5s8EQA~HGcck4{{oX<0r&s_ diff --git a/database/sqlite_db.py b/database/sqlite_db.py index 6efffda..0d9ef02 100644 --- a/database/sqlite_db.py +++ b/database/sqlite_db.py @@ -1,5 +1,6 @@ import logging import sqlite3 +from pathlib import Path from threading import local from typing import Any, Dict, Optional @@ -13,32 +14,50 @@ class SQLiteDatabase(DatabaseInterface): def __init__(self, db_path: str): self.db_path = db_path + # Erstelle Verzeichnis falls nicht vorhanden + self._ensure_database_directory() # Thread-local storage für Verbindungen self._local = local() + def _ensure_database_directory(self) -> None: + """Stellt sicher, dass das Database-Verzeichnis existiert""" + db_dir = Path(self.db_path).parent + if not db_dir.exists(): + try: + db_dir.mkdir(parents=True, exist_ok=True) + logger.info(f"Created database directory: {db_dir}") + except OSError as e: + logger.error(f"Failed to create database directory {db_dir}: {e}") + raise + def _get_connection(self) -> sqlite3.Connection: """Holt oder erstellt eine thread-lokale Verbindung""" if not hasattr(self._local, "connection") or self._local.connection is None: - self._local.connection = sqlite3.connect( - self.db_path, - check_same_thread=False, # Erlaubt thread-übergreifende Nutzung - timeout=30.0, - detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES, - ) - self._local.connection.row_factory = sqlite3.Row - # Optimierungen für bessere Performance - self._local.connection.execute("PRAGMA journal_mode=WAL") - self._local.connection.execute("PRAGMA synchronous=NORMAL") - self._local.connection.execute("PRAGMA cache_size=1000") - self._local.connection.execute("PRAGMA temp_store=MEMORY") - logger.debug("New SQLite connection created for thread") - + try: + # Absoluten Pfad verwenden für bessere Kompatibilität + abs_path = Path(self.db_path).resolve() + self._local.connection = sqlite3.connect( + str(abs_path), + check_same_thread=False, # Erlaubt thread-übergreifende Nutzung + timeout=30.0, + detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES, + ) + self._local.connection.row_factory = sqlite3.Row + # Optimierungen für bessere Performance + self._local.connection.execute("PRAGMA journal_mode=WAL") + self._local.connection.execute("PRAGMA synchronous=NORMAL") + self._local.connection.execute("PRAGMA cache_size=1000") + self._local.connection.execute("PRAGMA temp_store=MEMORY") + logger.debug(f"New SQLite connection created for thread: {abs_path}") + except sqlite3.Error as e: + logger.error(f"Failed to connect to SQLite database at {abs_path}: {e}") + raise return self._local.connection def connect(self) -> None: """Initialisiert die thread-lokale Verbindung""" try: - conn = self._get_connection() + self._get_connection() # Entfernt das unbenutzte 'conn' logger.info(f"Connected to SQLite database: {self.db_path}") except Exception as e: logger.error(f"Failed to connect to SQLite database: {e}") diff --git a/services/__pycache__/user_service.cpython-312.pyc b/services/__pycache__/user_service.cpython-312.pyc index feb1a13b485380bb2a61aba4c03ca56ca46e81df..68d0cf069670dfefcdd2a5f4ff6ed390180fc817 100644 GIT binary patch delta 274 zcmaE@`d*dyG%qg~0}%Y#@+4#ZM&5cB?o1%Bf#CxigOJ$fsVuon0m5B$=M2|TRcj5j2tKQMzCD(X;% zu^EK%fRnGIWJ1Uc<%y9U^%ptiKeK|=eURa16$9%6(I8DA8l(qEKj7r;NbZ#U%nD?G xkeZw+C}+gWY5`UaqCu)bw1qX8#M zQEl@Lj>}BEdLXG~V5tvGn|E*rGBH|j=Hh$F$mp{93%@jD92cuM;{xXo3?O;~Glbqz z451H{o(Q=R9DX4p`eJa*iTI1|v7f=}K1gt~imgcgzyPFnD1)gJ!eHt`VCaRg$cuqd zpFwgT#3ttp%9(JpTC4z?2&8rxgQ*h`-i7d(3$ck8lG86_WM53qxe%LkF+BG(NZkji W&HDuv8TlO;86`e3fXE_2pdSD(ms4s0 diff --git a/services/user_service.py b/services/user_service.py index e95d1d1..2cf6634 100644 --- a/services/user_service.py +++ b/services/user_service.py @@ -12,7 +12,9 @@ class UserService: def __init__(self, database: DatabaseInterface): self.db = database - def create_user(self, username: str, email: str, password: str) -> tuple[bool, list[str]]: + def create_user( + self, username: str, email: str, password: str + ) -> tuple[bool, list[str]]: errors = self._validate_user_inputs(username, email, password) if errors: return False, errors @@ -24,7 +26,9 @@ class UserService: password_hash = PasswordUtils.hash_password_simple(password) return self._attempt_user_creation(username, email, password_hash) - def _validate_user_inputs(self, username: str, email: str, password: str) -> list[str]: + def _validate_user_inputs( + self, username: str, email: str, password: str + ) -> list[str]: errors = [] if not ValidationUtils.validate_username(username): errors.append("Invalid username format") @@ -43,7 +47,9 @@ class UserService: errors.append("Username already taken") return errors - def _attempt_user_creation(self, username: str, email: str, password_hash: str) -> tuple[bool, list[str]]: + def _attempt_user_creation( + self, username: str, email: str, password_hash: str + ) -> tuple[bool, list[str]]: try: success = self.db.create_user(username, email.lower(), password_hash) if success: