3.0
This commit is contained in:
parent
c5d42d341f
commit
442aec63b6
Binary file not shown.
@ -69,6 +69,7 @@ MIDDLEWARE = [
|
|||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
'core.middleware.LoginRequiredMiddleware',
|
'core.middleware.LoginRequiredMiddleware',
|
||||||
|
'core.middleware.TimezoneMiddleware',
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
# Disable X-Frame-Options middleware to allow Flatlogic preview iframes.
|
# Disable X-Frame-Options middleware to allow Flatlogic preview iframes.
|
||||||
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -1,6 +1,9 @@
|
|||||||
|
import zoneinfo
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.utils import timezone
|
||||||
|
from core.models import CampaignSettings, Tenant
|
||||||
|
|
||||||
class LoginRequiredMiddleware:
|
class LoginRequiredMiddleware:
|
||||||
def __init__(self, get_response):
|
def __init__(self, get_response):
|
||||||
@ -11,7 +14,6 @@ class LoginRequiredMiddleware:
|
|||||||
path = request.path_info
|
path = request.path_info
|
||||||
|
|
||||||
# Allow access to login, logout, admin, and any other exempted paths
|
# Allow access to login, logout, admin, and any other exempted paths
|
||||||
# We use try/except in case URLs are not defined yet
|
|
||||||
try:
|
try:
|
||||||
login_url = reverse('login')
|
login_url = reverse('login')
|
||||||
logout_url = reverse('logout')
|
logout_url = reverse('logout')
|
||||||
@ -33,3 +35,34 @@ class LoginRequiredMiddleware:
|
|||||||
|
|
||||||
response = self.get_response(request)
|
response = self.get_response(request)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
class TimezoneMiddleware:
|
||||||
|
def __init__(self, get_response):
|
||||||
|
self.get_response = get_response
|
||||||
|
|
||||||
|
def __call__(self, request):
|
||||||
|
tzname = None
|
||||||
|
|
||||||
|
# 1. Try to get tenant from session
|
||||||
|
tenant_id = request.session.get("tenant_id")
|
||||||
|
if tenant_id:
|
||||||
|
try:
|
||||||
|
campaign_settings = CampaignSettings.objects.get(tenant_id=tenant_id)
|
||||||
|
tzname = campaign_settings.timezone
|
||||||
|
except CampaignSettings.DoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 2. If not found and user is authenticated, maybe they are in admin?
|
||||||
|
# In admin, we might not have tenant_id in session if they went directly there.
|
||||||
|
# But this is a multi-tenant app, usually they select a campaign first.
|
||||||
|
# If they are superuser in admin, we might want to default to something or let them see UTC.
|
||||||
|
|
||||||
|
if tzname:
|
||||||
|
try:
|
||||||
|
timezone.activate(zoneinfo.ZoneInfo(tzname))
|
||||||
|
except:
|
||||||
|
timezone.deactivate()
|
||||||
|
else:
|
||||||
|
timezone.deactivate()
|
||||||
|
|
||||||
|
return self.get_response(request)
|
||||||
|
|||||||
@ -18,6 +18,57 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Filters and Summary -->
|
||||||
|
<div class="row g-4 mb-5">
|
||||||
|
<!-- Date Filter Card -->
|
||||||
|
<div class="col-lg-5">
|
||||||
|
<div class="card border-0 shadow-sm rounded-4 h-100">
|
||||||
|
<div class="card-body p-4">
|
||||||
|
<h5 class="card-title fw-bold mb-3">Filter by Date Range</h5>
|
||||||
|
<form method="get" class="row g-2">
|
||||||
|
<div class="col-sm-5">
|
||||||
|
<label class="small text-muted mb-1 ms-1">From</label>
|
||||||
|
<input type="date" name="start_date" class="form-control rounded-pill border-light bg-light" value="{{ start_date|default:'' }}">
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-5">
|
||||||
|
<label class="small text-muted mb-1 ms-1">To</label>
|
||||||
|
<input type="date" name="end_date" class="form-control rounded-pill border-light bg-light" value="{{ end_date|default:'' }}">
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-2 d-flex align-items-end">
|
||||||
|
<button type="submit" class="btn btn-primary rounded-pill w-100 px-0">Filter</button>
|
||||||
|
</div>
|
||||||
|
{% if start_date or end_date %}
|
||||||
|
<div class="col-12 mt-1">
|
||||||
|
<a href="{% url 'door_visit_history' %}" class="text-secondary small ms-1">
|
||||||
|
<i class="bi bi-x-circle me-1"></i>Clear range
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Volunteer Counts Summary -->
|
||||||
|
<div class="col-lg-7">
|
||||||
|
<div class="card border-0 shadow-sm rounded-4 h-100">
|
||||||
|
<div class="card-body p-4">
|
||||||
|
<h5 class="card-title fw-bold mb-3">Visits per Volunteer</h5>
|
||||||
|
<div class="d-flex flex-wrap gap-2">
|
||||||
|
{% for v_name, count in volunteer_counts %}
|
||||||
|
<div class="bg-light rounded-pill px-3 py-2 d-flex align-items-center border">
|
||||||
|
<span class="fw-medium text-dark me-2">{{ v_name }}</span>
|
||||||
|
<span class="badge bg-primary rounded-pill">{{ count }}</span>
|
||||||
|
</div>
|
||||||
|
{% empty %}
|
||||||
|
<p class="text-muted small mb-0">No volunteer data available for this selection.</p>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Households List -->
|
<!-- Households List -->
|
||||||
<div class="card border-0 shadow-sm rounded-4 overflow-hidden">
|
<div class="card border-0 shadow-sm rounded-4 overflow-hidden">
|
||||||
<div class="card-header bg-white py-3 border-bottom d-flex justify-content-between align-items-center">
|
<div class="card-header bg-white py-3 border-bottom d-flex justify-content-between align-items-center">
|
||||||
@ -33,8 +84,9 @@
|
|||||||
<th class="ps-4 py-3 text-uppercase small ls-1">Household Address</th>
|
<th class="ps-4 py-3 text-uppercase small ls-1">Household Address</th>
|
||||||
<th class="py-3 text-uppercase small ls-1">Voters Visited</th>
|
<th class="py-3 text-uppercase small ls-1">Voters Visited</th>
|
||||||
<th class="py-3 text-uppercase small ls-1">Last Visit</th>
|
<th class="py-3 text-uppercase small ls-1">Last Visit</th>
|
||||||
|
<th class="py-3 text-uppercase small ls-1">Volunteer</th>
|
||||||
<th class="py-3 text-uppercase small ls-1">Outcome</th>
|
<th class="py-3 text-uppercase small ls-1">Outcome</th>
|
||||||
<th class="pe-4 py-3 text-uppercase small ls-1">Interactions</th>
|
<th class="pe-4 py-3 text-uppercase small ls-1">Comments</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -66,25 +118,37 @@
|
|||||||
<div class="fw-bold text-dark">{{ household.last_visit_date|date:"M d, Y" }}</div>
|
<div class="fw-bold text-dark">{{ household.last_visit_date|date:"M d, Y" }}</div>
|
||||||
<div class="small text-muted">{{ household.last_visit_date|date:"H:i" }}</div>
|
<div class="small text-muted">{{ household.last_visit_date|date:"H:i" }}</div>
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if household.last_volunteer %}
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<div class="bg-primary bg-opacity-10 text-primary rounded-circle d-flex align-items-center justify-content-center me-2" style="width: 32px; height: 32px;">
|
||||||
|
{{ household.last_volunteer.first_name|first }}{{ household.last_volunteer.last_name|first }}
|
||||||
|
</div>
|
||||||
|
<span class="fw-medium">{{ household.last_volunteer }}</span>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<span class="text-muted small">N/A</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="badge {% if 'Spoke' in household.last_outcome %}bg-success{% elif 'Literature' in household.last_outcome %}bg-info{% else %}bg-secondary{% endif %} bg-opacity-10 {% if 'Spoke' in household.last_outcome %}text-success{% elif 'Literature' in household.last_outcome %}text-info{% else %}text-secondary{% endif %} px-2 py-1">
|
<span class="badge {% if 'Spoke' in household.last_outcome %}bg-success{% elif 'Literature' in household.last_outcome %}bg-info{% else %}bg-secondary{% endif %} bg-opacity-10 {% if 'Spoke' in household.last_outcome %}text-success{% elif 'Literature' in household.last_outcome %}text-info{% else %}text-secondary{% endif %} px-2 py-1">
|
||||||
{{ household.last_outcome }}
|
{{ household.last_outcome }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="pe-4">
|
<td class="pe-4">
|
||||||
<span class="badge rounded-pill bg-light text-dark border">
|
<div class="small text-muted text-wrap" style="max-width: 200px;">
|
||||||
{{ household.interaction_count }} Visit{{ household.interaction_count|pluralize }}
|
{{ household.notes|default:"-" }}
|
||||||
</span>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="5" class="text-center py-5">
|
<td colspan="6" class="text-center py-5">
|
||||||
<div class="text-muted mb-2">
|
<div class="text-muted mb-2">
|
||||||
<i class="bi bi-calendar-x mb-2" style="font-size: 3rem; opacity: 0.3;"></i>
|
<i class="bi bi-calendar-x mb-2" style="font-size: 3rem; opacity: 0.3;"></i>
|
||||||
</div>
|
</div>
|
||||||
<p class="mb-0 fw-medium">No door visits logged yet.</p>
|
<p class="mb-0 fw-medium">No door visits found for this selection.</p>
|
||||||
<p class="small text-muted">Visit the <a href="{% url 'door_visits' %}">Planned Visits</a> page to start logging visits.</p>
|
<p class="small text-muted">Try clearing your filters or visit the <a href="{% url 'door_visits' %}">Planned Visits</a> page to log more visits.</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -98,12 +162,12 @@
|
|||||||
<ul class="pagination justify-content-center mb-0">
|
<ul class="pagination justify-content-center mb-0">
|
||||||
{% if history.has_previous %}
|
{% if history.has_previous %}
|
||||||
<li class="page-item">
|
<li class="page-item">
|
||||||
<a class="page-link rounded-circle mx-1 border-0 bg-light text-dark" href="?page=1" aria-label="First">
|
<a class="page-link rounded-circle mx-1 border-0 bg-light text-dark" href="?page=1{% if start_date %}&start_date={{ start_date }}{% endif %}{% if end_date %}&end_date={{ end_date }}{% endif %}" aria-label="First">
|
||||||
<i class="bi bi-chevron-double-left small"></i>
|
<i class="bi bi-chevron-double-left small"></i>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="page-item">
|
<li class="page-item">
|
||||||
<a class="page-link rounded-circle mx-1 border-0 bg-light text-dark" href="?page={{ history.previous_page_number }}" aria-label="Previous">
|
<a class="page-link rounded-circle mx-1 border-0 bg-light text-dark" href="?page={{ history.previous_page_number }}{% if start_date %}&start_date={{ start_date }}{% endif %}{% if end_date %}&end_date={{ end_date }}{% endif %}" aria-label="Previous">
|
||||||
<i class="bi bi-chevron-left small"></i>
|
<i class="bi bi-chevron-left small"></i>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@ -113,12 +177,12 @@
|
|||||||
|
|
||||||
{% if history.has_next %}
|
{% if history.has_next %}
|
||||||
<li class="page-item">
|
<li class="page-item">
|
||||||
<a class="page-link rounded-circle mx-1 border-0 bg-light text-dark" href="?page={{ history.next_page_number }}" aria-label="Next">
|
<a class="page-link rounded-circle mx-1 border-0 bg-light text-dark" href="?page={{ history.next_page_number }}{% if start_date %}&start_date={{ start_date }}{% endif %}{% if end_date %}&end_date={{ end_date }}{% endif %}" aria-label="Next">
|
||||||
<i class="bi bi-chevron-right small"></i>
|
<i class="bi bi-chevron-right small"></i>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="page-item">
|
<li class="page-item">
|
||||||
<a class="page-link rounded-circle mx-1 border-0 bg-light text-dark" href="?page={{ history.paginator.num_pages }}" aria-label="Last">
|
<a class="page-link rounded-circle mx-1 border-0 bg-light text-dark" href="?page={{ history.paginator.num_pages }}{% if start_date %}&start_date={{ start_date }}{% endif %}{% if end_date %}&end_date={{ end_date }}{% endif %}" aria-label="Last">
|
||||||
<i class="bi bi-chevron-double-right small"></i>
|
<i class="bi bi-chevron-double-right small"></i>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@ -196,6 +196,7 @@
|
|||||||
<input type="hidden" name="city" id="modal_city">
|
<input type="hidden" name="city" id="modal_city">
|
||||||
<input type="hidden" name="state" id="modal_state">
|
<input type="hidden" name="state" id="modal_state">
|
||||||
<input type="hidden" name="zip_code" id="modal_zip_code">
|
<input type="hidden" name="zip_code" id="modal_zip_code">
|
||||||
|
<input type="hidden" name="next_query_string" value="{{ request.GET.urlencode }}">
|
||||||
|
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label class="form-label fw-bold text-primary small text-uppercase">Visit Outcome</label>
|
<label class="form-label fw-bold text-primary small text-uppercase">Visit Outcome</label>
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
from django.utils.dateparse import parse_date
|
||||||
|
from datetime import datetime, time, timedelta
|
||||||
import base64
|
import base64
|
||||||
import re
|
import re
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
@ -1277,6 +1279,12 @@ def log_door_visit(request):
|
|||||||
tenant = get_object_or_404(Tenant, id=selected_tenant_id)
|
tenant = get_object_or_404(Tenant, id=selected_tenant_id)
|
||||||
campaign_settings, _ = CampaignSettings.objects.get_or_create(tenant=tenant)
|
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
|
# Get the volunteer linked to the current user
|
||||||
volunteer = Volunteer.objects.filter(user=request.user, tenant=tenant).first()
|
volunteer = Volunteer.objects.filter(user=request.user, tenant=tenant).first()
|
||||||
|
|
||||||
@ -1317,7 +1325,7 @@ def log_door_visit(request):
|
|||||||
|
|
||||||
if not voters.exists():
|
if not voters.exists():
|
||||||
messages.warning(request, f"No targeted voters found at {address_street}.")
|
messages.warning(request, f"No targeted voters found at {address_street}.")
|
||||||
return redirect('door_visits')
|
return redirect(redirect_url)
|
||||||
|
|
||||||
for voter in voters:
|
for voter in voters:
|
||||||
# 1) Update voter flags
|
# 1) Update voter flags
|
||||||
@ -1347,8 +1355,7 @@ def log_door_visit(request):
|
|||||||
else:
|
else:
|
||||||
messages.error(request, "There was an error in the visit log form.")
|
messages.error(request, "There was an error in the visit log form.")
|
||||||
|
|
||||||
return redirect('door_visits')
|
return redirect(redirect_url)
|
||||||
|
|
||||||
|
|
||||||
def door_visit_history(request):
|
def door_visit_history(request):
|
||||||
"""
|
"""
|
||||||
@ -1360,17 +1367,39 @@ def door_visit_history(request):
|
|||||||
return redirect("index")
|
return redirect("index")
|
||||||
tenant = get_object_or_404(Tenant, id=selected_tenant_id)
|
tenant = get_object_or_404(Tenant, id=selected_tenant_id)
|
||||||
|
|
||||||
# Get all "Door Visit" interactions for this tenant, ordered by date desc
|
# 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(
|
interactions = Interaction.objects.filter(
|
||||||
voter__tenant=tenant,
|
voter__tenant=tenant,
|
||||||
type__name="Door Visit"
|
type__name="Door Visit"
|
||||||
).select_related('voter', 'volunteer').order_by('-date')
|
).select_related("voter", "volunteer")
|
||||||
|
|
||||||
|
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)
|
# Grouping by household (unique address)
|
||||||
visited_households = {}
|
visited_households = {}
|
||||||
for interaction in interactions:
|
volunteer_counts = {}
|
||||||
|
|
||||||
|
for interaction in interactions.order_by("-date"):
|
||||||
v = interaction.voter
|
v = interaction.voter
|
||||||
# Use concatenated address if available, otherwise build it
|
|
||||||
addr = v.address.strip() if v.address else f"{v.address_street}, {v.city}, {v.state} {v.zip_code}".strip(", ")
|
addr = v.address.strip() if v.address else f"{v.address_street}, {v.city}, {v.state} {v.zip_code}".strip(", ")
|
||||||
if not addr:
|
if not addr:
|
||||||
continue
|
continue
|
||||||
@ -1378,38 +1407,43 @@ def door_visit_history(request):
|
|||||||
key = addr.lower()
|
key = addr.lower()
|
||||||
|
|
||||||
if key not in visited_households:
|
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
|
||||||
|
|
||||||
visited_households[key] = {
|
visited_households[key] = {
|
||||||
'address_display': addr,
|
"address_display": addr,
|
||||||
'address_street': v.address_street,
|
"address_street": v.address_street,
|
||||||
'city': v.city,
|
"city": v.city,
|
||||||
'state': v.state,
|
"state": v.state,
|
||||||
'zip_code': v.zip_code,
|
"zip_code": v.zip_code,
|
||||||
'neighborhood': v.neighborhood,
|
"neighborhood": v.neighborhood,
|
||||||
'district': v.district,
|
"district": v.district,
|
||||||
'last_visit_date': interaction.date,
|
"last_visit_date": interaction.date,
|
||||||
'last_outcome': interaction.description,
|
"last_outcome": interaction.description,
|
||||||
'voters_at_address': set(),
|
"last_volunteer": interaction.volunteer,
|
||||||
'interaction_count': 0,
|
"notes": interaction.notes,
|
||||||
'latest_interaction': interaction
|
"voters_at_address": set(),
|
||||||
|
"latest_interaction": interaction
|
||||||
}
|
}
|
||||||
|
|
||||||
visited_households[key]['voters_at_address'].add(f"{v.first_name} {v.last_name}")
|
visited_households[key]["voters_at_address"].add(f"{v.first_name} {v.last_name}")
|
||||||
visited_households[key]['interaction_count'] += 1
|
|
||||||
|
|
||||||
if interaction.date > visited_households[key]['last_visit_date']:
|
# Sort volunteer counts by total (descending)
|
||||||
visited_households[key]['last_visit_date'] = interaction.date
|
sorted_volunteer_counts = sorted(volunteer_counts.items(), key=lambda x: x[1], reverse=True)
|
||||||
visited_households[key]['last_outcome'] = interaction.description
|
|
||||||
visited_households[key]['latest_interaction'] = interaction
|
|
||||||
|
|
||||||
history_list = list(visited_households.values())
|
history_list = list(visited_households.values())
|
||||||
history_list.sort(key=lambda x: x['last_visit_date'], reverse=True)
|
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')
|
page_number = request.GET.get("page")
|
||||||
history_page = paginator.get_page(page_number)
|
history_page = paginator.get_page(page_number)
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'selected_tenant': tenant,
|
"selected_tenant": tenant,
|
||||||
'history': history_page,
|
"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)
|
return render(request, "core/door_visit_history.html", context)
|
||||||
|
|||||||
14
core_view_fix.tmp
Normal file
14
core_view_fix.tmp
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
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)
|
||||||
|
|
||||||
|
def log_door_visit(request):
|
||||||
|
"""
|
||||||
Loading…
x
Reference in New Issue
Block a user