Author: ijd
Date: Thu Jun 19 17:52:25 2014
New Revision: 1603977

URL: http://svn.apache.org/r1603977
Log:
First cut at launching and tracking backup tasks

Added:
    jena/Experimental/jena-fuseki2/src/main/webapp/js/app/models/task.js
Modified:
    jena/Experimental/jena-fuseki2/src/main/webapp/js/app/main.manage.js
    jena/Experimental/jena-fuseki2/src/main/webapp/js/app/models/dataset.js
    
jena/Experimental/jena-fuseki2/src/main/webapp/js/app/templates/dataset-management.tpl
    
jena/Experimental/jena-fuseki2/src/main/webapp/js/app/views/dataset-management.js

Modified: jena/Experimental/jena-fuseki2/src/main/webapp/js/app/main.manage.js
URL: 
http://svn.apache.org/viewvc/jena/Experimental/jena-fuseki2/src/main/webapp/js/app/main.manage.js?rev=1603977&r1=1603976&r2=1603977&view=diff
==============================================================================
--- jena/Experimental/jena-fuseki2/src/main/webapp/js/app/main.manage.js 
(original)
+++ jena/Experimental/jena-fuseki2/src/main/webapp/js/app/main.manage.js Thu 
Jun 19 17:52:25 2014
@@ -7,6 +7,7 @@ define( ['require', '../common-config'],
        'sprintf', 'bootstrap',
        'app/models/fuseki-server',
        'app/models/dataset',
+       'app/models/task',
        'app/views/dataset-management',
        'app/services/ping-service',
        'jquery.xdomainrequest'

Modified: 
jena/Experimental/jena-fuseki2/src/main/webapp/js/app/models/dataset.js
URL: 
http://svn.apache.org/viewvc/jena/Experimental/jena-fuseki2/src/main/webapp/js/app/models/dataset.js?rev=1603977&r1=1603976&r2=1603977&view=diff
==============================================================================
--- jena/Experimental/jena-fuseki2/src/main/webapp/js/app/models/dataset.js 
(original)
+++ jena/Experimental/jena-fuseki2/src/main/webapp/js/app/models/dataset.js Thu 
Jun 19 17:52:25 2014
@@ -9,7 +9,8 @@ define(
         Backbone = require( "backbone" ),
         _ = require( "underscore" ),
         fui = require( "app/fui" ),
-        sprintf = require( "sprintf" );
+        sprintf = require( "sprintf" ),
+        Task = require( "app/models/task" );
 
     /**
      * This model represents the core representation of the remote Fuseki
@@ -90,6 +91,20 @@ define(
           url: this.mgmtActionURL(),
           type: 'DELETE'
         } );
+      },
+
+      /** Perform the action of taking a backup of this dataset */
+      backupDataset: function() {
+        var backupURL = sprintf( "%s/$/backup%s", this.baseURL(), this.name() 
);
+        var ds = this;
+
+        return $.ajax( {
+          url: backupURL,
+          type: 'POST'
+        } )
+          .done( function( taskDescription ) {
+            new Task( ds, "backup", taskDescription );
+          } );
       }
 
     } );

Added: jena/Experimental/jena-fuseki2/src/main/webapp/js/app/models/task.js
URL: 
http://svn.apache.org/viewvc/jena/Experimental/jena-fuseki2/src/main/webapp/js/app/models/task.js?rev=1603977&view=auto
==============================================================================
--- jena/Experimental/jena-fuseki2/src/main/webapp/js/app/models/task.js (added)
+++ jena/Experimental/jena-fuseki2/src/main/webapp/js/app/models/task.js Thu 
Jun 19 17:52:25 2014
@@ -0,0 +1,105 @@
+/**
+ * A long-running task, which periodically pings the server for its task status
+ */
+
+define(
+  function( require ) {
+    "use strict";
+
+    var Marionette = require( "marionette" ),
+        Backbone = require( "backbone" ),
+        _ = require( "underscore" ),
+        fui = require( "app/fui" ),
+        sprintf = require( "sprintf" );
+
+    /* Constants */
+
+    var MS = 1000;
+    var MAX_DELAY = 10 * MS;
+
+    /**
+     * This model represents a long running task process
+     */
+    var Task = function( ds, operationType, taskDescription ) {
+      this.taskDescription = taskDescription;
+      this.ds = ds;
+      this.operationType = operationType;
+      this.delay = 500;
+
+      _.bindAll( this, "checkTaskStatus", "onCurrentTaskStatusFail", 
"onCurrentTaskStatus" );
+
+      this.checkTaskStatus();
+    };
+
+    _.extend( Task.prototype, {
+      /** Return the unique ID (on this server) of the task */
+      taskId: function() {
+        return this.taskDescription.taskId;
+      },
+
+      /** Return the URL for the task's API */
+      taskURL: function() {
+        return sprintf( "%s/$/tasks/%s", this.ds.baseURL(), this.taskId() );
+      },
+
+      /** Test the current status of the task */
+      checkTaskStatus: function() {
+        $.getJSON( this.taskURL() )
+         .done( this.onCurrentTaskStatus )
+         .fail( this.onCurrentTaskStatusFail )
+      },
+
+      /** Successful result from checking the task */
+      onCurrentTaskStatus: function( taskDescription ) {
+        this.taskDescription = taskDescription;
+
+        var status = {
+            task: this,
+            dsId: this.ds.name(),
+            finished: this.taskFinished()
+        };
+
+        fui.vent.trigger( "task.status", status );
+
+        this.queueTaskStatusCheck();
+      },
+
+      /** Failed to check the task */
+      onCurrentTaskStatusFail: function( jqxhr, msg, err ) {
+        var status = {
+            task: this,
+            dsId: this.ds.name(),
+            errorMessage: err || msg
+        };
+
+        fui.vent.trigger( "task.failed", status );
+      },
+
+      /** Re-queue the status check if the task is not yet complete */
+      queueTaskStatusCheck: function() {
+        if (!this.taskFinished()) {
+          _.delay( this.checkTaskStatus, this.statusDelay() );
+        }
+      },
+
+      /** Return the completion time if the task has been fid, otherwise null 
*/
+      taskFinished: function() {
+        return this.taskDescription.finished;
+      },
+
+      /** Return the delay in ms until the next status check is due. */
+      statusDelay: function() {
+        var t = this.delay;
+
+        if (t < MAX_DELAY) {
+          this.delay = t * 2;
+        }
+
+        return t;
+      }
+    } );
+
+    return Task;
+  }
+);
+

Modified: 
jena/Experimental/jena-fuseki2/src/main/webapp/js/app/templates/dataset-management.tpl
URL: 
http://svn.apache.org/viewvc/jena/Experimental/jena-fuseki2/src/main/webapp/js/app/templates/dataset-management.tpl?rev=1603977&r1=1603976&r2=1603977&view=diff
==============================================================================
--- 
jena/Experimental/jena-fuseki2/src/main/webapp/js/app/templates/dataset-management.tpl
 (original)
+++ 
jena/Experimental/jena-fuseki2/src/main/webapp/js/app/templates/dataset-management.tpl
 Thu Jun 19 17:52:25 2014
@@ -15,9 +15,8 @@
             <td><input type='checkbox' class='checkbox' checked /></td>
             <td>
               <div>
-                <a class="btn btn-sm action remove btn-primary" 
data-mgmt-uri='<%= ds.mgmtURL() %>' data-ds-id='<%= ds.name() %>'><i class='fa 
fa-times-circle'></i> remove</a>
-                <a class="btn btn-sm action backup btn-primary" data-uri='<%= 
ds.mgmtURL() %>'><i class='fa fa-download'></i> backup</a>
-                <a class="btn btn-sm action configure btn-primary" 
href="admin-dataset-details.html#<%= ds.name() %>"><i class='fa fa-wrench'></i> 
configure</a>
+                <a class="btn btn-sm action remove btn-primary" 
data-ds-id='<%= ds.name() %>'><i class='fa fa-times-circle'></i> remove</a>
+                <a class="btn btn-sm action backup btn-primary" 
data-ds-id='<%= ds.name() %>'><i class='fa fa-download'></i> backup</a>
                 <a class="btn btn-sm action add-data btn-primary" 
href="dataset.html?tab=upload&ds=<%= ds.name() %>"><i class='fa 
fa-plus-circle'></i> add data</a>
               </div>
               <div class="action feedback"></a>
@@ -32,19 +31,22 @@
 
 <!-- Modal dialogs -->
 
-<div class="modal fade" id="deleteConfirmModal" tabindex="-1" role="dialog" 
aria-labelledby="deleteConfirmModalLabel" aria-hidden="true">
+<div class="modal fade" id="actionConfirmModal" tabindex="-1" role="dialog" 
aria-labelledby="actionConfirmModalLabel" aria-hidden="true">
   <div class="modal-dialog">
     <div class="modal-content">
       <div class="modal-header">
         <button type="button" class="close" data-dismiss="modal" 
aria-hidden="true">&times;</button>
-        <h4 class="modal-title" id="deleteConfirmModalLabel">Confirm 
delete</h4>
+        <h4 class="modal-title" id="actionConfirmModalLabel">Confirm 
action</h4>
       </div>
       <div class="modal-body">
         <p></p>
       </div>
       <div class="modal-footer">
         <button type="button" class="btn btn-default" data-dismiss="modal"><i 
class="fa fa-icon-remove"></i> Cancel</button>
-        <button type="button" class="btn btn-primary action confirm"><i 
class="fa fa-icon-confirm"></i> Confirm delete</button>
+        <button type="button" class="btn btn-primary action confirm">
+          <i class="fa fa-icon-confirm"></i>
+          Confirm <span class="action-label">action</span>
+        </button>
       </div>
     </div><!-- /.modal-content -->
   </div><!-- /.modal-dialog -->

Modified: 
jena/Experimental/jena-fuseki2/src/main/webapp/js/app/views/dataset-management.js
URL: 
http://svn.apache.org/viewvc/jena/Experimental/jena-fuseki2/src/main/webapp/js/app/views/dataset-management.js?rev=1603977&r1=1603976&r2=1603977&view=diff
==============================================================================
--- 
jena/Experimental/jena-fuseki2/src/main/webapp/js/app/views/dataset-management.js
 (original)
+++ 
jena/Experimental/jena-fuseki2/src/main/webapp/js/app/views/dataset-management.js
 Thu Jun 19 17:52:25 2014
@@ -8,23 +8,31 @@ define(
     var DataManagementView = Backbone.Marionette.ItemView.extend( {
 
       initialize: function(){
-        _.bindAll( this, "onRemoveDataset", "onConfirmRemoveDataset",
-                         "onDatasetRemoveSuccess", "onDatasetRemoveFail" );
+        _.bindAll( this, "onRemoveDataset", "onConfirmAction",
+                         "onDatasetRemoveSuccess", "onDatasetRemoveFail",
+                         "onTaskStatus", "onTaskFailed" );
+
         this.listenTo( this.model, "change", this.onModelChange, this );
+
+        fui.vent.on( "action.delete.confirm", this.onConfirmRemoveDataset );
+        fui.vent.on( "action.backup.confirm", this.onConfirmBackupDataset );
+
+        fui.vent.on( "task.status", this.onTaskStatus );
+        fui.vent.on( "task.failed", this.onTaskFailed );
       },
 
       template: _.template( datasetManagementViewTpl ),
 
       ui: {
+        actionConfirmModal: "#actionConfirmModal"
       },
 
       el: "#dataset-management",
 
       events: {
         "click .action.remove": "onRemoveDataset",
-        "click .action.confirm": "onConfirmRemoveDataset"
-//        "change #independent-variable-selection": "selectVariable",
-//        "click a.action.filter": "onFilter"
+        "click .action.confirm": "onConfirmAction",
+        "click .action.backup": "onBackupDataset"
       },
 
       templateHelpers: {
@@ -33,7 +41,7 @@ define(
 
       /** If the model changes, update the summary */
       onModelChange: function( event ) {
-//        this.ui.summary.html( 
this.model.independentVar().component.range().summarise() );
+        this.render();
       },
 
       /** User has requested a dataset be removed */
@@ -41,22 +49,49 @@ define(
         e.preventDefault();
         var elem = $(e.currentTarget);
         var dsId = elem.data( "ds-id" );
-        var msg = sprintf( "Are you sure you want to delete dataset %s? This 
action cannot be reversed.", dsId );
-        var deleteConfirmModal = $( "#deleteConfirmModal" );
+        var msg = sprintf( "Are you sure you want to delete dataset 
<code>%s</code>? This action cannot be reversed.", dsId );
 
-        deleteConfirmModal.find( ".modal-body p" ).html( msg );
-        deleteConfirmModal.find( ".action.confirm" ).data( "ds-id", dsId );
+        this.showConfirmationModal( msg, dsId, "action.delete.confirm" );
+      },
 
-        this.clearFeedback();
+      /** User has requested a dataset be backed up */
+      onBackupDataset: function( e ) {
+        e.preventDefault();
+        var elem = $(e.currentTarget);
+        var dsId = elem.data( "ds-id" );
+        var msg = sprintf( "Are you sure you want to create a backup of 
dataset <code>%s</code>? This action may take some time to complete", dsId );
 
-        deleteConfirmModal.modal( 'show' );
+        this.showConfirmationModal( msg, dsId, "action.backup.confirm" );
       },
 
-      /** User has confirmed that the dataset can be deleted */
-      onConfirmRemoveDataset: function( e ) {
+      /** Show a generic modal confirmation */
+      showConfirmationModal: function( msg, dsId, eventId ) {
+        this.ui.actionConfirmModal
+               .find( ".modal-body p" )
+               .html( msg );
+
+        this.ui.actionConfirmModal
+               .find( ".action.confirm" )
+               .data( "ds-id", dsId )
+               .data( "event-id", eventId );
+
+        this.clearFeedback();
+        this.ui.actionConfirmModal.modal( 'show' );
+      },
+
+      /** Generic response to confirming the current modal dialogue */
+      onConfirmAction: function( e ) {
         e.preventDefault();
         var elem = $(e.currentTarget);
         var dsId = elem.data( "ds-id" );
+        var eventId = elem.data( "event-id" );
+
+        fui.vent.trigger( eventId, dsId );
+        this.ui.actionConfirmModal.modal( 'hide' );
+      },
+
+      /** User has confirmed that the dataset can be deleted */
+      onConfirmRemoveDataset: function( dsId ) {
         var self = this;
 
         fui.models
@@ -65,8 +100,6 @@ define(
            .deleteDataset()
            .done( function( data ) {self.onDatasetRemoveSuccess( data, dsId 
);} )
            .error( function( jqxhr, msg, err ) {self.onDatasetRemoveFail( 
jqxhr, msg, err, dsId );} );
-
-        $( "#deleteConfirmModal" ).modal( 'hide' );
       },
 
       onDatasetRemoveSuccess: function( data, dsId ) {
@@ -75,15 +108,48 @@ define(
 
       /** Removing the dataset did not work: notify the user */
       onDatasetRemoveFail: function( jqxhr, msg, err, dsId ) {
-        $(sprintf( ".action.remove[data-ds-id='%s']", dsId ) )
-          .parent()
-          .siblings(".action.feedback")
-          .html( sprintf( "<p class='text-warning'>Sorry, removing dataset %s 
did not work, because: '%s'</p>", dsId, err || msg ) );
+        this.feedbackArea( dsId )
+            .html( sprintf( "<p class='text-warning'>Sorry, removing dataset 
%s did not work, because: '%s'</p>", dsId, err || msg ) );
+      },
+
+      /** User has confirmed backing up the dataset */
+      onConfirmBackupDataset: function( dsId ) {
+        var self = this;
+
+        fui.models
+           .fusekiServer
+           .dataset( dsId )
+           .backupDataset();
       },
 
       /** Remove any current feedback content */
       clearFeedback: function() {
         $(".action.feedback").empty();
+      },
+
+      /** Long running task has updated status */
+      onTaskStatus: function( status ) {
+        var task = status.task;
+        var msg = sprintf( "<p>Task <strong>%s</strong> started at %s%s</p>",
+                           task.operationType,
+                           task.taskDescription.started,
+                           status.finished ? sprintf( ", finished at %s", 
status.finished ) : " &ndash; ongoing" );
+
+        this.feedbackArea( status.dsId )
+            .html( msg );
+      },
+
+      /** Long running task has failed to start */
+      onTaskFailed: function( status ) {
+        this.feedbackArea( status.dsId )
+            .html( sprintf( "<p class='text-danger'>Task %s failed: '%s'</p>", 
task.operationType, task.errorMessage ));
+      },
+
+      /** Find the feedback area for a particular dataset */
+      feedbackArea: function( dsId ) {
+        return $(sprintf( ".action[data-ds-id='%s']", dsId ) )
+               .parent()
+               .siblings(".action.feedback");
       }
 
     });


Reply via email to