diff --git a/ERD.md b/ERD.md new file mode 100644 index 0000000..b1bb0cf --- /dev/null +++ b/ERD.md @@ -0,0 +1,151 @@ +# Entity Relationship Diagram + +```mermaid +erDiagram + Tenant ||--o{ TenantUserRole : has + Tenant ||--o{ InteractionType : defines + Tenant ||--o{ DonationMethod : defines + Tenant ||--o{ ElectionType : defines + Tenant ||--o{ EventType : defines + Tenant ||--o{ ParticipationType : defines + Tenant ||--o{ Voter : belongs_to + Tenant ||--o{ Event : organizes + + User ||--o{ TenantUserRole : assigned_to + + Voter ||--o{ VotingRecord : has + Voter ||--o{ EventParticipation : participates + Voter ||--o{ Donation : makes + Voter ||--o{ Interaction : receives + Voter ||--o{ VoterLikelihood : has + + Event ||--o{ EventParticipation : includes + EventType ||--o{ Event : categorizes + + InteractionType ||--o{ Interaction : categorizes + DonationMethod ||--o{ Donation : categorizes + ElectionType ||--o{ VoterLikelihood : categorizes + ParticipationType ||--o{ EventParticipation : categorizes + + Tenant { + int id PK + string name + string slug + text description + datetime created_at + } + + User { + int id PK + string username + string email + string first_name + string last_name + } + + TenantUserRole { + int id PK + int user_id FK + int tenant_id FK + string role + } + + InteractionType { + int id PK + int tenant_id FK + string name + boolean is_active + } + + DonationMethod { + int id PK + int tenant_id FK + string name + boolean is_active + } + + ElectionType { + int id PK + int tenant_id FK + string name + boolean is_active + } + + EventType { + int id PK + int tenant_id FK + string name + boolean is_active + } + + ParticipationType { + int id PK + int tenant_id FK + string name + boolean is_active + } + + Voter { + int id PK + int tenant_id FK + string voter_id + string first_name + string last_name + text address + string phone + string email + string district + string precinct + date registration_date + string candidate_support + string yard_sign + datetime created_at + } + + VotingRecord { + int id PK + int voter_id FK + date election_date + string election_description + string primary_party + } + + Event { + int id PK + int tenant_id FK + date date + int event_type_id FK + text description + } + + EventParticipation { + int id PK + int event_id FK + int voter_id FK + int participation_type_id FK + } + + Donation { + int id PK + int voter_id FK + date date + int method_id FK + decimal amount + } + + Interaction { + int id PK + int voter_id FK + int type_id FK + date date + string description + text notes + } + + VoterLikelihood { + int id PK + int voter_id FK + int election_type_id FK + string likelihood + } +``` \ No newline at end of file diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index c60c979..b6b7208 100644 Binary files a/core/__pycache__/admin.cpython-311.pyc and b/core/__pycache__/admin.cpython-311.pyc differ diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index 2c3fe0d..887c76e 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/admin.py b/core/admin.py index ddcba09..1f6d628 100644 --- a/core/admin.py +++ b/core/admin.py @@ -1,7 +1,8 @@ from django.contrib import admin from .models import ( - Tenant, TenantUserRole, InteractionType, DonationMethod, ElectionType, EventType, Voter, - VotingRecord, Event, EventParticipation, Donation, Interaction, VoterLikelihood + Tenant, TenantUserRole, InteractionType, DonationMethod, ElectionType, EventType, + ParticipationType, Voter, VotingRecord, Event, EventParticipation, Donation, + Interaction, VoterLikelihood ) class TenantUserRoleInline(admin.TabularInline): @@ -44,6 +45,12 @@ class EventTypeAdmin(admin.ModelAdmin): list_filter = ('tenant', 'is_active') search_fields = ('name',) +@admin.register(ParticipationType) +class ParticipationTypeAdmin(admin.ModelAdmin): + list_display = ('name', 'tenant', 'is_active') + list_filter = ('tenant', 'is_active') + search_fields = ('name',) + class VotingRecordInline(admin.TabularInline): model = VotingRecord extra = 1 @@ -74,5 +81,5 @@ class EventAdmin(admin.ModelAdmin): @admin.register(EventParticipation) class EventParticipationAdmin(admin.ModelAdmin): - list_display = ('voter', 'event') - list_filter = ('event__tenant', 'event') + list_display = ('voter', 'event', 'participation_type') + list_filter = ('event__tenant', 'event', 'participation_type') \ No newline at end of file diff --git a/core/migrations/0005_participationtype_and_more.py b/core/migrations/0005_participationtype_and_more.py new file mode 100644 index 0000000..d89b0b9 --- /dev/null +++ b/core/migrations/0005_participationtype_and_more.py @@ -0,0 +1,31 @@ +# Generated by Django 5.2.7 on 2026-01-24 15:43 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0004_remove_voter_geocode_donationmethod_is_active_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='ParticipationType', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('is_active', models.BooleanField(default=True)), + ('tenant', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='participation_types', to='core.tenant')), + ], + options={ + 'unique_together': {('tenant', 'name')}, + }, + ), + migrations.AddField( + model_name='eventparticipation', + name='participation_type', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.participationtype'), + ), + ] diff --git a/core/migrations/__pycache__/0005_participationtype_and_more.cpython-311.pyc b/core/migrations/__pycache__/0005_participationtype_and_more.cpython-311.pyc new file mode 100644 index 0000000..557418f Binary files /dev/null and b/core/migrations/__pycache__/0005_participationtype_and_more.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index 92d0a64..cd5acd0 100644 --- a/core/models.py +++ b/core/models.py @@ -41,7 +41,7 @@ class InteractionType(models.Model): unique_together = ('tenant', 'name') def __str__(self): - return f"{self.name} ({self.tenant.name})" + return self.name class DonationMethod(models.Model): tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE, related_name='donation_methods') @@ -52,7 +52,7 @@ class DonationMethod(models.Model): unique_together = ('tenant', 'name') def __str__(self): - return f"{self.name} ({self.tenant.name})" + return self.name class ElectionType(models.Model): tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE, related_name='election_types') @@ -63,7 +63,7 @@ class ElectionType(models.Model): unique_together = ('tenant', 'name') def __str__(self): - return f"{self.name} ({self.tenant.name})" + return self.name class EventType(models.Model): tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE, related_name='event_types') @@ -74,7 +74,18 @@ class EventType(models.Model): unique_together = ('tenant', 'name') def __str__(self): - return f"{self.name} ({self.tenant.name})" + return self.name + +class ParticipationType(models.Model): + tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE, related_name='participation_types') + name = models.CharField(max_length=100) + is_active = models.BooleanField(default=True) + + class Meta: + unique_together = ('tenant', 'name') + + def __str__(self): + return self.name class Voter(models.Model): SUPPORT_CHOICES = [ @@ -127,6 +138,7 @@ class Event(models.Model): class EventParticipation(models.Model): event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name='participations') voter = models.ForeignKey(Voter, on_delete=models.CASCADE, related_name='event_participations') + participation_type = models.ForeignKey(ParticipationType, on_delete=models.SET_NULL, null=True, blank=True) def __str__(self): return f"{self.voter} at {self.event}" @@ -164,4 +176,4 @@ class VoterLikelihood(models.Model): unique_together = ('voter', 'election_type') def __str__(self): - return f"{self.voter} - {self.election_type}: {self.get_likelihood_display()}" + return f"{self.voter} - {self.election_type}: {self.get_likelihood_display()}" \ No newline at end of file