From d10151cf400d9efff414f6fa8b7cb3579db1d790 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Sun, 22 Feb 2026 12:26:15 +0000 Subject: [PATCH] Ver 01 --- config/__pycache__/settings.cpython-311.pyc | Bin 5552 -> 5613 bytes config/__pycache__/urls.cpython-311.pyc | Bin 1557 -> 887 bytes config/settings.py | 6 +- config/urls.py | 18 +- core/__pycache__/admin.cpython-311.pyc | Bin 212 -> 4879 bytes core/__pycache__/forms.cpython-311.pyc | Bin 0 -> 2327 bytes core/__pycache__/models.cpython-311.pyc | Bin 209 -> 12337 bytes core/__pycache__/urls.cpython-311.pyc | Bin 347 -> 464 bytes core/__pycache__/views.cpython-311.pyc | Bin 1364 -> 2238 bytes core/admin.py | 72 +++++- core/forms.py | 22 ++ core/migrations/0001_initial.py | 136 ++++++++++++ .../__pycache__/0001_initial.cpython-311.pyc | Bin 0 -> 9712 bytes core/models.py | 162 +++++++++++++- core/templates/base.html | 79 +++++-- core/templates/core/attendance_log.html | 82 +++++++ core/templates/core/index.html | 205 ++++++------------ core/urls.py | 8 +- core/views.py | 46 ++-- requirements.txt | 33 +++ static/css/custom.css | 52 ++++- 21 files changed, 712 insertions(+), 209 deletions(-) create mode 100644 core/__pycache__/forms.cpython-311.pyc create mode 100644 core/forms.py create mode 100644 core/migrations/0001_initial.py create mode 100644 core/migrations/__pycache__/0001_initial.cpython-311.pyc create mode 100644 core/templates/core/attendance_log.html diff --git a/config/__pycache__/settings.cpython-311.pyc b/config/__pycache__/settings.cpython-311.pyc index 881731c8d6a538112a17daaf3e733deb4d833646..e7b86fc4443c1a5f771e8aa39c09835b1ef6ffca 100644 GIT binary patch delta 615 zcmdm>{Z^ZIIWI340}zCNot0TQkyn!O)<*TGO!etZQA#OBQOc>DDaKXY3=B~!Obn^a zSzx6g52 z^8qrQ;xzdji@dOYs!kD8igT29ic6G13Ue@nrt2ow6b{iUVaK$h%;ZFUul$U}yu8%n zq|&1F$(g+JGCXP-p3Z-W z(f1aoud9ovV|-|k&n+$pGsxdRq{s;9m?C2!af>-KucXKX#5DsE<{-iXL|B3dYanrp zwYVTBv!uv=a=)Mv{VC delta 504 zcmaE>y+NCIIWI340}x2uotEh`kyn!O%0~64Olhf{DMnS?3=HYaQA$h3vV12F_dDWj<{P2oaQp#oH)il)je#e8xFv#hcjnz%&@FPd)k$Cr@P- zV6>Y2irH3J6HSM8ip}IV%+{V-XyUdhc4#JPr`WeJps8|5)kamdKxgtsK4DXa6vt(Z zKz{-;1h_CP12R^_xC~Lcsai!$DNa$EDb7)PDa^qPnl78!SW`GyZ!w34I8UzUl^118 zEG|whDZa&%m!FavpPQdjnv+_*c^7XvBjc*cM*NM8`kS}#A7V5sG6ou3WCA2^F=ytL z6q$m!<{-iXL|B3dD-dA=ByO=57vyA?6gf;jENIA;1{49gvH0HRe}ctKjDnMkM6|^! fF0zQmhm2fodFL<|`Lu;68i diff --git a/config/__pycache__/urls.cpython-311.pyc b/config/__pycache__/urls.cpython-311.pyc index 42d995d2a7469704c70f4eaa3946a8b2e1cd9ee3..ac4b4d6449bcc9af210f38819a6f267acfa6d423 100644 GIT binary patch delta 402 zcmbQr^PP=vIWI340}%ZGJS%e<<3v6Mw-m-4hFr!dMn;Adh7_h8rd;MI=3JI27BHVV zhc%ZiiVe(W$zjjsh~i*mU}8vRNnuT8%;E%@1Oi!H6Bmf_fW_H>By$S;#98w7+^K9? zJP-v83`J(C+(0P~pcpSq49Mn#vDa`cV`5-f4a5*o1ymKq4cEgD69?)MfU&XbN#P7; z0O85on0-Vv8E^5XbAg$YPqVm-N&@X<1mfbyK;i>4BO~Jt28GGStldn3jFTU-%A4F^;h$hO t!EA=c6)Bw^9#?GqF9-)*6b`roBv}F)yg~2-D-&ZN;|B&1StJfL2LPRoRpI~u literal 1557 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 LT%Pn7%jSOo2!I{0 delta 148 zcmeBIyTX{VoR^o20SF}SPRq0h(vLwL7+``jJ_`XE(-~42QW$d>av7r-85vTTf*CZK zUxE~9GTvfMOv%m6^V4Ly#g~$mn3tZfmz=%bkZhlH>PO4oI a2T+U=h>K+zCfD*ya$jIj0HPu`pb!9z{vTNY diff --git a/core/__pycache__/forms.cpython-311.pyc b/core/__pycache__/forms.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b6866dea68f20bdce9077caed723f8f505c7f6e GIT binary patch literal 2327 zcmb^yOKj9ebiC_deodeomM)@+6v%2#!xoUBimGO5Ae53#kVp&ip_W-sf`fOxjK>gY zDsez+s?sV4DxxX}RG~B#IpoNZBS$Z_#A+m@N=kfE*yf^PR z?>+zA(b0+k8f!n=o`}%ze9;8yEniQ<$ z4OJKC(2Sr|FvuyI2PPU_+m#N;OslR#MB@Rvu& z5*HII(h{7w$32T6;!>L{OSTkS_|H5ougFo~K+Zq0C(0475BzGeTDN*;35$x}X0<^$ z+h6S163+caOwF9aM9ZX@Db?o9Qhu|_mFQ+Y*6R6HOfK7=OIW9S853%kv0;{7ze1T* zaVhpl2N-d!ooCEI$*oZ0I=KWB>D4M`Qpqtrk0+E#De*`*XKjmEJ?uaxUF(X^UBIP_ z19qkA)2BQsbBTzRiNw9)jpf>zY8Vw0G#jkdFv_mwJAB@57?*t0i7ABF29tn@9VSoO z*s(mqg|U`n;NnRxoXKY*wQR>Q*LdEYiPuHnp?1~5Or5|p)Fjx15ZKlfrrs}T4P>Af z1NjSX8Rz};;JKg2Gq~ham*jb%ysBBk`I1X;o_pi%uU=&$4=-tlliV!tS3uX-pL$n% zPO5J+gJ{gjzoq)-kKY#1HiFuJU)?hdj;L8?$o6~a`Zo<211hv`H2j|9g)4+hT$>2tO8xiEdMp6OrMyZGMn z)aqBk!0{k6Qp=2lnUQ+Wz6F1AcJ;{pVsP-&pyyPr=Tz8ps@~l<|JkiCR>vM54K%Q5 zEa)Dub&rSL;b<>KcLcjMZto*+zIcR4!b`Hx1k5oSLjPXx(9Cz z)9aL3!A7%*yTbR-&1kcc0Mj(T#R%@avfJE)3J_Kjo2z;jO*cCw)Ws$3EA>q^Nj#?bsEY8V2Egg(>cN z-org!BM!xc?B=`bB_ETk9;Sqci17aebYT&ybdrfCnF98USE6r=VMJAJ82s;qdV71X zlfC?m2IyL5YolqZ-wt%|TR?wYM|E}QchWb?O(j%!F6;@^+?{?v@pz)Wzq0?aHuOjv zdO!nhsHTmE+UT5kvu*xlsJ6a%eL(~6;1+{BUHcX#%2cJNVl z@WGxSJ6Ow}2(u?5j*(E^6{?_w#4|ebhjqO4wuBwWFmeJp03?s6 zY$5iV_hG^j4~Az00{5CvBhe;7y=+C#_}=pkLPnH65gO><;kf#^?lHJ+*_t*hQ*PYy z`TWXFmVGmaUGGm@xKq;iLH zY{gq}+q6{*cdKnv#NF%`!Zw?=YoQQ|6v#s#yucQ4fj$h=0U``gAVARvzbTL(7J2G- z?r=yB$tas*FUiCEbMBoxch33lIp@x=1Az_!Dg5MTxrf^Y;eV)LCRa7H*=HAo4+Ke& zYZ_nH394QCSJMzvsSIWin&b)ihlk)JqD=*G@Q(m5T=R4+nDW6TS3ugt% z^R6I?vTfFHUhHFsAUwd&NU4BbkbSaOcC30o#?>F-XC$fPUEyP7AD9(FD)_a$z82mP zzq%h+4{UMuFy{(_E3^eygmZ<#71@F-%DJN8ifzFa<6NEKif_Ty$+;5X>e_-U&bhk5 zwQUQo1n1fgt{q!&b#bnp;M%nXSGV-kWg*$~n4U?}rn~2glAKqNbH1#|4F6&&HzzL^ z3o`PaVL6+d%jA*u&KH$ZTFKm?dXdRlId?;5+1jH;rY3~LD10EuLdqryDLWpyW7cUh z$}W`LW=XQG+MC45BRSp`QljL9kT}>GWc( z;RXh(m83~Z0HiFehhlebX`$V!xOd9O+vxwcZPoTM9>Ig!^CE=0%zsJySklJtGwI;>ql6Q5Vj?}h_RoRhCrw(&OU zs(f*-A@hMys@){rxMZ`!Dcf<|_D#!Wg?5l$ygAKP)oERls5kXgD-0$pY+V)@RT4N65U>~u20|I?P5QnV zs!q#(+eM@fO#7)w zgeIdbf|P5Yb>9vGrX4Ga>f2xO?bm$!%fsu@zU6)`+IO#0jSf_z16p*Td|^Ghk1F?} z^2E3ry;zA})S?$rALw1Wq6K+9Zfu5J`O!>o+O{-#7O?8J=Vv$Y6hJY zXv;;0onw|Hl_xngOR~q)YOqN$U|@kaYPRE=p(YNPP2eEV;0YYfW5nfz1QX4QVW}=D z0+s8QqENY*?tTS3uFTkXF$jAWNxM)Ql6ZFD% z;A$%6rgPXRf|6n3r04V4#g)Yn?dUR74$RLKOGV`X0MolLpD$*lH1>NM3Yxc&&o4$z zMyZ%xn3D@7)Qwd$tLYGwW zl7ajU+g;sOBN?-eQQGx z-%{h}EAjJM{Cs(A-M?#TMDy=b_Z@%O`Dj@6U#j>oY5q&)GbSLb&kjE7R3~0lqgN}@ zt6KCb;5X5~Jf$W2@7-LzttL)X5+}68iSqcme%V5rj@Yy&uID9FmCo1WH>6YGJq*{k2fW9 zkn>_-p5zWd9`x8ql1p*})UgpfsBH!*Kkq@#08g+=M5_*`f|3^i7?L`m%|6{ZA!p|5 z@Ta>37uNP$f8|1&7LK*CTz)2`$pMa&+z3CqIBpUn{7QVkJ`4BE|9# z+sf%Tm=rWAi!7S<=;rV67_73`ydOyRDeg*34y$}f&T74@soq_!Z?a(}v0CqK;_sNU zPrFi1$fK+7pv#_FbB@BQ&)OqJ#)fOQW^5n|!rl$?PD5qLDv{5_wcSNkM%4 zFdKlC4@^@_NFlxhE^=mhlB3`WH)$Q27LDU}HjNuujW%f=;x6DJROLwuuEv_~4bc>E zka=uybRybAYvX8sE~|;AvA6M_jWh1T_}xuwv0d5*t==JRhgR>+@j)h2dQ}2EPfBIN6gkkw8x7huuZ6$>$&bFy-Mec(+VSF zZ{ohQ0S?HRF))F^Lyy#Wt6sCjGQ>gl%ww`s*a!$*>2s7n4+1ZGrkI1(#Kwf{_mgVq2)--MkNjHiA)}y*N2>P2 z&SoIvCR2F)%9)oY&cApjefr$^`O{}EbMJ}0ia{C4FS|-aAtDhWe}e5KQX;}pN=8n1 z5>(gGkO$Q``mrfuBm;rHM&vSahidDanJXe1r3b6)PR|dPYgRPi0s ze23t<1$zi?dsaNa)syPQ*VN#(O7NN%yavqm@h&^>PTae)ntpinQAwSgQ+%{Qr2J0qV0A&dz1R{jj6uDI)grzoiL)ZH6viBFzV$I@!xjY{ARE$~Kp zq!u!1QoZP2ijVAFi#{Awqa&5*h!!1zeID*7&!B(hZS{}F)bMyEJg$Yu%jZeHkC1#f z6GOgfUhU=E4}|%PKbVBKjhGmb4iQt}sVz@aKu`A1)(mX~|1igTSeYM9IU39nPm3W= zrii0%ib$T8F``Stlnv&(n2OQiSE_SKxM@qp4a0r3m?`Kkgc1r>1!>ZaBe;)=_Td9E zk~ZG}`8%6+t5p1r8|7^wUDFY}Q1YNdHGPc#2WCO2W6f<_4k7uEY(KFXv0@%w#IcKg z{!KlMeWj4i&1drI>P{13@?0iY$Q7p3lZYKqAl=2Sa<&g+!!)3Yq=Dgou-<#C-zOXc``TN$z&cAZK z=egs#<6oAr$K0I)Y5a15{6irN4i*+WU|`Vv@W+0H>9sd2{QMTGVTT;uMct+_u;jQJ zI(|=D&Eb!};&DFHWa9UR5>k9diqHO>NIPU93qg0|s6#0h8hj&KWDY8Np!yuLaz39< zLcl2>^Jm0A{+LS-zolJ+n6XD57>< zsB~S>x-MYvU+><3*R6H$Ut^DUtKFlO?oq9KRCUI`4weGD2+4M>46A`;C6LqtNsQet z5U}BNxcWDQHl)MmdV!$o>xltcbe9&;GSy`BUjPX8O?ett=r3(42bA79>p~}ud<@dZiBdfq7fra*+Sz zoZW7&xD>>j1)-)Z?w~+%YNr$dv_|zvb!#7#W*3wah1Qa8oanlSiiHJ*{WV5HNgS{G zC2ZNK@D9t;0uCggGe6}uUj@0sza+z)p+VT+7}esK{5Hn|_P0hwXi&OA=+}$ua>n;j zulq8!%U9hkmZ$m-W)7(6T@5_x$y;gD=h72R4b`I})@G03TLc@z(D#j~H!C7!P#;l^h#D1pq-mFJX-23-I|wmx3MR*hsA9z55%;$${0=g z4v67*85lXFhK3A#i*jodpS*@Y`ietbXUCGen&49RPh7p&*y+wZ5T5%*x@bNM!%yQb8i(GW(-2>Wx{SxXcqI8l;%jHZ{e&8bkTfW-LD)@T zMa2z-hCMl0@LRq~_g?d-I)72G-7AYD5&MArrdHz*u36TF8$eM=1tRsw@sU~mhwwB1>rdFsd3 zPN=?972he%cM7i}z$QJ7IB=zASWTZ+=9h;hpCCj*}`MQ%Q|aw z@k;?5##u4lh>%%$Fmgg_I@Uuh&blT>+|1IiW$bQ#q_?elq!&DMb6zeeGMq^nC(B7E zy^M1@qs*{>#0-rBlh{W_!Et6}g8jHt8UUKVQh^7bIC?zcv`eGdHUh z_jm=H#UCenST?RoJ}X_e(P^z6E*j=I8>U`=5Av9zXbtWxS$ueT;vCgdM2rW*EJ%#S zF_3r3ZRWG4{{b+$!Fj*F|*N){@|N{++)@M}&k%@ieF zypbuTX-6@lTcWZsSt^z?dE;2JgIcTJho9G1OKp+%mvj{(e~O>d2jyu*T2}%{YZAn6 z{LUCEOj5qE;TLeWg?N<_e7dgw@S5s-z2bXa^SutRG|{Npp{^oVR*fF6L=S7x!}JzH zJ2;E)CGdvp{poczK3a*7YVlEkbf||wxaZ#CwRhCe=}PFd7CH^I4fRoV-@VC)k;dxC z9y*fPvu1yI=+QJx@hjKwCWrE|zub-1R!?4L0Cy1H5v6Vv#HV=uhW=7$+ZHlm@0Z56PqX*#9`(uyiR z5bn`pmsQ2ysxcF2(zphtqF!97bWatRjALqw7jgw*+#G3VX8bF$5cvgu3I&wv!e86F zgP->Q+kpB?Rt-v(pri#Q?9}G*ucF3|RANW8*byB5w%@)qKUVa5M48l;g%4 ze+qm($(uLsEsoq*;tAt*%#-XlD3S;%KZUH~jW(NYUD&Bwzw5$&)%sl*cB|I!y0AmF ze%FNo)%sl*_NdnHhSO!k>zJ)%x&Qs|eo6ID>Wx9c-cjyeO5Pn-ZM!SB-I{GT4$f_O zClLfE>Gr-_jo6+QZ|m7@y^XBB8=}XC*QQ#@($t;&msJ0x-WZJ#Z!1~4a_8EYRR5&j z*xx!Nzs%CHJ11JkjoA=*ZzaYYQ2(Uf_>9MfAZ9CB9(jM1#yvvwt#U+zHbfCxi7^M% zKdCqN#%*Mcwx@U9Klgvh+nzLX40O|#TFLU2_pu(Re^PHe>c-;|TFLU%`&ehxKWWp* HMfU#yRdj?y literal 209 zcmZ3^%ge<81QK_rWm*F1#~=<2FhLogg@BCd3@HpLj5!Rsj8Tk?3@J>(44TX@K?*b( zZ?Wa(r=;c-`)M-W;!Md(%uCPLOGzqX21>4E_zY6>OHV%|KQ~psG^sSNq*On(A~m_R zB)>?%JijQrxF9h(RX;huC{-U~j9x+IFAf_ZyEG@&u80Guoe_wOWr4&8W=2NF8w@fR Ku%RM0pb7wG3pAVn diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index ebb8c6ea5957e01e2e132c7f2744cb84e6f77cdc..399aecc0e2d25bb50c6507a4d9e4aa5250e41d5b 100644 GIT binary patch literal 464 zcmZ8cJx>Bb5S_i-J2(iEXlLjNiQ(*Qj1@*I8)JioIoS;#-tCgTJ3%W-3VVq^Kx4G_ zcN_@?xz>t~gpSHtkr@F8?k?#Wfz?%V8DnE0cj8d z%wVQ(1ZKk|U;rnKa#oE5VG60$5sVLXT6h6}D0)%E1@lCcyS{9>>CPW;jlg*&ljd316D?Vdl^-fS zl+wE?J&ej|SuQz}g3 zzt!ITDtjDA7W@1}6|qS@3pV0DzmZ!8l jquF6CarVcj)6!vbbe7Qigx06DzOc-qG0f|u3fSoz+);Pw delta 244 zcmcb>e49yqIWI340}x2uotBveq#uJgFu)3Be72dWu9}>}x`u5TGXukFAclZ+#weB) z_Fx80j+Y<-O~zX+1&JjYFBzenjQrfxTMR`YV?c6wiMgqMnyk0DQ?e5C()0C7i*kyK zK!)97Ez3+TFD_!8%*hxK!v_=rnNaKnBt9@RGBVy^khy@09x#YsKt&(e7+5(QTsnk0 eLT0dCV3EJbB7cQN{sS`;KT`uY2o~`GjROE^&pBrR diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 8d204fa83f3150a3294bfb86b56b16ef24ab87c4..96f277a51fd4a7522442d66b7607636cb65643eb 100644 GIT binary patch literal 2238 zcmcIl&uO|fvhnNX%o~GutFRuDQOPD&;v;<+5jRFk`IkG-XYnr>!q{f z0Fj|NZ~%lv4@jV@hX|pRDpUd~NImvX*tJ%&HBwGh58Nz8l`2ksvx$ENB&14xnmoUG z`_0UoH{W~trL!}FK>peJNuLlA`jb!U64*0#z60Y4(vT(?$P{vd8OQ|$J`Wf{Q_P8A z2Q|?MnNm(N<(zDWb78Y1*Wvp^M#PNfqCQ?SVrFNq6Kwe|%5`aB&~dHfGD=0>^21UA zCJ|iFFkxYWHJ#u*O$jVabrY{z1^iavAdyK|EOHG%@=VSMvoh|Xmb-O+4^_H0H2!z@(FN6Of8^MxL z3Y36AKL>A%w5Pdu32C8{xB(g6Y-AOvBF)ahTS{u4>MIy5QBx^S1z9JxsA|mL@Tp<5 zuHEVNU541Kn_8NBYhG>eVl|$(2p-XaZa33Q)HG5N!tc&xYk`x>X3|yNpg^-w-YOO- z;RzreTye^*2iphDaK=Dm#^JN2E-VKi|StcGS zF2KIU5quNpi_{_`Ja+rKn#Uu4Xe&CtVW+R(Vj(|B(hKW(g!XTs*3h=n`*^mZq{>R_ zmEtHVR~h$|@rp88Rwi8~<0+Z7Z?_YDkLN3ip>ksA)t63U$W2UmiHS-gQ%+>u#91$K zc5QY$`Pt(iD#_t;a`?@}*5vEVR;DsGRUVsi$7Z~-87DdHCbM2LTS;ChCoj0kOJ4Gl zBPuls^&YSGo~ZVntjS_rsv%Ho(|<=$?BI{bog*h-EV|KgFFNjs<2w*AYO6X496WD3 zoSRQj2`zJoFPd=8uQsC6#=9eqXw+ak%WexLVZ8-e-)BEN$pqk_$-OV_EEW)rHZjXQ zQeS|NfenaZvk?a%IRW-W+sFRPsBo(F2AW7YDfVQUGs&0s)7M+-wvO0;6J2;2+MffO zY@#7jNs~*0F1%Mg;aMhLyl{DLwZGXR2MlX*5o-f>F`tJHd9`R5w~qR3^Z5-mChG;A z>Z-AN*e7PQh~{CbUp!2_%A0JANhYRCmPU9A$)})LShwd_R72O8Xsauj#JE%5JB;uL z@e7I^;#RYu0)*d)^nz-C-B3%+D|8g;N)GyegH9C&3J8#J{2kcp04fUHU_Ob;pfPdC@6HoFYdc%-K{o>(-Xl!lIS7mn=e|{%yaH z`1xS5y>CUT8}@FykxWa&FyRfLr4@0uun^C^?Wde|?Jea^hv*A_ws+r>C+b(^pYLb# zU3e1qEE+gNhTs$bA7(!XRSOD&P({Pehej10b=pT2jXED2RW#_dk1G1yX&*H)B79Xt Q4RtsoObdMeA?@GdU%rp#mH+?% literal 1364 zcmZ`(&1)M+6ra_{dS%J>I!c! z|D2kdL_iDu-_09gg#HmqK%t><`ZWm8kbw-zLiw{@%1fY!tdJefM7AQl zBhn19Sa6Ea_&F0f52+#t(zs7E+?3c%aE4Gt31lN`$VZUXaWDY%0l$fN{S4($*^jWf zAQFOZ8L?*M2p%0Y9=dd7Ae9|G&s?)vuje~ z*w|s3fmzYCyi6leblsZSXw)-0vAJ@~b$=8ZkKMM4%pxUIZ^X86P1|903-h@L+xPEo zRqQc33p$+=>?7eip%aJUJ?4psnxMCzWy{3I+3KUJ!>U=lhxIBWG+QI|iRTt| zob~hDHF3?$xOE;8-gF7B3n+Sj0BfPw>ijEJJ5jZcy3$owUa6}m>S{+#chz)jv!|tA z{CuKaZ)?{N(g*36i>>ealjojZ{Vnq})5;BG^zo;?`IUZhwUbPDlj(kPrJMYslf2eV zUh6F^_AXrPFJC&Ww^QpcL!ISZcRANzT0Z=#edVjazv(R9>@M9L%*JNq0Rq;#^X5EC zoIAMEUcCCE)QPWk<7@56+9@On0Hh1B(RpB8KAS2P06puedS=OZWkMOW!1G(l6GL;A z0R;Uy__hCq=tXh$6JT$Z8#Xbj7G9^9K^1=i?=i4JSdye3inaW&hh|#-_ZofP9$!6l Xxjnvm=u&%p4P;gNa)91vejfh;ewJFtm;flBt?ueVS@wR(5sqA+)$tk;**=k7lt=(q(GyJSi#3OOj zw$Ac**c|WV%<>^Q8c&D{R9(7k81ZCG@8nHSA|BI3Bd#WO)0t2uS<$0e60B&-Hh%hD zHL@%-b|bvRMeMvyb}Z*avdPZn^4!F?9IygE>l5Q5E{5yQ^+>#gvpvfd`5sQ3r*ex$v_-i@a3MFe`ICl55j_m_Fcm3UDeUZnN?&50v1Sr*x(Oi<7E0tBbI_4C_p8 zp5&9xZhlsTRl~5V@>t0Kxy|#3jR2h0GCa_o^#LqS`n&{Q9mDhH@MPce*<5}3vwW>4 zSKF*F(m?~Ga4v@d8fYxSX<|6%58xEhiFr1g4@(Pk9LxK*YdMAO{GTjUE2HpX>tA3v zAC|oBFrAo9YX5Jv|HZ?yg2g(DFuyng^Gk+#&{H}~c*>P~r?ee!u5~!gsZ3UgkHwwhwXUnMDq51 zw7l1){t{9;j`8<_Q%HG|Ty+v}JwcpGH<%t8^VYn1$eO<>^y^sJF>?O$6mnWVm)jd` zr|`L3hqx^EIsOY_j_!e59jWa*yui^^D$X)6f|D!hvmok;e%6X{L{5`lD+-m zVT(blc$Wz-@~nTXVD}l%_D>Y_PiFfc74%PK`=<-~LyLT@O!AdnduB6SyR6k&KDW8Hb_NwIaMPl@QK$3*nA!@S~&?-coBUJ^FHeha7SKj%-@a3(3t z67waqg!>qu75}9^yTJ5tT%P&(xQ&*ITWP+)qoR`Z5&2X;g{F#6Ev545r{{o^oEsLI z^!Za3oN{AucC9FJ%3b;-FO5sfCA{?cC(%@-LmkJA24<&Z^pTSu^>#G-C3jL!Az@QcM}D5Y=SSkR`KX zMT2XiL)FxFS(PCL+ZjV7Z?N|GhbGLGpQUdwh|%Pukw_R;#F0na|E1w%DNV@&^zs>TTKe^awLO3Dm7U_ zzq1s=gJr6-dJD#r@~9yvz-Kg7N<|F|#c8o%mS^di9t9AEIAH1Pt^5zED0+Ec^rntbdZ!`i4|oLZfxBdCf=(+iQ8jK)_)C8t53 zCMU#r5}w|K1qg>^R5qOuraE|wGdr~m92%bD%4fmvgKEsIRv}J}ctXz7V5UTS^lG;8qDXwd(qCo7a z8ss#~XM`0oE}7L3XE=rq34^(naY10j^kt_8q~(-uVECI37C%nRRD)_VrE4N;1fppo zt7~n+>TnOq=%^6Q8a|44@;_IhW&KlfHnuf<$c1j#xct(OLz--LR0pJ0w67ml$2GM@ z=r$L&B^gqoBqyWm z8a~yU)q$DNePL#LcuE+X7#a?mm3fRfB=(BCY%?0e=Vi@_6DkMeu>fpSBbZxF_uVB? zv#OQZXKJhE8BK^WIg2y`p_{D^L5i`ePH9<8##FN`WyBTTbYZA$K*}@iN%f`a4#{hV zg;+VLs){ToSxdthFg+uXT_9&Xgq@L>7EBk*VVJwi?*^vt28M>s>M=379#RonWX?<^ z<&X-%YPzO}L&Eq>Flc&I$VJve`v2K}0`j(>f8D*LCgkqa0;Jefx4b4tL1L{Nt)Q>K zj@OMw?cRTY@9yd8xhiCJ-nFu>xqu1-xMAu+NZa4#|NdTe_Pcu-r_KH4?>*G@#s9kK zmv_>x0Cfd2cD}~>T`=RZxm$3odCyn(ci%Zud+Cp>wD!`rW4E0|1d`N9t)8ycX{}DY z#-6X1)OEf-PwP6jrb*q+blpu_cawO3vFB?f&0Vh-X>-^1g`E&-evoc{K${;B@54P` z3u(Rj<~(h^3PjJ6)?m6dNLz!%JGSR*C2jq0`e|GLjz~gb(l(oJo26~D#QS*9_c^Km z>U9UL|7xp!J4D8wk$NFrFVK2{c)#BBH4}f&8z=SmY#BT4WNMN4V`+bk`eVeqwCB4( z+WX#&(DuIV@L!(L+f(#*n6%HP+h=Kep74sah;TG}U&YY{Lsmhd9xjjGH0K7+#Y2Mde>#20t@%?4mdyn=$Bz3`bU69sU z+Q`eaeV6#}r2TiO{|@m64hc*Ax6=Mw)Q?Ot{ar)-UCY>Pw}<%xIK_vst9^$5k-_&2 zLXR)Z@A(=?)0Nj@+H{5Vzp}nC@_-NGl;$y<0uZJE#3|#}lpdVYv%~E!kfulJrbo2t z5%Es#`I<;e@9S0C(!0~TJG(neX6FAU{2-9{bJC)uTNK)&5O0F{LLd4<-%fx`g-Gj6 zx^;%O&Jb^y`PL=$txH?s@8{_CVS0U>UJn!bB`p69H;lY|!_6Qw^06 zai89JL~qQIB?H*n;3kl}rb*-%An*$i`F+G^+-2xeE_5jX?-69q?z>d|3HUuS9P_RO zZhXjNj__~hWBnLV{bXpCJdx-Vn3ziYQ`Da#-qlQn6Pa}*vjE;B$kgmJ+k7%+ub_Pk z74?FwehZ3uY3Zt-=?Wm)Hm|EPJ1+0U9xmrh+rsswzx2YtF{OWIld zYn+aR>4-o_mcV{%+ygY@1V4*>PVg~o`Ipel0HF&YnmGiBxUm;*B?9dhy0CN5YTE#kuswIeKx9cq4nhcG7Y4%_8l%x$7gdbEG4Z z?ugJ1T=`%kKOz}{R=&bS2@g6P6zJMfk`^> zn8cEx2!jitJ{kv6LjXZT08vAC`AoG7jaz}n1@In0=JS2yo-PoM5C?XX(g`27iUHgz z26n#K`C_-@|GWR%O{V8aAVQu>_?4fhpDOgJLXt1ZlQrP@6$dQmiy{62iu8(mh%#XT zV`Dr^O26SCE7@!jUOZ4N@XSjhd>kiW0VvOT1B*F6g~b~G+XKZN`-mM@SmW>6VFDI_ zGHRd3;w2x#BEZdHv1pHB@xs1#pjfy67N@_k-*LcnEC6N9F^2?q=Tgb{$y?OSdX5y$yCEb1dj z0U%HSh!o}zg=7Vpd`cRhr5m5o#%IJUWIpqtGoC?b1n?d~rg7gH{in(_M>r#7=WCd2 zukBnVkEV$~l=g?HKSaDUM@JF5-nW)}H3m^AfFKk=6lx5Ff+JWT9>My^;52!>NSb5m z<``|p6VY**ru_s`<HXNX(PfX zaYnm}Gynn@0FjFo%ayF{e`{#l*j@c$olXdJVu?)HxIs7{9I)qz2Rnc_!-D7XQtR ze}F&(Ag;br`p`6CU**m^zk7gx+P~+n7o8Pmopq1uk`}tG{{rgDU52z*fM%Y{jpuf`o1Wo+k zA^pDqWb7Qr!7YbtB{skH-Lo~4+;`8`Kn{QRa%czeza+L2-;DEHj&o)p@=C!!dZ@Sk NM>hEW2o%QsKLOsUetQ4_ literal 0 HcmV?d00001 diff --git a/core/models.py b/core/models.py index 71a8362..e38921a 100644 --- a/core/models.py +++ b/core/models.py @@ -1,3 +1,163 @@ from django.db import models +from django.contrib.auth.models import User +from django.utils import timezone +from decimal import Decimal +from django.db.models.signals import post_save +from django.dispatch import receiver -# Create your models here. +class UserProfile(models.Model): + user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile') + # Add any extra profile fields if needed in the future + + def __str__(self): + return self.user.username + +@receiver(post_save, sender=User) +def create_user_profile(sender, instance, created, **kwargs): + if created: + UserProfile.objects.get_or_create(user=instance) + +@receiver(post_save, sender=User) +def save_user_profile(sender, instance, **kwargs): + if hasattr(instance, 'profile'): + instance.profile.save() + +class Project(models.Model): + name = models.CharField(max_length=200) + description = models.TextField(blank=True) + supervisors = models.ManyToManyField(User, related_name='assigned_projects') + active = models.BooleanField(default=True) + + def __str__(self): + return self.name + +class Worker(models.Model): + name = models.CharField(max_length=200) + id_number = models.CharField(max_length=50, unique=True) + phone_number = models.CharField(max_length=20, blank=True) + monthly_salary = models.DecimalField(max_digits=10, decimal_places=2) + photo = models.ImageField(upload_to='workers/photos/', blank=True, null=True) + id_document = models.FileField(upload_to='workers/documents/', blank=True, null=True) + employment_date = models.DateField(default=timezone.now) + notes = models.TextField(blank=True) + active = models.BooleanField(default=True) + + @property + def daily_rate(self): + # monthly salary divided by 20 working days + return (self.monthly_salary / Decimal('20.00')).quantize(Decimal('0.01')) + + def __str__(self): + return self.name + +class Team(models.Model): + name = models.CharField(max_length=200) + workers = models.ManyToManyField(Worker, related_name='teams') + supervisor = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='supervised_teams') + active = models.BooleanField(default=True) + + def __str__(self): + return self.name + +class WorkLog(models.Model): + OVERTIME_CHOICES = [ + (Decimal('0.00'), 'None'), + (Decimal('0.25'), '1/4 Day'), + (Decimal('0.50'), '1/2 Day'), + (Decimal('0.75'), '3/4 Day'), + (Decimal('1.00'), 'Full Day'), + ] + + date = models.DateField(default=timezone.now) + project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='work_logs') + team = models.ForeignKey(Team, on_delete=models.SET_NULL, null=True, blank=True, related_name='work_logs') + workers = models.ManyToManyField(Worker, related_name='work_logs') + supervisor = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='work_logs_created') + notes = models.TextField(blank=True) + overtime_amount = models.DecimalField(max_digits=3, decimal_places=2, choices=OVERTIME_CHOICES, default=Decimal('0.00')) + priced_workers = models.ManyToManyField(Worker, related_name='priced_overtime_logs', blank=True) + + def __str__(self): + return f"{self.date} - {self.project.name}" + +class PayrollRecord(models.Model): + worker = models.ForeignKey(Worker, on_delete=models.CASCADE, related_name='payroll_records') + date = models.DateField(default=timezone.now) + amount_paid = models.DecimalField(max_digits=10, decimal_places=2) + work_logs = models.ManyToManyField(WorkLog, related_name='payroll_records') + + def __str__(self): + return f"{self.worker.name} - {self.date}" + +class Loan(models.Model): + worker = models.ForeignKey(Worker, on_delete=models.CASCADE, related_name='loans') + principal_amount = models.DecimalField(max_digits=10, decimal_places=2) + remaining_balance = models.DecimalField(max_digits=10, decimal_places=2) + date = models.DateField(default=timezone.now) + reason = models.TextField(blank=True) + active = models.BooleanField(default=True) + + def save(self, *args, **kwargs): + if not self.pk: + self.remaining_balance = self.principal_amount + super().save(*args, **kwargs) + + def __str__(self): + return f"{self.worker.name} - Loan - {self.date}" + +class PayrollAdjustment(models.Model): + TYPE_CHOICES = [ + ('Bonus', 'Bonus'), + ('Overtime', 'Overtime'), + ('Deduction', 'Deduction'), + ('Loan Repayment', 'Loan Repayment'), + ('New Loan', 'New Loan'), + ('Advance Payment', 'Advance Payment'), + ] + + worker = models.ForeignKey(Worker, on_delete=models.CASCADE, related_name='adjustments') + payroll_record = models.ForeignKey(PayrollRecord, on_delete=models.SET_NULL, null=True, blank=True, related_name='adjustments') + loan = models.ForeignKey(Loan, on_delete=models.SET_NULL, null=True, blank=True, related_name='repayments') + work_log = models.ForeignKey(WorkLog, on_delete=models.SET_NULL, null=True, blank=True, related_name='adjustments_by_work_log') + project = models.ForeignKey(Project, on_delete=models.SET_NULL, null=True, blank=True, related_name='adjustments_by_project') + amount = models.DecimalField(max_digits=10, decimal_places=2) + date = models.DateField(default=timezone.now) + description = models.TextField(blank=True) + type = models.CharField(max_length=50, choices=TYPE_CHOICES) + + def __str__(self): + return f"{self.worker.name} - {self.type} - {self.amount}" + +class ExpenseReceipt(models.Model): + METHOD_CHOICES = [ + ('Cash', 'Cash'), + ('Card', 'Card'), + ('EFT', 'EFT'), + ('Other', 'Other'), + ] + VAT_CHOICES = [ + ('Included', 'Included'), + ('Excluded', 'Excluded'), + ('None', 'None'), + ] + + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='expense_receipts') + date = models.DateField(default=timezone.now) + vendor_name = models.CharField(max_length=200) + description = models.TextField(blank=True) + payment_method = models.CharField(max_length=20, choices=METHOD_CHOICES) + vat_type = models.CharField(max_length=20, choices=VAT_CHOICES) + subtotal = models.DecimalField(max_digits=12, decimal_places=2) + vat_amount = models.DecimalField(max_digits=12, decimal_places=2, default=Decimal('0.00')) + total_amount = models.DecimalField(max_digits=12, decimal_places=2) + + def __str__(self): + return f"{self.vendor_name} - {self.date}" + +class ExpenseLineItem(models.Model): + receipt = models.ForeignKey(ExpenseReceipt, on_delete=models.CASCADE, related_name='line_items') + product_name = models.CharField(max_length=200) + amount = models.DecimalField(max_digits=12, decimal_places=2) + + def __str__(self): + return self.product_name diff --git a/core/templates/base.html b/core/templates/base.html index 1e7e5fb..45e4df7 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -1,25 +1,68 @@ +{% load static %} - - - {% block title %}Knowledge Base{% endblock %} - {% if project_description %} - - - - {% endif %} - {% if project_image_url %} - - - {% endif %} - {% load static %} - - {% block head %}{% endblock %} + + + {% block title %}Fox Fitt Construction{% endblock %} + + + + + + + + + + + {% block head %}{% endblock %} - - {% block content %}{% endblock %} - + + {% if messages %} +
+ {% for message in messages %} + + {% endfor %} +
+ {% endif %} + +
+ {% block content %}{% endblock %} +
+ +
+
+

© 2026 Fox Fitt Construction - Payroll Management

+
+
+ + + + {% block scripts %}{% endblock %} + diff --git a/core/templates/core/attendance_log.html b/core/templates/core/attendance_log.html new file mode 100644 index 0000000..6e354f8 --- /dev/null +++ b/core/templates/core/attendance_log.html @@ -0,0 +1,82 @@ +{% extends 'base.html' %} +{% load static %} + +{% block content %} +
+
+
+
+
+

Log Daily Attendance

+
+
+
+ {% csrf_token %} +
+ + {{ form.date }} +
+ +
+
+ + {{ form.project }} +
+
+ + {{ form.supervisor }} +
+
+ +
+ + {{ form.team }} +
+ +
+ +
+
+ {% for worker in form.workers %} +
+
+ {{ worker.tag }} + +
+
+ {% endfor %} +
+
+
+ +
+
+ + {{ form.overtime_amount }} +
+
+ +
+ + {{ form.notes }} +
+ +
+ +
+
+
+
+
+
+
+{% endblock %} + +{% block scripts %} + +{% endblock %} diff --git a/core/templates/core/index.html b/core/templates/core/index.html index faec813..5113fd6 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -1,145 +1,66 @@ -{% extends "base.html" %} - -{% block title %}{{ project_name }}{% endblock %} - -{% block head %} - - - - -{% endblock %} +{% extends 'base.html' %} +{% load static %} {% block content %} -
-
-

Analyzing your requirements and generating your app…

-
- Loading… +
+
+

Construction Payroll & Attendance

+

Efficiently track workers, projects, and payroll for Fox Fitt Construction.

+
-

AppWizzy AI is collecting your requirements and applying the first changes.

-

This page will refresh automatically as the plan is implemented.

-

- Runtime: Django {{ django_version }} · Python {{ python_version }} - — UTC {{ current_time|date:"Y-m-d H:i:s" }} -

-
-
-
- Page updated: {{ current_time|date:"Y-m-d H:i:s" }} (UTC) -
-{% endblock %} \ No newline at end of file + + +
+
+
+
+
+

{{ total_workers|default:"0" }}

+
Active Workers
+

Field workers registered in the system.

+
+
+
+
+
+
+

{{ total_projects|default:"0" }}

+
Projects
+

Ongoing solar farm foundation projects.

+
+
+
+
+
+
+

{{ today_attendance|default:"0" }}

+
Today's Attendance
+

Workers logged for today across all sites.

+
+
+
+
+
+ +
+
+
+

Streamlined Attendance Tracking

+

Supervisors can now quickly log attendance directly from their mobile devices while on-site. Select a whole team with one click or pick individual workers for each project.

+
    +
  • ✅ Real-time attendance logging
  • +
  • ✅ Integrated overtime calculations
  • +
  • ✅ Instant team selection
  • +
  • ✅ Offline-first field record management
  • +
+ Get Started +
+
+ Construction Worker +
+
+
+{% endblock %} diff --git a/core/urls.py b/core/urls.py index 6299e3d..ea76e98 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,7 +1,7 @@ from django.urls import path - -from .views import home +from . import views urlpatterns = [ - path("", home, name="home"), -] + path('', views.index, name='index'), + path('attendance/log/', views.attendance_log, name='attendance_log'), +] \ No newline at end of file diff --git a/core/views.py b/core/views.py index c9aed12..9def89e 100644 --- a/core/views.py +++ b/core/views.py @@ -1,25 +1,29 @@ -import os -import platform - -from django import get_version as django_version -from django.shortcuts import render +from django.shortcuts import render, redirect from django.utils import timezone +from .models import Worker, Project, WorkLog, Team +from .forms import AttendanceLogForm +from django.contrib import messages - -def home(request): - """Render the landing screen with loader and environment details.""" - host_name = request.get_host().lower() - agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic" - now = timezone.now() - +def index(request): + total_workers = Worker.objects.filter(active=True).count() + total_projects = Project.objects.filter(active=True).count() + today_attendance = WorkLog.objects.filter(date=timezone.now().date()).count() + context = { - "project_name": "New Style", - "agent_brand": agent_brand, - "django_version": django_version(), - "python_version": platform.python_version(), - "current_time": now, - "host_name": host_name, - "project_description": os.getenv("PROJECT_DESCRIPTION", ""), - "project_image_url": os.getenv("PROJECT_IMAGE_URL", ""), + 'total_workers': total_workers, + 'total_projects': total_projects, + 'today_attendance': today_attendance, } - return render(request, "core/index.html", context) + return render(request, 'core/index.html', context) + +def attendance_log(request): + if request.method == 'POST': + form = AttendanceLogForm(request.POST) + if form.is_valid(): + form.save() + messages.success(request, 'Attendance logged successfully!') + return redirect('index') + else: + form = AttendanceLogForm(initial={'date': timezone.now().date()}) + + return render(request, 'core/attendance_log.html', {'form': form}) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index e22994c..7908add 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,36 @@ +anyio==4.12.1 +asgiref==3.10.0 +certifi==2022.9.24 +chardet==5.1.0 +charset-normalizer==3.0.1 +dbus-python==1.3.2 +distro-info==1.5+deb12u1 Django==5.2.7 +h11==0.16.0 +httpcore==1.0.9 +httplib2==0.20.4 +httpx==0.28.1 +idna==3.3 +markdown-it-py==2.1.0 +mdurl==0.1.2 mysqlclient==2.2.7 +netifaces==0.11.0 +pillow==12.1.1 +pycurl==7.45.2 +Pygments==2.14.0 +PyGObject==3.42.2 +pyparsing==3.0.9 +PySimpleSOAP==1.16.2 +python-apt==2.6.0 +python-debian==0.1.49 +python-debianbts==4.0.1 python-dotenv==1.1.1 +PyYAML==6.0 +reportbug==12.0.0 +requests==2.28.1 +rich==13.3.1 +six==1.16.0 +sqlparse==0.5.3 +typing_extensions==4.15.0 +unattended-upgrades==0.1 +urllib3==1.26.12 diff --git a/static/css/custom.css b/static/css/custom.css index 925f6ed..0d3212e 100644 --- a/static/css/custom.css +++ b/static/css/custom.css @@ -1,4 +1,50 @@ -/* Custom styles for the application */ -body { - font-family: system-ui, -apple-system, sans-serif; +:root { + --primary: #2F3E46; + --secondary: #84A59D; + --accent: #FFD166; + --background: #F7F7F7; + --text: #354F52; } + +body { + font-family: 'Open Sans', sans-serif; + background-color: var(--background); + color: var(--text); +} + +h1, h2, h3, h4, h5, h6 { + font-family: 'Montserrat', sans-serif; + font-weight: 700; +} + +.navbar { + background-color: var(--primary) !important; +} + +.btn-primary { + background-color: var(--secondary); + border-color: var(--secondary); +} + +.btn-primary:hover { + background-color: var(--primary); + border-color: var(--primary); +} + +.card { + border: none; + box-shadow: 0 4px 6px rgba(0,0,0,0.1); +} + +.hero-section { + background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%); + color: white; + padding: 100px 0; +} + +.footer { + background-color: var(--primary); + color: white; + padding: 20px 0; + margin-top: 50px; +} \ No newline at end of file