diff --git a/opentech/apply/funds/templates/funds/applicationsubmission_detail.html b/opentech/apply/funds/templates/funds/applicationsubmission_detail.html index ee58bbbfbadf224402dc4917f90ff623c82a85b3..e8aec6e0fd2cb2d29f6368862c979e38a626af38 100644 --- a/opentech/apply/funds/templates/funds/applicationsubmission_detail.html +++ b/opentech/apply/funds/templates/funds/applicationsubmission_detail.html @@ -54,31 +54,7 @@ {% endif %} </h6> - <h3>Proposal Information</h3> - <div class="grid grid--proposal-info"> - <div> - <h5>Requested Funding</h5> - <p>{{ object.value }}</p> - </div> - - <div> - <h5>Project Duration</h5> - <p>{{ object.value }}</p> - </div> - - <div> - <h5>Legal Name</h5> - <p>{{ object.full_name }}</p> - </div> - - <div> - <h5>Email</h5> - <p>{{ object.email }}</p> - </div> - </div> - <div class="rich-text rich-text--answers"> - {{ object.render_answers }} - </div> + {% include "funds/includes/rendered_answers.html" %} </div> {% endif %} diff --git a/opentech/apply/funds/templates/funds/includes/rendered_answers.html b/opentech/apply/funds/templates/funds/includes/rendered_answers.html new file mode 100644 index 0000000000000000000000000000000000000000..e5b7f88b2266a90a71dc67d714ee0dbfa40e05b9 --- /dev/null +++ b/opentech/apply/funds/templates/funds/includes/rendered_answers.html @@ -0,0 +1,26 @@ +{% load workflow_tags %} +<h3>Proposal Information</h3> +<div class="grid grid--proposal-info"> + <div> + <h5>Requested Funding</h5> + <p>{{ object.value }}</p> + </div> + + <div> + <h5>Project Duration</h5> + <p>{{ object.value }}</p> + </div> + + <div> + <h5>Legal Name</h5> + <p>{{ object.full_name }}</p> + </div> + + <div> + <h5>Email</h5> + <p>{{ object.email }}</p> + </div> +</div> +<div class="rich-text rich-text--answers"> + {{ object.render_answers }} +</div> diff --git a/opentech/apply/funds/templates/funds/revisions_compare.html b/opentech/apply/funds/templates/funds/revisions_compare.html new file mode 100644 index 0000000000000000000000000000000000000000..8b07bf11c95192a6acf639bed11ac52131938ae7 --- /dev/null +++ b/opentech/apply/funds/templates/funds/revisions_compare.html @@ -0,0 +1,13 @@ +{% extends "base-apply.html" %} +{% block content %} +<div class="wrapper wrapper--breakout wrapper--admin"> + <div class="wrapper wrapper--medium"> + <h2 class="heading heading--no-margin">Comparing Revisions</h2> + <h5>For <a href="{% url "funds:submission" submission.id %}">{{ submission.title }}</a></h5> + </div> +</div> + +<div class="wrapper wrapper--medium wrapper--tabs"> + {% include "funds/includes/rendered_answers.html" with object=diff %} +</div> +{% endblock %} diff --git a/opentech/apply/funds/templates/funds/revisions_list.html b/opentech/apply/funds/templates/funds/revisions_list.html index ff39b209942ed67ef14ddfee7dd601ada431c436..e4ba90161be5948ab61c37bc108e918de6ada55b 100644 --- a/opentech/apply/funds/templates/funds/revisions_list.html +++ b/opentech/apply/funds/templates/funds/revisions_list.html @@ -8,13 +8,15 @@ </div> <div class="wrapper wrapper--medium wrapper--tabs"> + {% with base_revision=revisions.0.id %} {% for revision in revisions %} <div> {{ revision.date }} by {{ revision.author }} {% if not forloop.first %} - <a class="button button--primary" href="#">Compare</a> + <a class="button button--primary" href="{% url 'apply:revisions:compare' submission_pk=submission.id from=revision.id to=base_revision %}">Compare</a> {% endif %} </div> {% endfor %} + {% endwith %} </div> {% endblock %} diff --git a/opentech/apply/funds/urls.py b/opentech/apply/funds/urls.py index 60bc05307ba9eaa5254a1d560726950dbc287c0f..ede94849cb543a6a8dfc73317ba83edb50d4fefa 100644 --- a/opentech/apply/funds/urls.py +++ b/opentech/apply/funds/urls.py @@ -1,6 +1,7 @@ from django.urls import include, path from .views import ( + RevisionCompareView, RevisionListView, SubmissionDetailView, SubmissionEditView, @@ -10,7 +11,8 @@ from .views import ( revision_urls = ([ - path('', RevisionListView.as_view(), name='list') + path('', RevisionListView.as_view(), name='list'), + path('compare/<int:to>/<int:from>', RevisionCompareView.as_view(), name='compare'), ], 'revisions') diff --git a/opentech/apply/funds/views.py b/opentech/apply/funds/views.py index 457164ae291ff6fe66b1d1f591f54f7caae38c40..7eb7feb7308011b46ee8de20d91f4ad12532e35b 100644 --- a/opentech/apply/funds/views.py +++ b/opentech/apply/funds/views.py @@ -1,3 +1,5 @@ +from difflib import SequenceMatcher + from django.contrib.auth.decorators import login_required from django.core.exceptions import PermissionDenied from django.http import HttpResponseRedirect @@ -257,3 +259,43 @@ class RevisionListView(ListView): submission=self.submission, **kwargs, ) + + +class RevisionCompareView(TemplateView): + template_name = 'funds/revisions_compare.html' + + def compare_answer(self, answer_a, answer_b): + if not answer_a and not answer_b: + return answer_b + diff = SequenceMatcher(answer_a, answer_b) + output = [] + for opcode, a0, a1, b0, b1 in diff.get_opcodes(): + if opcode == 'equal': + output.append(diff.a[a0:a1]) + elif opcode == 'insert': + output.append('<span class="added">' + diff.b[b0:b1] + '</span>') + elif opcode == 'delete': + output.append('<span class="deleted">' + diff.a[a0:a1] + "</span>") + elif opcode == 'replace': + raise NotImplementedError("what to do with 'replace' opcode?") + else: + raise RuntimeError("unexpected opcode") + return ''.join(output) + + def compare(self, from_data, to_data): + diffed_form_data = { + field: self.compare_answer(from_data.form_data.get(field), to_data.form_data[field]) + for field in to_data.form_data + } + to_data.form_data = diffed_form_data + return to_data + + def get_context_data(self, **kwargs): + from_revision = ApplicationSubmission.objects.get(id=self.kwargs['from']) + to_revision = ApplicationSubmission.objects.get(id=self.kwargs['to']) + diff = self.compare(from_revision, to_revision) + return super().get_context_data( + submission = ApplicationSubmission.objects.get(id=self.kwargs['submission_pk']), + diff=diff, + **kwargs, + )