Commit c1f7908c authored by PiTrem's avatar PiTrem
Browse files

revamp messaging

isolate responsibity
reword some messages

pass message args  level, autoDismiss, position to FE

message for data_collection as DJ
parent 15919022
......@@ -219,12 +219,11 @@ module Chemotion
collection_attributes: params[:collection_attributes].merge(shared_by_id: current_user.id)
).execute!
channel = Channel.find_by(subject: Channel::SHARED_COLLECTION_WITH_ME)
return if channel.nil?
content = channel.msg_template
return if (content.nil?)
content['data'] = content['data'] % {:shared_by => current_user.name }
message = Message.create_msg_notification(channel.id,content,current_user.id,uids)
Message.create_msg_notification(
channel_subject: Channel::SHARED_COLLECTION_WITH_ME,
message_from: current_user.id, message_to: uids,
data_args: { 'shared_by': current_user.name }, level: 'info'
)
end
end
......@@ -400,14 +399,6 @@ module Chemotion
status 204
end
end
# desc "Poll import job"
# params do
# requires :id, type: String
# end
# get '/:id' do
# ActiveJob::Status.get(params[:id])
# end
end
end
......
......@@ -107,12 +107,12 @@ module Chemotion
optional :user_ids, type: Array, desc: 'notification user ids'
end
post do
content = {}
content['data'] = params[:content]
message = Message.create_msg_notification(params[:channel_id],
content,
current_user.id,
params[:user_ids])
message = Message.create_msg_notification(
channel_id: params[:channel_id],
message_content: { 'data': params[:content] },
message_from: current_user.id,
message_to: params[:user_ids]
)
{ message: message }
end
end
......
......@@ -85,16 +85,11 @@ module Chemotion
end
cp.save!
channel = Channel.find_by(subject: Channel::COMPUTED_PROPS_NOTIFICATION)
return if channel.nil?
content = channel.msg_template
return if content.nil?
content['data'] = "Calculation for Sample #{sample.id} has started"
content['cprop'] = cp
Message.create_msg_notification(
channel.id, content, uid, [uid]
message_subject: Channel::COMPUTED_PROPS_NOTIFICATION,
message_from: uid,
data_args: { sample_id: sample.id, status: 'started' },
cprop: cp, level: 'info'
)
end
end
......
......@@ -2,15 +2,22 @@ module Chemotion
class PublicAPI < Grape::API
helpers do
def send_notification(attachment, user, status, has_error = false)
channel = Channel.find_by(subject: Channel::EDITOR_CALLBACK)
content = channel.msg_template
content['research_plan_id'] = content['research_plan_id'] % {:research_plan_id => attachment.attachable_id }
content['attach_id'] = content['attach_id'] % {:attach_id => attachment.id }
content['data'] = content['data'] % {:filename => attachment.filename }
content['data'] = attachment.filename + ' error has occurred, the file is not changed.' if has_error
content['data'] = attachment.filename + ' file has not changed.' if status == 4
content['data'] = attachment.filename + ' error has occurred while force saving the document, please review your changes.' if @status == 7
message = Message.create_msg_notification(channel.id,content,user.id,[user.id])
data_args = { 'filename': attachment.filename, 'comment': 'the file has been updated' }
level = 'success'
if has_error
data_args['comment'] = ' an error has occurred, the file is not changed.'
level = 'error'
elsif status == 4
data_args['comment'] = ' file has not changed.'
level = 'info'
elsif @status == 7
data_args['comment'] = ' an error has occurred while force saving the document, please review your changes.'
level = 'error'
end
message = Message.create_msg_notification(
message_subject: Channel::EDITOR_CALLBACK, message_from: user.id,
data_args: data_args, attach_id: attachment.id, research_plan_id: attachment.attachable_id, level: level
)
end
end
......@@ -124,16 +131,10 @@ module Chemotion
ComputedProp.from_raw(cp.id, params[:data])
channel = Channel.find_by(subject: Channel::COMPUTED_PROPS_NOTIFICATION)
return if channel.nil?
content = channel.msg_template
return if content.nil?
content['data'] = "Calculation for Sample #{cp.sample_id} has finished"
content['cprop'] = ComputedProp.find(cp.id)
Message.create_msg_notification(
channel.id, content, cp.creator, [cp.creator]
channel_subject: Channel::COMPUTED_PROPS_NOTIFICATION, message_from: cp.creator,
data_args: { sample_id: sample.id, status: 'finished'}, cprop: ComputedProp.find(cp.id),
level: 'success'
)
end
end
......
......@@ -135,13 +135,13 @@ module Chemotion
params[:user_ids] = uids
Usecases::Sharing::SyncWithUsers.new(params, current_user).execute!
channel = Channel.find_by(subject: Channel::SYNCHRONIZED_COLLECTION_WITH_ME)
return if channel.nil?
content = channel.msg_template
return if (content.nil?)
c = Collection.find_by(id: params[:id])
content['data'] = content['data'] % {:synchronized_by => current_user.name, :collection_name => c.label }
message = Message.create_msg_notification(channel.id,content,current_user.id,uids)
Message.create_msg_notification(
channel_subject: Channel::SYNCHRONIZED_COLLECTION_WITH_ME,
message_from: current_user.id, message_to: uids,
data_args: { synchronized_by: current_user.name, collection_name: c.label }, level: 'info'
)
end
desc "delete sync by id"
......
......@@ -43,7 +43,7 @@ class NotificationActions {
params.message = `An issue occured with your ${type} (status ${status}); please contact the administrators of the site if the problem persists.`;
params.level = 'error';
}
this.add(params);
return this.add(params);
}
}
......
......@@ -16,63 +16,57 @@ const handleNotification = (nots, act, needCallback = true) => {
NotificationActions.removeByUid(n.id);
}
if (act === 'add') {
const newText = n.content.data.split('\n').map((i) => { return <p key={`${new Date().getTime() + i}`}>{i}</p> });
const newText = n.content.data.split('\n').map(i => <p key={`${new Date().getTime() + i}`}>{i}</p>);
const notification = {
title: `From ${n.sender_name} on ${n.updated_at}`,
message: newText,
level: 'warning',
level: n.content.level || 'warning',
dismissible: 'button',
autoDismiss: 0,
position: 'tr',
autoDismiss: n.content.autoDismiss || 0,
position: n.content.position || 'tr',
uid: n.id,
action: {
label: 'Got it',
callback() {
if (needCallback) {
const params = {
ids: [],
};
const params = { ids: [] };
params.ids[0] = n.id;
MessagesFetcher.acknowledgedMessage(params)
.then((result) => {
//console.log(JSON.stringify(result));
});
MessagesFetcher.acknowledgedMessage(params);
// .then((result) => { console.log(JSON.stringify(result)); });
}
}
}
};
NotificationActions.add(notification);
if (n.content.action) {
if (n.content.action === 'CollectionActions.fetchRemoteCollectionRoots') {
switch (n.content.action) {
case 'CollectionActions.fetchRemoteCollectionRoots':
CollectionActions.fetchRemoteCollectionRoots();
}
if (n.content.action === 'CollectionActions.fetchSyncInCollectionRoots') {
break;
case 'CollectionActions.fetchSyncInCollectionRoots':
CollectionActions.fetchSyncInCollectionRoots();
}
if (n.content.action === 'InboxActions.fetchInbox') {
break;
case 'InboxActions.fetchInbox':
InboxActions.fetchInbox();
}
if (n.content.action === 'ReportActions.updateProcessQueue') {
const ids = [];
ids.push(parseInt(n.content.report_id, 10));
ReportActions.updateProcessQueue(ids);
}
if (n.content.action === 'ElementActions.refreshComputedProp') {
const { cprop } = n.content;
ElementActions.refreshComputedProp(cprop);
}
if (n.content.action === 'RefreshChemotionCollection') {
break;
case 'ReportActions.updateProcessQueue':
ReportActions.updateProcessQueue([parseInt(n.content.report_id, 10)]);
break;
case 'ElementActions.refreshComputedProp':
if (n.contentc.prop) { ElementActions.refreshComputedProp(n.content.cprop); }
break;
case 'RefreshChemotionCollection':
CollectionActions.fetchUnsharedCollectionRoots();
}
if (n.content.action === 'CollectionActions.fetchUnsharedCollectionRoots') {
break;
case 'CollectionActions.fetchUnsharedCollectionRoots':
CollectionActions.fetchUnsharedCollectionRoots();
CollectionActions.fetchSyncInCollectionRoots();
}
if (n.content.action === 'ElementActions.fetchResearchPlanById') {
const id = parseInt(n.content.research_plan_id, 10);
ElementActions.fetchResearchPlanById(id);
}
break;
case 'ElementActions.fetchResearchPlanById':
ElementActions.fetchResearchPlanById(parseInt(n.content.research_plan_id, 10));
break;
default:
//
}
}
});
......
......@@ -274,7 +274,7 @@ export default class CollectionsFetcher {
body: JSON.stringify(params)
}).then((response) => {
NotificationActions.notifyExImportStatus('export', response.status);
if (response.ok) { return response.json(); }
if (response.ok) { return true; }
throw new Error(response.status);
}).catch((errorMessage) => { throw new Error(errorMessage); });
}
......@@ -289,8 +289,8 @@ export default class CollectionsFetcher {
body: data
}).then((response) => {
NotificationActions.notifyExImportStatus('import', response.status);
if (response.ok) { return response.json(); }
if (response.ok) { return true; }
throw new Error(response.status);
}).catch((errorMessage) => { throw new Error(errorMessage); });
}).catch((errorMessage) => { console.log(errorMessage); });
}
}
......@@ -2,52 +2,47 @@ import 'whatwg-fetch';
export default class MessagesFetcher {
static configuration() {
const promise = fetch('/api/v1/messages/config.json', {
return fetch('/api/v1/messages/config.json', {
credentials: 'same-origin'
})
.then(response => response.json()).then(json => json).catch((errorMessage) => {
console.log(errorMessage);
});
return promise;
}).then(response => response.json()).then(json => json).catch((errorMessage) => {
console.log(errorMessage);
});
}
static fetchMessages(isAck) {
const promise = fetch(`/api/v1/messages/list.json?is_ack=${isAck}`, {
return fetch(`/api/v1/messages/list.json?is_ack=${isAck}`, {
credentials: 'same-origin'
})
.then(response => response.json()).then(json => json).catch((errorMessage) => {
console.log(errorMessage);
});
return promise;
}).then(response => response.json()).then(json => json).catch((errorMessage) => {
console.log(errorMessage);
});
}
static fetchChannels(channelType) {
const promise = fetch(`/api/v1/messages/channels.json?channel_type=${channelType}`, {
return fetch(`/api/v1/messages/channels.json?channel_type=${channelType}`, {
credentials: 'same-origin'
})
.then(response => response.json()).then(json => json).catch((errorMessage) => {
console.log(errorMessage);
});
return promise;
}).then(response => response.json()).then(json => json).catch((errorMessage) => {
console.log(errorMessage);
});
}
static fetchChannelWithUser() {
const promise = fetch('/api/v1/messages/channels_user.json', {
return fetch('/api/v1/messages/channels_user.json', {
credentials: 'same-origin'
})
.then(response => response.json()).then(json => json).catch((errorMessage) => {
console.log(errorMessage);
});
return promise;
}).then(response => response.json()).then(json => json).catch((errorMessage) => {
console.log(errorMessage);
});
}
static channelIndividualUsers() {
const promise = fetch('/api/v1/messages/channel_individual.json', {
return fetch('/api/v1/messages/channel_individual.json', {
credentials: 'same-origin'
})
.then(response => response.json()).then(json => json).catch((errorMessage) => {
console.log(errorMessage);
});
return promise;
}).then(response => response.json()).then(json => json).catch((errorMessage) => {
console.log(errorMessage);
});
}
static acknowledgedMessage(params) {
const promise = fetch('/api/v1/messages/ack/', {
return fetch('/api/v1/messages/ack/', {
credentials: 'same-origin',
method: 'PUT',
headers: {
......@@ -55,14 +50,13 @@ export default class MessagesFetcher {
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
})
.then(response => response.json()).then(json => json).catch((errorMessage) => {
console.log(errorMessage);
});
return promise;
}).then(response => response.json()).then(json => json).catch((errorMessage) => {
console.log(errorMessage);
});
}
static subscribeChannel(params) {
const promise = fetch('/api/v1/messages/subscribe/', {
return fetch('/api/v1/messages/subscribe/', {
credentials: 'same-origin',
method: 'POST',
headers: {
......@@ -70,15 +64,13 @@ export default class MessagesFetcher {
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
})
.then(response => response.json()).then(json => json).catch((errorMessage) => {
console.log(errorMessage);
});
return promise;
}).then(response => response.json()).then(json => json).catch((errorMessage) => {
console.log(errorMessage);
});
}
static createMessage(params) {
const promise = fetch('/api/v1/messages/new/', {
return fetch('/api/v1/messages/new/', {
credentials: 'same-origin',
method: 'POST',
headers: {
......@@ -86,10 +78,8 @@ export default class MessagesFetcher {
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
})
.then(response => response.json()).then(json => json).catch((errorMessage) => {
console.log(errorMessage);
});
return promise;
}).then(response => response.json()).then(json => json).catch((errorMessage) => {
console.log(errorMessage);
});
}
}
......@@ -4,7 +4,7 @@ class ExportCollectionsJob < ActiveJob::Base
queue_as :export_collections
after_perform do |job|
if @success
begin
# Sweep file in 24h
CleanExportFilesJob.set(queue: "remove_files_#{job.job_id}", wait: 24.hours)
.perform_later(job.job_id, @extname)
......@@ -18,18 +18,14 @@ class ExportCollectionsJob < ActiveJob::Base
).deliver_now
# Notify ELNer
channel = Channel.find_by(subject: Channel::COLLECTION_ZIP)
content = channel.msg_template unless channel.nil?
if content.present?
content['data'] = format(
content['data'],
{ col_labels: "[#{@labels.join('], [')}]"[0..40], operation: 'export' }
)
content['url'] = @link
content['url_title'] = 'Download'
Message.create_msg_notification(channel.id, content, @user_id, [@user_id])
end
end
Message.create_msg_notification(
channel_subject: Channel::COLLECTION_ZIP,
message_from: @user_id,
data_args: {expires_at: @expires_at, operation: 'Import', col_labels: @labels}
)
rescue StandardError => e
Delayed::Worker.logger.error e
end if @success
end
def perform(collection_ids, extname, nested, user_id)
......@@ -51,7 +47,11 @@ class ExportCollectionsJob < ActiveJob::Base
export.to_file
rescue StandardError => e
Delayed::Worker.logger.error e
# TODO: Notify Elner
Message.create_msg_notification(
channel_subject: Channel::COLLECTION_ZIP_FAIL,
message_from: @user_id,
data_args: { operation: 'Export', col_labels: @labels}
)
fp = Rails.public_path.join(@extname, "#{job_id}.#{@extname}" )
File.delete(fp) if File.exist?(fp)
@success = false
......
......@@ -4,14 +4,15 @@ class ImportCollectionsJob < ActiveJob::Base
queue_as :import_collections
after_perform do |job|
if @success
channel = Channel.find_by(subject: Channel::COLLECTION_ZIP)
content = channel.msg_template unless channel.nil?
if content.present?
content['data'] = format(content['data'], { col_labels: '', operation: 'import'})
content['data'] = content['data'] + ' File: ' + filename
Message.create_msg_notification(channel.id, content, @user_id, [@user_id])
end
begin
Message.create_msg_notification(
channel_subject: Channel::COLLECTION_ZIP,
message_from: @user_id,
data_args: { col_labels: '', operation: 'import', expires_at: nil },
autoDismiss: 5
) if @success
rescue StandardError => e
Delayed::Worker.logger.error e
end
end
......@@ -24,7 +25,12 @@ class ImportCollectionsJob < ActiveJob::Base
import.import!
rescue => e
Delayed::Worker.logger.error e
# TODO: Message Error
Message.create_msg_notification(
channel_subject: Channel::COLLECTION_ZIP_FAIL,
message_from: @user_id,
data_args: { col_labels: '', operation: 'import' },
autoDismiss: 5
)
@success = false
end
end
......
# Delayed messaging to notify a ELNer about new available data in INBOX
class MessageIncomingDataJob < ActiveJob::Base
def perform(sender_name, from, to)
Message.create_msg_notification(
channel_subject: Channel::INBOX_ARRIVALS_TO_ME,
data_args: { device_name: sender_name },
message_from: from, message_to: [to]
)
end
end
......@@ -46,27 +46,21 @@ class MoveToCollectionJob < ActiveJob::Base
moresamples = CollectionsSample.select(:sample_id).where(collection_id: id)
.limit(1).pluck(:sample_id)
channel = Channel.find_by(subject: Channel::GATE_TRANSFER_NOTIFICATION)
return true if channel&.msg_template.nil?
error_samples = samples.select{ |o| o[:state] != MoveToCollectionJob::STATE_MOVED }
error_reactions = reactions.select{ |o| o[:state] != MoveToCollectionJob::STATE_MOVED }
raise "jobs are not completed!! " if error_samples&.count > 0 || error_reactions&.count > 0
rescue => e
Rails.logger.error moresamples if moresamples
Rails.logger.error error_samples if error_samples&.count > 0
Rails.logger.error error_reactions if error_reactions&.count > 0
raise "Jobs are not completed!! " + error_reactions&.to_json + error_samples&.to_json
if error_samples&.count > 0 || error_reactions&.count > 0
raise "Jobs are not completed!! "+ moresamples.inspect + error_reactions&.to_json + error_samples&.to_json
ensure
content = channel.msg_template
content['data'] = 'Still some samples are on the blanket, please sync. again.' if moresamples&.count > 0
content['data'] = 'Some samples/reaction are not completed....' if error_samples&.count > 0 || error_reactions&.count > 0
users = [collection.user_id]
users.push(101) if moresamples&.count > 0 || error_samples&.count > 0 || error_reactions&.count > 0
message = Message.create_msg_notification(channel.id,content,collection.user_id,users)
comment = 'operation completed'
comment = 'Some samples were not transferred, please sync. again.' if moresamples&.count > 0
comment = 'Some samples/reaction could not be transferred....' if error_samples&.count > 0 || error_reactions&.count > 0
Message.create_msg_notification(
channel_subject: Channel::GATE_TRANSFER_NOTIFICATION,
data_args: { comment: comment },
message_from: collection.user_id,
)
end
true
end
......
......@@ -18,4 +18,21 @@ class Channel < ActiveRecord::Base
EDITOR_CALLBACK = 'EditorCallback'
COLLECTION_ZIP = 'Collection Import and Export'
COLLECTION_ZIP_FAIL = 'Collection Import and Export Failure'
class << self
def build_message(**args)
channel_id = args[:channel_id] # args.delete(:channel_id)
channel_subject = args[:channel_subject] # args.delete(:channel_subject)
channel = channel_id ? find_by(id: channel_id) : find_by(subject: channel_subject)
return unless channel
data_args = args.delete(:data_args)
message = channel.msg_template
if message.present?
message['channel_id'] = channel.id
message['data'] = format(message['data'], data_args)
message = message.merge(args)
end
message
end
end
end
......@@ -5,16 +5,19 @@ class Message < ActiveRecord::Base
belongs_to :channel
scope :where_content, ->(field, value) { where("content ->> ? = ?", field, value) }
def self.create_msg_notification(channel_id, p_content, user_id, p_user_ids)
channel = Channel.find(channel_id)