diff --git a/ERD.md b/ERD.md index a5aac2c..68315ba 100644 --- a/ERD.md +++ b/ERD.md @@ -7,6 +7,7 @@ erDiagram Tenant ||--o{ DonationMethod : defines Tenant ||--o{ ElectionType : defines Tenant ||--o{ EventType : defines + Tenant ||--o{ ParticipationStatus : defines Tenant ||--o{ Voter : belongs_to Tenant ||--o{ Event : organizes @@ -20,6 +21,7 @@ erDiagram Event ||--o{ EventParticipation : includes EventType ||--o{ Event : categorizes + ParticipationStatus ||--o{ EventParticipation : defines_status InteractionType ||--o{ Interaction : categorizes DonationMethod ||--o{ Donation : categorizes @@ -76,6 +78,13 @@ erDiagram boolean is_active } + ParticipationStatus { + int id PK + int tenant_id FK + string name + boolean is_active + } + Voter { int id PK int tenant_id FK @@ -121,7 +130,7 @@ erDiagram int id PK int event_id FK int voter_id FK - string participation_type + int participation_status_id FK } Donation { @@ -147,4 +156,4 @@ erDiagram 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 cf83c17..5ea9b26 100644 Binary files a/core/__pycache__/admin.cpython-311.pyc and b/core/__pycache__/admin.cpython-311.pyc differ diff --git a/core/__pycache__/forms.cpython-311.pyc b/core/__pycache__/forms.cpython-311.pyc index aaf24e4..73a5120 100644 Binary files a/core/__pycache__/forms.cpython-311.pyc and b/core/__pycache__/forms.cpython-311.pyc differ diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index 9509470..42a3f85 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 3af98a6..02ac347 100644 --- a/core/admin.py +++ b/core/admin.py @@ -12,7 +12,7 @@ from django.template.response import TemplateResponse from .models import ( Tenant, TenantUserRole, InteractionType, DonationMethod, ElectionType, EventType, Voter, VotingRecord, Event, EventParticipation, Donation, Interaction, VoterLikelihood, CampaignSettings, - Interest, Volunteer, VolunteerEvent + Interest, Volunteer, VolunteerEvent, ParticipationStatus ) from .forms import ( VoterImportForm, EventImportForm, EventParticipationImportForm, @@ -47,7 +47,10 @@ VOTER_MAPPABLE_FIELDS = [ ] EVENT_MAPPABLE_FIELDS = [ + ('name', 'Name'), ('date', 'Date'), + ('start_time', 'Start Time'), + ('end_time', 'End Time'), ('event_type', 'Event Type (Name)'), ('description', 'Description'), ] @@ -57,7 +60,7 @@ EVENT_PARTICIPATION_MAPPABLE_FIELDS = [ ('event_id', 'Event ID'), ('event_date', 'Event Date'), ('event_type', 'Event Type (Name)'), - ('participation_type', 'Participation Type'), + ('participation_status', 'Participation Type'), ] DONATION_MAPPABLE_FIELDS = [ @@ -148,6 +151,20 @@ class EventTypeAdmin(admin.ModelAdmin): list_filter = ('tenant', 'is_active') search_fields = ('name',) + +@admin.register(ParticipationStatus) +class ParticipationStatusAdmin(admin.ModelAdmin): + list_display = ('name', 'tenant', 'is_active') + list_filter = ('tenant', 'is_active') + search_fields = ('name',) + change_list_template = 'admin/participationstatus_change_list.html' + + def changelist_view(self, request, extra_context=None): + extra_context = extra_context or {} + from core.models import Tenant + extra_context['tenants'] = Tenant.objects.all() + return super().changelist_view(request, extra_context=extra_context) + @admin.register(Interest) class InterestAdmin(admin.ModelAdmin): list_display = ('name', 'tenant') @@ -182,6 +199,12 @@ class VoterAdmin(BaseImportAdminMixin, admin.ModelAdmin): inlines = [VotingRecordInline, DonationInline, InteractionInline, VoterLikelihoodInline] readonly_fields = ('address',) change_list_template = "admin/voter_change_list.html" + + def changelist_view(self, request, extra_context=None): + extra_context = extra_context or {} + from core.models import Tenant + extra_context["tenants"] = Tenant.objects.all() + return super().changelist_view(request, extra_context=extra_context) def get_urls(self): urls = super().get_urls() @@ -352,9 +375,16 @@ class VoterAdmin(BaseImportAdminMixin, admin.ModelAdmin): @admin.register(Event) class EventAdmin(BaseImportAdminMixin, admin.ModelAdmin): - list_display = ('id', 'event_type', 'date', 'tenant') + list_display = ('id', 'name', 'event_type', 'date', 'start_time', 'end_time', 'tenant') list_filter = ('tenant', 'date', 'event_type') + search_fields = ('name', 'description') change_list_template = "admin/event_change_list.html" + + def changelist_view(self, request, extra_context=None): + extra_context = extra_context or {} + from core.models import Tenant + extra_context["tenants"] = Tenant.objects.all() + return super().changelist_view(request, extra_context=extra_context) def get_urls(self): urls = super().get_urls() @@ -384,9 +414,13 @@ class EventAdmin(BaseImportAdminMixin, admin.ModelAdmin): total_count += 1 date = row.get(mapping.get('date')) event_type_name = row.get(mapping.get('event_type')) + event_name = row.get(mapping.get('name')) exists = False if date and event_type_name: - exists = Event.objects.filter(tenant=tenant, date=date, event_type__name=event_type_name).exists() + q = Event.objects.filter(tenant=tenant, date=date, event_type__name=event_type_name) + if event_name: + q = q.filter(name=event_name) + exists = q.exists() if exists: update_count += 1 @@ -398,7 +432,7 @@ class EventAdmin(BaseImportAdminMixin, admin.ModelAdmin): if len(preview_data) < 10: preview_data.append({ 'action': action, - 'identifier': f"{date} - {event_type_name}", + 'identifier': f"{event_name or 'No Name'} ({date} - {event_type_name})", 'details': row.get(mapping.get('description', '')) or '' }) context = self.admin_site.each_context(request) @@ -439,6 +473,9 @@ class EventAdmin(BaseImportAdminMixin, admin.ModelAdmin): date = row.get(mapping.get('date')) if mapping.get('date') else None event_type_name = row.get(mapping.get('event_type')) if mapping.get('event_type') else None description = row.get(mapping.get('description')) if mapping.get('description') else None + name = row.get(mapping.get('name')) if mapping.get('name') else None + start_time = row.get(mapping.get('start_time')) if mapping.get('start_time') else None + end_time = row.get(mapping.get('end_time')) if mapping.get('end_time') else None if not date or not event_type_name: row["Import Error"] = "Missing date or event type" @@ -454,11 +491,18 @@ class EventAdmin(BaseImportAdminMixin, admin.ModelAdmin): defaults = {} if description and description.strip(): defaults['description'] = description + if name and name.strip(): + defaults['name'] = name + if start_time and start_time.strip(): + defaults['start_time'] = start_time + if end_time and end_time.strip(): + defaults['end_time'] = end_time Event.objects.update_or_create( tenant=tenant, date=date, event_type=event_type, + name=name or '', defaults=defaults ) count += 1 @@ -535,8 +579,8 @@ class VolunteerEventAdmin(admin.ModelAdmin): @admin.register(EventParticipation) class EventParticipationAdmin(BaseImportAdminMixin, admin.ModelAdmin): - list_display = ('voter', 'event', 'participation_type') - list_filter = ('event__tenant', 'event', 'participation_type') + list_display = ('voter', 'event', 'participation_status') + list_filter = ('event__tenant', 'event', 'participation_status') change_list_template = "admin/eventparticipation_change_list.html" def get_urls(self): @@ -592,7 +636,7 @@ class EventParticipationAdmin(BaseImportAdminMixin, admin.ModelAdmin): preview_data.append({ 'action': action, 'identifier': f"Voter: {voter_id}", - 'details': f"Participation: {row.get(mapping.get('participation_type', '')) or ''}" + 'details': f"Participation: {row.get(mapping.get('participation_status', '')) or ''}" }) context = self.admin_site.each_context(request) context.update({ @@ -630,7 +674,7 @@ class EventParticipationAdmin(BaseImportAdminMixin, admin.ModelAdmin): for row in reader: try: voter_id = row.get(mapping.get('voter_id')) if mapping.get('voter_id') else None - participation_type_val = row.get(mapping.get('participation_type')) if mapping.get('participation_type') else None + participation_status_val = row.get(mapping.get('participation_status')) if mapping.get('participation_status') else None if not voter_id: row["Import Error"] = "Missing voter ID" @@ -675,11 +719,11 @@ class EventParticipationAdmin(BaseImportAdminMixin, admin.ModelAdmin): continue defaults = {} - if participation_type_val and participation_type_val.strip(): - if participation_type_val in dict(EventParticipation.PARTICIPATION_TYPE_CHOICES): - defaults['participation_type'] = participation_type_val + if participation_status_val and participation_status_val.strip(): + if participation_status_val in dict(EventParticipation.PARTICIPATION_TYPE_CHOICES): + defaults['participation_status'] = participation_status_val else: - defaults['participation_type'] = 'invited' + defaults['participation_status'] = 'invited' EventParticipation.objects.update_or_create( event=event, @@ -1332,4 +1376,4 @@ class VoterLikelihoodAdmin(BaseImportAdminMixin, admin.ModelAdmin): @admin.register(CampaignSettings) class CampaignSettingsAdmin(admin.ModelAdmin): list_display = ('tenant', 'donation_goal') - list_filter = ('tenant',) \ No newline at end of file + list_filter = ('tenant',) diff --git a/core/forms.py b/core/forms.py index 8e811db..9afddb6 100644 --- a/core/forms.py +++ b/core/forms.py @@ -1,5 +1,5 @@ from django import forms -from .models import Voter, Interaction, Donation, VoterLikelihood, InteractionType, DonationMethod, ElectionType, Event, EventParticipation, EventType, Tenant +from .models import Voter, Interaction, Donation, VoterLikelihood, InteractionType, DonationMethod, ElectionType, Event, EventParticipation, EventType, Tenant, ParticipationStatus class VoterForm(forms.ModelForm): class Meta: @@ -81,23 +81,26 @@ class VoterLikelihoodForm(forms.ModelForm): class EventParticipationForm(forms.ModelForm): class Meta: model = EventParticipation - fields = ['event', 'participation_type'] + fields = ['event', 'participation_status'] def __init__(self, *args, tenant=None, **kwargs): super().__init__(*args, **kwargs) if tenant: self.fields['event'].queryset = Event.objects.filter(tenant=tenant) + self.fields['participation_status'].queryset = ParticipationStatus.objects.filter(tenant=tenant, is_active=True) for field in self.fields.values(): field.widget.attrs.update({'class': 'form-control'}) self.fields['event'].widget.attrs.update({'class': 'form-select'}) - self.fields['participation_type'].widget.attrs.update({'class': 'form-select'}) + self.fields['participation_status'].widget.attrs.update({'class': 'form-select'}) class EventForm(forms.ModelForm): class Meta: model = Event - fields = ['date', 'event_type', 'description'] + fields = ['name', 'date', 'start_time', 'end_time', 'event_type', 'description'] widgets = { 'date': forms.DateInput(attrs={'type': 'date'}), + 'start_time': forms.TimeInput(attrs={'type': 'time'}), + 'end_time': forms.TimeInput(attrs={'type': 'time'}), 'description': forms.Textarea(attrs={'rows': 2}), } @@ -161,4 +164,4 @@ class VoterLikelihoodImportForm(forms.Form): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['tenant'].widget.attrs.update({'class': 'form-control form-select'}) - self.fields['file'].widget.attrs.update({'class': 'form-control'}) + self.fields['file'].widget.attrs.update({'class': 'form-control'}) \ No newline at end of file diff --git a/core/migrations/0015_remove_eventparticipation_participation_type_and_more.py b/core/migrations/0015_remove_eventparticipation_participation_type_and_more.py new file mode 100644 index 0000000..458f78f --- /dev/null +++ b/core/migrations/0015_remove_eventparticipation_participation_type_and_more.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.7 on 2026-01-25 18:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0014_volunteer_assigned_events_alter_volunteerevent_event'), + ] + + operations = [ + migrations.RenameField( + model_name='eventparticipation', + old_name='participation_type', + new_name='participation_status', + ), + ] \ No newline at end of file diff --git a/core/migrations/0016_alter_eventparticipation_participation_status_and_more.py b/core/migrations/0016_alter_eventparticipation_participation_status_and_more.py new file mode 100644 index 0000000..95d46a5 --- /dev/null +++ b/core/migrations/0016_alter_eventparticipation_participation_status_and_more.py @@ -0,0 +1,37 @@ +# Generated by Django 5.2.7 on 2026-01-25 18:51 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0015_remove_eventparticipation_participation_type_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='eventparticipation', + name='participation_status', + field=models.CharField(blank=True, max_length=50), + ), + migrations.CreateModel( + name='ParticipationStatus', + 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_statuses', to='core.tenant')), + ], + options={ + 'verbose_name_plural': 'Participation Statuses', + 'unique_together': {('tenant', 'name')}, + }, + ), + migrations.AddField( + model_name='eventparticipation', + name='participation_status_link', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='core.participationstatus'), + ), + ] diff --git a/core/migrations/0017_remove_eventparticipation_participation_status_link_and_more.py b/core/migrations/0017_remove_eventparticipation_participation_status_link_and_more.py new file mode 100644 index 0000000..7ae907b --- /dev/null +++ b/core/migrations/0017_remove_eventparticipation_participation_status_link_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 5.2.7 on 2026-01-25 18:52 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0016_alter_eventparticipation_participation_status_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='eventparticipation', + name='participation_status', + ), + migrations.RenameField( + model_name='eventparticipation', + old_name='participation_status_link', + new_name='participation_status', + ), + ] \ No newline at end of file diff --git a/core/migrations/0018_event_end_time_event_name_event_start_time.py b/core/migrations/0018_event_end_time_event_name_event_start_time.py new file mode 100644 index 0000000..dfaa7c4 --- /dev/null +++ b/core/migrations/0018_event_end_time_event_name_event_start_time.py @@ -0,0 +1,28 @@ +# Generated by Django 5.2.7 on 2026-01-25 19:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0017_remove_eventparticipation_participation_status_link_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='event', + name='end_time', + field=models.TimeField(blank=True, null=True), + ), + migrations.AddField( + model_name='event', + name='name', + field=models.CharField(blank=True, max_length=255), + ), + migrations.AddField( + model_name='event', + name='start_time', + field=models.TimeField(blank=True, null=True), + ), + ] diff --git a/core/migrations/__pycache__/0015_remove_eventparticipation_participation_type_and_more.cpython-311.pyc b/core/migrations/__pycache__/0015_remove_eventparticipation_participation_type_and_more.cpython-311.pyc new file mode 100644 index 0000000..5b39ca6 Binary files /dev/null and b/core/migrations/__pycache__/0015_remove_eventparticipation_participation_type_and_more.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/0016_alter_eventparticipation_participation_status_and_more.cpython-311.pyc b/core/migrations/__pycache__/0016_alter_eventparticipation_participation_status_and_more.cpython-311.pyc new file mode 100644 index 0000000..d7b26f4 Binary files /dev/null and b/core/migrations/__pycache__/0016_alter_eventparticipation_participation_status_and_more.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/0016_participationstatus_and_more.cpython-311.pyc b/core/migrations/__pycache__/0016_participationstatus_and_more.cpython-311.pyc new file mode 100644 index 0000000..dd88a24 Binary files /dev/null and b/core/migrations/__pycache__/0016_participationstatus_and_more.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/0017_remove_eventparticipation_participation_status_link_and_more.cpython-311.pyc b/core/migrations/__pycache__/0017_remove_eventparticipation_participation_status_link_and_more.cpython-311.pyc new file mode 100644 index 0000000..b1a1754 Binary files /dev/null and b/core/migrations/__pycache__/0017_remove_eventparticipation_participation_status_link_and_more.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/0018_event_end_time_event_name_event_start_time.cpython-311.pyc b/core/migrations/__pycache__/0018_event_end_time_event_name_event_start_time.cpython-311.pyc new file mode 100644 index 0000000..9aea5af Binary files /dev/null and b/core/migrations/__pycache__/0018_event_end_time_event_name_event_start_time.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index eafb0d4..5c0a55b 100644 --- a/core/models.py +++ b/core/models.py @@ -6,9 +6,21 @@ import urllib.request import logging from decimal import Decimal from django.conf import settings +import re logger = logging.getLogger(__name__) +def format_phone_number(phone): + """Formats a phone number to (xxx) xxx-xxxx if it has 10 digits or 11 starting with 1.""" + if not phone: + return phone + digits = re.sub(r'\D', '', str(phone)) + if len(digits) == 10: + return f"({digits[:3]}) {digits[3:6]}-{digits[6:]}" + elif len(digits) == 11 and digits.startswith('1'): + return f"({digits[1:4]}) {digits[4:7]}-{digits[7:]}" + return phone + class Tenant(models.Model): name = models.CharField(max_length=100) created_at = models.DateTimeField(auto_now_add=True) @@ -76,6 +88,18 @@ class EventType(models.Model): def __str__(self): return self.name +class ParticipationStatus(models.Model): + tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE, related_name='participation_statuses') + name = models.CharField(max_length=100) + is_active = models.BooleanField(default=True) + + class Meta: + unique_together = ('tenant', 'name') + verbose_name_plural = 'Participation Statuses' + + 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) @@ -189,6 +213,9 @@ class Voter(models.Model): return False, err def save(self, *args, **kwargs): + # Auto-format phone number + self.phone = format_phone_number(self.phone) + # Ensure longitude is truncated to 12 characters before saving if self.longitude: self.longitude = Decimal(str(self.longitude)[:12]) @@ -243,11 +270,16 @@ class VotingRecord(models.Model): class Event(models.Model): tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE, related_name='events') + name = models.CharField(max_length=255, blank=True) date = models.DateField() + start_time = models.TimeField(null=True, blank=True) + end_time = models.TimeField(null=True, blank=True) event_type = models.ForeignKey(EventType, on_delete=models.PROTECT, null=True) description = models.TextField(blank=True) def __str__(self): + if self.name: + return f"{self.name} ({self.date})" return f"{self.event_type} on {self.date}" class Volunteer(models.Model): @@ -259,6 +291,11 @@ class Volunteer(models.Model): interests = models.ManyToManyField(Interest, blank=True, related_name='volunteers') assigned_events = models.ManyToManyField(Event, through='VolunteerEvent', related_name='assigned_volunteers') + def save(self, *args, **kwargs): + # Auto-format phone number + self.phone = format_phone_number(self.phone) + super().save(*args, **kwargs) + def __str__(self): return self.name @@ -271,18 +308,12 @@ class VolunteerEvent(models.Model): return f"{self.volunteer} at {self.event} as {self.role}" class EventParticipation(models.Model): - PARTICIPATION_TYPE_CHOICES = [ - ("invited", "Invited"), - ("invited_not_attended", "Invited but didn't attend"), - ("attended", "Attended"), - ] - 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.CharField(max_length=50, choices=PARTICIPATION_TYPE_CHOICES, default='invited') + participation_status = models.ForeignKey(ParticipationStatus, on_delete=models.PROTECT, null=True) def __str__(self): - return f"{self.voter} at {self.event} ({self.get_participation_type_display()})" + return f"{self.voter} at {self.event} ({self.participation_status})" class Donation(models.Model): voter = models.ForeignKey(Voter, on_delete=models.CASCADE, related_name='donations') @@ -328,4 +359,4 @@ class CampaignSettings(models.Model): verbose_name_plural = 'Campaign Settings' def __str__(self): - return f'Settings for {self.tenant.name}' + return f'Settings for {self.tenant.name}' \ No newline at end of file diff --git a/core/templates/admin/event_change_list.html b/core/templates/admin/event_change_list.html index df48ce9..b0d7076 100644 --- a/core/templates/admin/event_change_list.html +++ b/core/templates/admin/event_change_list.html @@ -1,7 +1,38 @@ {% extends "admin/change_list.html" %} +{% load i18n admin_urls static admin_list %} + {% block object-tools-items %}
  • Import Events
  • {{ block.super }} {% endblock %} + +{% block search %} + {{ block.super }} +
    + + +
    + + +{% endblock %} \ No newline at end of file diff --git a/core/templates/admin/participationstatus_change_list.html b/core/templates/admin/participationstatus_change_list.html new file mode 100644 index 0000000..4d3b166 --- /dev/null +++ b/core/templates/admin/participationstatus_change_list.html @@ -0,0 +1,31 @@ +{% extends "admin/change_list.html" %} +{% load i18n admin_urls static admin_list %} + +{% block search %} + {{ block.super }} +
    + + +
    + + +{% endblock %} \ No newline at end of file diff --git a/core/templates/admin/voter_change_list.html b/core/templates/admin/voter_change_list.html index eb36f95..c2393d1 100644 --- a/core/templates/admin/voter_change_list.html +++ b/core/templates/admin/voter_change_list.html @@ -1,7 +1,38 @@ {% extends "admin/change_list.html" %} +{% load i18n admin_urls static admin_list %} + {% block object-tools-items %}
  • Import Voters
  • {{ block.super }} {% endblock %} + +{% block search %} + {{ block.super }} +
    + + +
    + + +{% endblock %} \ No newline at end of file diff --git a/core/templates/base.html b/core/templates/base.html index c922003..d42c9a2 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -74,5 +74,35 @@ + + \ No newline at end of file diff --git a/core/templates/core/voter_detail.html b/core/templates/core/voter_detail.html index 2dab959..0e1f86f 100644 --- a/core/templates/core/voter_detail.html +++ b/core/templates/core/voter_detail.html @@ -327,7 +327,7 @@ Date Event Type - Type + Status Description Actions @@ -338,12 +338,12 @@ {{ participation.event.date|date:"M d, Y" }} {{ participation.event.event_type.name }} - {% if participation.participation_type == 'attended' %} + {% if participation.participation_status.name|lower == 'attended' %} Attended - {% elif participation.participation_type == 'invited_not_attended' %} + {% elif participation.participation_status.name|lower == "invited but didn't attend" or participation.participation_status.name|lower == "invited but didn't attend" %} Did Not Attend {% else %} - Invited + {{ participation.participation_status.name }} {% endif %} {{ participation.event.description|truncatechars:60 }} @@ -799,7 +799,7 @@
    - {{ event_participation_form.participation_type }} + {{ event_participation_form.participation_status }}
    - + {% for status in event_participation_form.fields.participation_status.queryset %} + {% endfor %}
    @@ -998,4 +998,4 @@ } }); -{% endblock %} +{% endblock %} \ No newline at end of file