Autosave: 20260124-155519

This commit is contained in:
Flatlogic Bot 2026-01-24 15:55:19 +00:00
parent aade4cc131
commit e2ad03e446
7 changed files with 210 additions and 9 deletions

151
ERD.md Normal file
View File

@ -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
}
```

View File

@ -1,7 +1,8 @@
from django.contrib import admin from django.contrib import admin
from .models import ( from .models import (
Tenant, TenantUserRole, InteractionType, DonationMethod, ElectionType, EventType, Voter, Tenant, TenantUserRole, InteractionType, DonationMethod, ElectionType, EventType,
VotingRecord, Event, EventParticipation, Donation, Interaction, VoterLikelihood ParticipationType, Voter, VotingRecord, Event, EventParticipation, Donation,
Interaction, VoterLikelihood
) )
class TenantUserRoleInline(admin.TabularInline): class TenantUserRoleInline(admin.TabularInline):
@ -44,6 +45,12 @@ class EventTypeAdmin(admin.ModelAdmin):
list_filter = ('tenant', 'is_active') list_filter = ('tenant', 'is_active')
search_fields = ('name',) 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): class VotingRecordInline(admin.TabularInline):
model = VotingRecord model = VotingRecord
extra = 1 extra = 1
@ -74,5 +81,5 @@ class EventAdmin(admin.ModelAdmin):
@admin.register(EventParticipation) @admin.register(EventParticipation)
class EventParticipationAdmin(admin.ModelAdmin): class EventParticipationAdmin(admin.ModelAdmin):
list_display = ('voter', 'event') list_display = ('voter', 'event', 'participation_type')
list_filter = ('event__tenant', 'event') list_filter = ('event__tenant', 'event', 'participation_type')

View File

@ -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'),
),
]

View File

@ -41,7 +41,7 @@ class InteractionType(models.Model):
unique_together = ('tenant', 'name') unique_together = ('tenant', 'name')
def __str__(self): def __str__(self):
return f"{self.name} ({self.tenant.name})" return self.name
class DonationMethod(models.Model): class DonationMethod(models.Model):
tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE, related_name='donation_methods') tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE, related_name='donation_methods')
@ -52,7 +52,7 @@ class DonationMethod(models.Model):
unique_together = ('tenant', 'name') unique_together = ('tenant', 'name')
def __str__(self): def __str__(self):
return f"{self.name} ({self.tenant.name})" return self.name
class ElectionType(models.Model): class ElectionType(models.Model):
tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE, related_name='election_types') tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE, related_name='election_types')
@ -63,7 +63,7 @@ class ElectionType(models.Model):
unique_together = ('tenant', 'name') unique_together = ('tenant', 'name')
def __str__(self): def __str__(self):
return f"{self.name} ({self.tenant.name})" return self.name
class EventType(models.Model): class EventType(models.Model):
tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE, related_name='event_types') tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE, related_name='event_types')
@ -74,7 +74,18 @@ class EventType(models.Model):
unique_together = ('tenant', 'name') unique_together = ('tenant', 'name')
def __str__(self): 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): class Voter(models.Model):
SUPPORT_CHOICES = [ SUPPORT_CHOICES = [
@ -127,6 +138,7 @@ class Event(models.Model):
class EventParticipation(models.Model): class EventParticipation(models.Model):
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name='participations') event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name='participations')
voter = models.ForeignKey(Voter, on_delete=models.CASCADE, related_name='event_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): def __str__(self):
return f"{self.voter} at {self.event}" return f"{self.voter} at {self.event}"
@ -164,4 +176,4 @@ class VoterLikelihood(models.Model):
unique_together = ('voter', 'election_type') unique_together = ('voter', 'election_type')
def __str__(self): 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()}"