From d834885e592e3473bc868f5c68b46bd687d815a9 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Sat, 24 Jan 2026 03:35:37 +0000 Subject: [PATCH] .1 --- core/__pycache__/admin.cpython-311.pyc | Bin 212 -> 3334 bytes core/__pycache__/forms.cpython-311.pyc | Bin 0 -> 3795 bytes core/__pycache__/models.cpython-311.pyc | Bin 209 -> 6932 bytes core/__pycache__/urls.cpython-311.pyc | Bin 347 -> 998 bytes core/__pycache__/views.cpython-311.pyc | Bin 1364 -> 6429 bytes core/admin.py | 40 ++- core/forms.py | 49 +++ core/migrations/0001_initial.py | 70 ++++ core/migrations/0002_eventparticipation.py | 24 ++ core/migrations/0003_donation_method.py | 18 + .../__pycache__/0001_initial.cpython-311.pyc | Bin 0 -> 4575 bytes .../0002_eventparticipation.cpython-311.pyc | Bin 0 -> 1451 bytes .../0003_donation_method.cpython-311.pyc | Bin 0 -> 915 bytes core/models.py | 94 +++++- core/templates/base.html | 88 +++-- core/templates/core/index.html | 227 +++++-------- core/templates/core/voter_detail.html | 311 ++++++++++++++++++ core/urls.py | 13 +- core/views.py | 119 +++++-- populate_engagement.py | 48 +++ static/css/custom.css | 77 ++++- staticfiles/css/custom.css | 84 ++++- 22 files changed, 1054 insertions(+), 208 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/0002_eventparticipation.py create mode 100644 core/migrations/0003_donation_method.py create mode 100644 core/migrations/__pycache__/0001_initial.cpython-311.pyc create mode 100644 core/migrations/__pycache__/0002_eventparticipation.cpython-311.pyc create mode 100644 core/migrations/__pycache__/0003_donation_method.cpython-311.pyc create mode 100644 core/templates/core/voter_detail.html create mode 100644 populate_engagement.py diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index a5ed392d6714413db63120e4233d2e96cbadb5de..5e21bfb61b8931a29d5b6fa7a405caab4956b84f 100644 GIT binary patch literal 3334 zcmb_eOK;mo5Z)z4N!H7fEWgs)c{ok0wz1F_MQt<<5HyBSG%<>%?SU7D7S{?>id2_W z8XpSikRRY%eC)|ZkUt{>ItY6z&{J+Qlv7TbSy{40$%br{5hu?lXJ2R_)(&+?& z>(;k{{g+0_fB4Y9qQ{NyV}+2H#3B}TNR{f85=*gEN2#j1T8-$Ds-|nzs2;7x^cW=y z*(6rv1+g?npT_%S2Pz@2;qG1fl(dS1RqPzAgtUr-)zmpwNokb;tK>OWDTTmjy03t# z2NV>+-Mb`53OLeWmFf2mt3H74*Zm=)Plp&IV;D2QI6X0AHpHlnVax(!ZeqqD>z-NAOsF#f2UPH%)`-Utl_ znIkmzJmBUl(uF?q?sn;kS7oL8cHIr?CH9<^>w(8he|r3Pzh;(M30B8Sa{a5dU7mz@ zB;f4b1%Srj(@rfkHrlClV*@`t-#5L)dNX}*4zHD`kP{PzV?OX0!Zbn_A%`HRoxv7* zR03waAJ$qJ(Tet$(29FA*zfc8qL8!oqC$=!S6RR(xftxagsz3r$U9%%6wY|2H?QC* z0=jy{o695KMAISF7%_NtZvmV{Iz+i@DG=?duCXYKu{fKmMD&CefymcHLVEWe<{!rT zGxW7#TVl#F{Xho3NbK0W-;P_hA8;EIFJ9xUY`f6Pl}*>OEi+(-U$50X9u!r614M}I zuE{OKw=1sU2WC+B`8DV+M)(r^@MVOn0FW|O({{Fs$xr}_lmpWlFlWs%cSTY@?br^M zhqTX3UVdWi*vzr~UgF4zl-rEn5iwh4vL6oI4{>4yi~;{Pz)6z*J5y;qXy-Q@Keh|^ z8xPyl*Z;oT{A#1QX*4sB8=Ls)MGd|`@YBtN%(+)(dtztoB ztf!`1@mA3V+}`FY^oyuGr+f~7{)jxedUakFPpnZQ{foI>cT9d<&g7XCO076&6-Z6U z!^A^8_2jB#PRS6I`z(DZ1&2c6UHne(9KFl$!F-rF{to=%?5LE|Ho4Xu{av7r-85vTTf*CZK zUxE~9GTvfMOv%m6^V4Ly#g~$mn3tZfmzKBJiZhlH>PO4oI c2T+U=h>K-`#0O?ZM#dWq3Ky`UA~v830Fdb=g8%>k diff --git a/core/__pycache__/forms.cpython-311.pyc b/core/__pycache__/forms.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d4709a8fd38f87e1bf061077f7fe89d31c6985e2 GIT binary patch literal 3795 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 literal 209 zcmZ3^%ge<81k-=yXIcX3#~=<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%RM0pb7xLi8T)Z diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index 5a69659f6c6e0ae848e54157af197c543a09315f..0fba60a955a1719446f95c0bbb1579408b919405 100644 GIT binary patch literal 998 zcmZvazi--55XYb4_-P0YAwyf#DpFN~lwfsi6J-JksTC5cx=^X$@}7tju+hUn%EXwV zdoyOJTB>&Fm@$8Xt$ILKw@%rRvSsQ$Fi1guem=eX-sj}!yZfF@#tE)J2c}jI5b{qr z%O!h<+b0qsUkD{sYLkvslO#d|G}sPwg0-MT0)$BvSnvdvi8S;p{4GexPka|^&P0$2 zEfpH|Wn##LS4`ZOk&%h4n1nBrL?*goHhh^BGO-o&z?a!XW?mt|(eyggiqvGf^_Ju_ zHv-5@E_7A%_y!9moX|jH!=6jrkfu|1Cc?VfVWZT*Fd0`4HQjvGJv~y~#9XLTW~y3y zlvxWis+o!#UogTM?W_?hrTZvpdTXDvropM=ZlYFSX!V6Q%KBtcLsw1B(D4o;Vd;(@ z`>;(zH`OLyMHZ;%jUM`tY`}EnVuG8WEBS;g(-T#18HGM?_qZ4hE;`M{NN~|lE}Fqb zwYeBQo)9J}%mx})#yl)87EbT|Ah#%yyFKOD=rE=Kv9Ftbg`Kfx-!!-~H27(+t2UW} zwZJfbu+aT<7d`A4wBKe&JcG%iN4>vjW-QTk?3B|_6^Yr<<&+jjW z7UXQmIgpz|bX>Gzk1k41w($Fd1w|W*4ir88>E*Vw^WrLDLCJ=a10_%2n=q$Pvfk7! zXxPwjpy35_lWiw|a9yyVYD3k5s;57ngq+=%*GUU1HdGv_c>1$T_SLw0Z9&Me#MkU1c^yu{p8KTXzK+$mX!dFlCjrA0Z# zMIgg&v6f|~mKPVXPF}$j5W@!)0hv(j1tdN&Gcq#XV34_hiXJeCUqD44*cezj8(cbs fIznc!U0{*F$RdA*Mg9Xb6F*Y}HwYH-0F46x5*j*o diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 2a36fd69370b38a98d8b01bf8eb9817c42f16ed6..fca47c7c7ed7ecdc27220cc413143c97e28c91c8 100644 GIT binary patch literal 6429 zcmeI0+iw%u8Ng>e<4f%0I8MkUAt88SDRDwBy8#+n0xY>eHa8mR?&4N*jb{iB9y@zx z%#z4dJZvQsMM{K_l2)mj2Sl?1Rr|=FF%ylH(Maw~)d${;SYCMQcgFU_7lMLVRr|ov zknrlZl8NOD4>60S_e;awIAn^&@wLgnT7}-#lQd%sdDA}`{Pt(tA<^5IEGUc_4&Z_oowMKafuw?&>t;HJjo-J`^kNG+* zrIO4O$Un8-H(>_ViFZ_u=`0^}j}giGSEce1_h|XnwdGyUYRYG_`E!rpe&%`q5qPx0 z++*a1-A_$Zyf`-F3rPTa<5-ct;3qAUykpv&(K_gr! zH<()yGFII}1oL4jHk$}bazsu^;2Dsdlq2!5IhthkBw)x}+R~yAY+mS%fe*Rcb4wQE z#HAFP*uPB)c)_rf9;Kk$U@nbZGZ^qQxQ5KlnfEr5;kX%#!xv$~rolwwal-|E1U~Tw zG>#@y2^o`x3$lh23(%(|<0J5nKOy2M2#Jsqfb^!j_f}FAdQ&%`QL0zCD@0Rr68CS+A5u|3LxKAvx)bFn zr~L~$>*pRF+VLOT_8-e4@8*btI#txEp-%F4NM|<^e5Nnidc~kIiC3@f;=_m01)GE7Y zOWS0^ZXu;@RsOTYKUMe^f$7STL{tbm47+IthC6SrH^~m?fjpRE*nIKwg?%#ricHgo-Pf#i3uLo2^KZC3 zw5HYb0=S2G-qKu{aBmtDh6sehWU9E12tN_^MC2_8cai*OM4SX+ILTnbUD>i6e1a6C z{9N;c;ImNF4u2BaG4E4Zbm;HGmQ6ujD(ccu*ZmP-xV|aN9o}kGxL%d()wtel({Z&a zs5J$%Eyw=azR{j-?ogXMwdT$&cS7ZQG_EJ-b@~7z5ce;=t}8Is|Kr!HXHfGDD(oQc zf#Kc`)1LsN_k@X>v(Vygx6M4BlS>eSfPgHcnE*DX0!JpzY?k0y)U1RBp9ek1ZtZTa z94eYB1!~UPx(Hh3BZFqiX2RRkwmCE4Bt2Ie%~dK|*Oz1inCOp6nfbKuSfU5DDH)#p4S-CMNhdpcb^5 zaN$!Vk9OfS9HJoKO7eL~&yl`eF)4f}5|8l)D@E=InEW1wv(PUl>z#-7?ewz5L@LlN zm>EQ@G9HHZglFkrAl?S9?yUX(&+FW_^RVJPob@%YH*K{2XjgovRo`jNcRIuB9^cB~ z(%^&h%ja_xQ*%Z~bt_{_V-Loc$1~$ubU;IG>t8Fsg8gAwL&FLh&Nj5JPi>{1-BlU} z)P@1AVL(9xIVUuI?WXElU?1>xsJ;%(*O6hf9zXG`{m)a%wQrPD*Wpn;A|B!~&*eX$?LCBYZjFY+3A3^WGd zPg(`B53UbyIv*p|*RA=wE0AVu&gqRUYu6uM-xOA_tBohL#uJ%Qy)}>-U7pZU{mS^# z_=Cyi$;{*~>}ORpsG&gx4Q4$Jt5ch)$9EM^pX%w;Jben=_mN!qQL3E%|AAaQ3Ii|t zibv&l^J+#1AI7Vz7`IdsiwLpBN<0CxB7E-`*4Fh)8xyK8sQH3bu%6MITGtjHE^OIX z7u2R6t*Iw-SwD0nb9s46M+a6WmnI)fFHdKtcVYdtiUu@9obX3je@Sk(0Bhk+C=gH1 z&hG78%_#Q6U^N3>xP7W*)k9bMc!h%!3Ou30F)&|xZTW5&YaY8kj9PEmaFy)V z5O&S;A)bLXyiUkn1F?^Y;MnHu``g&o@7KrFwtlUxKQpNx zIi8u+&|w|5?4b5-)SkZ)QBYtP;EO66($J8ChW7TgQ^8iFj-QJpW|KYAEx3__iy6s0 zoaWC7B>1ru_!~W9k{9Aq{z|B}(2kfZj@`%~790f)5)NW=Fv29%!-xoSq&Mv5Az|-> zF}d`~pIwGYj>J~xo@IIib76m=%$UXu$KJTk!hor`a1N98hESZ6{2+4-P18DcLD~D! zsdlCO(W#?K`J+=;mHj)y!P7{{fM~AWZ-O literal 1364 zcmZ`(&1)M+6ra_{dS%J>I&SM!uH%hUh$T{~CE!96oTjQ1oHkCdQ$lozPf-Q6#oM~=1-7}AYwoe=&3h>36z}rW~~)fO5P4{-h1=r&HMPV zzfVm~BA|nRhVUX&zZ=1NEJDd#(kRMro?7~GlVKiARAFbK7y={g8`rq_)Wa;XDEltUWCmB zkq~sth&3ZeP{;A878u54V{Oaty2i>_vx<&kIwj51DaMXgGg(=)ND+pj!HI^Q9g`Br z#tzdA%!;PvWg3a1>()Z2BR!#4DWHiJ1dw>FOuS)~xge&2p-9tZ06jh%7)=`o)~k%rY>m)oo?Fy$ z*3WOp#5FJD)_FvD(?z&0py>SutcBjF^RHFyMAbU#a#vk`t*)G?D;+i6Rnx7Fo|by~ z_(Z$b)~@ZR_tUQyT0itB&pp5LYvy^Tl^e+D)6aYJ%l+g^CzEdC%omzVp>MZ5DOS%5y(&3}_oxP=kek|W6^ zq^4$b*ux&@HXNxvWOr+P$YHmNV~!l#RXR|8nyNitg*S|+Cweu9m{fE9(FHc2zmjdM{4snRja*I4Xg{+WsExK~a7ky0&+o1!>X^=!XqBJk*DZ={#r1hHa^O0Q~fz3lMp4UH&hg78CU}O*t6^HBcagCwG zYK%&7wY93RHq^AwYFb-WLnCOkc>QoR%RO`~<#|EP{_$#7S64 z9bt-R`(yon^!s_ZeJl^2qda8Xi>J{PoEdkBf2Mj?ED+B_-BJu9sA!4^)2X3APfP0LWz7V+f`C9P_p1q~%bTiJ;yAqXWd81xJ@&e0*Q zS#?dxsw=vVBnXyzANRzCJ$f1ueh@Tzb`gFEGJk1LX!KMj{D{#5Pl&c5 zy`rmW#Uu?#S(Xb~m_IFRNTu^iW+9)~4J+=Vz=F1>>CZIM@U^2*swx0g%aFjl0|p&> zS~T6N4u_T(Ewj26{eUp8(%D@`A3Ti9R4_Wl4cS1Fsb)0Ev}CJbvYFi)@JZaWAi=Wd znXFmni{K;rh|1A#U`TWxhCrWdMK)iY`cgjBZVZEc;>!Y`Rskt{^iyGv?j&(Hqm{HE zU_K{jrF>SVOayXJ07%WKmg!LL&*~by!C(@o6qh7halTS-z={OPCxJqqvWA>KAFD;^ zBR<_gP<+6Pi?~_Md6!isi-2=I?|k<7Qo6?SO`1_fW0@yjIj0vi%ZO2@>4pTga1$QJ z=OBoO)h_W@$CxhyY28o~tQltW>zV|gQ;G#eZz#Y3IL90iEFBC@QbS2*7RhH12B}xow zM2eX;VC!AvrOhoJid!0X?r&zLx*Vy#o@t z_hbDldQRytEJ39d`jzKO8XPwIpXtV$nTL|@r$y2K;VZj;U|?WSs?dBj3Z;E$e0}& z!{YdEuoHJBHka+L#P)gIb*t2M%kH{`#j)LB2zOrDoU%KwlsXf3X9A1;-~)6UDUd)^H1Ea1?CQs{vldVt0G1FpSJy_w&!aCo5< zUa-RpSp51oI(u;M=+?5`JG#BFd_scXgVTEU{qQaVgiI{c;$N2W@VDLXQS#XAQ& zwij?@vJ{!LBa>K6me04gwUjS$!H&%*|G0uXua{lI>6Y^4v;M$a4#Ykc<)IHH*KvZ9 zi{jYOR<9i!D#fnZv1?cy*$tk<9f{3RyCbnJ?@Z#3d!>$hcE>#|&K<0g<$tO6=#)Kr z&mMh*m!ASH&-f%|rz1uA0ttM9q{?(tDqLF`jeZvEhDBnvyq!e&GSA^7G78g_t&4Pu{) z@|jNy`?w5!G=>^@yKN_i=O5wN<5KLg9ea$$Z$2KzI)w2(|0N4!nuRgV!kA`6-WAG? zUK)mDzHm;YTSft1>Gzts1fuNXdA{uB_-@Qao!>5Z2_N?>d&hWhnX4B+ Q25hnCE$!GprkDBfe}4|ZBLDyZ literal 0 HcmV?d00001 diff --git a/core/migrations/__pycache__/0002_eventparticipation.cpython-311.pyc b/core/migrations/__pycache__/0002_eventparticipation.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9c87672accb9d2d493f6f023315808fc768f66db GIT binary patch literal 1451 zcmZuxJ#5=X6h8il7Ae`0To-Cn)KUPquo6^~UBE!mqON1dZj;6dkN^d=5R~pDQ>IAu zNU3(v!9$0R*_yGFiozN)bjaATBMopcZYof8$VS7SGWFe2bYe7jyvO(6J-+wud++$$ z?5u%cEPo!@pD76a!-aHYrp);kFux#-u;ieo1aH}q`$|*kt4&oxGTK2{`3Yf_D82L> zGzM?Su#(ZtVCfO6W?ph}w<m zDl#JK>quC}T%+}7QU_RtnZep*W~@RMu#(Y{aaLGnQ_UEd<5WPfuZw*>RbgY28$$-i zu#y2bd0``P<2oK~GCP*R3RW_}YED??QVq%X(leo+mTG%*m)y0K!`E^NA6}O z^H*a45(WGYpt`{H`ze|Lt=je|VfjX*v0~b;9oUw`=I@M%8$7gVV7Kjoz@5o9e!R`( zZ?>u`%Ui?1GuxC{0l_RcpmyJ)hvq&xWV%l%xUipq&k><5&nKp9^$9Zw_i1AIB;|v{ z0eOkxAXNz}Y#o>mal64D%VFZTsXgHJh72<;$8z_X>JA-;Wky~=C|gLp4c<)Ae`4^n z)PzMkc!#0h!)|-_bd5fme6ydtRT^hpnL- z4C~~8v>`IB|KQPme_*vqonumeeShju=~Yv}cLlxL;E?i06NKLl|6d?SmowIoej`@o z1?}kD*g$&uXCulko)$m)yZCNcy7_bzm2SRJ|5^&0X6VweeoZ97_f5UqU|c7BWzx-IX@@cQ6^d?x6Hpr9!}l;fPXFc)WZ z-8e%)E;xym+`M*lmrq(t&t28n`$+|AxK&GjG_|x0bQ#Q;ez`BoFDjk@_X+nRT-AYl zmHi^=p)ctwi2RS>w}8ZoBuN0KbR$HS1k{A-wBZ=122xzbZRQm!{M>5pwrtc#9b;^1=HQqp{aUjzjjXOy+V15y0 zY0k|gqZsEw-6>alN{;#>*a3tdq0rMP8IYc?0UE6_P_wB@r}?1HFuk5 zW$apKxj4}RWsBoWRKPuRa*i@N=S((;(GYGRNz??TJuK8tL2!H%d zFUE9Ggd*=KhrHBJkQ)jcYZ1nDwPSP3Q~RFB9nTYWWsD+jdEQ3p$Ff#3iX%)Zra^=` zlaIo!CzL*yXD~xjyBqpUaqeUzoIcG@I1))XS#iTGLAUIeX;Heki9uOp%>9_L^?c+9 z=t{fXe^2TP;~sdmN#4Yx#nC5biqjOxAm0$Mbvi2(?0c22&6-;;D+lcPug=)Ix<7Yw z|I(HH_QLktY@1sz9>$5LEmjkiQpD>4XrWoOJf!&iSu0 WTg0s3JZwATi? - - - {% block title %}Knowledge Base{% endblock %} - {% if project_description %} - - - - {% endif %} - {% if project_image_url %} - - - {% endif %} - {% load static %} - - {% block head %}{% endblock %} + + + {% block title %}Voter Campaign CRM{% endblock %} + + {% if project_description %} + + + + {% endif %} + {% if project_image_url %} + + + {% endif %} + + {% load static %} + + + + + + + + {% block head %}{% endblock %} - - {% block content %}{% endblock %} - + - + {% if messages %} +
+ {% for message in messages %} + + {% endfor %} +
+ {% endif %} + +
+ {% block content %}{% endblock %} +
+ +
+
+

© 2026 Voter Campaign CRM. Designed for Campaign Victory.

+
+
+ + + + {% block scripts %}{% endblock %} + + \ No newline at end of file diff --git a/core/templates/core/index.html b/core/templates/core/index.html index faec813..03d1bf4 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -1,145 +1,88 @@ -{% 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… +
+
+

Voter Campaign CRM

+

Identify, Engaged, and Activate your base with a 360-degree voter view.

+ +
+
+
+
+ + +
+
+
+
-

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 Voters
+

{{ stats.total_voters }}

+
+
+
+
+
Active Support
+

{{ stats.supporters }}

+
+
+
+
+
Yard Signs
+

{{ stats.yard_signs }}

+
+
+
+ + +
+
+
+

{% if query %}Search Results{% else %}Recent Voters{% endif %}

+ + Add New Voter +
+ + {% if voters %} +
+ {% for voter in voters %} +
+
+
+
+
{{ voter.first_name }} {{ voter.last_name }}
+

ID: {{ voter.voter_id }}

+
+
+ + {{ voter.get_candidate_support_display }} + +
+
+
+ {{ voter.address|truncatechars:40 }} +
+ +
+
+ {% endfor %} +
+ {% else %} +
+

No voters found. Try a different search or add a new one.

+
+ {% endif %} +
+
+
+{% endblock %} diff --git a/core/templates/core/voter_detail.html b/core/templates/core/voter_detail.html new file mode 100644 index 0000000..3c2d385 --- /dev/null +++ b/core/templates/core/voter_detail.html @@ -0,0 +1,311 @@ +{% extends 'base.html' %} +{% load static %} + +{% block content %} +
+
+ ← Back to Dashboard +

{{ voter.first_name }} {{ voter.last_name }}

+

Voter ID: {{ voter.voter_id }} | {{ voter.district }} / {{ voter.precinct }}

+ +
+
+ +
+
+
+
+

Demographics & Contact

+
+
+ +

{{ voter.address }}

+
+
+ +

{{ voter.phone|default:"N/A" }}
{{ voter.email|default:"N/A" }}

+
+
+ +

{{ voter.registration_date|date:"M d, Y"|default:"Unknown" }}

+
+
+ +

{{ voter.district }} / {{ voter.precinct }}

+
+
+
+
+

Engagement

+
+ +
+
+
+ {{ voter.likelihood_to_vote }}/5 +
+
+ + + {{ voter.get_candidate_support_display }} + +
+
+ +

{{ voter.get_yard_sign_status_display }}

+
+
+
+
+ +
+ +
+
+
+
Voting
+ +
+ {% for record in voter.voting_records.all %} +
+ {{ record.description }} + {{ record.election_date|date:"Y" }} +
+ {% empty %} +

No voting records on file.

+ {% endfor %} +
+
+ + +
+
+
+
Donations
+ +
+ {% for donation in voter.donations.all %} +
+
+ ${{ donation.amount }} + {{ donation.donation_date|date:"M d, Y" }} +
+
{{ donation.method }}
+
+ {% empty %} +

No donation history.

+ {% endfor %} +
+
+ + +
+
+
+
Events
+ +
+ {% for participation in voter.event_participations.all %} +
+
+ {{ participation.event_type }} + {{ participation.event_date|date:"M d, Y" }} +
+ {% if participation.description %} +

{{ participation.description|truncatechars:50 }}

+ {% endif %} +
+ {% empty %} +

No events recorded.

+ {% endfor %} +
+
+ + +
+
+
+
Contacts
+ +
+ {% for contact in voter.contacts.all %} +
+
+ {{ contact.contact_type }} + {{ contact.contact_date|date:"M d, Y" }} +
+

{{ contact.description|truncatechars:50 }}

+
+ {% empty %} +

No contact history.

+ {% endfor %} +
+
+
+
+ + + + + + + + + + + + + + + + + + + +{% endblock %} \ No newline at end of file diff --git a/core/urls.py b/core/urls.py index 6299e3d..988d649 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,7 +1,12 @@ from django.urls import path - -from .views import home +from . import views urlpatterns = [ - path("", home, name="home"), -] + path('', views.index, name='index'), + path('voter//', views.voter_detail, name='voter_detail'), + path('voter//edit/', views.voter_edit, name='voter_edit'), + path('voter//add-voting-record/', views.add_voting_record, name='add_voting_record'), + path('voter//add-donation/', views.add_donation, name='add_donation'), + path('voter//add-contact/', views.add_contact, name='add_contact'), + path('voter//add-event/', views.add_event, name='add_event'), +] \ No newline at end of file diff --git a/core/views.py b/core/views.py index c9aed12..01fff2e 100644 --- a/core/views.py +++ b/core/views.py @@ -1,25 +1,98 @@ -import os -import platform +from django.shortcuts import render, get_object_or_404, redirect +from django.db.models import Q +from django.contrib import messages +from .models import Voter, VotingRecord, Donation, VoterContact, EventParticipation +from .forms import VoterForm, VotingRecordForm, DonationForm, VoterContactForm, EventParticipationForm -from django import get_version as django_version -from django.shortcuts import render -from django.utils import timezone - - -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() - - 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", ""), +def index(request): + query = request.GET.get('q') + if query: + voters = Voter.objects.filter( + Q(first_name__icontains=query) | + Q(last_name__icontains=query) | + Q(voter_id__icontains=query) + )[:10] + else: + voters = Voter.objects.all().order_by('-created_at')[:5] + + stats = { + 'total_voters': Voter.objects.count(), + 'supporters': Voter.objects.filter(candidate_support='supporting').count(), + 'yard_signs': Voter.objects.filter(yard_sign_status='has').count(), } - return render(request, "core/index.html", context) + + return render(request, 'core/index.html', { + 'voters': voters, + 'query': query, + 'stats': stats + }) + +def voter_detail(request, pk): + voter = get_object_or_404(Voter, pk=pk) + + # Forms for adding related records (initialized as empty) + context = { + 'voter': voter, + 'voter_form': VoterForm(instance=voter), + 'voting_form': VotingRecordForm(), + 'donation_form': DonationForm(), + 'contact_form': VoterContactForm(), + 'event_form': EventParticipationForm(), + } + return render(request, 'core/voter_detail.html', context) + +def voter_edit(request, pk): + voter = get_object_or_404(Voter, pk=pk) + if request.method == 'POST': + form = VoterForm(request.POST, instance=voter) + if form.is_valid(): + form.save() + messages.success(request, 'Voter updated successfully.') + return redirect('voter_detail', pk=voter.pk) + else: + form = VoterForm(instance=voter) + return render(request, 'core/voter_detail.html', {'voter': voter, 'voter_form': form}) + +def add_voting_record(request, pk): + voter = get_object_or_404(Voter, pk=pk) + if request.method == 'POST': + form = VotingRecordForm(request.POST) + if form.is_valid(): + record = form.save(commit=False) + record.voter = voter + record.save() + messages.success(request, 'Voting record added.') + return redirect('voter_detail', pk=voter.pk) + +def add_donation(request, pk): + voter = get_object_or_404(Voter, pk=pk) + if request.method == 'POST': + form = DonationForm(request.POST) + if form.is_valid(): + donation = form.save(commit=False) + donation.voter = voter + donation.save() + messages.success(request, 'Donation added.') + return redirect('voter_detail', pk=voter.pk) + +def add_contact(request, pk): + voter = get_object_or_404(Voter, pk=pk) + if request.method == 'POST': + form = VoterContactForm(request.POST) + if form.is_valid(): + contact = form.save(commit=False) + contact.voter = voter + contact.save() + messages.success(request, 'Contact logged.') + return redirect('voter_detail', pk=voter.pk) + +def add_event(request, pk): + voter = get_object_or_404(Voter, pk=pk) + if request.method == 'POST': + form = EventParticipationForm(request.POST) + if form.is_valid(): + participation = form.save(commit=False) + participation.voter = voter + participation.save() + messages.success(request, 'Event participation added.') + return redirect('voter_detail', pk=voter.pk) diff --git a/populate_engagement.py b/populate_engagement.py new file mode 100644 index 0000000..5866cff --- /dev/null +++ b/populate_engagement.py @@ -0,0 +1,48 @@ +import os +import django +import random +from datetime import date, timedelta + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') +django.setup() + +from core.models import Voter, Donation, EventParticipation + +def populate_engagement_data(): + voters = Voter.objects.all() + + event_types = ['Town Hall', 'Neighborhood Walk', 'Phone Bank', 'Fundraiser Dinner', 'Rally'] + event_descriptions = [ + 'Discussed local infrastructure issues.', + 'Canvassed the precinct with volunteers.', + 'Made 50 calls to likely voters.', + 'Annual gala for the candidate.', + 'Major campaign kickoff event.' + ] + + for voter in voters: + # 30% chance of having donations + if random.random() < 0.3: + num_donations = random.randint(1, 4) + for _ in range(num_donations): + Donation.objects.create( + voter=voter, + donation_date=date.today() - timedelta(days=random.randint(1, 365)), + amount=random.choice([10, 25, 50, 100, 250, 500]) + ) + + # 40% chance of having events + if random.random() < 0.4: + num_events = random.randint(1, 3) + for _ in range(num_events): + EventParticipation.objects.create( + voter=voter, + event_date=date.today() - timedelta(days=random.randint(1, 180)), + event_type=random.choice(event_types), + description=random.choice(event_descriptions) + ) + + print(f"Populated donations and events for {voters.count()} voters.") + +if __name__ == '__main__': + populate_engagement_data() diff --git a/static/css/custom.css b/static/css/custom.css index 925f6ed..30cc01d 100644 --- a/static/css/custom.css +++ b/static/css/custom.css @@ -1,4 +1,75 @@ -/* Custom styles for the application */ -body { - font-family: system-ui, -apple-system, sans-serif; +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Outfit:wght@600;700&display=swap'); + +:root { + --primary-navy: #0F172A; + --electric-blue: #3B82F6; + --emerald-accent: #10B981; + --soft-bg: #F8FAFC; + --card-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); } + +body { + font-family: 'Inter', sans-serif; + background-color: var(--soft-bg); + color: var(--primary-navy); +} + +h1, h2, h3, h4, .navbar-brand { + font-family: 'Outfit', sans-serif; + font-weight: 700; +} + +.hero-section { + background: linear-gradient(135deg, var(--primary-navy) 0%, #1E293B 100%); + color: white; + padding: 80px 0; + border-bottom: 4px solid var(--electric-blue); +} + +.search-glass { + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 12px; + padding: 20px; +} + +.card { + border: none; + border-radius: 16px; + box-shadow: var(--card-shadow); + transition: transform 0.2s ease; +} + +.card:hover { + transform: translateY(-4px); +} + +.btn-primary { + background-color: var(--electric-blue); + border: none; + padding: 10px 24px; + border-radius: 8px; + font-weight: 500; +} + +.badge-support { + background-color: var(--emerald-accent); + color: white; +} + +.nav-link { + font-weight: 500; +} + +.stat-card { + border-left: 4px solid var(--electric-blue); +} + +.voter-profile-header { + background-color: white; + padding: 40px; + border-radius: 20px; + margin-top: -40px; + box-shadow: var(--card-shadow); +} \ No newline at end of file diff --git a/staticfiles/css/custom.css b/staticfiles/css/custom.css index 108056f..30cc01d 100644 --- a/staticfiles/css/custom.css +++ b/staticfiles/css/custom.css @@ -1,21 +1,75 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Outfit:wght@600;700&display=swap'); :root { - --bg-color-start: #6a11cb; - --bg-color-end: #2575fc; - --text-color: #ffffff; - --card-bg-color: rgba(255, 255, 255, 0.01); - --card-border-color: rgba(255, 255, 255, 0.1); + --primary-navy: #0F172A; + --electric-blue: #3B82F6; + --emerald-accent: #10B981; + --soft-bg: #F8FAFC; + --card-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); } + body { - margin: 0; font-family: 'Inter', sans-serif; - background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end)); - color: var(--text-color); - display: flex; - justify-content: center; - align-items: center; - min-height: 100vh; - text-align: center; - overflow: hidden; - position: relative; + background-color: var(--soft-bg); + color: var(--primary-navy); } + +h1, h2, h3, h4, .navbar-brand { + font-family: 'Outfit', sans-serif; + font-weight: 700; +} + +.hero-section { + background: linear-gradient(135deg, var(--primary-navy) 0%, #1E293B 100%); + color: white; + padding: 80px 0; + border-bottom: 4px solid var(--electric-blue); +} + +.search-glass { + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 12px; + padding: 20px; +} + +.card { + border: none; + border-radius: 16px; + box-shadow: var(--card-shadow); + transition: transform 0.2s ease; +} + +.card:hover { + transform: translateY(-4px); +} + +.btn-primary { + background-color: var(--electric-blue); + border: none; + padding: 10px 24px; + border-radius: 8px; + font-weight: 500; +} + +.badge-support { + background-color: var(--emerald-accent); + color: white; +} + +.nav-link { + font-weight: 500; +} + +.stat-card { + border-left: 4px solid var(--electric-blue); +} + +.voter-profile-header { + background-color: white; + padding: 40px; + border-radius: 20px; + margin-top: -40px; + box-shadow: var(--card-shadow); +} \ No newline at end of file