overhaul entire report process

This commit is contained in:
Laura Klünder 2024-03-24 17:38:24 +01:00
parent 482da3b244
commit 0d91a71b9f
12 changed files with 206 additions and 45 deletions

View file

@ -29,7 +29,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='locationgroup',
name='description',
field=c3nav.mapdata.fields.I18nField(blank=True, fallback_any=True, help_text='to aid with selection in the report form', plural_name='descriptions', verbose_name='description'),
field=c3nav.mapdata.fields.I18nField(blank=True, fallback_any=True, fallback_value="", help_text='to aid with selection in the report form', plural_name='descriptions', verbose_name='description'),
),
migrations.AddField(
model_name='locationgroup',
@ -39,7 +39,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='locationgroup',
name='report_help_text',
field=c3nav.mapdata.fields.I18nField(blank=True, fallback_any=True, help_text='to explain the report form or rejection', plural_name='report_help_texts', verbose_name='report help text'),
field=c3nav.mapdata.fields.I18nField(blank=True, fallback_any=True, fallback_value="", help_text='to explain the report form or rejection', plural_name='report_help_texts', verbose_name='report help text'),
),
migrations.RunPython(forwards_func, backwards_func),
migrations.RemoveField(

View file

@ -368,9 +368,9 @@ class LocationGroup(Location, models.Model):
default=CanReportMissing.DONT_OFFER, max_length=16)
description = I18nField(_('description'), plural_name='descriptions', blank=True, fallback_any=True,
help_text=_('to aid with selection in the report form'))
fallback_value="", help_text=_('to aid with selection in the report form'))
report_help_text = I18nField(_('report help text'), plural_name='report_help_texts', blank=True, fallback_any=True,
help_text=_('to explain the report form or rejection'))
fallback_value="", help_text=_('to explain the report form or rejection'))
color = models.CharField(null=True, blank=True, max_length=32, verbose_name=_('background color'))
hub_import_type = models.CharField(max_length=100, verbose_name=_('hub import type'), null=True, blank=True,

View file

@ -242,7 +242,7 @@ def get_route(request, parameters: RouteParametersSchema):
request=parameters,
options=_new_serialize_route_options(options),
options_form=options.serialize(),
report_issue_url=reverse('site.report_create', kwargs={
report_issue_url=reverse('site.report_start', kwargs={
'origin': parameters.origin,
'destination': parameters.destination,
'options': options.serialize_string(),

View file

@ -11,7 +11,7 @@ from django.utils.translation import gettext_lazy as _
from c3nav.api.models import Secret
from c3nav.mapdata.forms import I18nModelFormMixin
from c3nav.mapdata.models.locations import Position
from c3nav.mapdata.models.locations import Position, LocationGroup
from c3nav.mapdata.models.report import Report, ReportUpdate
@ -26,8 +26,22 @@ class DeleteAccountForm(Form):
class ReportMissingLocationForm(I18nModelFormMixin, ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def __init__(self, *args, group=None, request=None, **kwargs):
super().__init__(*args, initial={"created_groups": [group] if group else []}, **kwargs)
if group:
self.fields['created_groups'].disabled = True
self.fields['created_groups'].queryset = LocationGroup.objects.filter(pk=group.pk)
else:
exists = LocationGroup.qs_for_request(request).filter(
can_report_missing=LocationGroup.CanReportMissing.MULTIPLE
).exists()
if exists:
self.fields['created_groups'].queryset = LocationGroup.qs_for_request(request).filter(
can_report_missing=LocationGroup.CanReportMissing.MULTIPLE
)
else:
self.fields['created_groups'].queryset = LocationGroup.objects.none()
self.fields['created_groups'].widget = self.fields['created_groups'].hidden_widget()
self.fields['created_groups'].label_from_instance = lambda obj: obj.title
class Meta:

View file

@ -152,7 +152,7 @@ main.map {
main.account form {
max-width: 400px;
}
#modal-content form button[type=submit] {
#modal-content form button[type=submit], #modal-content .answers .button {
display: block;
width: 100%;
}
@ -551,6 +551,14 @@ main.show-options #resultswrapper #route-options {
.location.location-form-value {
margin: -10px -10px 5px -10px;
}
.location-answers .location.location-form-value, .location-answers a {
margin-bottom: 5px;
color: $color-secondary;
display: block;
}
.location-answers {
margin-bottom: 10px;
}
.location .icon {
font-size: 36px;
position: absolute;

View file

@ -496,8 +496,6 @@ c3nav = {
var custom_location = typeof data.id !== 'number',
report_url = '/report/l/'+String(data.id)+'/';
$location_details.find('.report').attr('href', report_url);
$location_details.find('.report-issue').toggle(!custom_location);
$location_details.find('.report-missing').toggle(custom_location);
} else {
$location_details.find('.report').hide();
}
@ -1538,7 +1536,6 @@ c3nav = {
maxWidth: 500
}, 'autoPanPaddingTopLeft', 'autoPanPaddingBottomRight'));
var buttons = $('#location-popup-buttons').clone();
buttons.find('.report-issue').remove();
buttons.find('.report').attr('href', '/report/l/' + String(data.id) + '/');
newpopup.setLatLng(latlng).setContent(c3nav._build_location_html(data) + buttons.html());
c3nav._click_anywhere_popup = newpopup;
@ -1698,11 +1695,6 @@ c3nav = {
let buttons_html = '';
if (!c3nav.embed) {
let buttons = $('#location-popup-buttons').clone();
if (typeof location.id == 'number') {
buttons.find('.report-missing').remove();
} else {
buttons.find('.report-issue').remove();
}
buttons.find('.report').attr('href', '/report/l/'+String(location.id)+'/');
buttons_html = buttons.html();
}

View file

@ -1,5 +1,5 @@
<div class="location{% if form_value %} location-form-value{% endif %}">
<i class="icon material-symbols">{% if location.get_icon %}{{ location.get_icon }}{% else %}place{% endif %}</i>
<span>{{ location.title }}</span>
<small>{% if add_subtitle %}{{ add_subtitle }}, {% endif %}{{ location.subtitle }}</small>
<small>{% if replace_subtitle %}{{ replace_subtitle }}{% else %}{% if add_subtitle %}{{ add_subtitle }}, {% endif %}{{ location.subtitle }}{% endif %}</small>
</div>

View file

@ -84,7 +84,7 @@
<div class="buttons">
<a class="button button-clear report report-missing">
<i class="material-symbols">feedback</i>
{% trans 'Report missing location' %}
{% trans 'Report issue' %}
</a>
</div>
</section>
@ -110,7 +110,7 @@
</a>
<a class="button button-clear report report-missing">
<i class="material-symbols">feedback</i>
{% trans 'Report missing location' %}
{% trans 'Report issue' %}
</a>
</div>
</section>
@ -206,10 +206,6 @@
<i class="material-symbols">feedback</i>
{% trans 'Report issue' %}
</a>
<a class="button button-clear report report-missing">
<i class="material-symbols">feedback</i>
{% trans 'Report missing location' %}
</a>
<a class="button button-clear editor" target="_blank">
<i class="material-symbols">edit</i>
{% trans 'Open in Editor' %}

View file

@ -5,23 +5,15 @@
<main class="account">
<h2>{% trans 'Report issue' %}</h2>
{% include 'site/fragment_messages.html' %}
<form method="post" action="{{ request.path_info }}?{{ request.META.QUERY_STRING }}">
{% csrf_token %}
{% include 'site/fragment_report_meta.html' %}
{% if help_text %}
<ul class="messages">
<li class="alert-info">{{ help_text }}</li>
</ul>
{% endif %}
{{ form.as_p }}
<p>
<input type="checkbox" required="required" style="margin-bottom: 0" />
<blink>
<strong>
{% blocktrans trimmed %}
I understand that if I report an issue related to an assembly,
all that will happen is that I owe each member on the c3nav team one bottle of mate.
I can avoid this by contacting the assembly team, which will actually be able to fix my issue.
{% endblocktrans %}
</strong>
</blink>
</p>
<button type="submit">{% trans 'Submit' %}</button>
</form>
</main>

View file

@ -0,0 +1,29 @@
{% extends 'site/base.html' %}
{% load i18n %}
{% block content %}
<main class="account">
<h2>{% trans 'Report issue' %}</h2>
{% include 'site/fragment_messages.html' %}
{% include 'site/fragment_report_meta.html' %}
<p>{{ question }}</p>
{% if locations %}
<div class="location-answers">
{% for location in locations %}
{% if location.url %}
<a href="{{ location.url }}">
{% endif %}
{% include 'site/fragment_location.html' with form_value=1 location=location.location replace_subtitle=location.replace_subtitle %}
{% if location.url %}
</a>
{% endif %}
{% endfor %}
</div>
{% endif %}
<div class="answers">
{% for answer in answers %}
<p><a class="button" href="{{ answer.url }}">{{ answer.text }}</a></p>
{% endfor %}
</div>
</main>
{% endblock %}

View file

@ -6,7 +6,9 @@ from c3nav.site.converters import AtPositionConverter, CoordinatesConverter, IsE
from c3nav.site.views import (about_view, access_redeem_view, account_manage, account_view, api_secret_create,
api_secret_list, change_password_view, choose_language, delete_account_view, login_view,
logout_view, map_index, position_create, position_detail, position_list, position_set,
qr_code, register_view, report_create, report_detail, report_list)
qr_code, register_view, report_create, report_detail, report_list,
report_start_coordinates, report_start_location, report_start_route, report_missing_check,
report_select_location, report_missing_choose)
register_converter(CoordinatesConverter, 'coords')
register_converter(AtPositionConverter, 'at_pos')
@ -52,9 +54,16 @@ urlpatterns = [
path('reports/all/', report_list, {'filter': 'all'}, name='site.report_list'),
path('reports/<int:pk>/', report_detail, name='site.report_detail'),
path('reports/<int:pk>/<str:secret>/', report_detail, name='site.report_detail'),
path('report/l/<coords:coordinates>/', report_create, name='site.report_create'),
path('report/l/<int:location>/', report_create, name='site.report_create'),
path('report/r/<str:origin>/<str:destination>/<str:options>/', report_create, name='site.report_create'),
path('report/l/<coords:coordinates>/', report_start_coordinates, name='site.report_start'),
path('report/l/<coords:coordinates>/missing/', report_missing_check, name='site.report_missing_check'),
path('report/l/<coords:coordinates>/existing/', report_select_location, name='site.report_select_location'),
path('report/l/<coords:coordinates>/choose/', report_missing_choose, name='site.report_missing_choose'),
path('report/l/<int:location>/', report_start_location, name='site.report_start'),
path('report/r/<str:origin>/<str:destination>/<str:options>/', report_start_route, name='site.report_start'),
path('report/create/l/<coords:coordinates>/', report_create, name='site.report_create'),
path('report/create/l/<coords:coordinates>/<str:group>/', report_create, name='site.report_create'),
path('report/create/l/<int:location>/', report_create, name='site.report_create'),
path('report/create/r/<str:origin>/<str:destination>/<str:options>/', report_create, name='site.report_create'),
path('positions/', position_list, name='site.position_list'),
path('positions/create/', position_create, name='site.position_create'),
path('positions/<int:pk>/', position_detail, name='site.position_detail'),

View file

@ -30,7 +30,8 @@ from c3nav.api.models import Secret
from c3nav.mapdata.grid import grid
from c3nav.mapdata.models import Location, Source
from c3nav.mapdata.models.access import AccessPermission, AccessPermissionToken
from c3nav.mapdata.models.locations import LocationRedirect, Position, SpecificLocation, get_position_secret
from c3nav.mapdata.models.locations import LocationRedirect, Position, SpecificLocation, get_position_secret, \
LocationGroup
from c3nav.mapdata.models.report import Report, ReportUpdate
from c3nav.mapdata.utils.locations import (get_location_by_id_for_request, get_location_by_slug_for_request,
levels_by_short_label_for_request)
@ -453,15 +454,134 @@ def get_report_location_for_request(pk, request):
return location
@never_cache
def report_start_coordinates(request, coordinates):
return render(request, 'site/report_question.html', {
'question': _('What\'s wrong here?'),
'answers': [
{
'url': reverse('site.report_missing_check', kwargs={'coordinates': coordinates}),
'text': _('A location is missing'),
},
{
'url': reverse('site.report_select_location', kwargs={'coordinates': coordinates}),
'text': _('A location is there, but wrong'),
},
]
})
@never_cache
def report_missing_check(request, coordinates):
nearby = get_location_by_id_for_request(coordinates, request).nearby
if not nearby:
return redirect(reverse('site.report_missing_choose', kwargs={"coordinates": coordinates}))
return render(request, 'site/report_question.html', {
'question': _('Are you sure it\'s not one of these?'),
'locations': [
{
'location': get_location_by_id_for_request(location.id, request), # todo: correct subtitle w/o this
}
for location in nearby
],
'answers': [
{
'url': reverse('site.report_missing_choose', kwargs={"coordinates": coordinates}),
'text': _('Yeah, it\'s not in there'),
},
]
})
@never_cache
def report_select_location(request, coordinates):
location = get_location_by_id_for_request(coordinates, request)
nearby = list(location.nearby)
if location.space:
nearby.append(location.space)
if not nearby:
messages.error(request, _('There are no locations nearby.'))
return render(request, 'site/report_question.html', {})
return render(request, 'site/report_question.html', {
'question': _('Which one is it?'),
'locations': [
{
'url': reverse('site.report_create', kwargs={"location": location.id}),
'location': get_location_by_id_for_request(location.id, request), # todo: correct subtitle w/o this
}
for location in nearby
],
})
@never_cache
def report_missing_choose(request, coordinates):
groups = LocationGroup.qs_for_request(request).filter(can_report_missing__in=(
LocationGroup.CanReportMissing.SINGLE,
LocationGroup.CanReportMissing.REJECT,
))
if not groups.exists():
return redirect(reverse('site.report_create', kwargs={"coordinates": coordinates}))
return render(request, 'site/report_question.html', {
'question': _('Does one of these describe your missing location?'),
'locations': [
{
"url": reverse('site.report_create',
kwargs={"coordinates": coordinates, "group": group.get_slug()}),
"location": group,
"replace_subtitle": group.description
}
for group in groups
],
'answers': [
{
'url': reverse('site.report_create', kwargs={"coordinates": coordinates}),
'text': _('None of these fit'),
},
]
})
@never_cache
def report_start_location(request, location):
return redirect(reverse('site.report_create',
kwargs={"location": location}))
@never_cache
def report_start_route(request, origin, destination, options):
return redirect(reverse('site.report_create',
kwargs={"origin": origin, "destination": destination, "options": options}))
@never_cache
@login_required(login_url='site.login')
def report_create(request, coordinates=None, location=None, origin=None, destination=None, options=None):
def report_create(request, coordinates=None, location=None, origin=None, destination=None, options=None, group=None):
report = Report()
report.request = request
form_kwargs = {}
help_text = None
if coordinates:
report.category = 'missing-location'
report.coordinates_id = coordinates
form_kwargs["request"] = request
if group:
group = get_location_by_slug_for_request(group, request)
if not isinstance(group, LocationGroup):
raise Http404
if group.can_report_missing == LocationGroup.CanReportMissing.REJECT:
messages.error(request, format_html(
'{}<br><br>{}',
_('We do not accept reports for this type of location.'),
group.report_help_text,
))
return render(request, 'site/report_question.html', {})
if group.can_report_missing != LocationGroup.CanReportMissing.SINGLE:
raise Http404
help_text = group.report_help_text
form_kwargs["group"] = group
try:
report.coordinates
except ObjectDoesNotExist:
@ -491,7 +611,7 @@ def report_create(request, coordinates=None, location=None, origin=None, destina
report.options = options.serialize_string()
if request.method == 'POST':
form = report.form_cls(instance=report, data=request.POST)
form = report.form_cls(instance=report, data=request.POST, **form_kwargs)
if form.is_valid():
report = form.instance
if request.user.is_authenticated:
@ -508,12 +628,13 @@ def report_create(request, coordinates=None, location=None, origin=None, destina
messages.success(request, ' '.join(str(s) for s in success_messages))
return redirect(reverse('site.report_detail', kwargs=success_kwargs))
else:
form = report.form_cls(instance=report)
form = report.form_cls(instance=report, **form_kwargs)
return render(request, 'site/report_create.html', {
'report': report,
'options': options,
'form': form,
"help_text": help_text,
})