From 4dd997ce13bb250b125087526868ebe921d9f9ca Mon Sep 17 00:00:00 2001 From: Verox Date: Mon, 12 Feb 2024 21:07:55 +0100 Subject: [PATCH] Added models and relations --- ER.dia | Bin 2626 -> 4463 bytes UML.dia | Bin 1948 -> 2333 bytes app.py | 18 ++++ db/SQLiteClient.py | 79 +++++++++++++++--- .../1705434250_create_habit_lists_table.sql | 7 ++ ...4250_create_habit_lists_users_relation.sql | 9 ++ .../1705434260_create_habits_table.sql | 4 +- .../1706001378_create_habit_trackings.sql | 1 - models/Habit.py | 16 ++-- models/HabitList.py | 38 +++++++++ models/HabitTrackings.py | 7 +- models/User.py | 29 +++++-- 12 files changed, 172 insertions(+), 36 deletions(-) create mode 100644 db/migrations/1705434250_create_habit_lists_table.sql create mode 100644 db/migrations/1705434250_create_habit_lists_users_relation.sql create mode 100644 models/HabitList.py diff --git a/ER.dia b/ER.dia index 987ac8d046a669c010c14e0609a215366e7da8e2..2e41641d445d2686fa5747c81bf3d3e57f7eae17 100644 GIT binary patch literal 4463 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 B!?^$e literal 2626 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 diff --git a/UML.dia b/UML.dia index 702bbce572f913eade94574f0d5c2524d07ded75..fd384877159a0e45e4098fdd8743bf5a420c6295 100644 GIT binary patch literal 2333 zcmV+&3F7u2iwFP!000023+-K9Z{kK5zTaPAu{V9Ctg($hn&Md ziJ7jksXZi%RQ0!a41{2eG5&%;9;9p*GM>jXe$F{_&K%Bs_x;ytC_U07WpQ*jP-SHx z(a4K^76o^M&%gil!5(~n|L!}Vk#EIMkdUb)u87j?-Q8fq`Rv=_@agGE4i_omaUzFo zE~j+(F9}036pe<1`*)JG-GNUy$v#_uN;ps0c+RO5ktw|!jEVOpNaA_q4_2+#?YuaQ z6X}tJcZ2s2%hzDI?q-E(&}eByrXX}VUuFM8_zk)>=L(tUT&BHmf|i*CB# zeL73+{c?FrHmrIpH4nZ`4QkXk55b)IG_9anzD-F_lXbIU)vWKfJ9c7LAMF_HB}7OT zG+B55Q9L@D1W9J6rS%hDs~m}VoIO6j+nSVk{1^53dbFQ^{v>^nehf*P4qjFuV*SDV zyTPwYeyVl{CwdZEurOD%_=QliZQK~LYV1xxQGJvFjK{nEC*t-=6ohnxkwM&!6j{}7 znnKZ#PF*ifqT=G9yYVbRmRh7Zrj3=dWleRqUoEgElm!!B4(7wi5< zu2=6Qe)SWNd*=mQuNf6{0+nqj;g{-J8!1Bwe5s5Lyz||Jm#sgu|5o2ZN`xwVMz=TcA8GhVIrGSe|Hq-<@?kW+ zo#NfJ(?{n)-;#;S^`o-8epK{sXm~p0oCux{HqWt2T(wh5_XW32+#{yabn-8=;UG(w zhP%G~d>U>V7sDWxLEs&afcYh1-~+LfGE0|tY>H43?+i;!{`??VXGlelsz0pds+xt2 zFZRLFXg*!DZaZXCA7+mecVyVK!SrP^>!x13pBs(g_J*KNBs1CHPbM|1-w+%K&gBaZ zoe~y8av(X7oSKp|6GM866Cc6@;khQ^skM<-8V24Ydz;4XWv_Jar^)>8-2>{6p;bVg zj_pL^a7qb3rRcF}LKDVGVA!HhVpgmr z!A)@UaAW}F1Ub)0&PwOIe|}2;NWa7pJr7NLL4Gq-?Jl0wp-<~2lyC~K7G5p9T6nc^ zAFx7$`v7u+oLA~TfS>RgTKfzE<=5;mRJqvj8{jv z^YeGDDx4 z)e-MiM|MoR*K(bm4DzU=otkWqwyOeZJr?a$4SD2rP$BeC(aw)#%s8rcqG~6qcA{!$ zW2r>dP6<^zyB`CpcA{!0s&=AkC#rU$YA338q6!pBsP!x}^Hewrrxl!UGE9m2|APcU zf-YZzQ1=#s1How`I5CIdKyV;9bp?mBDNW((Kx`m3wOpNuao8IO4g{yB;G|*9VQwHb zS0go-P+z?~P3H=-t-Bo@Qm&D5jg)JoTqEWBRaW|{+F}AX!A+F!1UJFW8v7&QCb)S< zZXVu(fJFy0&$ik;6y?84Vya-~MKJSV7#%7T#6$&E*gd+yw++AO=7BmG-UwzybBn3%ddk7aCMa-;wm)Gqyg&Kh~xV>dc$dw;9q_sY5NR<`E&~G$H zHZ0ZX^ROXBc8oQWrW&`VC0ir$eq_kHruKW-P#1$d>aZbGHtinI7cyikU*X&C>3pHX z{yGjC!a+kgXb1-l;h-TLG=zhOaL^DA8p1(CIA~};E)E(xFi0FUgmS1Thk6l3gqPd+ zE+hw%1Iei;Ii9ebocb;~T@mh1zorf0f$&^(74A2de1^?lZ1&>29H`5UZ*g4r*K8|2 zGLRGGJQq2SaC-Wr=RpSu{KQw_@D(`Zm&08^Zsx7YYu#qJroF;#OqHWKTsq2P)9f~v zcd1`Ge>J5Jft4KsTb(fYaE`vwmc6PbG@xzWXqs%NR}bA=TCUkSu6&|8Dh0`~(S^-oN_~bI$}Q`2zs} DN^ywZ literal 1948 zcmV;N2V?jjiwFP!000023+-Lea^f}=ea}~T*q3%DF}8sOb~o8+JJZ>RZTryfGb3!p zs4Fa27RfTx>9~*Q;UY)7`~KkCFHK&zEFo=qrLK{&d@!Diyx(b{`)fO}>bT64K;sZbr2G zAK{#Ib)jzO?!quOEAWUS@ps9$L@CK8b487S%;;@rLfmh@6!XCAtV$*2T)~Aj9tgkf zyt`k%I^Cq2ZmOOgb%(^K6G_Rp;;JqE8jb2y9a5RCb{2|==^|56aTwWCMgx^ zLVTr|e z!tvw6$>YMYXdDWuBw;EKIT3k(l9aw$6 zR$Eo&`jUCOg_|l-P|G9p)O7s4rQurDzG$iT2Ntmjr`uZ30@Yaci)O0N-ks&_{e1a> zY*^J+Xdb*F9ne8_^We{!N23y&rOOo5G);Qb#kewl;UtPW&npnU{$Sp1=MO7ARN2ny zn)ED~8>>(}6UrP8E(fMP$PPe$`X~aJOtSS$-Fy=GoUSoqhz~u>w4KYkP?Xcz*cCF! zFAkdP&k|%gi{$&Xwo*gWu}7P)1F)u)`BPO4HZq4+>UC2OEVEupt(LwH5*it{<&_Gc-INa!P{7gH3a+6xVK)(rv+Q5_gZGydI@r*EKs?xHQcA_VXEE7tV)4 zN`t@|kAQgvVc;FHlOju(c5L!cfocp(5B_{7SYt?ekE%Va<*J%-rWV`aXfU58tlJFP zREOE(#O)b2tucJDX2+2$wsWI4+}04(ku($kel*pr-XRVJO5x@?so%B8qHSnfCW`e1nezKj7FA*@C0(kb<4WLWBDNa)O*^>OO#< z@EH#F8GNeF*XaYv?)f>Ti z=$-N7BG^3GJlH(Ae6V(~cJZ)!lv7wcSUXrdSUXrdSi6&1yJ~r+W+5kvww3tnss1RP zv2>M)vtDLS`_RmsJ;&@_4|{Pk%if^ZZ!te-jASp7tf5BwFc_FlUw?KibD+OyGhJsR zo!l#Fr(+I#n^}RJ7L#_`1G6`3AVcV`q@AD1gehe0MAlAZ?L^kj+Nwm>P6Jsxo9_d% zb|PyhvUVbCC$e@TYbUaHA`28!sI{z_d88eM;|k}87}H?hk5CX4^!gQqytfbzgi}X2 zLP0nX4un%xIEu|^1Xl;Lfov+dIssF#HxLelQ&Bh(7YgPEsyQ3gyoUVh#oKhwV7GO% zox_%EY`MmkYizm3mg^Up>8tD!6SxU(B7G;g32s(+J_2rnnFqq!E)wO!}N+(g^;+?jiVx01LQ@;2*e&;2$hHn2F#Y zh>6%AhzVlim@@?a5cosj51Ub8?_lp<+5u;TpY7RZoJBBsFnKU}c-95G2fO!HPI-9{ z4W>GS*@M}8L1wRuLlF@x!VH2L)NMz1;LmG-m*89`&fL2a@?q1 z-Q$`y@7=KY1kn8|jeDjCFwU}?Te&^`VoWtRl5_Qx&{WXx&%dS*8!f)rPA;}H7Z0-y i%{3{@-_>VM7F6C{tUl;JUz>}&i~j+=v!%=T*8l)MIMY=C diff --git a/app.py b/app.py index 71fff9e..6bc5be6 100644 --- a/app.py +++ b/app.py @@ -323,6 +323,24 @@ def delete_habit(): return {} +@app.route('/reorder', methods=['POST']) +@login_required +def reorder_habits(): + new_index = request.get_json()["newIndex"] + habit = Habit.get(request.get_json()["habitId"]) + + if habit is None: + return {"error": "Habit not found"} + + # Check if habit belongs to user + if habit.user_id != current_user.id: + return {"error": "Habit does not belong to user"} + + habit.update_slot(new_index) + + return {} + + # Run the application if __name__ == '__main__': app.run(port=5000, debug=True) diff --git a/db/SQLiteClient.py b/db/SQLiteClient.py index d6e0481..d31ac8d 100644 --- a/db/SQLiteClient.py +++ b/db/SQLiteClient.py @@ -57,7 +57,7 @@ def update_user(id: int, name: str, email: str, password: str = None): def delete_user(id: int): - query = f"DELETE FROM habits WHERE user_id = {id};" + query = f"DELETE FROM habit_lists WHERE (SELECT list_id FROM habit_users WHERE user_id = {id}) = id;" query2 = f"DELETE FROM users WHERE id = {id};" conn = con3() cursor = conn.cursor() @@ -69,9 +69,9 @@ def delete_user(id: int): ### Habit.py ### -def create_habit(user_id: int, name: str, times: int, unit: int, slot: int, note: str | None=None): +def create_habit(list_id: int, name: str, times: int, unit: int, slot: int, note: str | None=None): now = datetime.now().isoformat() - query = (f"INSERT INTO habits (user_id, name, note, times, unit, slot, created_at, updated_at) VALUES ('{user_id}', " + query = (f"INSERT INTO habits (list_id, name, note, times, unit, slot, created_at, updated_at) VALUES ('{list_id}', " f"'{name}', '{note}', '{times}', '{unit}', '{slot}', '{now}', '{now}');") conn = con3() cursor = conn.cursor() @@ -91,8 +91,8 @@ def get_habit(id: int): return habit -def get_habits(user_id: int): - query = f"SELECT * FROM habits WHERE user_id = {user_id};" +def get_habits(list_id: int): + query = f"SELECT * FROM habits WHERE list_id = {list_id};" conn = con3() cursor = conn.cursor() cursor.execute(query) @@ -104,8 +104,8 @@ def get_habits(user_id: int): def get_heatmap_value(user_id: int, days: int): date = (datetime.now() - timedelta(days=days)).date() print(date) - query = f"SELECT id FROM habits WHERE user_id = {user_id};" - query2 = (f"SELECT habits.id FROM habits, habit_trackings WHERE habits.user_id = {user_id} " + query = f"SELECT id FROM habits WHERE (SELECT id FROM habit_lists WHERE (SELECT list_id FROM habit_users WHERE user_id = {user_id}) = id) = list_id;" + query2 = (f"SELECT habits.id FROM habits, habit_trackings WHERE (SELECT id FROM habit_lists WHERE (SELECT list_id FROM habit_users WHERE user_id = {user_id}) = id) = list_id " f"AND habits.created_at LIKE '{date}%' AND habit_trackings.habit_id = habits.id;") print(query2) conn = con3() @@ -122,8 +122,8 @@ def get_heatmap_value(user_id: int, days: int): return int(count2 / count * 100) -def get_next_slot(user_id: int): - query = f"SELECT slot FROM habits WHERE user_id = {user_id} ORDER BY slot DESC LIMIT 1;" +def get_next_slot(list_id: int): + query = f"SELECT slot FROM habits WHERE list_id = {list_id} ORDER BY slot DESC LIMIT 1;" conn = con3() cursor = conn.cursor() cursor.execute(query) @@ -132,8 +132,8 @@ def get_next_slot(user_id: int): return slot[0] + 1 if slot else 0 -def get_slots(user_id: int): - query = f"SELECT id, slot FROM habits WHERE user_id = {user_id} ORDER BY slot;" +def get_slots(list_id: int): + query = f"SELECT id, slot FROM habits WHERE list_id = {list_id} ORDER BY slot;" conn = con3() cursor = conn.cursor() cursor.execute(query) @@ -174,10 +174,10 @@ def delete_habit(id: int): ### HabitTrackings.py ### -def create_habitTrackings(habit_id: int, times: int): +def create_habitTrackings(habit_id: int): now = datetime.now().isoformat() query = ( - f"INSERT INTO habit_trackings (habit_id, times, created_at) VALUES ('{habit_id}', '{times}','{now}');") + f"INSERT INTO habit_trackings (habit_id, created_at) VALUES ('{habit_id}','{now}');") conn = con3() cursor = conn.cursor() cursor.execute(query) @@ -215,6 +215,59 @@ def delete_habitTrackings(id: int): conn.close() +### HabitList.py ### +def create_habitList(user_id: int, name: str, description: str): + now = datetime.now().isoformat() + query = ( + f"INSERT INTO habit_lists (user_id, name, description, created_at, updated_at) VALUES ('{user_id}', '{name}', '{description}', '{now}');") + conn = con3() + cursor = conn.cursor() + cursor.execute(query) + conn.commit() + conn.close() + return cursor.lastrowid + + +def get_habitList(id: int): + query = f"SELECT * FROM habit_list WHERE id = {id};" + conn = con3() + cursor = conn.cursor() + cursor.execute(query) + habit_list = cursor.fetchone() + conn.close() + return habit_list + + +def get_habitLists(user_id: int): + query = f"SELECT * FROM habit_list WHERE user_id = {user_id};" + conn = con3() + cursor = conn.cursor() + cursor.execute(query) + habit_lists = cursor.fetchall() + conn.close() + return habit_lists + + +def update_habitList(id: int, name: str, description: str): + now = datetime.now().isoformat() + query = f"UPDATE habit_list SET name = {name}, description = {description}, updated_at = '{now}' WHERE id = {id};" + conn = con3() + cursor = conn.cursor() + cursor.execute(query) + conn.commit() + conn.close() + return cursor.lastrowid + + +def delete_habitList(id: int): + query = f"DELETE FROM habit_list WHERE id = {id};" + conn = con3() + cursor = conn.cursor() + cursor.execute(query) + conn.commit() + conn.close() + + if __name__ == "__main__": habits = get_habits(1) for habit in habits: diff --git a/db/migrations/1705434250_create_habit_lists_table.sql b/db/migrations/1705434250_create_habit_lists_table.sql new file mode 100644 index 0000000..36bd24e --- /dev/null +++ b/db/migrations/1705434250_create_habit_lists_table.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS habit_lists ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + description TEXT, + created_at TEXT NOT NULL, + updated_at TEXT NOT NULL +); \ No newline at end of file diff --git a/db/migrations/1705434250_create_habit_lists_users_relation.sql b/db/migrations/1705434250_create_habit_lists_users_relation.sql new file mode 100644 index 0000000..c201f43 --- /dev/null +++ b/db/migrations/1705434250_create_habit_lists_users_relation.sql @@ -0,0 +1,9 @@ +CREATE TABLE IF NOT EXISTS habit_users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER, + list_id INTEGER, + created_at TEXT NOT NULL, + updated_at TEXT NOT NULL, + FOREIGN KEY (user_id) REFERENCES users(id), + FOREIGN KEY (list_id) REFERENCES habit_lists(id) +); \ No newline at end of file diff --git a/db/migrations/1705434260_create_habits_table.sql b/db/migrations/1705434260_create_habits_table.sql index a38dd47..b02a86a 100644 --- a/db/migrations/1705434260_create_habits_table.sql +++ b/db/migrations/1705434260_create_habits_table.sql @@ -1,7 +1,7 @@ CREATE TABLE IF NOT EXISTS habits ( id INTEGER PRIMARY KEY AUTOINCREMENT, - user_id INTEGER NOT NULL, + list_id INTEGER NOT NULL, name TEXT NOT NULL, note TEXT, times INTEGER NOT NULL, @@ -9,5 +9,5 @@ CREATE TABLE IF NOT EXISTS habits slot INTEGER NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, - FOREIGN KEY (user_id) REFERENCES users(id) + FOREIGN KEY (list_id) REFERENCES habit_lists(id) ); \ No newline at end of file diff --git a/db/migrations/1706001378_create_habit_trackings.sql b/db/migrations/1706001378_create_habit_trackings.sql index 924617a..c3de725 100644 --- a/db/migrations/1706001378_create_habit_trackings.sql +++ b/db/migrations/1706001378_create_habit_trackings.sql @@ -2,7 +2,6 @@ CREATE TABLE IF NOT EXISTS habit_trackings ( id INTEGER PRIMARY KEY AUTOINCREMENT, habit_id INTEGER, - times INTEGER NOT NULL, created_at TEXT NOT NULL, FOREIGN KEY (habit_id) REFERENCES habits(id) ); \ No newline at end of file diff --git a/models/Habit.py b/models/Habit.py index 41a310d..27d4f55 100644 --- a/models/Habit.py +++ b/models/Habit.py @@ -16,7 +16,7 @@ from db.SQLiteClient import update_slot, create_habit, get_habit, delete_habit, @dataclass class Habit: id: int - user_id: int + list_id: int name: str note: str times: int @@ -28,10 +28,10 @@ class Habit: self.fill_statistics() @staticmethod - def create(user_id: int, name: str, times: int, note: str | None = None, unit: int | None = 1): - slot = get_next_slot(user_id) - id = create_habit(user_id, name, times, unit, slot, note) - return Habit(id, user_id, name, note, times, unit, slot) + def create(list_id: int, name: str, times: int, note: str | None = None, unit: int | None = 1): + slot = get_next_slot(list_id) + id = create_habit(list_id, name, times, unit, slot, note) + return Habit(id, list_id, name, note, times, unit, slot) @staticmethod def get(id: int): @@ -54,7 +54,7 @@ class Habit: def update_slot(self, new_slot: int): - slots = get_slots(self.user_id) + slots = get_slots(self.list_id) if new_slot > self.slot: slots = slots[self.slot:new_slot] for slot in slots: @@ -67,7 +67,7 @@ class Habit: def delete(self): - slots = get_slots(self.user_id)[self.slot+1:] + slots = get_slots(self.list_id)[self.slot+1:] print(slots) for slot in slots: update_slot(slot[0], slot[1] - 1) @@ -77,7 +77,7 @@ class Habit: def get_habitTrackings(self) -> list[HabitTrackings]: trackings = [] for rawTracking in get_habitTrackings_by_habit_id(self.id): - trackings.append(HabitTrackings(rawTracking[0], rawTracking[1], rawTracking[2], datetime.strptime(rawTracking[3], "%Y-%m-%dT%H:%M:%S.%f"))) + trackings.append(HabitTrackings(rawTracking[0], rawTracking[1], datetime.strptime(rawTracking[2], "%Y-%m-%dT%H:%M:%S.%f"))) return trackings diff --git a/models/HabitList.py b/models/HabitList.py new file mode 100644 index 0000000..7cecc4b --- /dev/null +++ b/models/HabitList.py @@ -0,0 +1,38 @@ +from dataclasses import dataclass +from datetime import date, datetime + +from db.SQLiteClient import create_habitTrackings, get_habitTrackings, delete_habitTrackings, create_habitList, \ + get_habitList, get_habits +from models.Habit import Habit + + +@dataclass +class HabitList: + id: int + user_id: int + name: str + description: str + created_at: date + updated_at: date + + @staticmethod + def create(user_id: int, name: str, description: str): + id = create_habitList(user_id, name, description) + return HabitList(id, user_id, name, description, datetime.now(), datetime.now()) + + @staticmethod + def get(id: int): + habitList = get_habitList(id) + return HabitList(habitList[0], habitList[1], habitList[2], habitList[3], datetime.strptime(habitList[4], "%Y-%m-%dT%H:%M:%S.%f"), datetime.strptime(habitList[5], "%Y-%m-%dT%H:%M:%S.%f")) if habitList else None + + def delete(self): + delete_habitTrackings(self.id) + + def get_habits(self): + raw_habits = get_habits(self.id) + habits = [] + for habit in raw_habits: + habit = Habit(habit[0], habit[1], habit[2], habit[3], habit[4], habit[5], habit[6]) + habits.append(habit) + + return habits diff --git a/models/HabitTrackings.py b/models/HabitTrackings.py index 33c03b6..9ad30f1 100644 --- a/models/HabitTrackings.py +++ b/models/HabitTrackings.py @@ -8,18 +8,17 @@ from db.SQLiteClient import create_habitTrackings, get_habitTrackings, delete_ha class HabitTrackings: id: int habit_id: int - times: int created_at: date @staticmethod def create(habit_id: int, times: int): - id = create_habitTrackings(habit_id, times) - return HabitTrackings(id, habit_id, times, datetime.now()) + id = create_habitTrackings(habit_id) + return HabitTrackings(id, habit_id, datetime.now()) @staticmethod def get(id: int): habitTrackings = get_habitTrackings(id) - return HabitTrackings(habitTrackings[0], habitTrackings[1], habitTrackings[2], datetime.strptime(habitTrackings[3], "%Y-%m-%dT%H:%M:%S.%f")) if habitTrackings else None + return HabitTrackings(habitTrackings[0], habitTrackings[1], datetime.strptime(habitTrackings[2], "%Y-%m-%dT%H:%M:%S.%f")) if habitTrackings else None def delete(self): delete_habitTrackings(self.id) diff --git a/models/User.py b/models/User.py index b33395c..6d36d6a 100644 --- a/models/User.py +++ b/models/User.py @@ -1,6 +1,10 @@ +from datetime import datetime + from flask_login import UserMixin -from db.SQLiteClient import create_user, get_user, get_user_by_email, get_habits, delete_user, update_user +from db.SQLiteClient import create_user, get_user, get_user_by_email, get_habits, delete_user, update_user, \ + get_habitLists from models.Habit import Habit +from models.HabitList import HabitList class User(UserMixin): @@ -31,10 +35,19 @@ class User(UserMixin): def delete(self): delete_user(self.id) - def get_habits(self): - raw_habits = get_habits(self.id) - habits = [] - for habit in raw_habits: - habit = Habit(habit[0], habit[1], habit[2], habit[3], habit[4], habit[5], habit[6]) - habits.append(habit) - return habits + # def get_habits(self): + # raw_habits = get_habits(self.id) + # habits = [] + # for habit in raw_habits: + # habit = Habit(habit[0], habit[1], habit[2], habit[3], habit[4], habit[5], habit[6]) + # habits.append(habit) + # return habits + + def get_habitLists(self): + raw_habitLists = get_habitLists(self.id) + habitLists = [] + for habitList in raw_habitLists: + habitList = HabitList(habitList[0], habitList[1], habitList[2], habitList[3], datetime.strptime(habitList[4], "%Y-%m-%dT%H:%M:%S.%f"), datetime.strptime(habitList[5], "%Y-%m-%dT%H:%M:%S.%f")) + habitLists.append(habitList) + + return habitLists