diff --git a/config/__pycache__/settings.cpython-311.pyc b/config/__pycache__/settings.cpython-311.pyc index f390f90..09c1038 100644 Binary files a/config/__pycache__/settings.cpython-311.pyc and b/config/__pycache__/settings.cpython-311.pyc differ diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index d28eeba..78e4e57 100644 Binary files a/core/__pycache__/urls.cpython-311.pyc and b/core/__pycache__/urls.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 84daac2..eebca54 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/views.py b/core/views.py index a44313c..693abe1 100644 --- a/core/views.py +++ b/core/views.py @@ -1844,6 +1844,19 @@ def door_visit_history(request): v_name = f"{v_obj.first_name} {v_obj.last_name}".strip() or v_obj.email if v_obj else "N/A" volunteer_counts[v_name] = volunteer_counts.get(v_name, 0) + 1 + # Parse street name and number for sorting + street_number = "" + street_name = v.address_street or "" + match = re.match(r'^(\d+)\s+(.*)$', street_name) + 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 + visited_households[key] = { 'address_display': addr, 'address_street': v.address_street, @@ -1856,6 +1869,7 @@ def door_visit_history(request): 'longitude': float(v.longitude) if v.longitude else None, 'street_name_sort': street_name.lower(), 'street_number_sort': street_number_sort, + 'last_visit_date': interaction.date, 'target_voters': [], 'voters_json': [] } @@ -1869,7 +1883,7 @@ def door_visit_history(request): history_list = list(visited_households.values()) history_list.sort(key=lambda x: x["last_visit_date"], reverse=True) - paginator = Paginator(history_list, 50); + paginator = Paginator(history_list, 50) page_number = request.GET.get("page") history_page = paginator.get_page(page_number) diff --git a/door_views_update.py b/door_views_update.py index 616727e..bf21f0c 100644 --- a/door_views_update.py +++ b/door_views_update.py @@ -1,16 +1,125 @@ import sys +import re file_path = 'core/views.py' with open(file_path, 'r') as f: content = f.read() -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}"))' +# Define the new function as a single string +new_func = """def door_visit_history(request): + """ + Shows a distinct list of Door visit interactions for addresses. + """ + selected_tenant_id = request.session.get("tenant_id") + if not selected_tenant_id: + messages.warning(request, "Please select a campaign first.") + return redirect("index") + tenant = get_object_or_404(Tenant, id=selected_tenant_id) + + # Date filter + start_date = request.GET.get("start_date") + end_date = request.GET.get("end_date") + + # Get all "Door Visit" interactions for this tenant + interactions = Interaction.objects.filter( + voter__tenant=tenant, + type__name="Door Visit" + ).select_related("voter", "volunteer") -if old_code in content: - new_content = content.replace(old_code, new_code) + if start_date or end_date: + try: + if start_date: + d = parse_date(start_date) + if d: + start_dt = timezone.make_aware(datetime.combine(d, time.min)) + interactions = interactions.filter(date__gte=start_dt) + if end_date: + d = parse_date(end_date) + if d: + # Use lt with next day to capture everything on the end_date + end_dt = timezone.make_aware(datetime.combine(d + timedelta(days=1), time.min)) + interactions = interactions.filter(date__lt=end_dt) + except Exception as e: + logger.error(f"Error filtering door visit history by date: {e}") + + # Summary of counts per volunteer + # Grouping by household (unique address) + visited_households = {} + volunteer_counts = {} + + for interaction in interactions.order_by("-date"): + v = interaction.voter + addr = v.address.strip() if v.address else f"{v.address_street}, {v.city}, {v.state} {v.zip_code}".strip(", ") + if not addr: + continue + + key = addr.lower() + + if key not in visited_households: + # Calculate volunteer summary - only once per household + v_obj = interaction.volunteer + v_name = f"{v_obj.first_name} {v_obj.last_name}".strip() or v_obj.email if v_obj else "N/A" + volunteer_counts[v_name] = volunteer_counts.get(v_name, 0) + 1 + + # Parse street name and number for sorting + street_number = "" + street_name = v.address_street or "" + match = re.match(r'^(\d+)\s+(.*)$', street_name) + 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 + + visited_households[key] = { + 'address_display': addr, + 'address_street': v.address_street, + 'city': v.city, + 'state': v.state, + 'zip_code': v.zip_code, + 'neighborhood': v.neighborhood, + 'district': v.district, + 'latitude': float(v.latitude) if v.latitude else None, + 'longitude': float(v.longitude) if v.longitude else None, + 'street_name_sort': street_name.lower(), + 'street_number_sort': street_number_sort, + 'last_visit_date': interaction.date, + 'target_voters': [], + 'voters_json': [] + } + + visited_households[key]["voters_json"].append({'id': v.id, 'name': f"{v.first_name} {v.last_name}"}) + visited_households[key]['target_voters'].append(v) + + # Sort volunteer counts by total (descending) + sorted_volunteer_counts = sorted(volunteer_counts.items(), key=lambda x: x[1], reverse=True) + + history_list = list(visited_households.values()) + history_list.sort(key=lambda x: x["last_visit_date"], reverse=True) + + paginator = Paginator(history_list, 50) + page_number = request.GET.get("page") + history_page = paginator.get_page(page_number) + + context = { + "selected_tenant": tenant, + "history": history_page, + "start_date": start_date, "end_date": end_date, + "volunteer_counts": sorted_volunteer_counts, + } + return render(request, "core/door_visit_history.html", context) +""" + +# Use regex to find and replace the function +pattern = r'def door_visit_history\(request\):.*?return render\(request, "core/door_visit_history\.html", context\)' +new_content = re.sub(pattern, new_func, content, flags=re.DOTALL) + +if new_content != content: with open(file_path, 'w') as f: f.write(new_content) - print("Successfully patched core/views.py") + print("Successfully updated door_visit_history") else: - print("Could not find the target line in core/views.py") + print("Could not find function to replace") \ No newline at end of file