diff --git a/beat/web/reports/api.py b/beat/web/reports/api.py index 2a38acc8f751e431faf1c0a469f162a7f2dac295..ae7fe4af08875a5c4fa520675eb3163c057f7431 100644 --- a/beat/web/reports/api.py +++ b/beat/web/reports/api.py @@ -68,6 +68,8 @@ from ..common.responses import BadRequestResponse, ForbiddenResponse import re +from django.utils.encoding import force_bytes, force_text + import simplejson as json @@ -546,3 +548,27 @@ class ReportResultsAllExperimentsView(CommonContextMixin, generics.RetrieveAPIVi results[experiment.fullname()] = serializer.data return Response(results) + + +#---------------------------------------------------------- + + +class ReportRSTCompileView(BaseReportActionView): + permission_classes = BaseReportActionView.permission_classes + [IsEditable] + + def post(self, request, owner_name, report_name): + result = {} + + try: + from docutils.core import publish_parts + except ImportError: + if settings.DEBUG: + raise template.TemplateSyntaxError("Error in ReportRSTCompileView: The Python docutils library isn't installed.") + result['html_str'] = force_text(value) + else: + docutils_settings = getattr(settings, "RESTRUCTUREDTEXT_FILTER_SETTINGS", {}) + parts = publish_parts(source=force_bytes(request.data['raw']), writer_name="html4css1", settings_overrides=docutils_settings) + result['html_str'] = force_text(parts["fragment"]) + + return Response(result) + #return BadRequestResponse(result) diff --git a/beat/web/reports/api_urls.py b/beat/web/reports/api_urls.py index f8d4017819df11bc44cd09b3717f8fe273008e06..9a0038c25bb3e039fb98f0055a0c28bf6ba4d38b 100644 --- a/beat/web/reports/api_urls.py +++ b/beat/web/reports/api_urls.py @@ -30,6 +30,12 @@ from . import api urlpatterns = [ + url( + r'^(?P<owner_name>\w+)/(?P<report_name>[\w\W]+)/rst/$', + api.ReportRSTCompileView.as_view(), + name='rst_compiler' + ), + url( r'^$', api.ReportListView.as_view(), diff --git a/beat/web/reports/static/reports/app/app.js b/beat/web/reports/static/reports/app/app.js index c765092820e139e2ec61c0c6f098ffc8f7551e6a..663256161a1b9cfeaeda43da27c3ffb0befa8836 100644 --- a/beat/web/reports/static/reports/app/app.js +++ b/beat/web/reports/static/reports/app/app.js @@ -19,7 +19,7 @@ * You should have received a copy of the GNU Affero Public License along * with the BEAT platform. If not, see http://www.gnu.org/licenses/. */ -angular.module('reportApp', ['ui.router', 'angular.filter', 'ui.sortable']); +angular.module('reportApp', ['ui.router', 'angular.filter', 'ui.sortable', 'ui.codemirror']); angular.module('reportApp').config(function ($stateProvider, $urlRouterProvider){ diff --git a/beat/web/reports/static/reports/app/controllers/reportController.js b/beat/web/reports/static/reports/app/controllers/reportController.js index eecfe0b68445db57b8c9583199119c2e852edcd0..a27174dd3a5dfafed522b5fd7cd99515ae3a01dc 100644 --- a/beat/web/reports/static/reports/app/controllers/reportController.js +++ b/beat/web/reports/static/reports/app/controllers/reportController.js @@ -101,6 +101,7 @@ angular.module('reportApp').controller('reportController',['$scope', 'reportFact // save groups data to the model GroupsService.loadGroups($scope.report.content.groups); + GroupsService.saveUrlBase(`/${$scope.user}/${$scope.report_id}`); } function getReportData(user, report_id){ diff --git a/beat/web/reports/static/reports/app/directives/groupTableItem.js b/beat/web/reports/static/reports/app/directives/groupTableItem.js index 16215ecf4f17340686e0edd4a601f7747f238a14..b319e4ac36307b9fd0df9fafc43d8ecaa1c04f16 100644 --- a/beat/web/reports/static/reports/app/directives/groupTableItem.js +++ b/beat/web/reports/static/reports/app/directives/groupTableItem.js @@ -134,12 +134,16 @@ angular.module('reportApp') ; let str = ''; - let fieldsStr = fields.join(','); + + let fieldsStr = fields + .map(f => `${f}(${scope.getFieldType(f)})`) + .join(','); + let expsStrs = exps - .map(e => fields.map(f => `${scope.getFieldVal(e, f)}(${scope.getFieldType(f)})`).join(',')) + .map(e => fields.map(f => `${scope.getFieldVal(e, f)}`).join(',')) .join('\n'); - str = `${fieldsStr}\n${expsStrs}`; + str = `<pre>${fieldsStr}\n${expsStrs}</pre>`; return str; }; diff --git a/beat/web/reports/static/reports/app/directives/groupTextItem.js b/beat/web/reports/static/reports/app/directives/groupTextItem.js index 6bb7ea5eb7b0f916604eff4d7a6073c60d60eb8e..68caadfcb80464f90cfe3d3d3f0a1c8e1f8e7dec 100644 --- a/beat/web/reports/static/reports/app/directives/groupTextItem.js +++ b/beat/web/reports/static/reports/app/directives/groupTextItem.js @@ -26,7 +26,7 @@ * displays a plot report item */ angular.module('reportApp') -.directive("groupTextItem", [function(){ +.directive("groupTextItem", ['GroupsService', function(GroupsService){ return { scope: { group: '=', @@ -37,45 +37,22 @@ angular.module('reportApp') link: function(scope){ scope.item = scope.reportItem; scope.domId = `${scope.group.name}_${scope.item.id}`; - const editorOptions = { - modules: { - toolbar: [ - [{ header: [1, 2, false] }], - ['bold', 'italic', 'underline'], - ['image', 'code-block'] - ] - }, - theme: 'snow' - }; - - // the text editor - scope.editor; - scope.txt = { val: '' }; - let setupEditor = (el) => { - scope.editor = new Quill(el, editorOptions); - if(typeof scope.item.content === 'object'){ - scope.editor.setContents(scope.item.content); - scope.txt.val = scope.editor.getText(); - } + scope.uicmOptions = { + mode: 'rst', }; - let editorId = `${scope.domId}-text-editor`; - scope.$watch(() => document.querySelector(`#${editorId}`), - (oldEl, newEl) => { - if(newEl){ - setupEditor(newEl); - } - } - ); - scope.saveContent = () => { - let newContent = scope.editor.getContents(); - scope.item.content = newContent; - scope.txt.val = scope.editor.getText(); + scope.compiledContent = { val: '' }; + scope.serializeFuncObj.val = () => { + return scope.compiledContent.val; + }; + scope.compileContent = () => { + GroupsService.compileRST(scope.item.content) + .then(data => { + scope.compiledContent.val = data.data.html_str; + }); }; - - scope.serializeFuncObj.val = () => scope.txt; }, template: ` <div id="{{domId}}-heading" class="panel-heading" role="tab"> @@ -91,8 +68,8 @@ angular.module('reportApp') {{ domId }} </a> <div class="btn-group" role="group" role='tab'> - <button class='btn btn-success' ng-click='saveContent()'> - Save Content + <button class='btn btn-success' ng-click='compileContent()'> + Compile Content </button> <button class='btn btn-default' ng-click='showSerialized.val = !showSerialized.val'> Toggle Serialize View @@ -108,7 +85,12 @@ angular.module('reportApp') class="panel-collapse collapse in" role="tabpanel" aria-labelledby="{{domId}}-heading"> - <div id='{{domId}}-text-editor'></div> + <div class='panel-body'> + <ui-codemirror ng-model='item.content' ui-codemirror-opts='uicmOptions'></ui-codemirror> + <p class='help-block'> + Describe the object thoroughly using <a href="http://docutils.sourceforge.net/rst.html">reStructuredText mark-up</a><br><i class="fa fa-thumbs-up"></i> The ruler at 80 columns indicate suggested <a href="https://en.wikipedia.org/wiki/POSIX">POSIX line breaks</a> (for readability).<br><i class="fa fa-thumbs-up"></i> The editor will automatically enlarge to accomodate the entirety of your input<br><i class="fa fa-thumbs-up"></i> Use <a href="http://codemirror.net/doc/manual.html#commands">keyboard shortcuts</a> for search/replace and faster editing. For example, use Ctrl-F (PC) or Cmd-F (Mac) to search through this box + </p> + </div> </div> ` }; diff --git a/beat/web/reports/static/reports/app/directives/groupViewSerialized.js b/beat/web/reports/static/reports/app/directives/groupViewSerialized.js index 558a0c2448602c98471c7cd8a7063192ffec2861..540a17c964f235e13f0bfeec057b92f95dea844e 100644 --- a/beat/web/reports/static/reports/app/directives/groupViewSerialized.js +++ b/beat/web/reports/static/reports/app/directives/groupViewSerialized.js @@ -26,19 +26,18 @@ * displays a plot report item */ angular.module('reportApp') -.directive("groupViewSerialized", [function(){ +.directive("groupViewSerialized", ['$sce', function($sce){ return { scope: { entity: '=', serializeFuncObj: '=' }, link: function(scope, el){ + scope.trustAsHtml = $sce.trustAsHtml; }, template: ` <div class='well'> -<pre> -{{ serializeFuncObj.val() }} -</pre> + <div ng-bind-html='trustAsHtml(serializeFuncObj.val())'></div> </div> ` }; diff --git a/beat/web/reports/static/reports/app/factories/reportFactory.js b/beat/web/reports/static/reports/app/factories/reportFactory.js index d7bb01802574db43bd802553fa16b6bbd36c7568..4e930ae3d2a0ee479b1d546bd6f78e1c82b77148 100644 --- a/beat/web/reports/static/reports/app/factories/reportFactory.js +++ b/beat/web/reports/static/reports/app/factories/reportFactory.js @@ -94,5 +94,20 @@ angular.module('reportApp').factory('reportFactory', ['$http', 'experimentFactor }); }; + reportFactory.compileRST = (urlSubstr, raw) => { + let data = { + raw + }; + + let url = `${urlBase}${urlSubstr}`; + + return $http({ + headers: {'Content-Type': 'application/json'}, + url, + method: "POST", + data + }); + }; + return reportFactory; }]); diff --git a/beat/web/reports/static/reports/app/services/groupsService.js b/beat/web/reports/static/reports/app/services/groupsService.js index 887a0482d0d7897946a6b3b408b0a94a964d5530..fc86d107df4daa3014984277bfb8e577b9135a5e 100644 --- a/beat/web/reports/static/reports/app/services/groupsService.js +++ b/beat/web/reports/static/reports/app/services/groupsService.js @@ -26,7 +26,7 @@ * The datastore for the reports app, including data fetched from the * server and cross-component app state */ -angular.module('reportApp').factory('GroupsService', ['$http', function($http){ +angular.module('reportApp').factory('GroupsService', ['reportFactory', function(reportFactory){ let groupsServiceInstance = {}; // experiments of reports are in arbitrary groups, // in a many-to-many relationship @@ -273,6 +273,17 @@ angular.module('reportApp').factory('GroupsService', ['$http', function($http){ return groupData.find(g => g.reportItems.find(i => i.id === id)); }; + groupsServiceInstance.urlBase = ''; + groupsServiceInstance.saveUrlBase = (url) => { + console.log(url); + groupsServiceInstance.urlBase = url; + }; + + groupsServiceInstance.compileRST = (raw) => { + let url = `${groupsServiceInstance.urlBase}/rst/`; + return reportFactory.compileRST(url, raw); + }; + // helper to assert that a group exists function checkForGroup (groupName) { if(!groupData.find(g => g.name === groupName)){ diff --git a/beat/web/reports/templates/reports/report.html b/beat/web/reports/templates/reports/report.html index a1beb4668de094f58cbab2edf38ff9f3ff28c755..5db1915161da0eca3040eb75deb727be49b51a5c 100644 --- a/beat/web/reports/templates/reports/report.html +++ b/beat/web/reports/templates/reports/report.html @@ -42,8 +42,6 @@ <link rel="stylesheet" href="{% fingerprint "chosen-bootstrap/chosen.bootstrap.min.css" %}" type="text/css" media="screen" /> <link rel="stylesheet" href="{% fingerprint "datatables/media/css/dataTables.bootstrap.min.css" %}" type="text/css" media="screen" /> <link rel="stylesheet" href="{% fingerprint "jquery-ui/themes/base/minified/jquery-ui.min.css" %}" type="text/css" media="screen" /> - <link href="//cdn.quilljs.com/1.2.2/quill.snow.css" rel="stylesheet"> - <link href="//cdn.quilljs.com/1.2.2/quill.bubble.css" rel="stylesheet"> {% code_editor_css %} {% endblock %} @@ -55,7 +53,6 @@ <script src="{% fingerprint "chosen/chosen.jquery.min.js" %}" type="text/javascript" charset="utf-8"></script> <script src="{% fingerprint "datatables/media/js/jquery.dataTables.min.js" %}" type="text/javascript" charset="utf-8"></script> <script src="{% fingerprint "datatables/media/js/dataTables.bootstrap.min.js" %}" type="text/javascript" charset="utf-8"></script> - <script src="//cdn.quilljs.com/1.2.2/quill.min.js"></script> <!-- Use Google's CDN for angular-js with a local fallback --> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script> @@ -70,6 +67,7 @@ <script src="{% fingerprint "jquery-ui/ui/minified/jquery.ui.mouse.min.js" %}" type="text/javascript" charset="utf-8"></script> <script src="{% fingerprint "jquery-ui/ui/minified/jquery.ui.sortable.min.js" %}" type="text/javascript" charset="utf-8"></script> <script src="{% fingerprint "angular-ui-sortable/sortable.min.js" %}" type="text/javascript" charset="utf-8"></script> + <script src="{% fingerprint "angular-ui-codemirror/ui-codemirror.min.js" %}" type="text/javascript" charset="utf-8"></script> <script src="{% fingerprint "experiments/js/utils.js" %}" type="text/javascript" charset="utf-8"></script> <script src="{% fingerprint "reports/js/base_64_encoder_decoder.js" %}" type="text/javascript" charset="utf-8"></script> diff --git a/buildout.cfg b/buildout.cfg index 74df35d3f5692d3a80575e3cd644eb2c9984e260..768a3401e470ca02c6658b79210869d3c7bfeda4 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -129,6 +129,7 @@ packages = jquery#~1.11.3 underscore#~1.8.3 datatables#~1.10.10 angular-ui-sortable#~0.14 + angular-ui-codemirror executable = ${buildout:bin-directory}/bower base-directory = beat/web downloads = static