Autosave: 20260124-155519
This commit is contained in:
parent
aade4cc131
commit
e2ad03e446
151
ERD.md
Normal file
151
ERD.md
Normal 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
|
||||||
|
}
|
||||||
|
```
|
||||||
Binary file not shown.
Binary file not shown.
@ -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')
|
||||||
31
core/migrations/0005_participationtype_and_more.py
Normal file
31
core/migrations/0005_participationtype_and_more.py
Normal 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'),
|
||||||
|
),
|
||||||
|
]
|
||||||
Binary file not shown.
@ -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()}"
|
||||||
Loading…
x
Reference in New Issue
Block a user