Commit 0795452a authored by Robin Sonnabend's avatar Robin Sonnabend

Search and fix misleading url_fors

parent f5a055fc
#!/usr/bin/env python3
import regex as re
import os
import sys
ROUTE_PATTERN = r'@(?:[[:alpha:]])+\.route\(\"(?<url>[^"]+)"[^)]*\)\s*(?:@[[:alpha:]_()., ]+\s*)*def\s+(?<name>[[:alpha:]][[:alnum:]_]*)\((?<params>[[:alnum:], ]*)\):'
quote_group = "[\"']"
URL_FOR_PATTERN = r'url_for\({quotes}(?<name>[[:alpha:]][[:alnum:]_]*){quotes}'.format(quotes=quote_group)
ROOT_DIR = "."
ENDINGS = [".py", ".html", ".txt"]
MAX_DEPTH = 2
def list_dir(dir, level=0):
if level >= MAX_DEPTH:
return
for file in os.listdir(dir):
path = os.path.join(dir, file)
if os.path.isfile(path):
if file == sys.argv[0]:
continue
for ending in ENDINGS:
if file.endswith(ending):
yield path
elif os.path.isdir(path):
yield from list_dir(path, level+1)
class Route:
def __init__(self, file, name, parameters):
self.file = file
self.name = name
self.parameters = parameters
def __repr__(self):
return "Route({file}, {name}, {parameters})".format(
file=self.file, name=self.name, parameters=self.parameters)
def get_parameter_set(self):
return {parameter.name for parameter in self.parameters}
class Parameter:
def __init__(self, name, type=None):
self.name = name
self.type = type
def __repr__(self):
return "Parameter({name}, {type})".format(name=self.name, type=self.type)
@staticmethod
def from_string(text):
if ":" in text:
type, name = text.split(":", 1)
return Parameter(name, type)
return Parameter(text)
def split_url_parameters(url):
params = []
current_param = None
for char in url:
if current_param is None:
if char == "<":
current_param = ""
else:
if char == ">":
params.append(Parameter.from_string(current_param))
current_param = None
else:
current_param += char
return params
def split_function_parameters(parameters):
return list(map(str.strip, parameters.split(",")))
def read_url_for_parameters(content):
params = []
bracket_level = 1
current_param = None
for char in content:
if bracket_level == 0:
if current_param is not None:
params.append(current_param.split("=")[0].strip())
return params
if char == ",":
if current_param is not None:
params.append(current_param.split("=")[0].strip())
current_param = ""
else:
if current_param is not None:
current_param += char
if char == "(":
bracket_level += 1
elif char == ")":
bracket_level -= 1
class UrlFor:
def __init__(self, file, name, parameters):
self.file = file
self.name = name
self.parameters = parameters
def __repr__(self):
return "UrlFor(file={file}, name={name}, parameters={parameters})".format(
file=self.file, name=self.name, parameters=self.parameters)
routes = {}
url_fors = []
for file in list_dir(ROOT_DIR):
with open(file, "r") as infile:
content = infile.read()
for match in re.finditer(ROUTE_PATTERN, content):
name = match.group("name")
function_parameters = split_function_parameters(match.group("params"))
url_parameters = split_url_parameters(match.group("url"))
routes[name] = Route(file, name, url_parameters)
for match in re.finditer(URL_FOR_PATTERN, content):
name = match.group("name")
begin, end = match.span()
parameters = read_url_for_parameters(content[end:])
url_fors.append(UrlFor(file=file, name=name, parameters=parameters))
for url_for in url_fors:
if url_for.name not in routes:
print("Missing route '{}' (for url_for in '{}')".format(url_for.name, url_for.file))
continue
route = routes[url_for.name]
route_parameters = route.get_parameter_set()
url_parameters = set(url_for.parameters)
if len(route_parameters ^ url_parameters) > 0:
print("Parameters not matching for '{}' in '{}:'".format(url_for.name, url_for.file))
only_route = route_parameters - url_parameters
only_url = url_parameters - route_parameters
if len(only_route) > 0:
print("Only in route: {}".format(only_route))
if len(only_url) > 0:
print("Only in url: {}".format(only_url))
......@@ -350,7 +350,7 @@ def list_protocols():
protocoltype = None
protocoltype_id = None
try:
protocoltype_id = int(request.args.get("protocoltype"))
protocoltype_id = int(request.args.get("protocoltype_id"))
except (ValueError, TypeError):
pass
search_term = request.args.get("search")
......@@ -456,7 +456,7 @@ def new_protocol():
db.session.commit()
tasks.push_tops_to_calendar(protocol)
return redirect(request.args.get("next") or url_for("show_protocol", protocol_id=protocol.id))
type_id = request.args.get("type_id")
type_id = request.args.get("protocoltype_id")
if type_id is not None:
form.protocoltype.data = type_id
upload_form.protocoltype.data = type_id
......@@ -731,7 +731,7 @@ def list_todos():
protocoltype = None
protocoltype_id = None
try:
protocoltype_id = int(request.args.get("protocoltype"))
protocoltype_id = int(request.args.get("protocoltype_id"))
except (ValueError, TypeError):
pass
search_term = request.args.get("search")
......@@ -774,7 +774,7 @@ def list_todos():
@login_required
def new_todo():
user = current_user()
protocoltype_id = optional_int_arg("type_id")
protocoltype_id = optional_int_arg("protocoltype_id")
protocol_id = optional_int_arg("protocol_id")
protocoltype = ProtocolType.query.filter_by(id=protocoltype_id).first()
protocol = Protocol.query.filter_by(id=protocol_id).first()
......@@ -819,7 +819,7 @@ def edit_todo(todo):
if form.validate_on_submit():
form.populate_obj(todo)
db.session.commit()
return redirect(request.args.get("next") or url_for("list_todos", protocoltype=todo.protocoltype.id))
return redirect(request.args.get("next") or url_for("list_todos", protocoltype_id=todo.protocoltype.id))
return render_template("todo-edit.html", form=form, todo=todo)
@app.route("/todo/show/<int:todo_id>")
......@@ -839,7 +839,7 @@ def delete_todo(todo):
db.session.delete(todo)
db.session.commit()
flash("Todo gelöscht.", "alert-success")
return redirect(request.args.get("next") or url_for("list_todos", protocoltype=type_id))
return redirect(request.args.get("next") or url_for("list_todos", protocoltype_id=type_id))
@app.route("/todo/merge", methods=["GET", "POST"])
@login_required
......@@ -861,7 +861,7 @@ def merge_todos():
db.session.delete(todo2)
db.session.commit()
flash("Merged todos {} and {}.".format(id1, id2), "alert-success")
return redirect(request.args.get("next") or url_for("show_todos"))
return redirect(request.args.get("next") or url_for("list_todos"))
return render_template("todos-merge.html", form=form, next_url=request.args.get("next"))
@app.route("/decisions/list")
......
......@@ -4,6 +4,6 @@
{% block content %}
<div class="container">
{{render_form(form, action_url=url_for("edit_default_top", type_id=protocoltype.id, defaulttop_id=defaulttop.id, next=url_for("show_type", protocoltype_id=protocoltype.id)), action_text="Ändern")}}
{{render_form(form, action_url=url_for("edit_default_top", protocoltype_id=protocoltype.id, defaulttop_id=defaulttop.id, next=url_for("show_type", protocoltype_id=protocoltype.id)), action_text="Ändern")}}
</div>
{% endblock %}
......@@ -4,6 +4,6 @@
{% block content %}
<div class="container">
{{render_form(form, action_url=url_for("new_default_top", type_id=protocoltype.id, next=url_for("show_type", protocoltype_id=protocoltype.id)), action_text="Anlegen")}}
{{render_form(form, action_url=url_for("new_default_top", protocoltype_id=protocoltype.id, next=url_for("show_type", protocoltype_id=protocoltype.id)), action_text="Anlegen")}}
</div>
{% endblock %}
......@@ -4,6 +4,6 @@
{% block content %}
<div class="container">
{{render_form(form, action_url=url_for("edit_defaultmeta", meta_id=meta.id, next=url_for("show_type", protocoltype_id=meta.protocoltype.id)), action_text="Ändern")}}
{{render_form(form, action_url=url_for("edit_defaultmeta", defaultmeta_id=meta.id, next=url_for("show_type", protocoltype_id=meta.protocoltype.id)), action_text="Ändern")}}
</div>
{% endblock %}
......@@ -4,6 +4,6 @@
{% block content %}
<div class="container">
{{render_form(form, action_url=url_for("edit_reminder", protocoltype_id=protocoltype.id, meetingreminder_id=meetingreminder.id, next=url_for("show_type", protocoltype_id=protocoltype.id)), action_text="Ändern")}}
{{render_form(form, action_url=url_for("edit_reminder", meetingreminder_id=meetingreminder.id, next=url_for("show_type", protocoltype_id=protocoltype.id)), action_text="Ändern")}}
</div>
{% endblock %}
......@@ -124,7 +124,7 @@ class ProtocolTypesTable(Table):
has_modify_right = protocoltype.has_modify_right(user)
protocoltype_link = url_for("show_type", protocoltype_id=protocoltype.id)
protocol_link = url_for("show_protocol", protocol_id=protocol.id)
new_protocol_link = url_for("new_protocol", type_id=protocoltype.id)
new_protocol_link = url_for("new_protocol", protocoltype_id=protocoltype.id)
mobile_name = "{} ({})".format(protocoltype.name, protocoltype.short_name)
mobile_links = [Table.link(protocol_link, protocol.get_identifier())]
if has_modify_right:
......@@ -221,10 +221,10 @@ class DefaultTOPsTable(Table):
top.name,
top.number,
Table.concat([
Table.link(url_for("move_default_top", protocoltype_id=self.protocoltype.id, defaulttop_id=top.id, diff=1), "Runter"),
Table.link(url_for("move_default_top", protocoltype_id=self.protocoltype.id, defaulttop_id=top.id, diff=-1), "Hoch"),
Table.link(url_for("move_default_top", defaulttop_id=top.id, diff=1), "Runter"),
Table.link(url_for("move_default_top", defaulttop_id=top.id, diff=-1), "Hoch"),
Table.link(url_for("edit_default_top", protocoltype_id=self.protocoltype.id, defaulttop_id=top.id), "Ändern"),
Table.link(url_for("delete_default_top", protocoltype_id=self.protocoltype.id, defaulttop_id=top.id), "Löschen", confirm="Bist du dir sicher, dass du den Standard-TOP {} löschen willst?".format(top.name))
Table.link(url_for("delete_default_top", defaulttop_id=top.id), "Löschen", confirm="Bist du dir sicher, dass du den Standard-TOP {} löschen willst?".format(top.name))
])
]
......@@ -244,8 +244,8 @@ class MeetingRemindersTable(Table):
reminder.additional_text or ""
]
action_links = [
Table.link(url_for("edit_reminder", protocoltype_id=self.protocoltype.id, meetingreminder_id=reminder.id), "Ändern"),
Table.link(url_for("delete_reminder", protocoltype_id=self.protocoltype.id, meetingreminder_id=reminder.id), "Löschen", confirm="Bist du dir sicher, dass du die Einladungsmail {} Tage vor der Sitzung löschen willst?".format(reminder.days_before))
Table.link(url_for("edit_reminder", meetingreminder_id=reminder.id), "Ändern"),
Table.link(url_for("delete_reminder", meetingreminder_id=reminder.id), "Löschen", confirm="Bist du dir sicher, dass du die Einladungsmail {} Tage vor der Sitzung löschen willst?".format(reminder.days_before))
]
action_part = [Table.concat(action_links)]
return general_part + action_part
......@@ -317,7 +317,7 @@ class TodosTable(Table):
todo.get_state(),
Table.link(url_for("show_protocol", protocol_id=protocol.id), protocol.get_identifier())
if protocol is not None
else Table.link(url_for("list_protocols", protocoltype=todo.protocoltype.id), todo.protocoltype.short_name),
else Table.link(url_for("list_protocols", protocoltype_id=todo.protocoltype.id), todo.protocoltype.short_name),
todo.who,
todo.description,
]
......@@ -437,7 +437,7 @@ class DefaultMetasTable(Table):
]
links = [
Table.link(url_for("edit_defaultmeta", defaultmeta_id=meta.id), "Ändern"),
Table.link(url_for("delete_defaultmeta", defaultmeta_id=meta.id, confirm="Bist du dir sicher, dass du das Metadatenfeld {} löschen willst?".format(meta.name)), "Löschen")
Table.link(url_for("delete_defaultmeta", defaultmeta_id=meta.id), confirm="Bist du dir sicher, dass du das Metadatenfeld {} löschen willst?".format(meta.name), "Löschen")
]
link_part = [Table.concat(links)]
return general_part + link_part
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment