From 88388f40b818e0a9323dc548ab3dd0e930ae4240 Mon Sep 17 00:00:00 2001 From: Kasper Brandt Date: Wed, 13 May 2015 17:39:03 +0200 Subject: [PATCH 1/3] [#1447] IATI export: performance increase through React checks --- akvo/iati/checks/v201.py | 10 ++- akvo/rest/urls.py | 3 + akvo/rest/views/__init__.py | 2 + akvo/rest/views/project_iati_checks.py | 25 +++++++ akvo/rsr/forms.py | 16 +---- akvo/rsr/static/scripts-src/my-iati.js | 92 +++++++++++++++++++++++++ akvo/rsr/static/scripts-src/my-iati.jsx | 92 +++++++++++++++++++++++++ akvo/settings/40-pipeline.conf | 6 ++ akvo/templates/myrsr/my_iati.html | 10 +++ 9 files changed, 238 insertions(+), 18 deletions(-) create mode 100644 akvo/rest/views/project_iati_checks.py create mode 100644 akvo/rsr/static/scripts-src/my-iati.js create mode 100644 akvo/rsr/static/scripts-src/my-iati.jsx diff --git a/akvo/iati/checks/v201.py b/akvo/iati/checks/v201.py index 2147e9a4db..96f7c13009 100644 --- a/akvo/iati/checks/v201.py +++ b/akvo/iati/checks/v201.py @@ -26,6 +26,7 @@ 'related_activity', 'legacy_data', 'conditions', + 'results', 'crs_add', 'fss', ] @@ -618,15 +619,18 @@ def results(self): for result in self.project.results.all(): if not result.type: - checks.append((u'warning', u'result (id: %s) has no type ' + self.all_checks_passed = False + checks.append((u'error', u'result (id: %s) has no type ' u'specified' % str(result.pk))) if not result.title: checks.append((u'warning', u'result (id: %s) has no title ' u'specified' % str(result.pk))) - if not result.indicators.all(): - checks.append((u'warning', u'result (id: %s) has no indicators' % str(result.pk))) + if not result.indicators.all() and not result.description: + self.all_checks_passed = False + checks.append((u'error', u'result (id: %s) has no description and no ' + u'indicator(s)' % str(result.pk))) for indicator in result.indicators.all(): if not indicator.measure: diff --git a/akvo/rest/urls.py b/akvo/rest/urls.py index 48f2537109..ea1667e2a4 100644 --- a/akvo/rest/urls.py +++ b/akvo/rest/urls.py @@ -86,6 +86,9 @@ url(r'^user/(?P[0-9]+)/request_organisation/$', views.request_organisation, name='user_request_organisation'), + url(r'^project_iati_check/(?P[0-9]+)/$', + views.ProjectIatiCheckView.as_view(), + name='project_iati_check'), ) # Typeahead diff --git a/akvo/rest/views/__init__.py b/akvo/rest/views/__init__.py index b9caab0aec..f1cedc831a 100644 --- a/akvo/rest/views/__init__.py +++ b/akvo/rest/views/__init__.py @@ -34,6 +34,7 @@ from .project_document import ProjectDocumentViewSet from .project_condition import ProjectConditionViewSet from .project_contact import ProjectContactViewSet +from .project_iati_checks import ProjectIatiCheckView from .project_location import ProjectLocationViewSet, MapProjectLocationViewSet from .project_update import ProjectUpdateViewSet, ProjectUpdateExtraViewSet from .project_update_location import ProjectUpdateLocationViewSet, MapProjectUpdateLocationViewSet @@ -85,6 +86,7 @@ 'ProjectContactViewSet', 'ProjectDocumentViewSet', 'ProjectExtraViewSet', + 'ProjectIatiCheckView', 'ProjectLocationViewSet', 'ProjectUpdateExtraViewSet', 'ProjectUpdateLocationViewSet', diff --git a/akvo/rest/views/project_iati_checks.py b/akvo/rest/views/project_iati_checks.py new file mode 100644 index 0000000000..2234730dda --- /dev/null +++ b/akvo/rest/views/project_iati_checks.py @@ -0,0 +1,25 @@ +from akvo.rsr.models import Project +from django.http import Http404 +from rest_framework.views import APIView +from rest_framework.response import Response + + +class ProjectIatiCheckView(APIView): + """ + List the result of IATI checks of a Project. + """ + def get_object(self, pk): + try: + return Project.objects.get(pk=pk) + except Project.DoesNotExist: + raise Http404 + + def get(self, request, pk, format=None): + project = self.get_object(pk) + check_results = project.check_mandatory_fields() + response = { + 'all_checks_passed': str(check_results[0]), + 'checks': check_results[1], + } + + return Response(response) diff --git a/akvo/rsr/forms.py b/akvo/rsr/forms.py index 13cbbc4015..1cbc157a20 100644 --- a/akvo/rsr/forms.py +++ b/akvo/rsr/forms.py @@ -394,24 +394,10 @@ def __init__(self, user, *args, **kwargs): ) -class CustomLabelModelChoiceField(forms.ModelMultipleChoiceField): - def label_from_instance(self, obj): - checks = obj.check_mandatory_fields() - if checks[0]: - return mark_safe(u'%s' % obj.__unicode__()) - else: - label = obj.__unicode__() - for check in checks[1]: - if check[0] == u'error': - label += u'
- %s' % check[1] - return mark_safe(u'%s' % label) - - - class IatiExportForm(forms.ModelForm): """Form for adding an entry to the IATI export model.""" is_public = forms.BooleanField(required=False, label=_(u"Show IATI file on organisation page")) - projects = CustomLabelModelChoiceField( + projects = forms.ModelMultipleChoiceField( widget=forms.CheckboxSelectMultiple, queryset=Project.objects.all(), label=_(u"Select the projects included in the export:") diff --git a/akvo/rsr/static/scripts-src/my-iati.js b/akvo/rsr/static/scripts-src/my-iati.js new file mode 100644 index 0000000000..174c4a9e87 --- /dev/null +++ b/akvo/rsr/static/scripts-src/my-iati.js @@ -0,0 +1,92 @@ +/** @jsx React.DOM */ + +// Akvo RSR is covered by the GNU Affero General Public License. +// See more details in the license.txt file located at the root folder of the +// Akvo RSR module. For additional details on the GNU license please see +// < http://www.gnu.org/licenses/agpl.html >. + + +function loadAsync(url, retryCount, retryLimit, label) { + var xmlHttp; + + xmlHttp = new XMLHttpRequest(); + + xmlHttp.onreadystatechange = function() { + if (xmlHttp.readyState == XMLHttpRequest.DONE) { + + if(xmlHttp.status == 200){ + processResponse(label, xmlHttp.responseText); + } else { + if (retryCount >= retryLimit) { + return; + } else { + retryCount = retryCount + 1; + loadAsync(url, retryCount, retryLimit); + } + } + } else { + return; + } + }; + + xmlHttp.open("GET", url, true); + xmlHttp.send(); +} + +function processResponse(label, response) { + var label_content, checks, all_checks_passed, span; + + label_content = label.innerHTML; + checks = JSON.parse(response); + + all_checks_passed = checks.all_checks_passed; + + if (all_checks_passed === "True") { + span = document.createElement("span"); + span.className = "success"; + span.innerHTML = label_content; + + label.innerHTML = ''; + label.appendChild(span); + + } else if (all_checks_passed === "False") { + span = document.createElement("span"); + span.className = "error"; + span.innerHTML = label_content; + + label.innerHTML = ''; + label.appendChild(span); + } +} + +function getProjectLabels() { + var labels; + + labels = document.getElementById('id_projects').getElementsByTagName('label'); + + for (var i = 0; i < labels.length; i++) { + var project_id; + + project_id = labels[i].getElementsByTagName('input')[0].value; + loadAsync('/rest/v1/project_iati_check/' + project_id + '/?format=json', 0, 3, labels[i]); + } +} + +function loadComponent(component_id) { + var Container; + + Container = React.createClass({displayName: 'Container', + render: function() { + return ( + React.DOM.a( {onClick:getProjectLabels}, "Perform checks") + ); + } + }); + + React.render( + Container(null ), + document.getElementById(component_id) + ); +} + +loadComponent('react_iati_checks'); diff --git a/akvo/rsr/static/scripts-src/my-iati.jsx b/akvo/rsr/static/scripts-src/my-iati.jsx new file mode 100644 index 0000000000..6d5abcdb9e --- /dev/null +++ b/akvo/rsr/static/scripts-src/my-iati.jsx @@ -0,0 +1,92 @@ +/** @jsx React.DOM */ + +// Akvo RSR is covered by the GNU Affero General Public License. +// See more details in the license.txt file located at the root folder of the +// Akvo RSR module. For additional details on the GNU license please see +// < http://www.gnu.org/licenses/agpl.html >. + + +function loadAsync(url, retryCount, retryLimit, label) { + var xmlHttp; + + xmlHttp = new XMLHttpRequest(); + + xmlHttp.onreadystatechange = function() { + if (xmlHttp.readyState == XMLHttpRequest.DONE) { + + if(xmlHttp.status == 200){ + processResponse(label, xmlHttp.responseText); + } else { + if (retryCount >= retryLimit) { + return; + } else { + retryCount = retryCount + 1; + loadAsync(url, retryCount, retryLimit); + } + } + } else { + return; + } + }; + + xmlHttp.open("GET", url, true); + xmlHttp.send(); +} + +function processResponse(label, response) { + var label_content, checks, all_checks_passed, span; + + label_content = label.innerHTML; + checks = JSON.parse(response); + + all_checks_passed = checks.all_checks_passed; + + if (all_checks_passed === "True") { + span = document.createElement("span"); + span.className = "success"; + span.innerHTML = label_content; + + label.innerHTML = ''; + label.appendChild(span); + + } else if (all_checks_passed === "False") { + span = document.createElement("span"); + span.className = "error"; + span.innerHTML = label_content; + + label.innerHTML = ''; + label.appendChild(span); + } +} + +function getProjectLabels() { + var labels; + + labels = document.getElementById('id_projects').getElementsByTagName('label'); + + for (var i = 0; i < labels.length; i++) { + var project_id; + + project_id = labels[i].getElementsByTagName('input')[0].value; + loadAsync('/rest/v1/project_iati_check/' + project_id + '/?format=json', 0, 3, labels[i]); + } +} + +function loadComponent(component_id) { + var Container; + + Container = React.createClass({ + render: function() { + return ( + Perform checks + ); + } + }); + + React.render( + , + document.getElementById(component_id) + ); +} + +loadComponent('react_iati_checks'); diff --git a/akvo/settings/40-pipeline.conf b/akvo/settings/40-pipeline.conf index 495e3383e5..a8dbf0f45a 100644 --- a/akvo/settings/40-pipeline.conf +++ b/akvo/settings/40-pipeline.conf @@ -148,4 +148,10 @@ PIPELINE_JS = { ), 'output_filename': 'scripts/rsr-project-main.min.js', }, + 'my_iati': { + 'source_filenames': ( + 'scripts-src/my-iati.js', + ), + 'output_filename': 'scripts/my-iati.min.js', + }, } diff --git a/akvo/templates/myrsr/my_iati.html b/akvo/templates/myrsr/my_iati.html index 29d7252dca..6e63c122f6 100644 --- a/akvo/templates/myrsr/my_iati.html +++ b/akvo/templates/myrsr/my_iati.html @@ -41,6 +41,7 @@

{% trans "My IATI" %}{% if selected_org %} {% trans "for" %} {{selected_org.

{% elif project_count > 0 %} +

{% csrf_token %} @@ -82,3 +83,12 @@

{% trans 'Existing IATI exports' %}

{% endif %} {% endblock %} + +{% block react_js %} + +{% endblock react_js %} + +{% block js %} + {{ block.super }} + {% compressed_js 'my_iati' %} +{% endblock js %} \ No newline at end of file From e62465828c345989fd1c35172ca627532f734cac Mon Sep 17 00:00:00 2001 From: Kasper Brandt Date: Fri, 15 May 2015 11:11:03 +0200 Subject: [PATCH 2/3] [#1447] Add button and help text --- akvo/rsr/static/scripts-src/my-iati.js | 28 ++++++++++++++++++++++++- akvo/rsr/static/scripts-src/my-iati.jsx | 28 ++++++++++++++++++++++++- akvo/templates/myrsr/my_iati.html | 4 ++-- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/akvo/rsr/static/scripts-src/my-iati.js b/akvo/rsr/static/scripts-src/my-iati.js index 174c4a9e87..68be875cec 100644 --- a/akvo/rsr/static/scripts-src/my-iati.js +++ b/akvo/rsr/static/scripts-src/my-iati.js @@ -70,15 +70,41 @@ function getProjectLabels() { project_id = labels[i].getElementsByTagName('input')[0].value; loadAsync('/rest/v1/project_iati_check/' + project_id + '/?format=json', 0, 3, labels[i]); } + + return true; } function loadComponent(component_id) { var Container; Container = React.createClass({displayName: 'Container', + getInitialState: function() { + return {active_button: true}; + }, + + handleClick: function() { + this.setState({active_button: false}); + getProjectLabels(); + }, + render: function() { return ( - React.DOM.a( {onClick:getProjectLabels}, "Perform checks") + React.DOM.p(null, + React.DOM.div( {className:"row"}, + React.DOM.div( {className:"col-md-8"}, + "In order to see which of your projects is fully IATI compliant, you can"+' '+ + "perform checks by clicking the \"Perform checks\" button. ", React.DOM.br(null), + "Projects with all mandatory IATI information filled in will be"+' '+ + "marked ", React.DOM.span( {className:"success"}, "green"), " and projects with missing"+' '+ + "information will be marked ", React.DOM.span( {className:"error"}, "red"),"." + ), + React.DOM.div( {className:"col-md-4"}, + React.DOM.button( {onClick:this.handleClick, className:this.state.active_button ? 'btn btn-primary' : 'btn btn-primary disabled'}, + this.state.active_button ? 'Perform checks' : 'Performing checks...' + ) + ) + ) + ) ); } }); diff --git a/akvo/rsr/static/scripts-src/my-iati.jsx b/akvo/rsr/static/scripts-src/my-iati.jsx index 6d5abcdb9e..78bce67656 100644 --- a/akvo/rsr/static/scripts-src/my-iati.jsx +++ b/akvo/rsr/static/scripts-src/my-iati.jsx @@ -70,15 +70,41 @@ function getProjectLabels() { project_id = labels[i].getElementsByTagName('input')[0].value; loadAsync('/rest/v1/project_iati_check/' + project_id + '/?format=json', 0, 3, labels[i]); } + + return true; } function loadComponent(component_id) { var Container; Container = React.createClass({ + getInitialState: function() { + return {active_button: true}; + }, + + handleClick: function() { + this.setState({active_button: false}); + getProjectLabels(); + }, + render: function() { return ( - Perform checks +

+

+
+ In order to see which of your projects is fully IATI compliant, you can + perform checks by clicking the "Perform checks" button.
+ Projects with all mandatory IATI information filled in will be + marked green and projects with missing + information will be marked red. +
+
+ +
+
+

); } }); diff --git a/akvo/templates/myrsr/my_iati.html b/akvo/templates/myrsr/my_iati.html index 6e63c122f6..bf074bddf8 100644 --- a/akvo/templates/myrsr/my_iati.html +++ b/akvo/templates/myrsr/my_iati.html @@ -7,10 +7,10 @@ {% block head %} {{block.super}} From a69551627fa6e5b0016a691f3d29c72fae581042 Mon Sep 17 00:00:00 2001 From: Kasper Brandt Date: Wed, 27 May 2015 11:52:59 +0200 Subject: [PATCH 3/3] [#1447] Translations for JSX files --- .../scripts-src/my-details-employments.js | 3 +- .../scripts-src/my-details-employments.jsx | 3 +- akvo/rsr/static/scripts-src/my-iati.js | 36 ++++++++---------- akvo/rsr/static/scripts-src/my-iati.jsx | 38 ++++++++----------- .../static/scripts-src/my-user-management.js | 3 +- .../static/scripts-src/my-user-management.jsx | 3 +- akvo/rsr/static/scripts-src/password-reset.js | 1 + .../rsr/static/scripts-src/password-reset.jsx | 1 + akvo/rsr/static/scripts-src/project-main.js | 3 +- akvo/rsr/static/scripts-src/project-main.jsx | 3 +- akvo/templates/myrsr/my_iati.html | 21 +++++++++- 11 files changed, 64 insertions(+), 51 deletions(-) diff --git a/akvo/rsr/static/scripts-src/my-details-employments.js b/akvo/rsr/static/scripts-src/my-details-employments.js index 6f330d2b77..17a5ae2204 100644 --- a/akvo/rsr/static/scripts-src/my-details-employments.js +++ b/akvo/rsr/static/scripts-src/my-details-employments.js @@ -19,7 +19,8 @@ var AddEmploymentForm, OrganisationInput, ResponseModal, initial_data, - request_link; + request_link, + i18n; diff --git a/akvo/rsr/static/scripts-src/my-details-employments.jsx b/akvo/rsr/static/scripts-src/my-details-employments.jsx index 0c302bb2ea..38937ad638 100644 --- a/akvo/rsr/static/scripts-src/my-details-employments.jsx +++ b/akvo/rsr/static/scripts-src/my-details-employments.jsx @@ -19,7 +19,8 @@ var AddEmploymentForm, OrganisationInput, ResponseModal, initial_data, - request_link; + request_link, + i18n; diff --git a/akvo/rsr/static/scripts-src/my-iati.js b/akvo/rsr/static/scripts-src/my-iati.js index 68be875cec..b11d950270 100644 --- a/akvo/rsr/static/scripts-src/my-iati.js +++ b/akvo/rsr/static/scripts-src/my-iati.js @@ -5,6 +5,7 @@ // Akvo RSR module. For additional details on the GNU license please see // < http://www.gnu.org/licenses/agpl.html >. +var i18n; function loadAsync(url, retryCount, retryLimit, label) { var xmlHttp; @@ -16,16 +17,17 @@ function loadAsync(url, retryCount, retryLimit, label) { if(xmlHttp.status == 200){ processResponse(label, xmlHttp.responseText); + return true; } else { if (retryCount >= retryLimit) { - return; + return false; } else { retryCount = retryCount + 1; loadAsync(url, retryCount, retryLimit); } } } else { - return; + return false; } }; @@ -70,8 +72,6 @@ function getProjectLabels() { project_id = labels[i].getElementsByTagName('input')[0].value; loadAsync('/rest/v1/project_iati_check/' + project_id + '/?format=json', 0, 3, labels[i]); } - - return true; } function loadComponent(component_id) { @@ -88,24 +88,17 @@ function loadComponent(component_id) { }, render: function() { - return ( - React.DOM.p(null, - React.DOM.div( {className:"row"}, - React.DOM.div( {className:"col-md-8"}, - "In order to see which of your projects is fully IATI compliant, you can"+' '+ - "perform checks by clicking the \"Perform checks\" button. ", React.DOM.br(null), - "Projects with all mandatory IATI information filled in will be"+' '+ - "marked ", React.DOM.span( {className:"success"}, "green"), " and projects with missing"+' '+ - "information will be marked ", React.DOM.span( {className:"error"}, "red"),"." - ), - React.DOM.div( {className:"col-md-4"}, - React.DOM.button( {onClick:this.handleClick, className:this.state.active_button ? 'btn btn-primary' : 'btn btn-primary disabled'}, - this.state.active_button ? 'Perform checks' : 'Performing checks...' - ) + if (this.state.active_button) { + return ( + React.DOM.p(null, + React.DOM.button( {onClick:this.handleClick, className:"btn btn-primary"}, i18n.perform_checks) ) - ) - ) - ); + ); + } else { + return ( + React.DOM.p(null) + ); + } } }); @@ -115,4 +108,5 @@ function loadComponent(component_id) { ); } +i18n = JSON.parse(document.getElementById("perform-checks-text").innerHTML); loadComponent('react_iati_checks'); diff --git a/akvo/rsr/static/scripts-src/my-iati.jsx b/akvo/rsr/static/scripts-src/my-iati.jsx index 78bce67656..efb8374f8c 100644 --- a/akvo/rsr/static/scripts-src/my-iati.jsx +++ b/akvo/rsr/static/scripts-src/my-iati.jsx @@ -5,6 +5,7 @@ // Akvo RSR module. For additional details on the GNU license please see // < http://www.gnu.org/licenses/agpl.html >. +var i18n; function loadAsync(url, retryCount, retryLimit, label) { var xmlHttp; @@ -16,16 +17,17 @@ function loadAsync(url, retryCount, retryLimit, label) { if(xmlHttp.status == 200){ processResponse(label, xmlHttp.responseText); + return true; } else { if (retryCount >= retryLimit) { - return; + return false; } else { retryCount = retryCount + 1; loadAsync(url, retryCount, retryLimit); } } } else { - return; + return false; } }; @@ -70,8 +72,6 @@ function getProjectLabels() { project_id = labels[i].getElementsByTagName('input')[0].value; loadAsync('/rest/v1/project_iati_check/' + project_id + '/?format=json', 0, 3, labels[i]); } - - return true; } function loadComponent(component_id) { @@ -88,24 +88,17 @@ function loadComponent(component_id) { }, render: function() { - return ( -

-

-
- In order to see which of your projects is fully IATI compliant, you can - perform checks by clicking the "Perform checks" button.
- Projects with all mandatory IATI information filled in will be - marked green and projects with missing - information will be marked red. -
-
- -
-
-

- ); + if (this.state.active_button) { + return ( +

+ +

+ ); + } else { + return ( +

+ ); + } } }); @@ -115,4 +108,5 @@ function loadComponent(component_id) { ); } +i18n = JSON.parse(document.getElementById("perform-checks-text").innerHTML); loadComponent('react_iati_checks'); diff --git a/akvo/rsr/static/scripts-src/my-user-management.js b/akvo/rsr/static/scripts-src/my-user-management.js index d48076444d..7abf3604b0 100644 --- a/akvo/rsr/static/scripts-src/my-user-management.js +++ b/akvo/rsr/static/scripts-src/my-user-management.js @@ -19,7 +19,8 @@ var ApproveModal, Table = ReactBootstrap.Table, TriggerModal, UserTable, - initial_data; + initial_data, + i18n; DeleteModal = React.createClass({displayName: 'DeleteModal', diff --git a/akvo/rsr/static/scripts-src/my-user-management.jsx b/akvo/rsr/static/scripts-src/my-user-management.jsx index 8bf3b57f36..a7e83d0d79 100644 --- a/akvo/rsr/static/scripts-src/my-user-management.jsx +++ b/akvo/rsr/static/scripts-src/my-user-management.jsx @@ -19,7 +19,8 @@ var ApproveModal, Table = ReactBootstrap.Table, TriggerModal, UserTable, - initial_data; + initial_data, + i18n; DeleteModal = React.createClass({ diff --git a/akvo/rsr/static/scripts-src/password-reset.js b/akvo/rsr/static/scripts-src/password-reset.js index 1dc88e2d3e..9f6c257782 100644 --- a/akvo/rsr/static/scripts-src/password-reset.js +++ b/akvo/rsr/static/scripts-src/password-reset.js @@ -5,6 +5,7 @@ var Button = ReactBootstrap.Button; var Modal = ReactBootstrap.Modal; var ModalTrigger = ReactBootstrap.ModalTrigger; var Input = ReactBootstrap.Input; +var i18n; var ResetModal = React.createClass({displayName: 'ResetModal', resetPassword: function() { diff --git a/akvo/rsr/static/scripts-src/password-reset.jsx b/akvo/rsr/static/scripts-src/password-reset.jsx index 8697765a2b..34aa1df3b5 100644 --- a/akvo/rsr/static/scripts-src/password-reset.jsx +++ b/akvo/rsr/static/scripts-src/password-reset.jsx @@ -5,6 +5,7 @@ var Button = ReactBootstrap.Button; var Modal = ReactBootstrap.Modal; var ModalTrigger = ReactBootstrap.ModalTrigger; var Input = ReactBootstrap.Input; +var i18n; var ResetModal = React.createClass({ resetPassword: function() { diff --git a/akvo/rsr/static/scripts-src/project-main.js b/akvo/rsr/static/scripts-src/project-main.js index 92822d5ff4..4430ecd464 100755 --- a/akvo/rsr/static/scripts-src/project-main.js +++ b/akvo/rsr/static/scripts-src/project-main.js @@ -10,7 +10,8 @@ var Accordion = ReactBootstrap.Accordion, Carousel = ReactBootstrap.Carousel, CarouselInstance, CarouselItem = ReactBootstrap.CarouselItem, - Panel = ReactBootstrap.Panel; + Panel = ReactBootstrap.Panel, + i18n; Indicator = React.createClass({displayName: 'Indicator', render: function () { diff --git a/akvo/rsr/static/scripts-src/project-main.jsx b/akvo/rsr/static/scripts-src/project-main.jsx index 826b32ab29..7ff16cb9cb 100755 --- a/akvo/rsr/static/scripts-src/project-main.jsx +++ b/akvo/rsr/static/scripts-src/project-main.jsx @@ -10,7 +10,8 @@ var Accordion = ReactBootstrap.Accordion, Carousel = ReactBootstrap.Carousel, CarouselInstance, CarouselItem = ReactBootstrap.CarouselItem, - Panel = ReactBootstrap.Panel; + Panel = ReactBootstrap.Panel, + i18n; Indicator = React.createClass({ render: function () { diff --git a/akvo/templates/myrsr/my_iati.html b/akvo/templates/myrsr/my_iati.html index bf074bddf8..4c6c931b8e 100644 --- a/akvo/templates/myrsr/my_iati.html +++ b/akvo/templates/myrsr/my_iati.html @@ -41,6 +41,15 @@

{% trans "My IATI" %}{% if selected_org %} {% trans "for" %} {{selected_org.

{% elif project_count > 0 %} +

+ {% blocktrans %} + In order to see which of your projects is fully IATI compliant, you can + perform checks by clicking the "Perform checks" button.
+ Projects with all mandatory IATI information filled in will be + marked green and projects with missing + information will be marked red. + {% endblocktrans %} +

@@ -89,6 +98,14 @@

{% trans 'Existing IATI exports' %}

{% endblock react_js %} {% block js %} - {{ block.super }} - {% compressed_js 'my_iati' %} + {{ block.super }} + + {# Translation strings #} + + + {% compressed_js 'my_iati' %} {% endblock js %} \ No newline at end of file