From 928a2e5ee0798202f610c01c18c2d715019e0ee1 Mon Sep 17 00:00:00 2001 From: a2-imeri <Alfret2.imeri@live.uwe.ac.uk> Date: Thu, 16 May 2024 10:25:58 +0100 Subject: [PATCH] Intigrate rules with Admin --- .gitignore | 7 +- MisplaceAI/Dockerfile | 1 + .../templates/admin_app/admin_dashboard.html | 5 +- .../__pycache__/apps.cpython-310.pyc | Bin 419 -> 419 bytes MisplaceAI/docker-compose.yml | 1 + .../rules/__pycache__/__init__.cpython-39.pyc | Bin 198 -> 0 bytes .../rules/__pycache__/admin.cpython-39.pyc | Bin 239 -> 0 bytes .../rules/__pycache__/apps.cpython-39.pyc | Bin 454 -> 0 bytes .../rules/__pycache__/forms.cpython-310.pyc | Bin 1154 -> 1102 bytes .../rules/__pycache__/models.cpython-310.pyc | Bin 549 -> 1176 bytes .../rules/__pycache__/models.cpython-39.pyc | Bin 630 -> 0 bytes .../rules/__pycache__/urls.cpython-310.pyc | Bin 499 -> 915 bytes .../rules/__pycache__/utils.cpython-310.pyc | Bin 2064 -> 0 bytes .../rules/__pycache__/views.cpython-310.pyc | Bin 1714 -> 3741 bytes MisplaceAI/rules/forms.py | 28 ++--- MisplaceAI/rules/migrations/0001_initial.py | 25 +++- .../__pycache__/0001_initial.cpython-310.pyc | Bin 684 -> 1095 bytes MisplaceAI/rules/models.py | 19 ++- .../rules/templates/rules/add_rule.html | 26 ++-- .../rules/templates/rules/admin_add_item.html | 58 +++++++++ .../templates/rules/admin_add_location.html | 57 +++++++++ .../rules/templates/rules/confirm_delete.html | 21 ++-- .../templates/rules/confirm_delete_item.html | 15 +++ .../rules/confirm_delete_location.html | 17 +++ .../rules/templates/rules/edit_item.html | 16 +++ .../rules/templates/rules/edit_location.html | 16 +++ .../rules/templates/rules/list_rules.html | 66 ++++++---- .../rules/templates/rules/rule_detail.html | 22 +++- .../rules/templates/rules/update_rule.html | 21 +++- MisplaceAI/rules/urls.py | 19 ++- MisplaceAI/rules/utils.py | 70 +++++------ MisplaceAI/rules/views.py | 116 ++++++++++++++---- README.md | 78 ++++++++++++ 33 files changed, 554 insertions(+), 150 deletions(-) delete mode 100644 MisplaceAI/rules/__pycache__/__init__.cpython-39.pyc delete mode 100644 MisplaceAI/rules/__pycache__/admin.cpython-39.pyc delete mode 100644 MisplaceAI/rules/__pycache__/apps.cpython-39.pyc delete mode 100644 MisplaceAI/rules/__pycache__/models.cpython-39.pyc delete mode 100644 MisplaceAI/rules/__pycache__/utils.cpython-310.pyc create mode 100644 MisplaceAI/rules/templates/rules/admin_add_item.html create mode 100644 MisplaceAI/rules/templates/rules/admin_add_location.html create mode 100644 MisplaceAI/rules/templates/rules/confirm_delete_item.html create mode 100644 MisplaceAI/rules/templates/rules/confirm_delete_location.html create mode 100644 MisplaceAI/rules/templates/rules/edit_item.html create mode 100644 MisplaceAI/rules/templates/rules/edit_location.html diff --git a/.gitignore b/.gitignore index 29f259e..3c37699 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ +# Ignore all __pycache__ directories __pycache__/ -**/migrations/* -!**/migrations/__init__.py -registration \ No newline at end of file + +# Ignore all migrations directories +migrations/ diff --git a/MisplaceAI/Dockerfile b/MisplaceAI/Dockerfile index 2751409..bbeaee3 100644 --- a/MisplaceAI/Dockerfile +++ b/MisplaceAI/Dockerfile @@ -1,3 +1,4 @@ +#Dockerfile # Use the full Python runtime as a parent image FROM python:3.10 diff --git a/MisplaceAI/admin_app/templates/admin_app/admin_dashboard.html b/MisplaceAI/admin_app/templates/admin_app/admin_dashboard.html index 93e3301..7d43f1d 100644 --- a/MisplaceAI/admin_app/templates/admin_app/admin_dashboard.html +++ b/MisplaceAI/admin_app/templates/admin_app/admin_dashboard.html @@ -10,7 +10,10 @@ <h1>Admin Dashboard</h1> <div class="list-group"> <a href="{% url 'admin_users' %}" class="list-group-item list-group-item-action">Users Activity</a> - <!-- Add other admin links here --> + <a href="{% url 'rules:list_rules' %}" class="list-group-item list-group-item-action">Manage Rules</a> + <a href="{% url 'rules:admin_add_location' %}" class="list-group-item list-group-item-action">Add + Location</a> + <a href="{% url 'rules:admin_add_item' %}" class="list-group-item list-group-item-action">Add Item</a> </div> </div> </div> diff --git a/MisplaceAI/authentication/__pycache__/apps.cpython-310.pyc b/MisplaceAI/authentication/__pycache__/apps.cpython-310.pyc index 3d7bbc9a78b073b047dfd0b96999c880a622acb1..aac04fde8475c476d4a28433344a6abb5dedec2d 100644 GIT binary patch delta 65 zcmZ3?yqI~yX~tU<&jhn^F)%O`aZOfb4AFux!F&+G&A`BLi^C>2KczG$)sB&YfuWd% Lfq{XCL4*ka2IdU$ delta 65 zcmZ3?yqI~yX~w9DXM)){85kIfI47$zhG;>UU_OZ8Vqjpn#bJ}1pHiBWYRAaHz);M> Lz`(%6Ai@Ly=;#b< diff --git a/MisplaceAI/docker-compose.yml b/MisplaceAI/docker-compose.yml index 216683c..dc72d67 100644 --- a/MisplaceAI/docker-compose.yml +++ b/MisplaceAI/docker-compose.yml @@ -1,3 +1,4 @@ +#docker-compose.yml version: '3.7' services: diff --git a/MisplaceAI/rules/__pycache__/__init__.cpython-39.pyc b/MisplaceAI/rules/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index cf5573c4ac264229497c6125f1ed0780162d5844..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 198 zcmYe~<>g{vU|=ZR=aL4ZAA<;F%*epN;K0DZP|U)>z>vZa%%I8Wx00a<B#a<_WjkBN zgche3700+0rKY6B_~)g%6lIpB#<=7sm*%GCl@!OgW#%R3C{(5<7R6+yq~?`mre!84 zmSpDV>E@^D=4KWb<Rm7irs!stq~;dK_(J)Po-swGIjO}l@$s2?nI-Y@dIgoYIBatB PQ%ZAE?LdzB400?0PM$Wy diff --git a/MisplaceAI/rules/__pycache__/admin.cpython-39.pyc b/MisplaceAI/rules/__pycache__/admin.cpython-39.pyc deleted file mode 100644 index 1bf9591e9ac3cc1c8238693e6444b34c1696be78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 239 zcmYe~<>g{vU|=ZR=aS~cz`*br#6iYP3=9ko3=9m#0t^fcDGVu$ISjdsQH+cXDNMl( zn#?ajYBd>eu_mVEX6E^6GDY#FWF_XM=j$cs=am#?CKZ7USjkYt%)kI4eq}gY#e^28 z78S?16{V)6#Q5i>x)f!WrN+4ACzs}?=9Lu3xMk)g<|tIACKknHrljVTWTs^%CzfR9 z=jrCB>E>n@7vv-+r>5v;mZatu$M{0|j-D|^r8%j^F<__Y6;$5hu*uC&Da}c>V`N}p I_zZF>0BYVs5C8xG diff --git a/MisplaceAI/rules/__pycache__/apps.cpython-39.pyc b/MisplaceAI/rules/__pycache__/apps.cpython-39.pyc deleted file mode 100644 index 31706469dbe44504ed6dba139fb1d69414c59785..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 454 zcmYe~<>g{vU|<lt;gYtIfq~&Mh=Yt-7#J8F7#J9e6&M&8QW#Pga~N_NqZk<(+!<1s zQkYv9QkYX2o0+4SQdoi+G+AGQ^lCER;&d!1aL&(5%S=xOi6CP}n290`3=F9ZQH&`J zQA{a}QOqe!Q7kFU!3>%#x445!b5e^T21KzIfjCtHU@-;X#Jt4x)ZEm(5<gAWTO9H6 zd5O8H@$t8~;^TAkQ%Z9{Y@Yb|!qUVXs0<5;zQt3PT9lMuoC;RIlA(x`fdNANN_V!3 z2`x@7Dvoh0N=-?L@y|<jDatHMjd96OE(KXs9OIUmmzbkanVMJ>lbMp5SCW~QnVeXX znV+YdpQf9eSzM5ln4FrTn^}^YTO8vH<vV)DfL$JwSWr-`S5R5R%)r0^!o{FK<zQrC v1dI4-GT-7($x6&i&({N~EMfw=45SX>MhFWOQ@1#5Al|eCg<~;DEe{g_3UqXI diff --git a/MisplaceAI/rules/__pycache__/forms.cpython-310.pyc b/MisplaceAI/rules/__pycache__/forms.cpython-310.pyc index 7417812a7a9b27f25d270f78b952272824883522..48868c69e39a330151c6a798a8488645e623b8dc 100644 GIT binary patch literal 1102 zcmd1j<>g{vU|@JH?V2{3k%8ech=Yt-7#J8F7#J9eqZk+%QW#Pga~N_NqZk<(QkYVh zbC`0OqnLA9qF8cSqgWXk+!<0>QdnCUQdm<Nv)G!MquAXUQrJ@1TNqN<p&}ga3@IEb zoGlC~oKO+Y6s}+fP41T<TQwPPv8Lr0<rcqWWME*>WWL1`RGO1|i^C^BIk6-&KkpWc zXGv;qGDs;hW`;WQvlIgZLn=cQV+unQlRHBSV+vCXLkd$fQxtOwb1;J@%PkI&d2abd zm}W3R%_x>YG9#5aiY1*Pg>eyM6l)4oFoPy@6)(&LJ>S%lM356pi&Kkkv1FE{=HBAW zfjYF<Pm}EyM|^x<Vs2`D{4K8d_}u)I(i{+*CqBNgG%*JvQv?d%TdcYHDXBTP*wQjn zb5e>|G873iFhGc3!up8?1^Pv$IjP0^;E>TPsJz7jvYHhXD{Kr53>*wBj694*!VC-y zek)muctCo2K?EO&00|?^gRq1^5*!Q+3?SP-g9r`=A;uz*2+puAVh6<t%Pk&QTwo1F zP_%)P%V#mPP)uP=M}(muvKioTyu|_v4?j&dbmxP7S;P-=uK<XE2Mm%&Kt2Q|;?J<a z;DQE5kq9m=AlGq%Bt=04h)2-h9H5kk*Uv@bpb%oN;)SUM`>Y6*p0Icf<X9p+2KKrn z$dw>(`r-BxDCvVE9PA|!&rg%<7F$YIVqSWF5h!)vVgo0$VsM%(0tYO}5w|#fL8%Yq sHE;?j0tH7A$N)$fBZ3^1Y;JMbKvJ?DD5{G=;m*S#!YITf#45%L0PsG*!~g&Q literal 1154 zcmd1j<>g{vU|{%l!6hw;nStRkh=Yt-7#J8F7#J9e^%xi!QW#Pga~N_NqZk<(QkYVh zbC`0OqnH^P+!<0>QdnCUQdm<Nvsjv$qgYeef*CZ~UxG~1WW2?imS2=x{F0G@fkBh; z7E4fRPHHkp3K=uQEHYwXU`S<%VoYI(Vsd9lVN79aVMt+WW{P4?VNPLbVTfW$VNGFc zVTfW)VNc;`VTfW&;S6Tb<hsQHGS@A?C>O(QCYae0NM@%pN3o<cq%baGjABh;3TDt` zuHuE6py!)flBmgiizP2HH}w{0a(-S)W=Up#-YvGoWDxDA$##n)J{}|+AAgH0K0Y@; zr8FlsKK>R@e0*VPVh%*62o!p^Sab7JQgd#xrDdk(q!h1YDB@#afDpfg^%DyU^ovS! zQj7J$VWd}3d5Z;PIV&j4*%%lYI2c$Mc^HcX7#J9mK>-L0GmxJ^Vdl)h!0=g)fq|ih zA&X%FV+!L!##+W2#$w(QrUlF?ObZ#C7;6|ozVj;r8CRtel98$aa-IU%JqpQ*d3pII z3Q4I7IjLzS3Q0MMdD(g^S#I$p=cFd)rKZHEB$g!JVopyjDFRt}i$5$eCo?4x6bi0I zMfpXVOt)ByQ*+Y5rX##o#LvLMaEl9U2soTLLGI*WU|`^3WMSlDWMQllhKDvn6;3Y} z34#(2vtJQA0|P^qCdd=;Si$F$A|a4IrXn#=n1l49JCPq^IovRiyNf{Sq)GzKeQ+g& z9SQPhl^V#Akn}>d3qcyuT?mPZc!)`&Ab)_JCyM4gh`gUBdl4w8i$K}Bh#RCDlyr*3 zK`coSAq67fNf2Tg*wdgS2THKTAgvq>e2g3{9Be#nMIdodf@e#~O3X{oha>|waH1}T oq;O7OPzncSJ+NYknFuRDrr+YQf#eZ8P$Dh{B}E<v9!4=H0I`z&aR2}S diff --git a/MisplaceAI/rules/__pycache__/models.cpython-310.pyc b/MisplaceAI/rules/__pycache__/models.cpython-310.pyc index 2824feb030c55b134bd72336e3f50b46a45a196a..8b2afb9c30caa32b45e9aa9b932de5cb8dc483e2 100644 GIT binary patch literal 1176 zcmd1j<>g{vU|{&I=$cl{#K7<v#6iX^3=9ko3=9m#K@1EGDGVu$ISjdsQH+crHd78$ zE^`z!BZE6b3Udle3quM^Dq|K)GjkNHJ3|U<3R??93L8{}&7C2IJ%yu%A%z1f!k)qz z%%I8j5@eeu<1Mz_{FKz3Vok<dETP4zMadvxWDIl2XC(#(hE#?q#uSDqrc~xEmK4Sm zrWEF0rYP1FmK4?&hA6fawqOQL_FEi2`N@eTnfZAy|1&T!gsfz`#g&^_5ucNqmtK-_ zi>)*-v#>NZnGwkn5F3P@85kHovoSC*)G%Z*1T(B;^jpbzizP2HHx)z|r{<)sWGE6~ zU|{$qqMuk$pkGv)lUl3~@qk`I<t_I3_~MeH_;_}ZIZO-;3~Y>50x;L;K_&fGvftu} zj|Z6=AAgH0K0Y@;r8FlsKK>R@e0*VPVh%*62&DfOr*lSPky~bJPD&9k0|P^m5QqS| z0PGA1AqWy<Wnf?c1r^x&91MJnJWNF(F`VI91X8WZev8GkBsI4P<mr$ikh^gPOc5x- z6mf$r;{g$TAOhhOuq!~$1G`h0fq|il2V^wd+eIR%8o*k?x}+Ew7{G>0f>eNbI9*+A zhn5ObSyP#_*ismKnWET1sUnpF&gV>JUci;gy^t}A2b5A;7@~MnSb`ZeS#PlfmFA>s zGT!3M&x=n<%}Fi6Ob(#X2PKDMkT*feA&YS~Lki<ureFq5Cci3H1+WoJw^%Yk!3Xv; zNF^jj5MBcXh$tu<fz0D$tl|M_g9nKwUlB;MNDSm}aS#Dgc8kj`zbG{`J<mI}vWS_1 zf#DXrvtzKcql@b;meS(Xq9RZT70H4m_<a-eDns%?BqWi5qWl(T4m1N4<4!Nq3=9k) zXBLA3o`XSvQGk&Plx9HUewrM&I8(9`^V0M6Qj#DEQ3}dW&d)0;%1qKrEG@~<gQR?j u8(Dop`52t$APxkXgcKYg%ZfnJeT%~elH%<^Sq^MJ4+9UQ5R(v_7#jeJd*q1# delta 391 zcmbQixs-)3pO=@5fq{YH35RRiJBEpTG8Re<3=Am@DU3M`xr|Yaj12A!DNHHMEet8l zsf<}n&CF5EDJ;PZnyfED+B6w&vE}Bcq~;VSPZX4NHDX|3NM(p(Oks#(N@dPsNnr%( z?PZE$O=VudwvZu;9ZGYgu%xiIFhp^tumv+{vfp9}D$PlK`JaJ-Aw+ZH8$G3BHU<WU z8ip)}U<OS_KTXD4EP08!sUW&IH79K)LlNg>8%8A=4h9B>VkQO#1~$ej9*_}w@$tna zMe*@|np~6X7)|Th85kIDafYN;l(=Q4=A_)>OwP|s$t=mt&%4Ezm<*zexEUB2ia1dT zP>>WcGcYiKa50F-!NAAJ!N|eL!vq%h(`32DnUa;5m!7Yel2in8!Yx)`P(XmCVdg2r dj0PETi^C>2KczG$)s7M5Y!(Iv1|9}6CIC3eRYw2- diff --git a/MisplaceAI/rules/__pycache__/models.cpython-39.pyc b/MisplaceAI/rules/__pycache__/models.cpython-39.pyc deleted file mode 100644 index 3f0a1653bc660437e4e5e302b81d9b463a2ebb6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 630 zcmYe~<>g{vU|<N??~?Y8fq~&Mh=Yt-7#J8F7#J9el^7TpQW#Pga~N_NqZk<(+!<1s zQkYv9QkYX2vzVHhqnJ}zf*CYfUxIXNGTvg#%}+_qDNY7)kulsrBL)VBRE8+V6ox3K zROT#}6h@GNy-ZQ8smu%57BWP!Lurl_mK4?&hA7SywqOQL_FF7Lr8%iD|1&T!glIC| z;>t~|h|fvQOE1Z|#a5b^Sy-By%!p(mhz-KdAWyI{Ffi0GWHAIYXfpa~GTvgzOUz9L z(Z#7bX)76uI2jlieq}ma#e^2878S?16{V)6#Q5i>x)f!WrN+4ACzs}?=9Lu3xMk)g z<|tIACKknHrljVTWTs^%CzfR9=jrCB>E>n@7vv-+r>5v;mZatu$M{0|j-D|^r8%j^ zF_4haE2zB19v@#^QWPK00dgD@$kmKhJRtAsK_&b&xo&a9$Ag?1AAgH0K0Y@;r8Fls zKK>R@e0*VPVh%*62&DHGr*lSPky~bJPD&9w0|Ub?&XClK5-|T3XL5dCN+!sKx7ZSs zL39x}0|UgvU|&NBP+BNrW?*0d;bM@hI2idDIT$&Zz@mPdEVnpQvJ&&s^Yv1aAeOTF fg5nu04KV{0h7cCWv|AiD5GUJ#BB2=MP97!zpJ19U diff --git a/MisplaceAI/rules/__pycache__/urls.cpython-310.pyc b/MisplaceAI/rules/__pycache__/urls.cpython-310.pyc index 0afb21aa3fd21a6317c48e6bf1b3afcfbcfb2731..9b94bafd72cfdc090116bbe497c908634a18faf0 100644 GIT binary patch literal 915 zcmd1j<>g{vU|{%h!8PqCGXuk85C<9aGcYhXFfcF_UtnNhNMT4}%wfo7jACSDNMTB0 z&SA=Bj$+PbiDJoRjbhDZi(<=Vk7Cc|h~miQjN;7YisH)Uj^fVciQ>uSjpAiwNMVWM zOJz)9O=V7DOJVP2PGwBtNM%XkgtEC(SyQ;7Y@SrM6kaHsFO@xoAIcU;<wy~PvV~GP zQ-q;xkyNe}Q7Bt1l{-Zo%9cpwNs)xIrBZoQq(SU-?kN5gnP3J@*_R-{YBJtpDM&2I zc*)4Xz@W)}iz_FyxFo))G$*zA7Dr-A3Yd9|BRv%&c#FFzH8;O36(Y=CT9A@h0_6xL zrsQVk#e?+Z<R>SVWaj7H;zNjKmZaw1;!I7+EQtp*xl>YeQcF@H9A2<ETpK@B4lck7 zaS%fh3&@p>w^;HLb5pBW5>rz2i&z;L7^?VeGV@BTK&s+1Q|$DM*g%4!5Vz{1$+3gw zAWlb@;{eGCBU}x0fqoGuNQNIF1M-A^5hwvwNq}7s764lXVnLk24bmh9^$kcEVjqYF zQNROIpa9knw+d_qj0aK13sR;8H3udIF$>0nDCA>cVDQrvxW%24m6(^FuUA@>Q(VNv zz`$^ewJbBWycisS;2;ABQ4uICia?$Q`w#4$B9NntKn^PcSz81$@)k#8K|wqy&~I^< z7UdKqmXxFx<rS}FC=z2}fDpfg^b-pT^g&TjtPiqQub}c4hfQvNN@-529Vm4b%P}x8 U@GzJ#vas?n@h~DmD39X{0G{9f+W-In delta 228 zcmbQt{+U@jpO=@5fq{YH8Ha1yZbk-%#~=<e=44=CaA06yC@z?&t;xp7kirthF|o)- zE}b=sGesbnK~wN0NVz8CEtZ1Bl8l#(3=9mKtdm6;ttYQ$R16icDK06pDk{xMjn7NW zO|{c6Vq;)ns1h$q&CM@M)yI%!2g{Zgq$HMLk>y}uVDQuAntYqdXtFl56q^7814EI( bWN&6QF(C#91|9|(MiwR>CLTrz<oFE$VR17B diff --git a/MisplaceAI/rules/__pycache__/utils.cpython-310.pyc b/MisplaceAI/rules/__pycache__/utils.cpython-310.pyc deleted file mode 100644 index 6ec72ae2621dab37e52edba017451b6382a1ca8f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2064 zcmd1j<>g{vU|>kv?~?YEm4V?gh=Yuo85kHG7#J9e<ro+kQW#Pga~N_NqZk<(+!<1s zQkYv9Qka{WqL@-xf*CYfUotW<FlaK~VhJkENlivJ2E=B98Rf#jz>vxi#hAhn#gxLB z!qmbL#hk*N!qUPJ#gf9B!q&nN#hSvN!qLJI#g@XE!qvhM#h${Q!qdVK#gW3B!q>tO z#hJn%%%CZ7iw9&=v2S8tVtQ&(GRS(6O&|<ngKTsLIi8P!fuV+B0mDLu8ishr6oz01 zO-4UWrdwP&`H3m<MWs2Z#kW|&)Jn!%EXAogX)76uI2afhehKR*78K}%rSwZnGINUc z3My}L#K&jmWtPOpvw#c-*}}(IC5r46J(vm%H-Q2a>?RRVfP#Hr!jQ$dfN3Fv3qvzw zFoPy@m7-66Vv0gZMydkXVuiG#{9G_MC9xziDX}<J55-uJ1K1cC7=l5r0mV}$Lk&YL zcP(QLV-`aVV-|>JN@1A8G?}T83FP&aOt(0b^Yc<NOEUBGZm}gMgJ@0WTP%5rxv51w z3=9lKyr7t5y2YetaEk@x2ykdX2u=nDhFe@VDVfP7$@#ejb|8K+$PID~RoW<lfCybZ zo1FaQ#GGO~J%m0@<|2@;TkQEsS*gh-#kZIfb8?D69)Z{c3jZRI;YFZeW(Ro=q+W!n zN*sqR$smQG$OmDNL97f64B)`lV_;wa1#=ct4Py;c4RZ?P9HtbeUZxnPT9#T?m`Dvn z7IO`w4MPfZFoPybl}vDAS*k)JI1&^}@=;@;h#MS2MWCd0iz6j9EwMDGq}WfB1?;IJ zAy6O*loq5UmZZk#7sV$Rr6!i77J(e3$yNmN3|J>3u!{shI@lrVQqV%+7H4r{St>Z8 zf~+kD`BREXgt1BtB_!aAl0g9l3Iq@au|a_W4hT^40Of=l#uP?Lh8o5cCNRk?$*_=_ zkpYs8tK=P1QWO#u@>0uj_#NbFgwy>r8H>arp#k<7$S3F?;7Ck?WHgW@B(*T1Wf_<X z6vu;n3Q7oI$47v(aTdb@#u~<jjI~TPOvPGNVkJyj%%C)$!r04L%Ur{p#ZtqZ#hSu2 zhY6&jh5?iZY#72BYM6o<SQtu}ve+s$85uw@5**XzU>Q(+2c?!2Wu_wHJgHJ4v!qx7 zlq>XBGD0E@6#jmi?BJNZ#hjj6f{1TKh}`0F$xkiz%P(=Q$Sf|=WC2I`ElyBIiU*kn zj_fE7Fh4&j3nQ@7Q%k^+4Jspw#TXbEc$ioinHc#P`51W^S(y3$u`x3JXF(4@m@bq6 z1jRnQ%+Ub_AR{P9${317Ky(eGEkg-o785Apg92~?%R&Z5h7_h?FxF(Qk_t-A&Bq=j zMUo5*41Sud;IKdh!!5S7%$$<cqFZb!sX3`7shUh+?-$8{$`w#tLc9yg*|)fhz@~#e zs{qR3AbBn(4n{FXmVZ?eD5)DsDN6AND(T@q1f|Rph8hM)i3%=H8LRkxGK)(T5_59E z$+=h$RMh2U7MFk>ev7RvF{d=O7+cA6iwmR-?7Ct#kn=#H$%jz@WfnuCtJqJI3sgCP zqBuVO7FT?HZhlH>PHKGoEuQ%J!qUVXh)fZv&IOlh;Cxdg0SXye5CIB<B4rQ@6cCVr z0<%D&RRr=S2p5Ag5eEYYBL_1F3kNF)3kMGm8(7v)llc}~ZhlH?PBA1xK+2Jfftz)U S!v^9uJ5ca}3lts(AtnG&-M&Bo diff --git a/MisplaceAI/rules/__pycache__/views.cpython-310.pyc b/MisplaceAI/rules/__pycache__/views.cpython-310.pyc index 5175198a3ed3629c01840fa93443e5fea63fd89f..35a893392adfc093578df244f706a42561585443 100644 GIT binary patch literal 3741 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 BCnW#? delta 916 zcmbO$yNOpjpO=@5fq{YH1&3?eCUypf#~=<eW@2DqaA06yD9)Lvt>Vj^!ji+5%O1tf z$dJOE!kWX8%NfPV$dJO8!rsCV#g)R5!r8(Q#ht>H!rj6U#goF5!rQ_S#hb#H!r#IW z#g`%g<qHNgXbMeY)K}y2D$dUfN-ZwP&nr%S$;iOKpvicPC8#tfRg>`+2Z(mdFUp;q zz$7(!FQXG9`y?hM4o(IJhLsFOypy$<RQTCIZew9!VBlgDU@YRF9Lprj7sSB8P{WYL zkiwYFR3taKgGouCgsleTVwM`lOvYyBV1~(Sn8MjO7#J9e1SWGbE7<aYm_i_e7f$do zFfiQWNK8qIFDlJR1qD;F0N5ffMh-9(U}9quU@Q`wypBs|auc&UD@$HtZtCPU%xWI& zAX7kLx01OCWcDpikQwnH`65Y>9J)2>sU=`*#9;p60r^J;Y_tiBHUCOBzanvvMvxPV z1Si+A$P0lCTFF)f@_iB5LgC5lS;S1GK+4d}=PpXk%`Zy@TLF^)4Dt>SBg7{Hj75r* zf3e6i7EE5iDZ!{U*^pI7sD!hIA%(4mxrQl)eGU^i%;H%UxZrNkoZQJO$0$8{1*=>= zIN(68(d0sQ4agmkFb1(iK!hlWU<MHqAOaLZnk?WDDv|~XfD9=D8;9<J(t?!45{L&B zL16?^1o8kMBL^=d2O}S&5EB=Z5Mz<b<aJ!K#<C#8<UoWxhyZ!H2-U<QkSY2gNhJmb z20u;y$%(v*^|!cFvJ&&s^Yt=HN(zctK`DSOH$Npcrx+Z<w^-Bii*k#Lz`o)KX$Hkt zkqn3hGOtJl#6nmEW<k=CO>TZlX-=vgD2*3`!a{_BhY<uhm^fHCcsN)%*m?K>l~bd+ diff --git a/MisplaceAI/rules/forms.py b/MisplaceAI/rules/forms.py index f45d524..2260014 100644 --- a/MisplaceAI/rules/forms.py +++ b/MisplaceAI/rules/forms.py @@ -1,25 +1,17 @@ from django import forms -from .models import Rule +from .models import Rule, Location, Item class RuleForm(forms.ModelForm): class Meta: model = Rule - fields = ['name', 'condition', 'action'] # Include all the fields you want from the model + fields = ['user', 'item', 'locations'] - def clean_name(self): - name = self.cleaned_data.get('name') - if not name: - raise forms.ValidationError("The name field cannot be left blank.") - return name - - def clean_condition(self): - condition = self.cleaned_data.get('condition') - if not condition: - raise forms.ValidationError("The condition field cannot be left blank.") - return condition +class LocationForm(forms.ModelForm): + class Meta: + model = Location + fields = ['name'] - def clean_action(self): - action = self.cleaned_data.get('action') - if not action: - raise forms.ValidationError("The action field cannot be left blank.") - return action +class ItemForm(forms.ModelForm): + class Meta: + model = Item + fields = ['name'] diff --git a/MisplaceAI/rules/migrations/0001_initial.py b/MisplaceAI/rules/migrations/0001_initial.py index 30eb830..c88616d 100644 --- a/MisplaceAI/rules/migrations/0001_initial.py +++ b/MisplaceAI/rules/migrations/0001_initial.py @@ -1,6 +1,8 @@ -# Generated by Django 3.2 on 2024-05-15 15:06 +# Generated by Django 3.2 on 2024-05-15 20:43 +from django.conf import settings from django.db import migrations, models +import django.db.models.deletion class Migration(migrations.Migration): @@ -8,16 +10,31 @@ class Migration(migrations.Migration): initial = True dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( - name='Rule', + name='Item', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, unique=True)), + ], + ), + migrations.CreateModel( + name='Location', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=255, unique=True)), - ('condition', models.TextField()), - ('action', models.TextField()), + ], + ), + migrations.CreateModel( + name='Rule', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='rules.item')), + ('locations', models.ManyToManyField(to='rules.Location')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], ), ] diff --git a/MisplaceAI/rules/migrations/__pycache__/0001_initial.cpython-310.pyc b/MisplaceAI/rules/migrations/__pycache__/0001_initial.cpython-310.pyc index 3cbc07602d64ef4ef2f9a6599d6144f132f80e71..41f9305df4cc8cb517a5b241eaef71e4d84b52e9 100644 GIT binary patch literal 1095 zcmd1j<>g{vU|>j+b4^>v%E0g##6iX^3=9ko3=9m#RtyXbDGVu$ISjdsQH+crHd78$ zE^`!fE=v>(n9rQU8pY<$kiwF}+QN{+n#!ET-pm}up28f=pvm?UWU40PEso;Ul9J54 z^kPk>TU@!B=|zbpnfZCex7c#?Q&Mw^{gOfIkTDO;1&<gQ7*ZLc7*iOcm{J&{m{VC6 zu%@zQu`gsyXN=-VWy#`9VM<|6<;vntVN78Jxgv$FmnDrUg*}xkizkI6g)xP*mnn@Y zohgN@mkF$%n?&_ISk?2Ua%J(R@@4U7ab*c)38wI+@Pq705lH3A5=s$F5$a`3V@eT5 zRRwZ6L|r-y$UW)IQNk%A!3>(Bw>W)a!4h(d#j_+e_ZCxTirXzFPZv#=TRe%SCHe8m zMX8A;sVTR(3yLyx6N@V2vr{W?aTcc*WhUliR;AwJDN8L%$}dih&r8fry~P3|U;bxc zU;qbtVnuvTYF>It#x1teyv)MVR86KLUIqq+TWo2WsW~adw>W(Alflt)izTQuC-oLf zW=U#p6<1MdPHM3ph^@(Vi!(njJ|#6LwIuZxQ%U|U&K#(g;wnCfI+z)nj74G~!&yp; zQ;YmGMQ(A#gIyILe~T+VJ~uz5G$%Da{uWPsd|_!~4#=hP@weDB^D;{^6LX4~L0UzM z%M%L<5|eUL<5N-#Qu9($^O7rzm_Snej-eqQ@u9)4LGix+F0MYec;G5BQ;TnLJA(tt z7nHV&Kxz3Fk5gv4V`)jg8_27-IGr;Riy%xcxBQ~i%=A3()XH0IDOrhm>G`*qQj%_Q zfPI;npLdJh*)iDJ(Z%%^zi(n*Wk^1VgzC*NNQEZkl?+8%3=9zBm#ThZL4iIvJoFK1 zUEjdKz%U-_L%o8^B31?l1`bdrGXgVMn7Ei({<E+$F^Vt=Ff#pP`NzVJ&X!>0W0YcK z`or>%<0DwVCdVyqh^zII^YhXmp~DFkNl5|+BqRhR;evXQJf{cqVUY^R<sjD}`~YET agTk7_1`=C#j3Bpy9L&Qcz{tTU#tZ;D1~W4N delta 458 zcmX@kv4)i|pO=@5fq{V``MgV7DAPnfnR+z_28I-d6viBeT*fHIT&5@{Mh16=6s8pB z7KRk&RK_gkX67j76qaBHP1ctnt(r`?xN<YoixNvR^Ye;tvE}Bcq~;VSgQSo#Cj$e6 z0|Ns?aSQ_kLn=cQV+unQQwn1gb2>v5ODby?8_0@O_OL9D6vh<B6qXd$6t-TLG^P~x zRQ4>+6p$!qFH;&*3Rf!o0<MJ&X-p~HFjhKC3QsRnI%5=f3U4rjCf_YiUzmeJZm|TF z=A=$u#+aabi!(VtFC`P?&RcAW$sk&jsfdSxf#DWgT4riaO0l0N&*WkzU8P$*DX9gi zc`2!R$(gCeMNA+i+|EU*i6yDNAnz41PkzTF%gPy&T2V6Dky(UImVtqxNOp2Gvvv^+ z0|Nsa$fFVr3=A9$EKDp+EG$fnB1{5|T#Q_dOn+GZar_6X^3!Cw#hH?on3tZfmy!gw nrHC115Xgukh>;*Jm?Z~t1BXp+eoARhsvRT90+2ERCNX9JCFf^{ diff --git a/MisplaceAI/rules/models.py b/MisplaceAI/rules/models.py index a11306e..cef49b1 100644 --- a/MisplaceAI/rules/models.py +++ b/MisplaceAI/rules/models.py @@ -1,9 +1,22 @@ from django.db import models +from django.contrib.auth.models import User -class Rule(models.Model): +class Location(models.Model): + name = models.CharField(max_length=255, unique=True) + + def __str__(self): + return self.name + +class Item(models.Model): name = models.CharField(max_length=255, unique=True) - condition = models.TextField() - action = models.TextField() def __str__(self): return self.name + +class Rule(models.Model): + user = models.ForeignKey(User, on_delete=models.CASCADE) + item = models.ForeignKey(Item, on_delete=models.CASCADE) + locations = models.ManyToManyField(Location) + + def __str__(self): + return f"{self.item.name} Rule" diff --git a/MisplaceAI/rules/templates/rules/add_rule.html b/MisplaceAI/rules/templates/rules/add_rule.html index 078583c..66bcc05 100644 --- a/MisplaceAI/rules/templates/rules/add_rule.html +++ b/MisplaceAI/rules/templates/rules/add_rule.html @@ -1,11 +1,15 @@ -<h1>Add Rule</h1> -<form method="post"> - {% csrf_token %} - <label for="name">Name:</label> - <input type="text" id="name" name="name"><br> - <label for="condition">Condition:</label> - <textarea id="condition" name="condition"></textarea><br> - <label for="action">Action:</label> - <textarea id="action" name="action"></textarea><br> - <input type="submit" value="Submit"> -</form> \ No newline at end of file +{% extends 'core/base.html' %} +{% load static %} + +{% block title %}Add Rule{% endblock %} + +{% block content %} +<div class="container mt-5"> + <h1>Add Rule</h1> + <form method="post"> + {% csrf_token %} + {{ form.as_p }} + <button type="submit" class="btn btn-success">Save Rule</button> + </form> +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/rules/templates/rules/admin_add_item.html b/MisplaceAI/rules/templates/rules/admin_add_item.html new file mode 100644 index 0000000..39aca1d --- /dev/null +++ b/MisplaceAI/rules/templates/rules/admin_add_item.html @@ -0,0 +1,58 @@ +{% extends 'core/base.html' %} +{% load static %} + +{% block title %}Add Item{% endblock %} + +{% block content %} +<div class="container mt-5"> + <h1>Add Item</h1> + <form method="post"> + {% csrf_token %} + {{ form.as_p }} + <button type="submit" class="btn btn-success">Add Item</button> + </form> + + <h2>Existing Items</h2> + <table class="table"> + <thead> + <tr> + <th>Item Name</th> + <th>Actions</th> + </tr> + </thead> + <tbody> + {% for item in page_obj %} + <tr> + <td>{{ item.name }}</td> + <td> + <a href="{% url 'rules:edit_item' item.id %}" class="btn btn-warning btn-sm">Edit</a> + <a href="{% url 'rules:delete_item' item.id %}" class="btn btn-danger btn-sm" + onclick="return confirm('Are you sure you want to delete this item?');">Delete</a> + </td> + </tr> + {% empty %} + <tr> + <td colspan="2">No items found.</td> + </tr> + {% endfor %} + </tbody> + </table> + + <!-- Pagination --> + <nav aria-label="Page navigation"> + <ul class="pagination"> + {% if page_obj.has_previous %} + <li class="page-item"><a class="page-link" href="?page={{ page_obj.previous_page_number }}">Previous</a> + </li> + {% endif %} + {% for num in page_obj.paginator.page_range %} + <li class="page-item {% if page_obj.number == num %}active{% endif %}"><a class="page-link" + href="?page={{ num }}">{{ num }}</a></li> + {% endfor %} + {% if page_obj.has_next %} + <li class="page-item"><a class="page-link" href="?page={{ page_obj.next_page_number }}">Next</a></li> + {% endif %} + </ul> + </nav> +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/rules/templates/rules/admin_add_location.html b/MisplaceAI/rules/templates/rules/admin_add_location.html new file mode 100644 index 0000000..2df1d82 --- /dev/null +++ b/MisplaceAI/rules/templates/rules/admin_add_location.html @@ -0,0 +1,57 @@ +{% extends 'core/base.html' %} +{% load static %} + +{% block title %}Add Location{% endblock %} + +{% block content %} +<div class="container mt-5"> + <h1>Add Location</h1> + <form method="post"> + {% csrf_token %} + {{ form.as_p }} + <button type="submit" class="btn btn-success">Add Location</button> + </form> + + <h2>Existing Locations</h2> + <table class="table"> + <thead> + <tr> + <th>Location Name</th> + <th>Actions</th> + </tr> + </thead> + <tbody> + {% for location in page_obj %} + <tr> + <td>{{ location.name }}</td> + <td> + <a href="{% url 'rules:edit_location' location.id %}" class="btn btn-warning btn-sm">Edit</a> + <a href="{% url 'rules:delete_location' location.id %}" class="btn btn-danger btn-sm" + onclick="return confirm('Are you sure you want to delete this location?');">Delete</a> + </td> + </tr> + {% empty %} + <tr> + <td colspan="2">No locations found.</td> + </tr> + {% endfor %} + </tbody> + </table> + <!-- Pagination --> + <nav aria-label="Page navigation"> + <ul class="pagination"> + {% if page_obj.has_previous %} + <li class="page-item"><a class="page-link" href="?page={{ page_obj.previous_page_number }}">Previous</a> + </li> + {% endif %} + {% for num in page_obj.paginator.page_range %} + <li class="page-item {% if page_obj.number == num %}active{% endif %}"><a class="page-link" + href="?page={{ num }}">{{ num }}</a></li> + {% endfor %} + {% if page_obj.has_next %} + <li class="page-item"><a class="page-link" href="?page={{ page_obj.next_page_number }}">Next</a></li> + {% endif %} + </ul> + </nav> +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/rules/templates/rules/confirm_delete.html b/MisplaceAI/rules/templates/rules/confirm_delete.html index 6e2fa10..f1ea751 100644 --- a/MisplaceAI/rules/templates/rules/confirm_delete.html +++ b/MisplaceAI/rules/templates/rules/confirm_delete.html @@ -1,18 +1,13 @@ -<!DOCTYPE html> -<html lang="en"> +{% extends 'core/base.html' %} +{% block title %}Delete Rule{% endblock %} -<head> - <meta charset="UTF-8"> - <title>Delete Rule</title> -</head> - -<body> +{% block content %} +<div class="container mt-5"> <h1>Are you sure you want to delete "{{ rule.name }}"?</h1> <form method="post"> {% csrf_token %} - <input type="submit" value="Confirm Delete"> - <a href="{% url 'rules:list_rules' %}">Cancel</a> + <button type="submit" class="btn btn-danger">Confirm Delete</button> + <a href="{% url 'rules:list_rules' %}" class="btn btn-secondary">Cancel</a> </form> -</body> - -</html> \ No newline at end of file +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/rules/templates/rules/confirm_delete_item.html b/MisplaceAI/rules/templates/rules/confirm_delete_item.html new file mode 100644 index 0000000..ae87987 --- /dev/null +++ b/MisplaceAI/rules/templates/rules/confirm_delete_item.html @@ -0,0 +1,15 @@ +{% extends 'core/base.html' %} +{% load static %} + +{% block title %}Confirm Delete{% endblock %} + +{% block content %} +<div class="container mt-5"> + <h1>Are you sure you want to delete "{{ item.name }}"?</h1> + <form method="post"> + {% csrf_token %} + <button type="submit" class="btn btn-danger">Delete</button> + <a href="{% url 'rules:admin_add_item' %}" class="btn btn-secondary">Cancel</a> + </form> +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/rules/templates/rules/confirm_delete_location.html b/MisplaceAI/rules/templates/rules/confirm_delete_location.html new file mode 100644 index 0000000..5a2665c --- /dev/null +++ b/MisplaceAI/rules/templates/rules/confirm_delete_location.html @@ -0,0 +1,17 @@ +{% extends 'core/base.html' %} +{% load static %} + +{% block title %}Confirm Delete{% endblock %} + +{% block content %} +<div class="container mt-5"> + <h1>Delete Location</h1> + <p>Are you sure you want to delete "{{ location.name }}"?</p> + + <form method="post"> + {% csrf_token %} + <button type="submit" class="btn btn-danger">Delete</button> + <a href="{% url 'rules:admin_add_location' %}" class="btn btn-secondary">Cancel</a> + </form> +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/rules/templates/rules/edit_item.html b/MisplaceAI/rules/templates/rules/edit_item.html new file mode 100644 index 0000000..9960b94 --- /dev/null +++ b/MisplaceAI/rules/templates/rules/edit_item.html @@ -0,0 +1,16 @@ +{% extends 'core/base.html' %} +{% load static %} + +{% block title %}Edit Item{% endblock %} + +{% block content %} +<div class="container mt-5"> + <h1>Edit Item</h1> + <form method="post"> + {% csrf_token %} + {{ form.as_p }} + <button type="submit" class="btn btn-success">Save Changes</button> + <a href="{% url 'rules:admin_add_item' %}" class="btn btn-secondary">Cancel</a> + </form> +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/rules/templates/rules/edit_location.html b/MisplaceAI/rules/templates/rules/edit_location.html new file mode 100644 index 0000000..a772b20 --- /dev/null +++ b/MisplaceAI/rules/templates/rules/edit_location.html @@ -0,0 +1,16 @@ +{% extends 'core/base.html' %} +{% load static %} + +{% block title %}Edit Location{% endblock %} + +{% block content %} +<div class="container mt-5"> + <h1>Edit Location</h1> + <form method="post" novalidate> + {% csrf_token %} + {{ form.as_p }} + <button type="submit" class="btn btn-primary">Update</button> + <a href="{% url 'rules:admin_add_location' %}" class="btn btn-secondary">Cancel</a> + </form> +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/rules/templates/rules/list_rules.html b/MisplaceAI/rules/templates/rules/list_rules.html index 2be2586..9ecc344 100644 --- a/MisplaceAI/rules/templates/rules/list_rules.html +++ b/MisplaceAI/rules/templates/rules/list_rules.html @@ -1,28 +1,44 @@ -<!DOCTYPE html> -<html lang="en"> +{% extends 'core/base.html' %} +{% load static %} -<head> - <meta charset="UTF-8"> - <title>List of Rules</title> -</head> +{% block title %}List of Rules{% endblock %} -<body> +{% block content %} +<div class="container mt-5"> <h1>List of Rules</h1> - <ul> - {% for rule in rules %} - <li> - {{ rule.name }} - {% if rule.name %} - : <a href="{% url 'rules:get_rule' rule.name %}">View</a> - | <a href="{% url 'rules:update_rule' rule.name %}">Edit</a> - | <a href="{% url 'rules:remove_rule' rule.name %}">Delete</a> - {% else %} - : Name unavailable - {% endif %} - </li> - {% endfor %} - </ul> - <a href="{% url 'rules:add_rule' %}">Add New Rule</a> -</body> - -</html> \ No newline at end of file + <table class="table"> + <thead> + <tr> + <th>Item</th> + <th>Locations</th> + <th>Actions</th> + </tr> + </thead> + <tbody> + {% for rule in rules %} + <tr> + <td>{{ rule.item.name }}</td> + <td> + {% for location in rule.locations.all %} + {{ location.name }}{% if not forloop.last %}, {% endif %} + {% endfor %} + </td> + <td> + <a href="{% url 'rules:get_rule' rule.id %}" class="btn btn-info">View</a> + <a href="{% url 'rules:update_rule' rule.id %}" class="btn btn-warning">Edit</a> + <form action="{% url 'rules:remove_rule' rule.id %}" method="post" style="display:inline;"> + {% csrf_token %} + <button type="submit" class="btn btn-danger">Delete</button> + </form> + </td> + </tr> + {% empty %} + <tr> + <td colspan="3">No rules available.</td> + </tr> + {% endfor %} + </tbody> + </table> + <a href="{% url 'rules:add_rule' %}" class="btn btn-primary">Add New Rule</a> +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/rules/templates/rules/rule_detail.html b/MisplaceAI/rules/templates/rules/rule_detail.html index 5b35231..da291d5 100644 --- a/MisplaceAI/rules/templates/rules/rule_detail.html +++ b/MisplaceAI/rules/templates/rules/rule_detail.html @@ -1,4 +1,18 @@ -<h1>Rule Details</h1> -<p><strong>Name:</strong> {{ rule.name }}</p> -<p><strong>Condition:</strong> {{ rule.condition }}</p> -<p><strong>Action:</strong> {{ rule.action }}</p> \ No newline at end of file +{% extends 'core/base.html' %} +{% load static %} + +{% block title %}Rule Detail{% endblock %} + +{% block content %} +<div class="container mt-5"> + <h1>Rule Detail</h1> + <p><strong>Item:</strong> {{ rule.item.name }}</p> + <p><strong>Locations:</strong> {% for location in rule.locations.all %}{{ location.name }}{% if not forloop.last %}, + {% endif %}{% endfor %}</p> + <a href="{% url 'rules:update_rule' rule.id %}" class="btn btn-warning">Edit</a> + <form action="{% url 'rules:remove_rule' rule.id %}" method="post" style="display:inline;"> + {% csrf_token %} + <button type="submit" class="btn btn-danger">Delete</button> + </form> +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/rules/templates/rules/update_rule.html b/MisplaceAI/rules/templates/rules/update_rule.html index 0a5843e..4d3ca50 100644 --- a/MisplaceAI/rules/templates/rules/update_rule.html +++ b/MisplaceAI/rules/templates/rules/update_rule.html @@ -1,6 +1,15 @@ -<h1>Update Rule</h1> -<form method="post"> - {% csrf_token %} - {{ form.as_p }} - <button type="submit">Save changes</button> -</form> \ No newline at end of file +{% extends 'core/base.html' %} +{% load static %} + +{% block title %}Update Rule{% endblock %} + +{% block content %} +<div class="container mt-5"> + <h1>Update Rule</h1> + <form method="post"> + {% csrf_token %} + {{ form.as_p }} + <button type="submit" class="btn btn-success">Save Changes</button> + </form> +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/rules/urls.py b/MisplaceAI/rules/urls.py index a556df6..345f00c 100644 --- a/MisplaceAI/rules/urls.py +++ b/MisplaceAI/rules/urls.py @@ -1,12 +1,23 @@ from django.urls import path -from .views import list_rules, add_rule, get_rule, remove_rule, update_rule +from .views import ( + list_rules, add_rule, get_rule, remove_rule, update_rule, + admin_add_location, admin_add_item, edit_item, delete_item,edit_location, delete_location +) app_name = 'rules' urlpatterns = [ path('', list_rules, name='list_rules'), path('add/', add_rule, name='add_rule'), - path('<str:rule_name>/', get_rule, name='get_rule'), - path('remove/<str:rule_name>/', remove_rule, name='remove_rule'), - path('update/<str:rule_name>/', update_rule, name='update_rule'), + path('<int:rule_id>/', get_rule, name='get_rule'), + path('remove/<int:rule_id>/', remove_rule, name='remove_rule'), + path('update/<int:rule_id>/', update_rule, name='update_rule'), + path('admin_add_location/', admin_add_location, name='admin_add_location'), + path('admin_add_item/', admin_add_item, name='admin_add_item'), + + path('edit_item/<int:item_id>/', edit_item, name='edit_item'), + path('delete_item/<int:item_id>/', delete_item, name='delete_item'), + path('edit_location/<int:location_id>/', edit_location, name='edit_location'), + path('delete_location/<int:location_id>/', delete_location, name='delete_location'), + ] diff --git a/MisplaceAI/rules/utils.py b/MisplaceAI/rules/utils.py index fd8fbcd..3de4b76 100644 --- a/MisplaceAI/rules/utils.py +++ b/MisplaceAI/rules/utils.py @@ -1,40 +1,38 @@ +from django.core.exceptions import ValidationError from .models import Rule -class RulesManager: - def __init__(self): - self.rules = self.load_rules() +def validate_rule(rule): + """ + Validates a rule to ensure there are no conflicting rules. + """ + existing_rules = Rule.objects.filter(item=rule.item).exclude(id=rule.id) + for existing_rule in existing_rules: + if set(existing_rule.locations.all()) & set(rule.locations.all()): + raise ValidationError(f"A rule for {rule.item.name} already exists for one or more of the selected locations.") + +def check_item_location(item, location): + """ + Checks if the given item is allowed at the given location based on the existing rules. + """ + rules = Rule.objects.filter(item=item, locations=location) + if not rules.exists(): + return False + return True - def load_rules(self): - """Load the rules from the database.""" - return {rule.name: {'condition': rule.condition, 'action': rule.action} for rule in Rule.objects.all()} +def get_item_location_rules(item): + """ + Retrieves all the locations where the given item is allowed based on the existing rules. + """ + rules = Rule.objects.filter(item=item) + allowed_locations = set() + for rule in rules: + allowed_locations.update(rule.locations.all()) + return allowed_locations - def save_rule(self, name, condition, action): - """Save a rule to the database.""" - rule, created = Rule.objects.update_or_create(name=name, defaults={'condition': condition, 'action': action}) - self.rules[name] = {'condition': condition, 'action': action} - - def add_rule(self, rule): - """Add a new rule to the database.""" - self.save_rule(rule["name"], rule["condition"], rule["action"]) - - def get_rule(self, rule_name): - """Retrieve a rule by its name.""" - rule = self.rules.get(rule_name) - if not rule: - try: - rule_obj = Rule.objects.get(name=rule_name) - rule = {'condition': rule_obj.condition, 'action': rule_obj.action} - self.rules[rule_name] = rule - except Rule.DoesNotExist: - return None - return rule - - def remove_rule(self, rule_name): - """Remove a rule by its name.""" - if rule_name in self.rules: - del self.rules[rule_name] - Rule.objects.filter(name=rule_name).delete() - - def list_rules(self): - """List all rules.""" - return list(self.rules.values()) +def notify_user_of_misplacement(user, item, location): + """ + Notifies the user that the given item is misplaced at the given location. + """ + # This function can be implemented to send notifications to the user. + # For now, we will just print a message. + print(f"User {user.username}, the item '{item.name}' is misplaced at '{location.name}'.") diff --git a/MisplaceAI/rules/views.py b/MisplaceAI/rules/views.py index 9ef5872..a97a2e7 100644 --- a/MisplaceAI/rules/views.py +++ b/MisplaceAI/rules/views.py @@ -1,7 +1,11 @@ from django.shortcuts import render, redirect, get_object_or_404 -from django.http import JsonResponse -from .models import Rule -from .forms import RuleForm +from django.contrib.auth.decorators import login_required +from django.contrib import messages +from .models import Rule, Location, Item +from .forms import RuleForm, LocationForm, ItemForm +from django.core.paginator import Paginator + +from django.urls import reverse def list_rules(request): rules = Rule.objects.all() @@ -13,40 +17,108 @@ def add_rule(request): if form.is_valid(): form.save() return redirect('rules:list_rules') - else: - return render(request, 'rules/add_rule.html', {'form': form}) else: - form = RuleForm() # An unbound form for GET requests - return render(request, 'rules/add_rule.html', {'form': form}) + form = RuleForm() + return render(request, 'rules/add_rule.html', {'form': form}) -def get_rule(request, rule_name): - rule = get_object_or_404(Rule, name=rule_name) +def get_rule(request, rule_id): + rule = get_object_or_404(Rule, id=rule_id) return render(request, 'rules/rule_detail.html', {'rule': rule}) -def remove_rule(request, rule_name): - rule = get_object_or_404(Rule, name=rule_name) +def remove_rule(request, rule_id): + rule = get_object_or_404(Rule, id=rule_id) if request.method == 'POST': rule.delete() return redirect('rules:list_rules') return render(request, 'rules/confirm_delete.html', {'rule': rule}) -def update_rule(request, rule_name): - rule = get_object_or_404(Rule, name=rule_name) +def update_rule(request, rule_id): + rule = get_object_or_404(Rule, id=rule_id) if request.method == 'POST': form = RuleForm(request.POST, instance=rule) if form.is_valid(): form.save() - return redirect('rules:get_rule', rule_name=rule.name) - else: - return render(request, 'rules/update_rule.html', {'form': form, 'rule': rule}) + return redirect('rules:get_rule', rule_id=rule.id) else: form = RuleForm(instance=rule) - return render(request, 'rules/update_rule.html', {'form': form, 'rule': rule}) + return render(request, 'rules/update_rule.html', {'form': form, 'rule': rule}) +@login_required +def admin_add_location(request): + locations = Location.objects.all().order_by('name') + paginator = Paginator(locations, 10) # Show 10 locations per page + page_number = request.GET.get('page') + page_obj = paginator.get_page(page_number) -def remove_rule(request, rule_name): - rule = get_object_or_404(Rule, name=rule_name) if request.method == 'POST': - rule.delete() - return redirect('rules:list_rules') - return render(request, 'rules/confirm_delete.html', {'rule': rule}) + form = LocationForm(request.POST) + if form.is_valid(): + form.save() + messages.success(request, 'Location added successfully.') + return redirect('rules:admin_add_location') + else: + form = LocationForm() + return render(request, 'rules/admin_add_location.html', {'form': form, 'page_obj': page_obj}) + + +@login_required +def admin_add_item(request): + items = Item.objects.all().order_by('name') + paginator = Paginator(items, 10) # Show 10 items per page + page_number = request.GET.get('page') + page_obj = paginator.get_page(page_number) + + if request.method == 'POST': + form = ItemForm(request.POST) + if form.is_valid(): + form.save() + messages.success(request, 'Item added successfully.') + return redirect('rules:admin_add_item') + else: + form = ItemForm() + return render(request, 'rules/admin_add_item.html', {'form': form, 'page_obj': page_obj}) + + + +def edit_item(request, item_id): + item = get_object_or_404(Item, id=item_id) + if request.method == 'POST': + form = ItemForm(request.POST, instance=item) + if form.is_valid(): + form.save() + return redirect('rules:admin_add_item') + else: + form = ItemForm(instance=item) + return render(request, 'rules/edit_item.html', {'form': form}) + +def delete_item(request, item_id): + item = get_object_or_404(Item, id=item_id) + if request.method == 'POST': + item.delete() + return redirect('rules:admin_add_item') + return render(request, 'rules/confirm_delete_item.html', {'item': item}) + + + + + +def edit_location(request, location_id): + location = get_object_or_404(Location, id=location_id) + if request.method == 'POST': + form = LocationForm(request.POST, instance=location) + if form.is_valid(): + form.save() + # Use the 'reverse' to correctly use named URLs with namespaces + return redirect(reverse('rules:admin_add_location')) + else: + form = LocationForm(instance=location) + return render(request, 'rules/edit_location.html', {'form': form}) + + + +def delete_location(request, location_id): + location = get_object_or_404(Location, id=location_id) + if request.method == 'POST': + location.delete() + return redirect('rules:admin_add_location') + return render(request, 'rules/confirm_delete_location.html', {'location': location}) diff --git a/README.md b/README.md index e57dc36..bb3e373 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,84 @@ # Identification of Misplaced Items +## DATABASE + +### Connect though terminal + +```bash +docker exec -it misplaceai-db-1 mysql -u root -p +``` +<br> +Then enter password + +```bash +Enter password: rootpassword +``` + +<br> +Select Database: +```bash +mysql> USE misplaceai; +``` + +### Drop all tables + +Disable foreign key checks: + +``bash +SET FOREIGN_KEY_CHECKS = 0; +``` + +Generate and execute the drop script: + +```bash +SET @tables = NULL; +SELECT GROUP_CONCAT('`', table_name, '`') INTO @tables +FROM information_schema.tables +WHERE table_schema = (SELECT DATABASE()); + +SET @tables = CONCAT('DROP TABLE IF EXISTS ', @tables); +PREPARE stmt FROM @tables; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +``` + +Enable foreign key checks: + +```bash +SET FOREIGN_KEY_CHECKS = 1; + +``` + +Verify that all tables are dropped: + +```sql +SHOW TABLES; +``` + +exir + +```bash +mysql> EXIT; +Bye +``` + + +## +migrations +```bash +docker-compose exec web python manage.py makemigrations + docker-compose exec web python manage.py migrate +``` + +create superuser: + +``` bash + + docker-compose exec web python manage.py createsuperusers +``` + ## Getting started -- GitLab