mirror of
https://github.com/alchemy-fr/Phraseanet.git
synced 2025-10-12 04:23:19 +00:00
Bind task manager backbone app to websocket using autobahn
This commit is contained in:
@@ -52,7 +52,7 @@ module.exports = function(grunt) {
|
||||
"autobahnjs": {
|
||||
"expand": true,
|
||||
"src": [
|
||||
"<%= path.bower %>/autobahnjs/build/autobahn.min.js",
|
||||
"<%= path.bower %>/autobahnjs/build/autobahn.js",
|
||||
"<%= path.bower %>/autobahnjs/LICENSE"
|
||||
],
|
||||
"dest": "<%= path.asset %>/autobahnjs/",
|
||||
|
@@ -14,6 +14,7 @@ namespace Alchemy\Phrasea\Controller\Admin;
|
||||
use Alchemy\Phrasea\Exception\InvalidArgumentException;
|
||||
use Alchemy\Phrasea\Form\TaskForm;
|
||||
use Alchemy\Phrasea\Model\Entities\Task;
|
||||
use Alchemy\Phrasea\TaskManager\TaskManagerStatus;
|
||||
use Silex\Application;
|
||||
use Silex\ControllerProviderInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
@@ -46,6 +47,10 @@ class TaskManager implements ControllerProviderInterface
|
||||
->get('/scheduler', 'controller.admin.task:getScheduler')
|
||||
->bind('admin_scheduler');
|
||||
|
||||
$controllers
|
||||
->get('/live', 'controller.admin.task:getLiveInformation')
|
||||
->bind('admin_tasks_live_info');
|
||||
|
||||
$controllers
|
||||
->post('/tasks/create', 'controller.admin.task:postCreateTask')
|
||||
->bind('admin_tasks_task_create');
|
||||
@@ -124,22 +129,38 @@ class TaskManager implements ControllerProviderInterface
|
||||
return $app->redirectPath('admin_tasks_list');
|
||||
}
|
||||
|
||||
public function getLiveInformation(Application $app, Request $request)
|
||||
{
|
||||
if ($request->getRequestFormat() !== "json") {
|
||||
$app->abort(406, 'Only JSON format is accepted.');
|
||||
}
|
||||
|
||||
foreach ($app['manipulator.task']->getRepository()->findAll() as $task) {
|
||||
$tasks[$task->getId()] = $app['task-manager.live-information']->getTask($task);
|
||||
}
|
||||
|
||||
return $app->json([
|
||||
'manager' => $app['task-manager.live-information']->getManager(),
|
||||
'tasks' => $tasks
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function getScheduler(Application $app, Request $request)
|
||||
{
|
||||
if ($request->getRequestFormat() !== "json") {
|
||||
$app->abort(406, 'Only JSON format is accepted.');
|
||||
}
|
||||
|
||||
$scheduler = array_replace($app['task-manager.live-information']->getManager(), [
|
||||
return $app->json([
|
||||
'name' => $app->trans('Task Scheduler'),
|
||||
'configuration' => $app['task-manager.status']->getStatus(),
|
||||
'urls' => [
|
||||
'start' => $app->path('admin_tasks_scheduler_start'),
|
||||
'stop' => $app->path('admin_tasks_scheduler_stop'),
|
||||
'log' => $app->path('admin_tasks_scheduler_log'),
|
||||
]
|
||||
]);
|
||||
|
||||
return $app->json($scheduler);
|
||||
}
|
||||
|
||||
public function getTasks(Application $app, Request $request)
|
||||
@@ -147,11 +168,11 @@ class TaskManager implements ControllerProviderInterface
|
||||
$tasks = [];
|
||||
|
||||
foreach ($app['repo.tasks']->findAll() as $task) {
|
||||
$tasks[] = array_replace(
|
||||
$app['task-manager.live-information']->getTask($task), [
|
||||
$tasks[] = [
|
||||
'id' => $task->getId(),
|
||||
'name' => $task->getName()
|
||||
]);
|
||||
'name' => $task->getName(),
|
||||
'configuration' => $task->getStatus()
|
||||
];
|
||||
}
|
||||
|
||||
if ($request->getRequestFormat() === "json") {
|
||||
@@ -165,10 +186,11 @@ class TaskManager implements ControllerProviderInterface
|
||||
return $app['twig']->render('admin/task-manager/index.html.twig', [
|
||||
'available_jobs' => $app['task-manager.available-jobs'],
|
||||
'tasks' => $tasks,
|
||||
'scheduler' => array_replace(
|
||||
$app['task-manager.live-information']->getManager(), [
|
||||
'name' => $app->trans('Task Scheduler')
|
||||
])
|
||||
'scheduler' => [
|
||||
'id' => '',
|
||||
'name' => $app->trans('Task Scheduler'),
|
||||
'configuration' => $app['task-manager.status']->getStatus(),
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
|
@@ -51,6 +51,7 @@ $groups = [
|
||||
, '//assets/blueimp-load-image/load-image.js'
|
||||
, '//assets/jquery-file-upload/jquery.iframe-transport.js'
|
||||
, '//assets/jquery-file-upload/jquery.fileupload.js'
|
||||
, '//assets/autobahnjs/autobahn.js'
|
||||
],
|
||||
'report' => [
|
||||
'//assets/jquery.ui/i18n/jquery-ui-i18n.js'
|
||||
|
@@ -1,19 +1,25 @@
|
||||
<div class="page-header">
|
||||
<div id="task-manager-app">
|
||||
<div class="page-header">
|
||||
<h1>{{ 'Task Scheduler' | trans }}
|
||||
<small style="font-size:16px;">
|
||||
{% set updateTime %}
|
||||
<span id="pingTime">{{ "now"|date(constant("DateTime::ISO8601")) }}</span>
|
||||
<span id="pingTime" class="ping-view">{{ "now"|date(constant("DateTime::ISO8601")) }}</span>
|
||||
{% endset %}
|
||||
{% trans with {'%updateTime%' : updateTime} %}Last update on %updateTime%{% endtrans %}
|
||||
</small>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="task-manager-app">
|
||||
<table class="admintable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="refresh-view" style="width:80px;" data-refresh-url="{{ path('admin_tasks_live_info') }}">
|
||||
<button class="btn btn-refresh">
|
||||
<i class="icon icon-refresh"/>
|
||||
</button>
|
||||
<i id="spinner" style="font-size:16px" class='icon-spinner icon-spin'>
|
||||
</th>
|
||||
<th>ID</th>
|
||||
<th>PID</th>
|
||||
<th>!</th>
|
||||
@@ -22,7 +28,7 @@
|
||||
<th>{{ "name" | trans | upper }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="scheduler-view">
|
||||
<tbody class="scheduler-view">
|
||||
<tr>
|
||||
<td class="menu">
|
||||
<div class="btn-group">
|
||||
@@ -49,14 +55,14 @@
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
<td>{{ scheduler["process-id"] }}</td>
|
||||
<td></td>
|
||||
<td>{{ scheduler["actual"] }}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>{{ scheduler["configuration"] }}</td>
|
||||
<td>{{ scheduler["name"] }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody id="tasks-list-view">
|
||||
<tbody class="tasks-list-view">
|
||||
{% for task in tasks %}
|
||||
<tr>
|
||||
<td class="menu">
|
||||
@@ -93,10 +99,10 @@
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
<td>{% if task["id"] != "taskmanager" %}{{ task["id"] }}{% endif %}</td>
|
||||
<td>{{ task["process-id"] }}</td>
|
||||
<td>{{ task["id"] }}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>{{ task["actual"] }}</td>
|
||||
<td>{{ task["configuration"] }}</td>
|
||||
<td>{{ task["name"] }}</td>
|
||||
</tr>
|
||||
@@ -115,6 +121,8 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{#<script src="/assets/autobahnjs/autobahn.js"></script>#}
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
$("form[name='form-create-task'] select").bind("change", function() {
|
||||
@@ -126,4 +134,5 @@
|
||||
{# include js templates #}
|
||||
{% include 'admin/task-manager/templates.html.twig' %}
|
||||
|
||||
<script type="text/javascript" src="{{ path('minifier', { 'f' : 'assets/requirejs/require.js,/scripts/apps/admin/tasks-manager/main.js' }) }}"></script>
|
||||
<script type="text/javascript" src="{{ path('minifier', { 'f' : 'assets/requirejs/require.js' }) }}"></script>
|
||||
<script type="text/javascript" src="{{ path('minifier', { 'f' : 'scripts/apps/admin/tasks-manager/main.js' }) }}"></script>
|
||||
|
@@ -12,58 +12,90 @@ define([
|
||||
"underscore",
|
||||
"backbone",
|
||||
"models/scheduler",
|
||||
"common/websockets/connection",
|
||||
"apps/admin/tasks-manager/views/scheduler",
|
||||
"apps/admin/tasks-manager/views/tasks",
|
||||
"apps/admin/tasks-manager/views/ping",
|
||||
"apps/admin/tasks-manager/views/refresh",
|
||||
"apps/admin/tasks-manager/collections/tasks"
|
||||
], function ($, _, Backbone, Scheduler, SchedulerView, TasksView, PingView, TasksCollection) {
|
||||
], function ($, _, Backbone, Scheduler, WSConnection, SchedulerView, TasksView, PingView, RefreshView, TasksCollection) {
|
||||
var create = function() {
|
||||
window.TaskManagerApp = {
|
||||
$scope: $("#task-manager-app"),
|
||||
$tasksListView : $("#tasks-list-view", this.$scope),
|
||||
$schedulerView : $("#scheduler-view", this.$scope),
|
||||
$pingView : $("#pingTime", this.$scope)
|
||||
$tasksListView : $(".tasks-list-view", this.$scope),
|
||||
$schedulerView : $(".scheduler-view", this.$scope),
|
||||
$pingView : $(".ping-view", this.$scope),
|
||||
$refreshView : $(".refresh-view", this.$scope),
|
||||
eventAggregator: _.extend({}, Backbone.Events),
|
||||
wsuri: "ws://dev.phrasea.net:9090/websockets",
|
||||
wstopic: "http://phraseanet.com/topics/admin/task-manager"
|
||||
};
|
||||
|
||||
TaskManagerApp.tasksCollection = new TasksCollection();
|
||||
TaskManagerApp.Scheduler = new Scheduler();
|
||||
|
||||
TaskManagerApp.pingView = new PingView({
|
||||
el: TaskManagerApp.$pingView
|
||||
TaskManagerApp.pingView = new PingView({el: TaskManagerApp.$pingView});
|
||||
TaskManagerApp.refreshView = new RefreshView({
|
||||
el: TaskManagerApp.$refreshView,
|
||||
pingView: TaskManagerApp.pingView,
|
||||
tasksCollection: TaskManagerApp.tasksCollection,
|
||||
scheduler: TaskManagerApp.Scheduler
|
||||
});
|
||||
}
|
||||
|
||||
var load = function() {
|
||||
TaskManagerApp.refreshView.refreshAction();
|
||||
// fetch objects
|
||||
$.when.apply($, [
|
||||
TaskManagerApp.tasksCollection.fetch(),
|
||||
TaskManagerApp.Scheduler.fetch()
|
||||
]).done(
|
||||
function () {
|
||||
TaskManagerApp.schedulerView = new SchedulerView({
|
||||
model: TaskManagerApp.Scheduler,
|
||||
el: TaskManagerApp.$schedulerView
|
||||
});
|
||||
TaskManagerApp.tasksView = new TasksView({
|
||||
collection: TaskManagerApp.tasksCollection,
|
||||
el: TaskManagerApp.$tasksListView
|
||||
});
|
||||
// Init & render views
|
||||
TaskManagerApp.schedulerView = new SchedulerView({model: TaskManagerApp.Scheduler, el: TaskManagerApp.$schedulerView});
|
||||
TaskManagerApp.tasksView = new TasksView({collection: TaskManagerApp.tasksCollection, el: TaskManagerApp.$tasksListView});
|
||||
|
||||
// render views
|
||||
TaskManagerApp.tasksView.render();
|
||||
TaskManagerApp.schedulerView.render();
|
||||
|
||||
// Sets connection to the web socket
|
||||
var ws = new WSConnection({url:TaskManagerApp.wsuri, topic: TaskManagerApp.wstopic, eventAggregator: TaskManagerApp.eventAggregator});
|
||||
ws.run();
|
||||
|
||||
// On ticks re-render ping view, update tasks & scheduler model
|
||||
TaskManagerApp.eventAggregator.on("ws:manager-tick", function(response) {
|
||||
var $this = this;
|
||||
$this.pingView.render();
|
||||
$this.Scheduler.set({"actual": "started", "process-id": response.message.manager["process-id"]});
|
||||
_.each(response.message.jobs, function(data, id) {
|
||||
var jobModel = $this.tasksCollection.get(id);
|
||||
if ("undefined" !== jobModel) {
|
||||
jobModel.set({"actual": data["status"], "process-id": data["process-id"]});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
var initialize = function () {
|
||||
create();
|
||||
var regexp = /task-manager/;
|
||||
$(document).ajaxComplete(function(event, request, settings) {
|
||||
if ("undefined" !== typeof settings && regexp.test(settings.url)) {
|
||||
TaskManagerApp.refreshView.loadState(false);
|
||||
}
|
||||
});
|
||||
|
||||
$(document).ajaxStart(function(event, request, settings) {
|
||||
if ("undefined" !== typeof settings && regexp.test(settings.url)) {
|
||||
TaskManagerApp.refreshView.loadState(true);
|
||||
}
|
||||
});
|
||||
|
||||
load();
|
||||
};
|
||||
|
||||
return {
|
||||
create: create,
|
||||
load: load,
|
||||
initialize: initialize
|
||||
};
|
||||
});
|
||||
|
72
www/scripts/apps/admin/tasks-manager/views/refresh.js
Normal file
72
www/scripts/apps/admin/tasks-manager/views/refresh.js
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2014 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
define([
|
||||
"jquery",
|
||||
"underscore",
|
||||
"backbone",
|
||||
""
|
||||
], function ($, _, Backbone) {
|
||||
var RefreshView = Backbone.View.extend({
|
||||
initialize: function(options) {
|
||||
if (!"pingView" in options) {
|
||||
throw "You must set the ping view"
|
||||
}
|
||||
this.pingView = options.pingView;
|
||||
if (!"scheduler" in options) {
|
||||
throw "You must set the scheduler model"
|
||||
}
|
||||
this.scheduler = options.scheduler;
|
||||
if (!"tasksCollection" in options) {
|
||||
throw "You must set the tasks collection model"
|
||||
}
|
||||
this.tasksCollection = options.tasksCollection;
|
||||
|
||||
this.refreshUrl = this.$el.data('refresh-url');
|
||||
},
|
||||
events: {
|
||||
"click .btn-refresh": "refreshAction"
|
||||
},
|
||||
refreshAction: function(event) {
|
||||
var $this = this;
|
||||
$.ajax({
|
||||
dataType: "json",
|
||||
url: $this.refreshUrl,
|
||||
data: {},
|
||||
success: function(response) {
|
||||
$this.pingView.render();
|
||||
$this.scheduler.set({
|
||||
"actual": response.manager["actual"],
|
||||
"process-id": response.manager["process-id"],
|
||||
"configuration": response.manager["configuration"]
|
||||
});
|
||||
_.each(response.tasks, function(data, id) {
|
||||
var jobModel = $this.tasksCollection.get(id);
|
||||
if ("undefined" !== jobModel) {
|
||||
jobModel.set({
|
||||
"actual": data["actual"],
|
||||
"process-id": data["process-id"],
|
||||
"configuration": data["configuration"]
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
loadState: function(state) {
|
||||
if (state) {
|
||||
$("#spinner", this.$el).addClass('icon-spinner icon-spin');
|
||||
} else {
|
||||
$("#spinner", this.$el).removeClass('icon-spinner icon-spin');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return RefreshView;
|
||||
});
|
36
www/scripts/common/websockets/connection.js
Normal file
36
www/scripts/common/websockets/connection.js
Normal file
@@ -0,0 +1,36 @@
|
||||
define([
|
||||
"underscore"
|
||||
], function (_) {
|
||||
return function (options) {
|
||||
if (!"url" in options) {
|
||||
throw "You must set the websocket 'url'"
|
||||
}
|
||||
if (!"topic" in options) {
|
||||
throw "You must set the websocket 'topic'"
|
||||
}
|
||||
if (!"eventAggregator" in options) {
|
||||
throw "You must set an event aggregator"
|
||||
}
|
||||
|
||||
var eventAggregator = options.eventAggregator;
|
||||
|
||||
return {
|
||||
run: function() {
|
||||
// autobahn js is defined as a global object there is no way to load
|
||||
// it as a UMD module
|
||||
ab.connect(options.url, function (session) {
|
||||
eventAggregator.trigger("ws:connect", session);
|
||||
session.subscribe(options.topic, function (topic, msg) {
|
||||
// double encoded string
|
||||
var msg = JSON.parse(JSON.parse(msg));
|
||||
eventAggregator.trigger("ws:"+msg.event, msg, session);
|
||||
}
|
||||
);
|
||||
},
|
||||
function (code, reason) {
|
||||
eventAggregator.trigger("ws:session-gone", code,reason);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
Reference in New Issue
Block a user