Home > classes > @ltpda_uo > submit.m

submit

PURPOSE ^

SUBMIT submits the given collection of objects to an LTPDA Repository.

SYNOPSIS ^

function varargout = submit(varargin)

DESCRIPTION ^

 SUBMIT submits the given collection of objects to an LTPDA Repository.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

 DESCRIPTION: Submits the given collection of objects to an LTPDA Repository.
              The input object(s) will be submitted to the LTPDA Repository
              configured in startup.m. If multiple objects are submitted
              together, a corresponding collection entry will be made.

              This is meant to be called by class/submit methods.

 CALL:        [ids, cids] = submit(o1, sinfo)
              [ids, cids] = submit(o1,o2, sinfo)
              [ids, cids] = submit([o1 o2], o3, {o4, o5}, sinfo)

 INPUTS:
              o1,o2,... - input objects to be submitted
              sinfo     - a structure containing the following fields:

                 'conn'                   - database connection object
                 'experiment_title'       - a title for the submission (Mandatory, >4 characters)
                 'experiment_description' - a description of this submission (Mandatory, >10 characters)
                 'analysis_description    - a description of the analysis performed  (Mandatory, >10 characters));
                 'quantity'               - the physical quantity represented by the data);
                 'keywords'               - a comma-delimited list of keywords);
                 'reference_ids'          - a string containing any reference object id numbers
                 'additional_comments'    - any additional comments
                 'additional_authors'     - any additional author names

 M-FILE INFO: Get information about this methods by calling
              >> ao.getInfo('submit')

              Get information about a specified set-plist by calling:
              >> ao.getInfo('submit', 'Default')

 VERSION:     $Id: submit.m,v 1.6 2008/09/04 15:29:30 ingo Exp $

 HISTORY:     09-05-07 M Hewitson
                 Creation

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SUBFUNCTIONS ^

SOURCE CODE ^

0001 % SUBMIT submits the given collection of objects to an LTPDA Repository.
0002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
0003 %
0004 % DESCRIPTION: Submits the given collection of objects to an LTPDA Repository.
0005 %              The input object(s) will be submitted to the LTPDA Repository
0006 %              configured in startup.m. If multiple objects are submitted
0007 %              together, a corresponding collection entry will be made.
0008 %
0009 %              This is meant to be called by class/submit methods.
0010 %
0011 % CALL:        [ids, cids] = submit(o1, sinfo)
0012 %              [ids, cids] = submit(o1,o2, sinfo)
0013 %              [ids, cids] = submit([o1 o2], o3, {o4, o5}, sinfo)
0014 %
0015 % INPUTS:
0016 %              o1,o2,... - input objects to be submitted
0017 %              sinfo     - a structure containing the following fields:
0018 %
0019 %                 'conn'                   - database connection object
0020 %                 'experiment_title'       - a title for the submission (Mandatory, >4 characters)
0021 %                 'experiment_description' - a description of this submission (Mandatory, >10 characters)
0022 %                 'analysis_description    - a description of the analysis performed  (Mandatory, >10 characters));
0023 %                 'quantity'               - the physical quantity represented by the data);
0024 %                 'keywords'               - a comma-delimited list of keywords);
0025 %                 'reference_ids'          - a string containing any reference object id numbers
0026 %                 'additional_comments'    - any additional comments
0027 %                 'additional_authors'     - any additional author names
0028 %
0029 % M-FILE INFO: Get information about this methods by calling
0030 %              >> ao.getInfo('submit')
0031 %
0032 %              Get information about a specified set-plist by calling:
0033 %              >> ao.getInfo('submit', 'Default')
0034 %
0035 % VERSION:     $Id: submit.m,v 1.6 2008/09/04 15:29:30 ingo Exp $
0036 %
0037 % HISTORY:     09-05-07 M Hewitson
0038 %                 Creation
0039 %
0040 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
0041 
0042 function varargout = submit(varargin)
0043 
0044   %% Process inputs
0045   %%% Check if this is a call for parameters
0046   if utils.helper.isinfocall(varargin{:})
0047     varargout{1} = getInfo(varargin{3});
0048     return
0049   end
0050 
0051   import utils.const.*
0052   utils.helper.msg(msg.MNAME, 'running %s/%s', mfilename('class'), mfilename);
0053   
0054   %%% Collect all AOs
0055 %   objs  = utils.helper.collect_objects(varargin(:), 'ltpda_uo');
0056   [sinfo, invars, objs] = utils.helper.collect_objects(varargin(:), 'struct');
0057 
0058   if isempty(objs)
0059     error('### Please input at least one LTPDA user object.');
0060   end
0061   if isempty(sinfo)
0062     error('### Please provide an input submission structure.');
0063   end
0064 
0065   %% Some setup
0066   % Check the comments
0067   sinfo = check_sinfo(sinfo);
0068 
0069   % make copy of connection object
0070   conn = sinfo.conn;
0071   if isempty(conn)
0072     error('### No valid database connect supplied. Aborting submit process.');
0073   end
0074 
0075   %% Process each object and collect id numbers
0076 
0077   utils.helper.msg(msg.PROC1, 'submitting objects to repository.');
0078 
0079   try
0080 
0081     % Look-up user id
0082     userid = utils.mysql.getUserID(conn);
0083     utils.helper.msg(msg.PROC1, 'got user id %d for user: %s', userid, conn.Username);
0084     if userid < 1 || isnan(userid)  || strcmp(userid, 'No Data') || ischar(userid)
0085       error('### Unknown username.');
0086     end
0087 
0088     % The date for the transaction table
0089     t     = time();
0090     tdate = format(t, 'yyyy-mm-dd HH:MM:SS');
0091 
0092     % Machine details
0093     prov = provenance();
0094 
0095     ids = [];
0096     for kk=1:numel(objs)
0097       % this object
0098       obj = objs{kk};
0099       
0100       for jj=1:numel(obj)
0101         utils.helper.msg(msg.PROC1, 'submitting object: %s / %s', class(obj(jj)), obj(jj).name);
0102 
0103         % Object name
0104         name = obj(jj).name;
0105 
0106         % Creation time
0107         if isa(obj(jj).created, 'time')
0108           created = obj(jj).created.format('yyyy-mm-dd HH:MM:SS');
0109         else
0110           created = '1970-01-01 00:00:00';
0111         end
0112 
0113         % Version
0114         objver = obj(jj).version;
0115 
0116         %%%%%%%%%%% Convert object to XML
0117         % make pointer to xml document
0118         xml = com.mathworks.xml.XMLUtils.createDocument('ltpda_object');
0119         % extract parent node
0120         parent = xml.getDocumentElement;
0121         % add Attribute 'ltpda_version' to the root node
0122         ltpda_version   = getappdata(0, 'ltpda_version');
0123         parent.setAttribute('ltpda_version', ltpda_version);
0124         % write obj into xml
0125         utils.helper.xmlwrite(obj(jj), xml, parent, '');    % Save the XML document.
0126         otxt = xmlwrite(xml); % then to string
0127 
0128         %%%%%%%%%%% Create MD5 hash of object
0129         md5hash = utils.prog.hash(otxt, 'MD5');
0130 
0131         %%%%%%%%%%% Submit object to objs table
0132         try
0133           utils.helper.msg(msg.PROC1, 'uploading XML data...\f');
0134           curs = exec(conn, ['insert into objs(xml,hash) values(''' otxt ''', ''' char(md5hash) ''' );']);
0135           close(curs);
0136           utils.helper.msg(msg.PROC1, '\bdone.');
0137         catch
0138           utils.helper.msg(msg.PROC1, '### Server returned: %s', curs.Message);
0139           error('### Submission error');
0140         end
0141 
0142         try
0143           objid = getObjID(conn, md5hash);
0144           utils.helper.msg(msg.PROC1, 'submitted object %s with id %d', class(obj(jj)), objid);
0145         catch
0146           error('### Failed to get ID of submitted object.');
0147         end
0148 
0149         %%%%%%%%%%% Make objmeta entry
0150 
0151         % For now reference IDs are stored in a string
0152         if ischar(sinfo.reference_ids)
0153           refIds = sinfo.reference_ids;
0154         else
0155           refIds = csv(sinfo.reference_ids);
0156         end
0157 
0158         try
0159           message = utils.mysql.insert(conn, ...
0160             'objmeta',...
0161             'obj_id', objid,...
0162             'obj_type', class(obj(jj)),...
0163             'name', name,...
0164             'created', created,...
0165             'version', objver,...
0166             'ip', prov.ip,...
0167             'hostname', prov.hostname,...
0168             'os', prov.os,...
0169             'submitted', datestr(now, 31),...
0170             'experiment_title', sinfo.experiment_title,...
0171             'experiment_desc', sinfo.experiment_description,...
0172             'reference_ids', refIds,...
0173             'additional_comments', sinfo.additional_comments,...
0174             'additional_authors', sinfo.additional_authors,...
0175             'keywords', sinfo.keywords, ...
0176             'quantity', sinfo.quantity, ...
0177             'analysis_desc', sinfo.analysis_description...
0178             );
0179 
0180           utils.helper.msg(msg.PROC1, 'made meta-data entry');
0181         catch
0182           lasterr
0183           error('### failed to make meta-data entry');
0184         end
0185 
0186         %%%%%%%%%%% Update object tables
0187         eid = update_object_tables(conn, obj(jj), objid);
0188 
0189         %%%%%%%%%%% Update transactions table
0190         try
0191           message = utils.mysql.insert(conn, ...
0192             'transactions',...
0193             'obj_id', objid,...
0194             'user_id', userid,...
0195             'transdate', tdate,...
0196             'direction', 'in'...
0197             );
0198           utils.helper.msg(msg.PROC1, 'updated transactions table');
0199         catch
0200           error('### Failed to update transactions table');
0201         end
0202 
0203         % Collect this ID
0204         ids = [ids objid];
0205       end
0206     end
0207 
0208     %% Now make collection entry
0209     if ~isempty(ids)
0210       try
0211         message = utils.mysql.insert(conn, ...
0212           'collections',...
0213           'nobjs', length(ids),...
0214           'obj_ids', csv(ids)...
0215           );
0216         utils.helper.msg(msg.PROC1, 'made collection entry');
0217       catch
0218         error('### Failed to make an entry in the collections table');
0219       end
0220     end
0221   catch ME
0222     utils.helper.msg(msg.PROC1, '### Submission error - cleaning up.');
0223     rethrow(ME)
0224   end
0225 
0226   % Pass back outptus
0227   varargout{1} = ids;
0228   varargout{2} = getCollectionID(conn, ids);
0229 
0230   utils.helper.msg(msg.PROC1, 'submission complete.');
0231 
0232 end
0233 
0234 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
0235 %                               Local Functions                               %
0236 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
0237 
0238 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
0239 %  Update entries for various object tables
0240 %
0241 % eid = update_object_tables(conn, obj)
0242 % eid = update_object_tables(conn, obj, objid)
0243 %
0244 function  eid = update_object_tables(varargin)
0245 
0246   import utils.const.*
0247   
0248   if nargin < 2
0249     error('### Incorrect usage.');
0250   end
0251   conn = varargin{1};
0252   obj  = varargin{2};
0253   if nargin == 3
0254     objid = varargin{3};
0255   else
0256     objid = [];
0257   end
0258 
0259   % Class of object
0260   cl = class(obj);
0261   utils.helper.msg(msg.PROC2, 'making meta data entry for %s', cl);
0262   eid = [];
0263 
0264   switch cl
0265     %------------------ AO
0266     case 'ao'
0267 
0268       %-- First we need to insert the data object info
0269       did = update_object_tables(conn, obj.data);
0270 
0271       % Now insert the AO info
0272       try
0273         message = utils.mysql.insert(conn, ...
0274           'ao',...
0275           'obj_id', objid,...
0276           'data_type', class(obj.data),...
0277           'data_id', did, ...
0278           'description', obj.description, ...
0279           'mfilename', obj.mfilename, ...
0280           'mdlfilename', obj.mdlfilename...
0281           );
0282         utils.helper.msg(msg.PROC2, 'made meta-data entry for %s', cl);
0283         % get the entry id
0284         eid = getObjID(conn, '');
0285       catch
0286         lasterr
0287         error('### failed to make meta-data entry for %s table', cl);
0288       end
0289 
0290       %------------------ CSDATA
0291     case 'cdata'
0292       % Make the meta-data entry
0293       try
0294         message = utils.mysql.insert(conn, ...
0295           'cdata',...
0296           'xunits', char(obj.xunits),...
0297           'yunits', char(obj.yunits) ...
0298           );
0299         utils.helper.msg(msg.PROC2, 'made meta-data entry for %s', cl);
0300         % get the entry id
0301         eid = getObjID(conn, '');
0302       catch
0303         lasterr
0304         error('### failed to make meta-data entry for %s table', cl);
0305       end
0306       %------------------ FSDATA
0307     case 'fsdata'
0308       % Make the meta-data entry
0309       try
0310         if ~isnan(obj.fs) && ~isempty(obj.fs)
0311           message = utils.mysql.insert(conn, ...
0312             'fsdata',...
0313             'xunits', char(obj.xunits),...
0314             'yunits', char(obj.yunits),...
0315             'fs', obj.fs ...
0316             );
0317         else
0318           message = utils.mysql.insert(conn, ...
0319             'fsdata',...
0320             'xunits', char(obj.xunits),...
0321             'yunits', char(obj.yunits));
0322         end
0323         utils.helper.msg(msg.PROC2, 'made meta-data entry for %s', cl);
0324         % get the entry id
0325         eid = getObjID(conn, '');
0326       catch
0327         lasterr
0328         error('### failed to make meta-data entry for %s table', cl);
0329       end
0330       %------------------ MFIR
0331     case 'mfir'
0332       % Make the meta-data entry
0333       try
0334         if isempty(obj.fs)          
0335           message = utils.mysql.insert(conn, ...
0336             'mfir',...
0337             'obj_id', objid,...
0338             'in_file', obj.infile...
0339             );
0340         else          
0341           message = utils.mysql.insert(conn, ...
0342             'mfir',...
0343             'obj_id', objid,...
0344             'in_file', obj.infile,...
0345             'fs', obj.fs ...
0346             );
0347         end
0348         utils.helper.msg(msg.PROC2, 'made meta-data entry for %s', cl);
0349         % get the entry id
0350         eid = getObjID(conn, '');
0351       catch
0352         lasterr
0353         error('### failed to make meta-data entry for %s table', cl);
0354       end
0355       %------------------ MIIR
0356     case 'miir'
0357       % Make the meta-data entry
0358       try
0359         if isempty(obj.fs)
0360           message = utils.mysql.insert(conn, ...
0361             'miir',...
0362             'obj_id', objid,...
0363             'in_file', obj.infile...
0364             );
0365         else
0366           message = utils.mysql.insert(conn, ...
0367             'miir',...
0368             'obj_id', objid,...
0369             'in_file', obj.infile,...
0370             'fs', obj.fs ...
0371             );
0372         end
0373         utils.helper.msg(msg.PROC2, 'made meta-data entry for %s', cl);
0374         % get the entry id
0375         eid = getObjID(conn, '');
0376       catch
0377         lasterr
0378         error('### failed to make meta-data entry for %s table', cl);
0379       end
0380       %------------------ TSDATA
0381     case 'tsdata'
0382       % Make the meta-data entry
0383       try
0384         message = utils.mysql.insert(conn, ...
0385           'tsdata',...
0386           'xunits', char(obj.xunits),...
0387           'yunits', char(obj.yunits),...
0388           'fs', obj.fs,...
0389           'nsecs', obj.nsecs,...
0390           't0', format(obj.t0, 'yyyy-mm-dd HH:MM:SS') ...
0391           );
0392         utils.helper.msg(msg.PROC2, 'made meta-data entry for %s', cl);
0393         % get the entry id
0394         eid = getObjID(conn, '');
0395       catch
0396         lasterr
0397         error('### failed to make meta-data entry for %s table', cl);
0398       end
0399       %------------------ XYDATA
0400     case 'xydata'
0401       % Make the meta-data entry
0402       try
0403         message = utils.mysql.insert(conn, ...
0404           'xydata',...
0405           'xunits', char(obj.xunits),...
0406           'yunits', char(obj.yunits) ...
0407           );
0408         utils.helper.msg(msg.PROC2, 'made meta-data entry for %s', cl);
0409         % get the entry id
0410         eid = getObjID(conn, '');
0411       catch
0412         lasterr
0413         error('### failed to make meta-data entry for %s table', cl);
0414       end
0415     otherwise
0416       utils.helper.msg(msg.PROC2, '# no special table for objects of type %s', cl);
0417   end
0418 
0419   if ~isempty(eid)
0420     utils.helper.msg(msg.PROC2, 'made meta-data entry in %s table with id %d', cl, eid);
0421   end
0422 end
0423 
0424 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
0425 %
0426 function id = getObjID(conn, hash)
0427 
0428   try
0429     q = sprintf('select last_insert_id()');
0430     curs = exec(conn, q);
0431     curs = fetch(curs);
0432     id  = curs.Data{1};
0433     close(curs);
0434   catch
0435     error('### Failed to get object ID. Server returned %s', curs.Message);
0436   end
0437 end
0438 
0439 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
0440 %
0441 function Cid = getCollectionID(conn, objids)
0442 
0443   try
0444     curs = exec(conn, sprintf('select id from collections where obj_ids="%s"', csv(objids)));
0445     curs = fetch(curs);
0446     Cid  = curs.Data{1};
0447     close(curs);
0448   catch
0449     error('### Failed to get Collection ID. Server returned %s', curs.Message);
0450   end
0451 
0452   if strcmp(Cid, 'No Data')
0453     Cid = [];
0454   end
0455 end
0456 
0457 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
0458 % make comma separated list of numbers
0459 %
0460 function s = csv(x)
0461 
0462   s = sprintf('%g,', x);
0463   s = s(1:end-1);
0464 end
0465 
0466 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
0467 % check sinfo structure
0468 %
0469 function sinfo = check_sinfo(sinfo)
0470 
0471   import utils.const.*
0472   
0473   % --- check fieldnames
0474   manfields = {'conn', 'experiment_title', 'experiment_description', 'analysis_description'};
0475   allfields = {'conn', 'experiment_title', 'experiment_description', 'analysis_description', ...
0476     'quantity', 'keywords', 'reference_ids', 'additional_comments', 'additional_authors'};
0477 
0478   if ~isstruct(sinfo)
0479     sinfo_description();
0480   end
0481 
0482   % get input fieldnames
0483   fnames = fieldnames(sinfo);
0484 
0485   % check these
0486   for jj=1:length(manfields)
0487     if ~ismember(fnames, manfields{jj})
0488       for kk=1:length(manfields)
0489         utils.helper.msg(msg.OFF, 'Required Field: %s', manfields{kk});
0490       end
0491       error('The sinfo structure should contain at least the above fields.');
0492     end
0493   end
0494 
0495   % Check other fields exist
0496   if ~isfield(sinfo, 'quantity')
0497     sinfo.quantity = '';
0498   end
0499   if ~isfield(sinfo, 'keywords')
0500     sinfo.keywords = '';
0501   end
0502   if ~isfield(sinfo, 'reference_ids')
0503     sinfo.reference_ids = '';
0504   end
0505   if ~isfield(sinfo, 'additional_comments')
0506     sinfo.additional_comments = '';
0507   end
0508   if ~isfield(sinfo, 'additional_authors')
0509     sinfo.additional_authors = '';
0510   end
0511 
0512   %---- Additional checks
0513   if length(sinfo.experiment_title) < 5
0514     error('### Experiment title is mandatory and should be at least 5 characters long.');
0515   end
0516   if length(sinfo.experiment_description) < 10
0517     error('### Experiment description is mandatory and should be more that 10 characters long.');
0518   end
0519   if length(sinfo.analysis_description) < 10
0520     error('### Analysis description is mandatory and should be more that 10 characters long.');
0521   end
0522 
0523   % check all fields are present otherwise make them empty
0524   for jj=1:length(allfields)
0525     if ~ismember(fnames, allfields{jj})
0526       utils.helper.msg(msg.PROC2, 'setting default for field %s', allfields{jj});
0527       sinfo.(allfields{jj}) = '';
0528     end
0529   end
0530 
0531   utils.helper.msg(msg.PROC1, 'sinfo structure is valid.');
0532 
0533 end
0534 
0535 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
0536 % Throw description of sinfo to terminal
0537 %
0538 function sinfo_description
0539   disp('The sinfo structure should look like:');
0540   disp('');
0541   disp('  sinfo.conn                   - a database connection object');
0542   disp('  sinfo.experiment_title       - the experiment title       (Mandatory, >5 characters)');
0543   disp('  sinfo.experiment_description - the experiment description (Mandatory, >10 characters)');
0544   disp('  sinfo.analysis_description   - the analysis description   (Mandatory, >10 characters)');
0545   disp('  sinfo.quantity               - the physical quantity represented by the data');
0546   disp('  sinfo.keywords               - a comma-delimited list of keywords');
0547   disp('  sinfo.reference_ids          - IDs of other applicable LTPDA objects in the repository');
0548   disp('  sinfo.additional_comments    - any additional comments');
0549   disp('  sinfo.additional_authors     - any additional authors');
0550   disp('');
0551   error('### Incorrect specification for sinfo.')
0552 end
0553 
0554 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
0555 %
0556 function ii = getInfo(varargin)
0557   if nargin == 1 && strcmpi(varargin{1}, 'None')
0558     sets = {};
0559     pl   = [];
0560   else
0561     sets = {'Default'};
0562     pl   = getDefaultPlist;
0563   end
0564   % Build info object
0565   ii = minfo(mfilename, 'ltpda_uo', '', utils.const.categories.internal, '$Id: submit.m,v 1.6 2008/09/04 15:29:30 ingo Exp $', sets, pl);
0566 end
0567 
0568 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
0569 %
0570 function plo = getDefaultPlist()
0571   sinfo.conn                   = [];
0572   sinfo.experiment_title       = 'please add title';
0573   sinfo.experiment_description = 'please add experiment description';
0574   sinfo.analysis_description   = 'please add description';
0575   sinfo.quantity               = 'none';
0576   sinfo.keywords               = 'none';
0577   sinfo.reference_ids          = '';
0578   sinfo.additional_comments    = 'none';
0579   sinfo.additional_authors     = 'no one';
0580   plo = plist('sinfo', sinfo);
0581 end
0582

Generated on Mon 08-Sep-2008 13:18:47 by m2html © 2003