add do_submit task management stuff

This commit is contained in:
Laura Klünder 2016-10-04 20:41:03 +02:00
parent 1d56f10ec1
commit cd99ba1c0a
6 changed files with 131 additions and 58 deletions

View file

@ -5,7 +5,7 @@ from django.conf import settings
from django.urls.base import reverse from django.urls.base import reverse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from c3nav.editor.tasks import check_access_token, request_access_token from c3nav.editor.tasks import check_access_token, request_access_token, submit_edit
from c3nav.mapdata.models import Package from c3nav.mapdata.models import Package
@ -114,6 +114,11 @@ class Hoster(ABC):
session_data['checking_progress_id'] = task.id session_data['checking_progress_id'] = task.id
self._handle_checking_task(request, task, session_data) self._handle_checking_task(request, task, session_data)
def submit_edit(self, request, data):
session_data = self._get_session_data(request)
task = submit_edit.apply_async(access_token=session_data['access_token'], data=data)
return task
@abstractmethod @abstractmethod
def get_auth_uri(self, request): def get_auth_uri(self, request):
""" """
@ -133,7 +138,7 @@ class Hoster(ABC):
""" """
Task method for requesting the access token asynchroniously. Task method for requesting the access token asynchroniously.
Returns a dict with a 'state' key containing the new hoster state, an optional 'error' key containing an Returns a dict with a 'state' key containing the new hoster state, an optional 'error' key containing an
error message and an optional 'access_token' keys containing a new access token. error message and an optional 'access_token' key containing a new access token.
""" """
pass pass
@ -144,3 +149,13 @@ class Hoster(ABC):
Returns a dict with a 'state' key containing the new hoster state. Returns a dict with a 'state' key containing the new hoster state.
""" """
pass pass
@abstractmethod
def do_submit_edit(self, access_token, data):
"""
Task method for submitting an edit (e.g. creating a pull request).
Returns a dict with a 'success' key that contains a boolean, an optional 'error' key containing an error
message and an optional 'url' key containing an URL to the created pull request.
"""
pass

View file

@ -80,3 +80,6 @@ class GithubHoster(Hoster):
return {'state': 'missing_permissions'} return {'state': 'missing_permissions'}
return {'state': 'logged_in'} return {'state': 'logged_in'}
def do_submit_edit(self, access_token, data):
raise NotImplementedError

View file

@ -75,3 +75,6 @@ class GitlabHoster(Hoster):
return {'state': 'logged_out'} return {'state': 'logged_out'}
return {'state': 'logged_in'} return {'state': 'logged_in'}
def do_submit_edit(self, access_token, data):
raise NotImplementedError

View file

@ -11,3 +11,9 @@ def request_access_token(hoster, *args, **kwargs):
def check_access_token(hoster, access_token): def check_access_token(hoster, access_token):
from c3nav.editor.hosters import hosters from c3nav.editor.hosters import hosters
return hosters[hoster].do_check_access_token(access_token) return hosters[hoster].do_check_access_token(access_token)
@app.task()
def submit_edit(hoster, access_token, data):
from c3nav.editor.hosters import hosters
return hosters[hoster].do_submit_edit(access_token, data)

View file

@ -3,58 +3,78 @@
{% load bootstrap3 %} {% load bootstrap3 %}
{% block content %} {% block content %}
{% if hoster %} {% if hoster %}
{% if hoster_error %} {% if not task %}
<div class="alert alert-danger" role="alert">
<p>{{ hoster_error }}</p>
</div>
{% endif %}
{% if hoster_state == 'logged_in' %}
<h2>Propose Changes</h2>
<p>Please provide a short helpful title for your change.</p>
<form action="{% url 'editor.finalize' %}" method="POST">
{% csrf_token %}
<input type="hidden" name="data" value="{{ data }}">
{% bootstrap_form commit_form %}
{% buttons %} {% if hoster_error %}
<button type="submit" class="btn btn-lg btn-primary">Create Pull Request</button><br> <div class="alert alert-danger" role="alert">
<small><em> <p>{{ hoster_error }}</p>
{{ hoster.name }} {{ hoster.base_url }} </div>
</em></small> {% endif %}
{% endbuttons %} {% if hoster_state == 'logged_in' %}
</form> <h2>Propose Changes</h2>
<p>Please provide a short helpful title for your change.</p>
{% elif hoster_state == 'checking' %} <form action="{% url 'editor.finalize' %}" method="POST">
<h2>Sign in with {{ hoster.title }}</h2> {% csrf_token %}
<form action="{% url 'editor.finalize' %}" method="POST" data-check-hoster="{{ hoster.name }}"> <input type="hidden" name="data" value="{{ data }}">
{% csrf_token %} <input type="hidden" name="action" value="submit">
<input type="hidden" name="data" value="{{ data }}"> <input type="hidden" name="editor_submit_token" value="{{ editor_submit_token }}">
<p><img src="{% static 'img/loader.gif' %}"></p> {% bootstrap_form commit_form %}
<p><em>Checking authentication, please wait…</em></p>
</form> {% buttons %}
<button type="submit" class="btn btn-lg btn-primary">Create Pull Request</button><br>
{% else %} <small><em>
{% if hoster_state == 'misssing_permissions' %} {{ hoster.name }} {{ hoster.base_url }}
<h2>Missing {{ hoster.title }} Permissions</h2> </em></small>
<p>c3nav is missing permissions that it needs to propose your edit.</p> {% endbuttons %}
<p>Please click the button below to grant the missing permissions.</p> </form>
{% else %}
<h2>Sign in with {{ hoster.title }}</h2> {% elif hoster_state == 'checking' %}
<p>Please sign in to continue and propose your edit.</p> <h2>Sign in with {{ hoster.title }}</h2>
<form action="{% url 'editor.finalize' %}" method="POST" data-check-hoster="{{ hoster.name }}">
{% csrf_token %}
<input type="hidden" name="data" value="{{ data }}">
<p><img src="{% static 'img/loader.gif' %}"></p>
<p><em>Checking authentication, please wait…</em></p>
</form>
{% else %}
{% if hoster_state == 'misssing_permissions' %}
<h2>Missing {{ hoster.title }} Permissions</h2>
<p>c3nav is missing permissions that it needs to propose your edit.</p>
<p>Please click the button below to grant the missing permissions.</p>
{% else %}
<h2>Sign in with {{ hoster.title }}</h2>
<p>Please sign in to continue and propose your edit.</p>
{% endif %}
<form action="{% url 'editor.finalize' %}" method="POST">
{% csrf_token %}
<input type="hidden" name="data" value="{{ data }}">
<input type="hidden" name="action" value="oauth">
<p>
<button type="submit" class="btn btn-lg btn-primary">Sign in with {{ hoster.title }}</button><br>
<small><em>
{{ hoster.name }} {{ hoster.base_url }}
</em></small>
</p>
</form>
{% endif %}
<p>Alternatively, you can copy your edit below and send it to the maps maintainer.</p>
{% else %}
{% if not task.ready or redirect %}
<h2>Creating Pull Request…</h2>
<form action="{% url 'editor.finalize' %}" method="POST"{% if redirect %} name="redirect"{% else %} data-check-task="{{ task.id }}"{% endif %}>
{% csrf_token %}
<input type="hidden" name="data" value="{{ data }}">
<input type="hidden" name="action" value="result">
<input type="hidden" name="task" value="{{ task.id }}">
<p><img src="{% static 'img/loader.gif' %}"></p>
<p><em>Creating Pull Request…</em></p>
</form>
{% else %}
<h2>Pull Request created</h2>
<p>You can find it here:</p>
{% endif %} {% endif %}
<form action="{% url 'editor.finalize' %}" method="POST" data-task="{{ task }}">
{% csrf_token %}
<input type="hidden" name="data" value="{{ data }}">
<input type="hidden" name="action" value="oauth">
<p>
<button type="submit" class="btn btn-lg btn-primary">Sign in with {{ hoster.title }}</button><br>
<small><em>
{{ hoster.name }} {{ hoster.base_url }}
</em></small>
</p>
</form>
{% endif %} {% endif %}
<p>Alternatively, you can copy your edit below and send it to the maps maintainer.</p>
{% else %} {% else %}
<h2>Copy your edit</h2> <h2>Copy your edit</h2>
<p>In order to propose your edit, please copy it and send it to the maps maintainer.</p> <p>In order to propose your edit, please copy it and send it to the maps maintainer.</p>

View file

@ -1,12 +1,16 @@
import string
from django.conf import settings from django.conf import settings
from django.core import signing from django.core import signing
from django.core.exceptions import PermissionDenied, SuspiciousOperation from django.core.exceptions import PermissionDenied, SuspiciousOperation
from django.http.response import Http404 from django.http.response import Http404
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.utils.crypto import get_random_string
from django.views.decorators.http import require_POST from django.views.decorators.http import require_POST
from c3nav.editor.forms import CommitForm, FeatureForm from c3nav.editor.forms import CommitForm, FeatureForm
from c3nav.editor.hosters import get_hoster_for_package, hosters from c3nav.editor.hosters import get_hoster_for_package, hosters
from c3nav.editor.tasks import submit_edit
from c3nav.mapdata.models.feature import FEATURE_TYPES, Feature from c3nav.mapdata.models.feature import FEATURE_TYPES, Feature
from c3nav.mapdata.models.package import Package from c3nav.mapdata.models.package import Package
from c3nav.mapdata.packageio.write import json_encode from c3nav.mapdata.packageio.write import json_encode
@ -112,22 +116,41 @@ def finalize(request):
hoster = get_hoster_for_package(package) hoster = get_hoster_for_package(package)
action = request.POST.get('action') action = request.POST.get('action')
if 'commit_msg' in request.POST or action == 'submit':
form = CommitForm(request.POST)
else:
form = CommitForm({'commit_msg': data['commit_msg']})
task = None
new_submit_token = False
if action == 'check': if action == 'check':
hoster.check_state(request) hoster.check_state(request)
elif action == 'oauth': elif action == 'oauth':
hoster.set_tmp_data(request, raw_data) hoster.set_tmp_data(request, raw_data)
return redirect(hoster.get_auth_uri(request)) return redirect(hoster.get_auth_uri(request))
elif action == 'submit' and hoster.get_state(request) == 'logged_in':
if request.POST.get('editor_submit_token', '') != request.session.get('editor_submit_token', None):
raise SuspiciousOperation('Invalid submit token.')
if form.is_valid():
new_submit_token = True
data['commit_msg'] = form.cleaned_data['commit_msg']
task = hoster.submit_edit(request, data)
elif action == 'result':
if 'task' not in request.POST:
raise SuspiciousOperation('Missing task id.')
task = submit_edit.AsyncResult(task_id=request.POST['task'])
try:
task.ready()
except:
raise Http404()
if 'editor_submit_token' not in request.session or new_submit_token:
request.session['editor_submit_token'] = get_random_string(42, string.ascii_letters + string.digits)
hoster_state = hoster.get_state(request) hoster_state = hoster.get_state(request)
hoster_error = hoster.get_error(request) if hoster_state == 'logged_out' else None hoster_error = hoster.get_error(request) if hoster_state == 'logged_out' else None
if request.method == 'POST' and 'commit_msg' in request.POST:
form = CommitForm(request.POST)
if form.is_valid() and hoster_state == 'logged_in':
pass
else:
form = CommitForm({'commit_msg': data['commit_msg']})
return render(request, 'editor/finalize.html', { return render(request, 'editor/finalize.html', {
'data': raw_data, 'data': raw_data,
'action': data['action'], 'action': data['action'],
@ -137,6 +160,9 @@ def finalize(request):
'hoster': hoster, 'hoster': hoster,
'hoster_state': hoster_state, 'hoster_state': hoster_state,
'hoster_error': hoster_error, 'hoster_error': hoster_error,
'redirect': action == 'submit' and not settings.CELERY_ALWAYS_EAGER,
'editor_submit_token': request.session['editor_submit_token'],
'task': {'id': task.id, 'ready': task.ready(), 'result': task.result} if task is not None else None,
'file_path': data['file_path'], 'file_path': data['file_path'],
'file_contents': data.get('content') 'file_contents': data.get('content')
}) })