Mercurial > hg > ltpda
view m-toolbox/classes/@ltpda_uo/submit.m @ 43:bc767aaa99a8
CVS Update
author | Daniele Nicolodi <nicolodi@science.unitn.it> |
---|---|
date | Tue, 06 Dec 2011 11:09:25 +0100 |
parents | f0afece42f48 |
children | d58813ab1b92 |
line wrap: on
line source
% SUBMIT Submits the given objects to an LTPDA repository %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % DESCRIPTION: Submits the given objects to an LTPDA repository. If multiple % objects are submitted together a corresponding collection entry will be made. % % If not explicitly disabled the user will be prompt for entering submission % metadata and for chosing the database where to submit the objects. % % CALL: [IDS, CIDS] = submit(O1, PL) % [IDS, CIDS] = submit(O1, O2, PL) % % INPUTS: O1, O2, ... - objects to be submitted % PL - plist whih submission and repository informations % % OUTPUTS: IDS - IDs assigned to the submitted objects % CID - ID of the collection entry % % <a href="matlab:utils.helper.displayMethodInfo('ltpda_uo', 'submit')">Parameters Description</a> % % METADATA: % % 'experiment title' - title for the submission (required >4 characters) % 'experiment description' - description of this submission (required >10 characters) % 'analysis description' - description of the analysis performed (required >10 characters) % 'quantity' - the physical quantity represented by the data % 'keywords' - comma-delimited list of keywords % 'reference ids' - comma-delimited list object IDs % 'additional comments' - additional comments % 'additional authors' - additional author names % % VERSION: $Id: submit.m,v 1.91 2011/11/18 08:08:26 mauro Exp $ % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Notes on submission % % We can check ask the database for a list of allowed modules. This needs a % new table in the database. Then this list is passed to validate so that % if the 'validate' plist option (which needs to be added) is set to true, % then we call validate on the object before submitting. If validate is % true, then we set the validated flag in the database after submission if % it passes. % % function varargout = submit(varargin) % check if this is a call for parameters if utils.helper.isinfocall(varargin{:}) varargout{1} = getInfo(varargin{3}); return end import utils.const.* utils.helper.msg(msg.PROC3, 'running %s/%s', mfilename('class'), mfilename); % collect all AOs [pls, invars, rest] = utils.helper.collect_objects(varargin(:), 'plist'); [sinfo, invars, objs] = utils.helper.collect_objects(rest(:), 'struct'); % identify plists which are only used for the submission process mask = false(numel(pls), 1); for ii = 1:numel(pls) if ~utils.helper.isSubmissionPlist(pls(ii)) mask(ii) = true; end end % add all plist that do not look to contain parameters for the % submission to the list of objects submitted to the repository if sum(mask) objs = [objs, {pls(mask)}]; end % keep all the other as parameters plist if sum(~mask) pls = combine(pls(~mask)); end % rearrange nested objects lists into a single cell array objs = flatten(objs); if isempty(objs) error('### input at least one object to submit to the repository'); end % combine user plist with default pls = fixPlist(pls); dpl = getDefaultPlist(); pls = combine(pls, dpl.pset('HOSTNAME', '')); % for backwards compatibility convert any user supplied sinfo-structure into a plist pls = ltpda_uo.convertSinfo2Plist(pls, sinfo); % read XML submission informations file filename = pls.find('sinfo filename'); if ~isempty(filename) try pl = fixPlist(utils.xml.read_sinfo_xml(filename)); pls = combine(pl, pls); catch err error('### unable to read specified file: %s', filename); end end % collect additional informations sinfo = ltpda_uo.submitDialog(pls); if isempty(sinfo) [varargout{1}, varargout{2}] = userCanceled(); return end % check completeness of user supplied informations sinfo = checkSinfo(sinfo); % user supplied connection conn = find(pls, 'conn'); % obtain a connection from the connection manager rm = LTPDARepositoryManager(); if isempty(conn) connections = rm.findConnections(pls); if isempty(connections) % no connection found. create a new one conn = rm.newConnection(pls); if isempty(conn) [varargout{1}, varargout{2}] = userCanceled(); return end elseif numel(connections) == 1 && ~utils.prog.yes2true(pls.find('use selector')) % found only one connection and we are not forcing the use of the selector conn = connections(1); else % make the user select the desired database connection conn = rm.manager.selectConnection([]); if isempty(conn) [varargout{1}, varargout{2}] = userCanceled(); return end end end % avoid to ask for a password if one was specified in the plist if isjava(conn) && ~conn.isConnected() passwd = pls.find('password'); if ~isempty(passwd) conn.setPassword(passwd); end end % connect to the database if isempty(conn.openConnection()) error('### unable to connect to the database') end utils.helper.msg(msg.PROC1, 'submitting %d objects to repository', numel(objs)); try % lock connection to avoid expire during processing conn.setLocked(true); % reload connection table if we have a GUI if ~isempty(rm.gui) rm.gui.reloadConnectionTable(); end % look-up user id userid = utils.jmysql.getUserID(conn); username = conn.getUsername(); utils.helper.msg(msg.PROC1, 'got user id %d for user: %s', userid, char(username)); if userid < 1 || isnan(userid) || strcmp(userid, 'No Data') || ischar(userid) error('### Unknown username.'); end % author of the data: let's take the username author = username; % date for the transaction.transdata and objmeta.submitted columns as UTC time string t = time(); tdate = format(t, 'yyyy-mm-dd HH:MM:SS', 'UTC'); % machine details prov = provenance(); utils.helper.msg(msg.IMPORTANT, 'submitting to %s@%s:%s', ... char(conn.getUsername), char(conn.getHostname), char(conn.getDatabase)); % obtain the underlying connection c = conn.getConn(); % start a transaction. either we submitt all objects or we roll back all changes c.setAutoCommit(false); % process each object and collect id numbers ids = zeros(numel(objs), 1); cid = []; for kk = 1:numel(objs) % this object obj = objs{kk}; utils.helper.msg(msg.PROC1, 'submitting object: %s / %s', class(obj), obj.name); % format object creation time as UTC time string if isa(obj, 'plist') % plist-objects stores creatins time as milli secs since the epoch created = time().format('yyyy-mm-dd HH:MM:SS', 'UTC'); else created = obj.created.format('yyyy-mm-dd HH:MM:SS', 'UTC'); end % Set the UUID if it is empty. This should only happen for PLIST % objects. if isempty(obj.UUID) obj.UUID = char(java.util.UUID.randomUUID); end % create an XML representaion of the object if utils.prog.yes2true(pls.find('binary')); utils.helper.msg(msg.PROC2, 'binary submit'); otxt = ['binary submit ' datestr(now)]; else utils.helper.msg(msg.PROC2, 'xml submit'); otxt = utils.prog.obj2xml(obj); end % create an MD5 hash of the xml representation md5hash = utils.prog.hash(otxt, 'MD5'); % create a binary representaion of the object bobj = utils.prog.obj2binary(obj); if isempty(bobj) error('### failed to obtain a binary representation'); end % submit object to objs table stmt = c.prepareStatement(... 'INSERT INTO objs (xml, hash, uuid) VALUES (?, ?, ?)'); stmt.setObject(1, otxt); stmt.setObject(2, char(md5hash)); stmt.setObject(3, obj.UUID); stmt.executeUpdate(); % obtain object id rs = stmt.getGeneratedKeys(); if rs.next() objid = rs.getInt(1); else objid = []; end rs.close(); stmt.close(); % insert binary representation stmt = c.prepareStatement(... 'INSERT INTO bobjs (obj_id, mat) VALUES (?,?)'); stmt.setObject(1, objid); stmt.setObject(2, bobj); stmt.execute(); stmt.close(); % reference IDs are stored in a CSV string if ischar(sinfo.reference_ids) refids = sinfo.reference_ids; else refids = utils.prog.csv(sinfo.reference_ids); end % insert object meta data stmt = c.prepareStatement(... [ 'INSERT INTO objmeta (obj_id, obj_type, name, created, version, ' ... 'ip, hostname, os, submitted, experiment_title, experiment_desc, ' ... 'reference_ids, additional_comments, additional_authors, keywords, ' ... 'quantity, analysis_desc, author) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)' ]); stmt.setObject( 1, objid); stmt.setObject( 2, java.lang.String(class(obj))); stmt.setObject( 3, java.lang.String(obj.name)); stmt.setObject( 4, java.lang.String(created)); stmt.setObject( 5, java.lang.String(getappdata(0, 'ltpda_version'))); stmt.setObject( 6, java.lang.String(prov.ip)); stmt.setObject( 7, java.lang.String(prov.hostname)); stmt.setObject( 8, java.lang.String(prov.os)); stmt.setObject( 9, java.lang.String(tdate)); stmt.setObject(10, java.lang.String(sinfo.experiment_title)); stmt.setObject(11, java.lang.String(sinfo.experiment_description)); stmt.setObject(12, java.lang.String(refids)); stmt.setObject(13, java.lang.String(sinfo.additional_comments)); stmt.setObject(14, java.lang.String(sinfo.additional_authors)); stmt.setObject(15, java.lang.String(sinfo.keywords)); stmt.setObject(16, java.lang.String(sinfo.quantity)); stmt.setObject(17, java.lang.String(sinfo.analysis_description)); stmt.setObject(18, java.lang.String(author)); stmt.execute(); stmt.close(); % update other meta-data tables cols = utils.jmysql.execute(c, 'SHOW COLUMNS FROM tsdata'); if utils.helper.ismember('obj_id', cols(:,1)) % the tsdata table contains an obj id column. use the new database schema utils.jmysql.insertObjMetadata(c, obj, objid); else % otherwise use the old one utils.helper.msg(msg.PROC2, 'using back-compatibility code'); utils.jmysql.insertObjMetadataV1(c, obj, objid); end % update transactions table stmt = c.prepareStatement(... 'INSERT INTO transactions (obj_id, user_id, transdate, direction) VALUES (?, ?, ?, ?)'); stmt.setObject(1, objid); stmt.setObject(2, userid); stmt.setObject(3, java.lang.String(tdate)); stmt.setObject(4, java.lang.String('in')); stmt.execute(); stmt.close(); % collect the id of the submitted object ids(kk) = objid; end % make collection entry if numel(objs) > 1 % insert record into collections table stmt = c.prepareStatement(... 'INSERT INTO collections (nobjs, obj_ids) VALUES (?, ?)'); stmt.setObject(1, length(ids)); stmt.setObject(2, java.lang.String(utils.prog.csv(ids))); stmt.executeUpdate(); % obtain collection id rs = stmt.getGeneratedKeys(); if rs.next() cid = rs.getInt(1); else cid = []; end rs.close(); stmt.close(); end catch ex utils.helper.msg(msg.IMPORTANT, 'submission error. no object submitted') c.close() conn.setLocked(false); rethrow(ex) end % commit the transaction c.commit(); % report IDs of the inserted objects for kk = 1:numel(objs) utils.helper.msg(msg.IMPORTANT, 'submitted %s object with ID: %d, UUID: %s, name: %s', ... class(objs{kk}), ids(kk), objs{kk}.UUID, objs{kk}.name); end if ~isempty(cid) utils.helper.msg(msg.IMPORTANT, 'made collection entry with ID: %d', cid); end % unlock connection expire conn.setLocked(false); % reset timer LTPDARepositoryManager.resetTimer(rm.timerClearPass, conn); LTPDARepositoryManager.resetTimer(rm.timerDisconnect, conn); % pass back outputs if nargout > 0 varargout{1} = ids; end if nargout > 1 varargout{2} = cid; end end function varargout = userCanceled() % signal that the user cancelled the submission import utils.const.* utils.helper.msg(msg.PROC1, 'user cancelled'); varargout{1} = []; varargout{2} = []; end function sinfo = checkSinfo(sinfo) % check sinfo structure import utils.const.* % fieldnames mainfields = {'experiment_title', 'experiment_description', 'analysis_description'}; extrafields = {'quantity', 'keywords', 'reference_ids', 'additional_comments', 'author', 'additional_authors'}; % fieldnames of the input structure fnames = fieldnames(sinfo); % check mandatory fields for jj = 1:length(mainfields) if ~ismember(fnames, mainfields{jj}) error('### the sinfo structure should contain a ''%s'' field', mainfields{jj}); end end % check extra fields for jj = 1:length(extrafields) if ~ismember(fnames, extrafields{jj}) utils.helper.msg(msg.PROC2, 'setting default for field %s', extrafields{jj}); sinfo.(extrafields{jj}) = ''; end end % additional checks if length(sinfo.experiment_title) < 5 error('### ''experiment title'' should be at least 5 characters long'); end if length(sinfo.experiment_description) < 10 error('### ''experiment description'' should be at least 10 characters long'); end if length(sinfo.analysis_description) < 10 error('### ''analysis description'' should be at least 10 characters long'); end end function ii = getInfo(varargin) if nargin == 1 && strcmpi(varargin{1}, 'None') sets = {}; pl = []; else sets = {'Default'}; pl = getDefaultPlist(); end % Build info object ii = minfo(mfilename, 'ltpda_uo', 'ltpda', utils.const.categories.internal, '$Id: submit.m,v 1.91 2011/11/18 08:08:26 mauro Exp $', sets, pl); ii.setModifier(false); end function plout = getDefaultPlist() persistent pl; if ~exist('pl', 'var') || isempty(pl) pl = buildplist(); end plout = pl; end function plo = buildplist() plo = plist.TO_REPOSITORY_PLIST; p = param({'sinfo filename', 'Path to an XML file containing submission metadata'}, paramValue.EMPTY_STRING); plo.append(p); p = param({'binary', 'Submit only binary version of the objects'}, paramValue.FALSE_TRUE); plo.append(p); end function pl = fixPlist(pl) % accept parameters where words are separated either by spaces or underscore if ~isempty(pl) for ii = 1:pl.nparams pl.params(ii).setKey(strrep(pl.params(ii).key, '_', ' ')); end end end function flat = flatten(objs) % flatten nested lists into a single cell array flat = {}; while iscell(objs) && numel(objs) == 1 objs = objs{1}; end if numel(objs) == 1 flat = {objs}; return; end for jj = 1:numel(objs) obj = flatten(objs(jj)); for kk = 1:numel(obj) flat = [ flat obj(kk) ]; end end end