From e0f6e045f3a6397ddb836d85a59adb045c07e3f2 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Wed, 4 Feb 2026 22:19:54 +0000 Subject: [PATCH] Autosave: 20260204-221954 --- core/__pycache__/views.cpython-311.pyc | Bin 95475 -> 95503 bytes core/templates/core/door_visit_history.html | 19 +- .../templates/core/voter_advanced_search.html | 3 - core/templates/core/voter_list.html | 3 - core/views.py | 2 +- door_views_update.py | 221 ++---------------- 6 files changed, 26 insertions(+), 222 deletions(-) diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index f6e7da6653c67cd6be0c789f8fd2cad2b29682aa..7430a50bcca80891c3bea808f8c1fd0f04ada6f5 100644 GIT binary patch delta 277 zcmezTlC}R8EAMh%UM>b8=&5eXT-?aJm6vg;1>=$Fjv977^dHOzk#!yD1=^LXMMW@f#VHBKh<-jP&n7-Y|fl-f{(PVO< zvC8xUB}UfiOPm=uu@*T34c#8*!l=f`o;hUXUBue&lXV`NWa{Kz23nzX&e voiUJ6LvfGF0mCCU7aZb2C@Ab8nB~xvIlGZ}D=*_x3&w5J9W@wjw!gPvtYT*L-kxQ{n94N$ zwjHCa$}&aXO!fRMv~xKpa+)dvS$=# z)StZ3OmzBYdqxSy#OY`38ABNjCo5KnPM@#CC^+52fl-h#b$gTpqaHJ(@#H{bmFWda zjI7f)IWunBp6SA<#>i-~e1a<@6MGcnM+Q08sO{6;83P&R9ryTLu#5nr6NYE3A*_oW Ukykh(FK|Qx8QTRs8UJeo0C1{BmjD0& diff --git a/core/templates/core/door_visit_history.html b/core/templates/core/door_visit_history.html index 1d8537e..e4d2c46 100644 --- a/core/templates/core/door_visit_history.html +++ b/core/templates/core/door_visit_history.html @@ -103,18 +103,18 @@ {% endif %}
- {% with voters=household.voters_at_address %} - {{ voters|join:", " }} - {% endwith %} + {% for v_id, v_name in household.voters_at_address %} + {{ v_name }}{% if not forloop.last %}, {% endif %} + {% endfor %}
- {% for voter_name in household.voters_at_address %} - - {{ voter_name }} - + {% for v_id, v_name in household.voters_at_address %} + + {{ v_name }} + {% endfor %}
@@ -214,6 +214,11 @@ .text-info { color: #055160 !important; } + .voter-badge:hover { + background-color: #e9ecef !important; + border-color: #adb5bd !important; + box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); + } @media (max-width: 576px) { .container-fluid { padding-left: 0.75rem !important; diff --git a/core/templates/core/voter_advanced_search.html b/core/templates/core/voter_advanced_search.html index 594536d..6305f05 100644 --- a/core/templates/core/voter_advanced_search.html +++ b/core/templates/core/voter_advanced_search.html @@ -6,9 +6,6 @@

Advanced Voter Search

- Back to Registry
diff --git a/core/templates/core/voter_list.html b/core/templates/core/voter_list.html index 0e5ab06..a8d3bb4 100644 --- a/core/templates/core/voter_list.html +++ b/core/templates/core/voter_list.html @@ -6,9 +6,6 @@

Voter Registry

- Advanced Search {% if can_edit_voter %} + Add New Voter diff --git a/core/views.py b/core/views.py index bb19060..ef40c1a 100644 --- a/core/views.py +++ b/core/views.py @@ -1487,7 +1487,7 @@ def door_visit_history(request): "latest_interaction": interaction } - visited_households[key]["voters_at_address"].add(f"{v.first_name} {v.last_name}") + visited_households[key]["voters_at_address"].add((v.id, f"{v.first_name} {v.last_name}")) # Sort volunteer counts by total (descending) sorted_volunteer_counts = sorted(volunteer_counts.items(), key=lambda x: x[1], reverse=True) diff --git a/door_views_update.py b/door_views_update.py index 765c0e7..616727e 100644 --- a/door_views_update.py +++ b/door_views_update.py @@ -1,211 +1,16 @@ -def door_visits(request): - """ - Manage door knocking visits. Groups unvisited targeted voters by household. - """ - selected_tenant_id = request.session.get("tenant_id") - if not selected_tenant_id: - messages.warning(request, "Please select a campaign first.") - return redirect("index") +import sys - tenant = get_object_or_404(Tenant, id=selected_tenant_id) - - # Filters from GET parameters - district_filter = request.GET.get('district', '').strip() - neighborhood_filter = request.GET.get('neighborhood', '').strip() - address_filter = request.GET.get('address', '').strip() +file_path = 'core/views.py' +with open(file_path, 'r') as f: + content = f.read() - # Initial queryset: unvisited targeted voters for this tenant - voters = Voter.objects.filter(tenant=tenant, door_visit=False, is_targeted=True) +old_code = 'visited_households[key]["voters_at_address"].add(f"{v.first_name} {v.last_name}")' +new_code = 'visited_households[key]["voters_at_address"].add((v.id, f"{v.first_name} {v.last_name}"))' - # Apply filters if provided - if district_filter: - voters = voters.filter(district=district_filter) - if neighborhood_filter: - voters = voters.filter(neighborhood__icontains=neighborhood_filter) - if address_filter: - voters = voters.filter(Q(address_street__icontains=address_filter) | Q(address__icontains=address_filter)) - - # Grouping by household (unique address) - households_dict = {} - for voter in voters: - # Key for grouping is the unique address components - key = (voter.address_street, voter.city, voter.state, voter.zip_code) - if key not in households_dict: - # Parse street name and number for sorting - street_number = "" - street_name = voter.address_street - match = re.match(r'^(\d+)\s+(.*)$', voter.address_street) - if match: - street_number = match.group(1) - street_name = match.group(2) - - try: - street_number_sort = int(street_number) - except ValueError: - street_number_sort = 0 - - households_dict[key] = { - 'address_street': voter.address_street, - 'city': voter.city, - 'state': voter.state, - 'zip_code': voter.zip_code, - 'neighborhood': voter.neighborhood, - 'district': voter.district, - 'latitude': float(voter.latitude) if voter.latitude else None, - 'longitude': float(voter.longitude) if voter.longitude else None, - 'street_name_sort': street_name.lower(), - 'street_number_sort': street_number_sort, - 'target_voters': [], - 'voters_json': [] - } - households_dict[key]['target_voters'].append(voter) - households_dict[key]['voters_json'].append({'id': voter.id, 'name': f"{voter.first_name} {voter.last_name}"}) - - households_list = list(households_dict.values()) - for h in households_list: - h['voters_json_str'] = json.dumps(h['voters_json']) - - households_list.sort(key=lambda x: ( - (x['neighborhood'] or '').lower(), - x['street_name_sort'], - x['street_number_sort'] - )) - - # Prepare data for Google Map (all filtered households with coordinates) - map_data = [ - { - 'lat': h['latitude'], - 'lng': h['longitude'], - 'address': f"{h['address_street']}, {h['city']}, {h['state']}", - 'voters': ", ".join([f"{v.first_name} {v.last_name}" for v in h['target_voters']]) - } - for h in households_list if h['latitude'] and h['longitude'] - ] - - paginator = Paginator(households_list, 50) - page_number = request.GET.get('page') - households_page = paginator.get_page(page_number) - - context = { - 'selected_tenant': tenant, - 'households': households_page, - 'district_filter': district_filter, - 'neighborhood_filter': neighborhood_filter, - 'address_filter': address_filter, - 'map_data_json': json.dumps(map_data), - 'google_maps_api_key': getattr(settings, 'GOOGLE_MAPS_API_KEY', ''), - 'visit_form': DoorVisitLogForm(), - } - return render(request, 'core/door_visits.html', context) - -@role_required(['admin', 'campaign_manager', 'campaign_staff', 'system_admin', 'campaign_admin'], permission='core.view_voter') -def log_door_visit(request): - """ - Mark all targeted voters at a specific address as visited, update their flags, - and create interaction records. - """ - selected_tenant_id = request.session.get("tenant_id") - if not selected_tenant_id: - return redirect("index") - - tenant = get_object_or_404(Tenant, id=selected_tenant_id) - campaign_settings, _ = CampaignSettings.objects.get_or_create(tenant=tenant) - - # Capture query string for redirecting back with filters - next_qs = request.POST.get("next_query_string", "") - redirect_url = reverse("door_visits") - if next_qs: - redirect_url += f"?{next_qs}" - - # Get the volunteer linked to the current user - volunteer = Volunteer.objects.filter(user=request.user, tenant=tenant).first() - - if request.method == "POST": - form = DoorVisitLogForm(request.POST) - if form.is_valid(): - address_street = request.POST.get("address_street") - city = request.POST.get("city") - state = request.POST.get("state") - zip_code = request.POST.get("zip_code") - - outcome = form.cleaned_data["outcome"] - notes = form.cleaned_data["notes"] - wants_yard_sign = form.cleaned_data["wants_yard_sign"] - candidate_support = form.cleaned_data["candidate_support"] - follow_up = form.cleaned_data["follow_up"] - follow_up_voter_id = form.cleaned_data.get("follow_up_voter") - call_notes = form.cleaned_data["call_notes"] - - # Determine date/time in campaign timezone - campaign_tz_name = campaign_settings.timezone or "America/Chicago" - try: - tz = zoneinfo.ZoneInfo(campaign_tz_name) - except: - tz = zoneinfo.ZoneInfo("America/Chicago") - - interaction_date = timezone.now().astimezone(tz) - - # Get or create InteractionType - interaction_type, _ = InteractionType.objects.get_or_create(tenant=tenant, name="Door Visit") - - # Find targeted voters at this exact address - voters = Voter.objects.filter( - tenant=tenant, - address_street=address_street, - city=city, - state=state, - zip_code=zip_code, - is_targeted=True - ) - - if not voters.exists(): - messages.warning(request, f"No targeted voters found at {address_street}.") - return redirect(redirect_url) - - # Get default caller for follow-ups - default_caller = None - if follow_up: - default_caller = Volunteer.objects.filter(tenant=tenant, is_default_caller=True).first() - - for voter in voters: - # 1) Update voter flags - voter.door_visit = True - - # 2) If "Wants a Yard Sign" checkbox is selected - if wants_yard_sign: - voter.yard_sign = "wants" - - # 3) Update support status if Supporting or Not Supporting - if candidate_support in ["supporting", "not_supporting"]: - voter.candidate_support = candidate_support - - voter.save() - - # 4) Create interaction - Interaction.objects.create( - voter=voter, - volunteer=volunteer, - type=interaction_type, - date=interaction_date, - description=outcome, - notes=notes - ) - - # 5) Create ScheduledCall if follow_up is checked and this is the selected voter - if follow_up and follow_up_voter_id and str(voter.id) == follow_up_voter_id: - ScheduledCall.objects.create( - tenant=tenant, - voter=voter, - volunteer=default_caller, - comments=call_notes, - status="pending" - ) - - if follow_up: - messages.success(request, f"Door visit logged and follow-up call scheduled for {address_street}.") - else: - messages.success(request, f"Door visit logged for {address_street}.") - else: - messages.error(request, "There was an error in the visit log form.") - - return redirect(redirect_url) \ No newline at end of file +if old_code in content: + new_content = content.replace(old_code, new_code) + with open(file_path, 'w') as f: + f.write(new_content) + print("Successfully patched core/views.py") +else: + print("Could not find the target line in core/views.py")