From dc2bd6214296f43e76da4ab2615a9a94605ae08f Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Sun, 25 Jan 2026 17:50:45 +0000 Subject: [PATCH] Autosave: 20260125-175045 --- core/__pycache__/admin.cpython-311.pyc | Bin 70578 -> 72102 bytes core/__pycache__/models.cpython-311.pyc | Bin 20928 -> 23597 bytes core/admin.py | 34 +++++++-- ...description_remove_tenant_slug_and_more.py | 71 ++++++++++++++++++ ...igned_events_alter_volunteerevent_event.py | 24 ++++++ ...emove_tenant_slug_and_more.cpython-311.pyc | Bin 0 -> 3865 bytes ...alter_volunteerevent_event.cpython-311.pyc | Bin 0 -> 1334 bytes core/models.py | 31 ++++++++ 8 files changed, 155 insertions(+), 5 deletions(-) create mode 100644 core/migrations/0013_remove_tenant_description_remove_tenant_slug_and_more.py create mode 100644 core/migrations/0014_volunteer_assigned_events_alter_volunteerevent_event.py create mode 100644 core/migrations/__pycache__/0013_remove_tenant_description_remove_tenant_slug_and_more.cpython-311.pyc create mode 100644 core/migrations/__pycache__/0014_volunteer_assigned_events_alter_volunteerevent_event.cpython-311.pyc diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index eff357576f95d46b6e2c47df0ed3f769def56fd5..cf83c170db96a108b806e0ba8dfb57fab8bf0088 100644 GIT binary patch delta 10034 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 delta 8707 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 RXrA=FM_W zalY=o=bn4c;oUgL-h7uiFE|`l9gZIc`@2kQ&o~#b|GicGW4%?c+p3d`Ue?J=V2tJ3 z3Ix9t`DKDH6#R1Ji;2%%EE`&NzLN2G^jF-l$z@?NIAaOd;~p7kV>++b=`Q0;jdrV? z`LeFvCYy1MMKd?{Mis88QO(jdL#cc@nsN()6$+&ai(}RZLejE~Fe+N%zGL4)X;xkq z3aL>Z3boN14+8u&kOwELvrV}qal;w&BG)rS5;WsfkHSiKuI=W*xT8JcXiqxY;qLq# zh`G12XU2nBuNw0Ypp{S0d+^VoV^6p@sus3JyE+ak9HqTr@z$9+>Mk+PVYj#1ts=uo z(!l*hD)4J>Haj@}dvBYSof&s7RgCNkL`(ea2)t8rlD!7CzLlmo$n+KH_f@lVaMibo z{b>Ai->XK`5t9EFPM6ipyMUxh@hdFkStR}){IhHc`x6+-pKN=Z1Q`UcA!yF9EMqiP z3WuUZ`50;5BDhL0PB2PvgWwv0jG$#ko;#p)L{)y4MAzX|`AhB(ksP$PMeu#f)NsG@ z{&>-<96kFC)>n*}ZjoIa?pBl+iTyqxK7E>I>*HM!j$VXu^;KBCW}!(O_BQNXb9()C zGN$c$HiG5|g*v*!s@kD+cZWhg#{Ys+l_6|SU6@=He(pWD@CgHxq1@$#Co2EuK1>Qq zVUaCCehB`&z7C#V*UYBi^16CSEZj)?djz+CR<(fr8@yG;*3XD1xCh&+eu9^xE$>(T z9m@P3xU-(K2hddAntz+jz99HMf|l8`t2tO-8|42b(FD9-z05jEB*8S?uRfVaR0q!V z$ef}6Q^H`fYFm?HQ49w}*^xpv;E!=M<2=6MWqL<3+HHCrTG?lL*)*DY9s_cjf+9w{ zLs&Rrs5a-gYmT*9HjG;4l$|7O+_USId3Y`xwVpTNt~gVpef}J)EZHX8M;&vlvSlZh zut#=b2}_#gcw40VFcz=EHEaJYnX2NT=1xmOeLV7fSGRJ>$cynfo-P<(LhQr0{2sA~ z5U|jwpoCs?rcbHwS9+t`e9V}x{k@7D%1Ixg`t(|6w39~;@9*R#@Rei75_fvpY?Cly z5Ji*VIko{u7ZLSYm>Hu z5#yNYn(Kxf-_;gZUWnTU61IV)Z6H<$SB##0_C+TelJ-RjdqL7(5UZcC=MkTGQ9rgc zZm&$(E0gw06ids8Oi5|^$O~i9o27ASPeR(0l=j3LCp@J?&nG>l7k9@u1maKk#XW}- zo=)R~w?L@YIp2gnih=2+Po)IB?(()%`aYVRHra#;VwW>9 zLCpMXT%@_htwX6G^iTjqo7b|_kN1dfru&bq$by=>jZ1unrU%BtaI|m`Q7!ZtG)sD& zmLax=TbX&=(~m83WcJ0K=$*P0y@=^1=6Qz8;x0e_j>vBuNKs?*wXPHuH~VL@!a-S4 zAl^qb5F2HUsC7G0A`Mo3ZUh7CMH~IF|mIkQP()7mxVH`mY^}2lvON&V zS&?v@e~ph;JpP*vRm|2$j=9L&qfnFSg>N?Q$>mn!*O8_Q!DmQyM!FM3Yj*1M7<^RI zpDivb?k#RBll?u|wQ04DV#x{I3{GwOmEOrMs zGBoi@5>1mjb+Tp(_e8LH@Gl^NF23h?vt{sCe^Z4xQh1PR-Ly_L;s8+wbU-upDACS{ z%JlTTPGGDf0wb$2D?xL&u0Wl_>@oD({c!xS(sa(EN%y^Pq~M2VP+ zZ2pqi38<+ra21i7paiFSO+@jPdS~Icv-$YVV0`Zn;@yLB>1aYanv{;l8ezCTsxJ~5 zF<#X0bH?t$yIWUgl;JKL;a^){a$LqUW^=9_2A^tVo8iN!da~!Zyo+2Zhla*7D|x^P z*1)mGKj`tFD5YE5whM*g6NxfyKTcijhS85Zr&5lVZ^2JTFRb0Zm%Ru>+gD~(;vNm~ z$L(*jZO|8}@^C%vY$j8YW~+$010M%UtxuAMU<-T`IBg=zR|La5Dzb*rNV6a2-Q8W! zmGy!GPLX{2p)w0Lw|JnX<;b&ETJ$&!nTG9C zB%gk$Twr3|aHH)!bag(cD2CIz~C1%6J)mc2@S%*)RL?an^4r$(h3y2(5Rp~lKN)r zVQ!xp-p=2#=>nUIat5oFn$a{4H}f;eXYgl!k^3?#;t<0OF{97#r-CSUi4qkAiGF~V zDa15l1~Gy71aS$`$q-XV4)+M%l1R^?>KdFZIBA+?;-J4XajTfz3kyeWbPykZ*9;G(_k z(5j)i#7S<$SV@hIFWiUrMZ|T6m_Z1#N&}H_mmfEmf`(tfSem81g(Bi7C@(!tZt*lf zmwv0_*BxfDUm{CTRo0e=D-U=_&b)*d{tv<<0_n>w!gH6FJh{Xv5LRf<3 zx)%AW45|+dB=J1F#J|y*SG)*cAIR6O;2iP040QG9$qKLM^ZM^BchNG4=wo0lE}|^K z)q|d-d#G8@rt*dG(&!Rc8+H&iG&DGh|K~{8PNI=5jA`0xTwRX##>SW`BuGaie!766 zRRpfMEm}FdQ;HyPx8aOjPJkG;kZ<5eb0y4F88h(``S2^Il@kh?Y>GW=!=shU1VxVy zMz?K-T}_FwJ@Jt|BiAK7@L{Xe6D;}^whkYjM8D>g%wzJ$vlm_q<4qZfy!MU02}4D7 ziJ?adbqt6{0AyDUe4R(2-vTI1`lI*{D S{qbS@GU``0jAoeghW-Kl<*Vla diff --git a/core/admin.py b/core/admin.py index a166bb4..3af98a6 100644 --- a/core/admin.py +++ b/core/admin.py @@ -11,7 +11,8 @@ from django.shortcuts import render, redirect from django.template.response import TemplateResponse from .models import ( Tenant, TenantUserRole, InteractionType, DonationMethod, ElectionType, EventType, Voter, - VotingRecord, Event, EventParticipation, Donation, Interaction, VoterLikelihood, CampaignSettings + VotingRecord, Event, EventParticipation, Donation, Interaction, VoterLikelihood, CampaignSettings, + Interest, Volunteer, VolunteerEvent ) from .forms import ( VoterImportForm, EventImportForm, EventParticipationImportForm, @@ -147,6 +148,12 @@ class EventTypeAdmin(admin.ModelAdmin): list_filter = ('tenant', 'is_active') search_fields = ('name',) +@admin.register(Interest) +class InterestAdmin(admin.ModelAdmin): + list_display = ('name', 'tenant') + list_filter = ('tenant',) + search_fields = ('name',) + class VotingRecordInline(admin.TabularInline): model = VotingRecord extra = 1 @@ -163,6 +170,10 @@ class VoterLikelihoodInline(admin.TabularInline): model = VoterLikelihood extra = 1 +class VolunteerEventInline(admin.TabularInline): + model = VolunteerEvent + extra = 1 + @admin.register(Voter) class VoterAdmin(BaseImportAdminMixin, admin.ModelAdmin): list_display = ('first_name', 'last_name', 'nickname', 'voter_id', 'tenant', 'district', 'candidate_support', 'is_targeted', 'city', 'state', 'prior_state') @@ -509,6 +520,19 @@ class EventAdmin(BaseImportAdminMixin, admin.ModelAdmin): context['opts'] = self.model._meta return render(request, "admin/import_csv.html", context) +@admin.register(Volunteer) +class VolunteerAdmin(admin.ModelAdmin): + list_display = ('name', 'email', 'phone', 'tenant', 'user') + list_filter = ('tenant',) + search_fields = ('name', 'email', 'phone') + inlines = [VolunteerEventInline, InteractionInline] + filter_horizontal = ('interests',) + +@admin.register(VolunteerEvent) +class VolunteerEventAdmin(admin.ModelAdmin): + list_display = ('volunteer', 'event', 'role') + list_filter = ('event__tenant', 'event', 'role') + @admin.register(EventParticipation) class EventParticipationAdmin(BaseImportAdminMixin, admin.ModelAdmin): list_display = ('voter', 'event', 'participation_type') @@ -912,9 +936,9 @@ class DonationAdmin(BaseImportAdminMixin, admin.ModelAdmin): @admin.register(Interaction) class InteractionAdmin(BaseImportAdminMixin, admin.ModelAdmin): - list_display = ('id', 'voter', 'type', 'date', 'description') - list_filter = ('voter__tenant', 'type', 'date') - search_fields = ('voter__first_name', 'voter__last_name', 'voter__voter_id', 'description') + list_display = ('id', 'voter', 'volunteer', 'type', 'date', 'description') + list_filter = ('voter__tenant', 'type', 'date', 'volunteer') + search_fields = ('voter__first_name', 'voter__last_name', 'voter__voter_id', 'description', 'volunteer__name') change_list_template = "admin/interaction_change_list.html" def get_urls(self): @@ -1308,4 +1332,4 @@ class VoterLikelihoodAdmin(BaseImportAdminMixin, admin.ModelAdmin): @admin.register(CampaignSettings) class CampaignSettingsAdmin(admin.ModelAdmin): list_display = ('tenant', 'donation_goal') - list_filter = ('tenant',) + list_filter = ('tenant',) \ No newline at end of file diff --git a/core/migrations/0013_remove_tenant_description_remove_tenant_slug_and_more.py b/core/migrations/0013_remove_tenant_description_remove_tenant_slug_and_more.py new file mode 100644 index 0000000..4964de2 --- /dev/null +++ b/core/migrations/0013_remove_tenant_description_remove_tenant_slug_and_more.py @@ -0,0 +1,71 @@ +# Generated by Django 5.2.7 on 2026-01-25 16:33 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0012_voter_prior_state_alter_voter_state'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.RemoveField( + model_name='tenant', + name='description', + ), + migrations.RemoveField( + model_name='tenant', + name='slug', + ), + migrations.AlterField( + model_name='tenant', + name='name', + field=models.CharField(max_length=100), + ), + migrations.AlterField( + model_name='tenantuserrole', + name='role', + field=models.CharField(choices=[('admin', 'Admin'), ('campaign_manager', 'Campaign Manager'), ('campaign_staff', 'Campaign Staff')], max_length=20), + ), + migrations.CreateModel( + name='Interest', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('tenant', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='interests', to='core.tenant')), + ], + options={ + 'unique_together': {('tenant', 'name')}, + }, + ), + migrations.CreateModel( + name='Volunteer', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('email', models.EmailField(max_length=254)), + ('phone', models.CharField(blank=True, max_length=20)), + ('interests', models.ManyToManyField(blank=True, related_name='volunteers', to='core.interest')), + ('tenant', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='volunteers', to='core.tenant')), + ('user', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='volunteer_profile', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.AddField( + model_name='interaction', + name='volunteer', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='interactions', to='core.volunteer'), + ), + migrations.CreateModel( + name='VolunteerEvent', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('role', models.CharField(max_length=100)), + ('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='volunteers', to='core.event')), + ('volunteer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='event_assignments', to='core.volunteer')), + ], + ), + ] diff --git a/core/migrations/0014_volunteer_assigned_events_alter_volunteerevent_event.py b/core/migrations/0014_volunteer_assigned_events_alter_volunteerevent_event.py new file mode 100644 index 0000000..34d0459 --- /dev/null +++ b/core/migrations/0014_volunteer_assigned_events_alter_volunteerevent_event.py @@ -0,0 +1,24 @@ +# Generated by Django 5.2.7 on 2026-01-25 16:34 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0013_remove_tenant_description_remove_tenant_slug_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='volunteer', + name='assigned_events', + field=models.ManyToManyField(related_name='assigned_volunteers', through='core.VolunteerEvent', to='core.event'), + ), + migrations.AlterField( + model_name='volunteerevent', + name='event', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='volunteer_assignments', to='core.event'), + ), + ] diff --git a/core/migrations/__pycache__/0013_remove_tenant_description_remove_tenant_slug_and_more.cpython-311.pyc b/core/migrations/__pycache__/0013_remove_tenant_description_remove_tenant_slug_and_more.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d301f1162e9daa73eda5642439c813f9c58b4c0 GIT binary patch literal 3865 zcmb_fJ#Z7-72aL_tz=u4jE%7DXYfx9GGOrtV*(}CP7THS?gZOYgW5r zS2EZ$nW0FLl9?2tPT`P2g^O2EqQ6mA5dp5hX4kdfcMHPbania1`-8W4Vekim2t?F{ya-Q8m&(4pPeh(i z_m>0tKslHXih?982*m$efdpt^Ez~l!}2kYryj^@E&Z9w?yy`w|AAK ze2B}+Ee?^0ukPQHKniZ}IwBD`RwCSz2OB}qiL7A4>6#DV)bun-|#ofb+0 z>Hecgdg{^6*p9UWd*@xGm&CZ8o;D{=;-rt9sP}IHJ}=uC75|5rK}O=%qAhE*cP-W$ zukqT!*;>5K>uZh306AGtbmm-?NL`N9WOz@f84 zj+{Q&kBgo0zZbiMN**QCM=SX-9v@&Y4OkpK8UDjNP?Gbf$R+5NpR(}nR$qiB=`WZT zW#=wk`t-80ZdR$KR4mQ36uYWcsiNx0@%Ws>f>mm$MwN9DY8Nc6g6);}TI{cip_VE0 zd(_rzB?~*pE&&3)IZ{@iC^|Jt)zy^G!qdr@S)iyO`?G=Zd)y2 zsjzU{4(oo0Z*N9<7zeA-d#w<&>l6wuK=H8|;bMHu;{2hgwhgsVhE@AcYLLkaM^jN) zL{Z>pzot_dcPq-HnyO=IDvE_aT&&-ItX3-O3ak@ap$4HwVZ*|A3)Y)i&V8;dFJ-?} zZZFPdZ?W#Sghp-F^(8Hv>vW#iZ6t$-gpVz3f-F3{s(N$QHOp(}HXaVv{Zp-!fkN6> zn}?!+xPJyk6eMe^Q8JlCR+!971EC7dW|n3%b6FP2;&ys!y-?sAITNYJ=vg#q&IIC3 z<)v&+xwCxh7LzlC?7K26bpJ)@&%#>>QTVQ$UNy@!U0Z>&sio-?T7bZ;^kdU{Xjjw% zO(V?dgBPeYG>Gpj7EfF8Dj7g)cYs4Hc=Q$3AOJrUer3bL*Ede^P04->7(Z=)nn5A_ z!-jnJcM(@U`$Bfl{bWbZxN@c$@bw3u-fDIW;nTP_(ny?c#7BS~j=T|o>>+8&9m|Ph zmPb(g6Rw?T^o>26bo<6$6r8@xJAIekzRS&!9}NQ8L(+^0;g-hPM!c=j6odwkraNLfvC4l=+lK5mM4sgE0#AE^rws{1+Mi1t$%}~O#qGIQ)z|CaZn%qzySVBu zJ_3o1ltaP$l8RdQrAIhRT4)F;uz;c=TWa>ofM1{s@dC1kqkpi@-+;iZw1je} zbEpXvG=ZY#veXO*5GEO605bC=DpTyEO*c@H+~mJ-^rhOUH?Z`4yO2T&8`Az`b1HLW+RR| m!Wr+|Xbs-soO6U@zRlo|#b8r7N}fgB*u)!L*gIyGNA+)JnAz3< literal 0 HcmV?d00001 diff --git a/core/migrations/__pycache__/0014_volunteer_assigned_events_alter_volunteerevent_event.cpython-311.pyc b/core/migrations/__pycache__/0014_volunteer_assigned_events_alter_volunteerevent_event.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..60aaa6ba454823fe588858b0749f494e1c03aab3 GIT binary patch literal 1334 zcmZuwJ8u&~5Z-(E?7P?waX*pF&|I7uL2^bdHiLG|cYhE=tJW-R#WH&Ns8)?0w7S3pA&jtOqpAe6Y|D;PRUEadN=QZv2rHivR*BMzW6)PX+lMb2)fARqpXRmHKeK{Bch&V!ZPM7S}Pp` zU=^pZc91?_!$Ib}El@H^jE)U~qQ@)5Xbr@AOG~tDqUB-@4?wo;dGw3f8gey{bHG8K z=}Yn1fe5wkQNo^1O+9>MQsQ{)#0-dQxq*p^U#HDB=jejRx5I{Mx!81gtiJBqp&JlF z*`Ve7&4x>`N!E!Q_-r^?NH+PK!#ri_Z5%bnw{99@QG^v0mZikD0>E)Chp=?8M!m4H z#^k^Qry=?kxm2+swb~>$X89zEz+tIqx|S@PQ8^B;4S;kw8FZcmgmPAC9>j8SkY!8( zZko(6;fy%63FukV{193;PslAy+Qh}gtv88J`9))y3dZxi5UO%>aLIBvUVB`MRc2Id z$aJdD0|!9WBH3UXZdq=_V=}I>jHm}FXX%;BtC`B|95cK&IlZbs&=DY`6T7_TIiwuc zfcLOWK9PDDc(nY{qwoE;RVQUmX8G)jm!Z9n&5JZ7Z89xh*qL9@7PYX{-k|*M3BfP8 zzYk(3dHNdCSERloU)6SA_6?-p`eJl5H+v&vy`iyQVRH9=w=mgxveUdk7!uGGi zcDJzIPpi6dh(H{3!2?Hm;O*YXgWk~fUSXUEj(=Z0Xm(4}-O@t0w9!oc3$v@PQ=w0HFhInx`b<`=$|jebkkAT#`MH5Ix6}Y9e23$ziQXQX>|FNtwINCCqyNRO(Y-Qp$Q{R*cts8W E0BYcLqyPW_ literal 0 HcmV?d00001 diff --git a/core/models.py b/core/models.py index 97216d5..eafb0d4 100644 --- a/core/models.py +++ b/core/models.py @@ -76,6 +76,16 @@ class EventType(models.Model): def __str__(self): return self.name +class Interest(models.Model): + tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE, related_name='interests') + name = models.CharField(max_length=100) + + class Meta: + unique_together = ('tenant', 'name') + + def __str__(self): + return self.name + class Voter(models.Model): SUPPORT_CHOICES = [ ('unknown', 'Unknown'), @@ -240,6 +250,26 @@ class Event(models.Model): def __str__(self): return f"{self.event_type} on {self.date}" +class Volunteer(models.Model): + tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE, related_name='volunteers') + user = models.OneToOneField(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='volunteer_profile') + name = models.CharField(max_length=255) + email = models.EmailField() + phone = models.CharField(max_length=20, blank=True) + interests = models.ManyToManyField(Interest, blank=True, related_name='volunteers') + assigned_events = models.ManyToManyField(Event, through='VolunteerEvent', related_name='assigned_volunteers') + + def __str__(self): + return self.name + +class VolunteerEvent(models.Model): + volunteer = models.ForeignKey(Volunteer, on_delete=models.CASCADE, related_name='event_assignments') + event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name='volunteer_assignments') + role = models.CharField(max_length=100) + + def __str__(self): + return f"{self.volunteer} at {self.event} as {self.role}" + class EventParticipation(models.Model): PARTICIPATION_TYPE_CHOICES = [ ("invited", "Invited"), @@ -265,6 +295,7 @@ class Donation(models.Model): class Interaction(models.Model): voter = models.ForeignKey(Voter, on_delete=models.CASCADE, related_name='interactions') + volunteer = models.ForeignKey(Volunteer, on_delete=models.SET_NULL, null=True, blank=True, related_name='interactions') type = models.ForeignKey(InteractionType, on_delete=models.SET_NULL, null=True) date = models.DateField() description = models.CharField(max_length=255)