Commit 83a2aee3 authored by hh1966's avatar hh1966
Browse files

Add activejob-status and refactor front-end code and api accordingly

parent c3e85d0c
Pipeline #44030 failed with stage
......@@ -149,6 +149,8 @@ gem 'gman'
gem 'ruby-mailchecker'
gem 'swot'
gem 'activejob-status'
group :development, :test do
gem 'binding_of_caller'
......
......@@ -103,6 +103,9 @@ GEM
activejob (4.2.11)
activesupport (= 4.2.11)
globalid (>= 0.3.0)
activejob-status (0.1.5)
activejob (>= 4.2)
activesupport (>= 4.2)
activemodel (4.2.11)
activesupport (= 4.2.11)
builder (~> 3.1)
......@@ -619,6 +622,7 @@ PLATFORMS
DEPENDENCIES
aasm
activejob-status
ancestry
awesome_print
axlsx!
......
......@@ -367,18 +367,8 @@ module Chemotion
end
end
# create an id for the export
export_id = SecureRandom.uuid
# create the lock file
lock_file_path = Export::ExportCollections.lock_file_path(export_id, format)
File.open(lock_file_path, 'w') {}
# run the asyncronous export job and return its id to the client
ExportCollectionsJob.perform_later(export_id, collection_ids, format, nested)
# return the export_id to the client
return {:export_id => export_id}
ExportCollectionsJob.perform_later(collection_ids, format, nested).job_id
end
desc "Poll export job"
......@@ -386,52 +376,33 @@ module Chemotion
requires :id, type: String
end
get '/:id' do
export_id = params[:id]
# look for the lock file file
['json', 'zip'].each do |format|
file_path = Export::ExportCollections.file_path(export_id, format)
lock_file_path = Export::ExportCollections.lock_file_path(export_id, format)
if File.exist?(file_path) and !File.exist?(lock_file_path)
return {
:status => 'COMPLETED',
:url => Export::ExportCollections.file_url(export_id, format)
}
elsif File.exist?(lock_file_path)
return {:status => 'EXECUTING'}
end
end
error! :not_found, 404
ActiveJob::Status.get(params[:id])
end
end
namespace :imports do
desc "Create import job"
desc "Create import job"
params do
requires :file, type: File
end
post do
# create an id for the import
# create an id for the import,
# this is not the job_id, but will be used as file_name
import_id = SecureRandom.uuid
# create the `tmp/imports/` if it does not exist yet
import_path = Import::ImportCollections.import_path
import_path = File.join('tmp', 'import')
FileUtils.mkdir_p(import_path) unless Dir.exist?(import_path)
# store the file as `tmp/imports/<import_id>.zip`
file_path = Import::ImportCollections.zip_file_path(import_id)
File.open(file_path, 'wb') do |file|
file.write(params[:file][:tempfile].read)
zip_file_path = File.join('tmp', 'import', '#{import_id}.zip')
File.open(zip_file_path, 'wb') do |file|
file.write(params[:file][:tempfile].read)
end
# run the asyncronous import job
ImportCollectionsJob.perform_later(import_id, current_user.id)
# return the import_id to the client
return {:import_id => import_id}
# run the asyncronous import job and return its id to the client
ImportCollectionsJob.perform_later(import_id, current_user.id).job_id
end
desc "Poll import job"
......@@ -439,18 +410,7 @@ module Chemotion
requires :id, type: String
end
get '/:id' do
import_id = params[:id]
# look for the lock file file
file_path = Import::ImportCollections.zip_file_path(import_id)
if File.exist?(file_path)
return {
:status => 'EXECUTING',
}
end
error! :not_found, 404
ActiveJob::Status.get(params[:id])
end
end
......
......@@ -283,6 +283,34 @@ export default class CollectionsFetcher {
return promise;
}
static showExportError() {
// TODO move to the right place
NotificationActions.removeByUid('export_collections')
NotificationActions.add({
title: "Error",
message: "An error occured with your export, please contact the administrators of the site if the problem persists.",
level: "error",
dismissible: true,
uid: "export_collections_error",
position: "bl",
autoDismiss: null
});
}
static showImportError() {
// TODO move to the right place
NotificationActions.removeByUid('import_collections')
NotificationActions.add({
title: "Error",
message: "An error occured with your import, please contact the administrators of the site if the problem persists.",
level: "error",
dismissible: true,
uid: "import_collections_error",
position: "bl",
autoDismiss: null
});
}
static createExportJob(params) {
let promise = fetch('/api/v1/collections/exports/', {
......@@ -299,23 +327,11 @@ export default class CollectionsFetcher {
} else {
throw new Error(response.status);
}
}).then((json) => {
// after a short delay, start polling
setTimeout(() => {
CollectionsFetcher.pollExportJob(json.export_id)
}, 1000);
}).then((job_id) => {
// start polling
CollectionsFetcher.pollExportJob(job_id);
}).catch((errorMessage) => {
// remove the export notification and add an error notififation
NotificationActions.removeByUid('export_collections')
NotificationActions.add({
title: "Error",
message: "An error occured with your export, please contact the administrators of the site if the problem persists.",
level: "error",
dismissible: true,
uid: "export_collections_error",
position: "bl",
autoDismiss: null
});
CollectionsFetcher.showExportError();
});
return promise;
......@@ -337,30 +353,26 @@ export default class CollectionsFetcher {
throw new Error(response.status);
}
}).then((json) => {
if (json.status == 'EXECUTING') {
// continue polling
setTimeout(() => {
CollectionsFetcher.pollExportJob(exportId);
}, 1000);
} else if (json.status == 'COMPLETED') {
// remove the notification
NotificationActions.removeByUid('export_collections')
// download the file, headers will prevent the browser from reloading the page
window.location.href = json.url;
if (json.error) {
CollectionsFetcher.showExportError();
} else {
if (json.status == 'completed') {
// remove the notification
NotificationActions.removeByUid('export_collections')
// download the file, headers will prevent the browser from reloading the page
window.location.href = `/zip/${exportId}.zip`;
} else if (json.status == 'queued' || json.status == 'working') {
// continue polling
setTimeout(() => {
CollectionsFetcher.pollExportJob(exportId);
}, 1000);
} else {
CollectionsFetcher.showExportError();
}
}
}).catch((errorMessage) => {
// create an error notififation
NotificationActions.removeByUid('export_collections')
NotificationActions.add({
title: "Error",
message: "An error occured with your export, please contact the administrators of the site if the problem persists.",
level: "error",
dismissible: true,
uid: "export_collections_error",
position: "bl",
autoDismiss: null
});
CollectionsFetcher.showExportError();
});
return promise;
......@@ -381,25 +393,11 @@ export default class CollectionsFetcher {
} else {
throw new Error(response.status);
}
}).then((json) => {
// after a short delay, start polling
setTimeout(() => {
CollectionsFetcher.pollImportJob(json.import_id)
}, 1000);
return json;
}).then((job_id) => {
// start polling
CollectionsFetcher.pollImportJob(job_id);
}).catch((errorMessage) => {
// remove the export notification and add an error notififation
NotificationActions.removeByUid('import_collections')
NotificationActions.add({
title: "Error",
message: "An error occured with your export, please contact the administrators of the site if the problem persists.",
level: "error",
dismissible: true,
uid: "import_collections_error",
position: "bl",
autoDismiss: null
});
CollectionsFetcher.showImportError();
});
return promise;
......@@ -421,30 +419,26 @@ export default class CollectionsFetcher {
throw new Error(response.status);
}
}).then((json) => {
if (json.status == 'EXECUTING') {
// continue polling
setTimeout(() => {
CollectionsFetcher.pollImportJob(importId);
}, 1000);
if (json.error) {
CollectionsFetcher.showImportError();
} else {
// remove the notification
NotificationActions.removeByUid('import_collections')
// reload the unshared collections
CollectionActions.fetchUnsharedCollectionRoots()
if (json.status == 'completed') {
// remove the notification
NotificationActions.removeByUid('import_collections')
// reload the unshared collections
CollectionActions.fetchUnsharedCollectionRoots()
} else if (json.status == 'queued' || json.status == 'working') {
// continue polling
setTimeout(() => {
CollectionsFetcher.pollImportJob(importId);
}, 1000);
} else {
CollectionsFetcher.showImportError();
}
}
}).catch((errorMessage) => {
// remove the export notification and add an error notififation
NotificationActions.removeByUid('import_collections')
NotificationActions.add({
title: "Error",
message: "An error occured with your export, please contact the administrators of the site if the problem persists.",
level: "error",
dismissible: true,
uid: "import_collections_error",
position: "bl",
autoDismiss: null
});
CollectionsFetcher.showImportError();
});
return promise;
......
class ExportCollectionsJob < ActiveJob::Base
include ActiveJob::Status
queue_as :export_collections
def perform(export_id, collection_ids, format, nested)
export = Export::ExportCollections.new(export_id, collection_ids, format, nested)
def perform(collection_ids, format, nested)
export = Export::ExportCollections.new(self.job_id, collection_ids, format, nested)
export.prepare_data
export.to_file
export.cleanup
end
end
class ImportCollectionsJob < ActiveJob::Base
include ActiveJob::Status
queue_as :import_collections
def perform(import_id, current_user_id)
......
ActiveJob::Status.store = ActiveSupport::Cache::FileStore.new "tmp/cache/active_job_status"
\ No newline at end of file
module Export
class ExportCollections
# static methods
class << self
def file_path(export_id, format)
File.join('public', format, "#{export_id}.#{format}")
end
def file_url(export_id, format)
"/#{format}/#{export_id}.#{format}"
end
def lock_file_path(export_id, format)
File.join('public', format, "#{export_id}.lock")
end
def schema_file_path
File.join('public', 'json', 'schema.json')
end
end
def initialize(export_id, collection_ids, format, nested)
@export_id = export_id
@collection_ids = collection_ids
@format = format
@nested = nested
@file_path = ExportCollections.file_path(export_id, format)
@lock_file_path = ExportCollections.lock_file_path(export_id, format)
@file_path = File.join('public', format, "#{export_id}.#{format}")
@schema_file_path = File.join('public', 'json', 'schema.json')
@data = {}
@uuids = {}
......@@ -63,7 +47,7 @@ module Export
# write the json schema
zip.put_next_entry File.join('schema.json')
zip.write File.read(ExportCollections.schema_file_path)
zip.write File.read(@schema_file_path)
end
zip.rewind
......@@ -100,10 +84,6 @@ module Export
end
end
def cleanup
File.delete(@lock_file_path) if File.exist?(@lock_file_path)
end
private
def fetch_samples(collection)
......
......@@ -3,26 +3,12 @@ require 'json'
module Import
class ImportCollections
# static methods
class << self
def import_path
File.join('tmp', 'import')
end
def zip_file_path(import_id)
File.join('tmp', 'import', "#{import_id}.zip")
end
def directory_path(import_id)
File.join('tmp', 'import', import_id)
end
end
def initialize(import_id, current_user_id)
@import_id = import_id
@current_user_id = current_user_id
@zip_file_path = ImportCollections.zip_file_path(import_id)
@directory = ImportCollections.directory_path(import_id)
@zip_file_path = File.join('tmp', 'import', '#{import_id}.zip')
@directory = File.join('tmp', 'import', import_id)
@data = nil
@instances = {}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment