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);
+ −
+ − % database connection
+ − c = LTPDADatabaseConnectionManager().connect(pls);
+ −
+ − utils.helper.msg(msg.PROC1, 'submitting %d objects to repository', numel(objs));
+ −
+ − try
+ − % get username and userid
+ − [username, userid] = utils.repository.getUser(c);
+ −
+ − % 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();
+ −
+ − % 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.mysql.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.repository.insertObjMetadata(c, obj, objid);
+ − else
+ − % otherwise use the old one
+ − utils.helper.msg(msg.PROC2, 'using back-compatibility code');
+ − utils.repository.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()
+ − rethrow(ex)
+ − end
+ −
+ − % commit the transaction
+ − c.commit();
+ −
+ − % close the connection if we own it
+ − if isempty(find(pls, 'conn'))
+ − c.close();
+ − end
+ −
+ − % 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
+ −
+ − % 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