Skip to content

Commit 7b19005

Browse files
FIX - cleaned up and merged MD5 hashing of input and output variables. It was implemented twice, where one could be controlled with cfg.trackdatainfo and the other one not.
1 parent 28ac737 commit 7b19005

File tree

10 files changed

+141
-203
lines changed

10 files changed

+141
-203
lines changed

ft_analysispipeline.m

Lines changed: 56 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@
106106
cfg.filename = ft_getopt(cfg, 'filename');
107107
cfg.showinfo = ft_getopt(cfg, 'showinfo', {'functionname'});
108108
cfg.keepremoved = ft_getopt(cfg, 'keepremoved', 'no');
109-
cfg.feedback = ft_getopt(cfg, 'feedback', 'yes');
109+
cfg.feedback = ft_getopt(cfg, 'feedback', 'text');
110110
cfg.prune = ft_getopt(cfg, 'prune', 'yes');
111111
cfg.filetype = ft_getopt(cfg, 'filetype');
112112
cfg.fontsize = ft_getopt(cfg, 'fontsize', 10);
@@ -254,9 +254,7 @@
254254
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
255255
% SUBFUNCTION for recursive walking along the cfg.previous.previous info
256256
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
257-
function info = walktree(cfg, count)
258-
259-
ft_progress(0.1); % FIXME no percentage complete known
257+
function info = walktree(cfg)
260258

261259
if isempty(cfg) || (isstruct(cfg) && numel(fieldnames(cfg))==0)
262260
info = [];
@@ -289,6 +287,9 @@
289287
end
290288
end
291289

290+
ft_progress(rand(1), 'parsing provenance for %s\n', this.name); % FIXME no percentage complete known
291+
drawnow
292+
292293
% the order of the output elements matters for the recursion
293294
info = [{this} branch previous];
294295

@@ -300,13 +301,13 @@
300301
node.cfg = cfg;
301302
node.name = f;
302303
node.id = getvalue(cfg, 'version.id');
303-
node.this = struct2hash(cfg);
304+
node.this = ft_hash(cfg);
304305
if isfield(cfg, 'previous') && ~isempty(cfg.previous) && iscell(cfg.previous)
305306
% skip the entries that are empty
306307
cfg.previous = cfg.previous(~cellfun(@isempty, cfg.previous));
307-
node.parent = cellfun(@struct2hash, cfg.previous, 'UniformOutput', false);
308+
node.parent = cellfun(@ft_hash, cfg.previous, 'UniformOutput', false);
308309
elseif isfield(cfg, 'previous') && ~isempty(cfg.previous) && isstruct(cfg.previous)
309-
node.parent = {struct2hash(cfg.previous)};
310+
node.parent = {ft_hash(cfg.previous)};
310311
elseif isfield(cfg, 'previous') && ~isempty(cfg.previous)
311312
error('unexpected content in cfg.previous');
312313
else
@@ -360,9 +361,7 @@
360361
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
361362
function pipeline2matlabfigure(cfg, pipeline)
362363

363-
if istrue(cfg.feedback)
364-
fprintf('plotting pipeline as MATLAB figure\n');
365-
end
364+
fprintf('plotting pipeline as MATLAB figure\n');
366365

367366
layout = cell(numel(pipeline));
368367
for i=1:length(pipeline)
@@ -388,44 +387,44 @@ function pipeline2matlabfigure(cfg, pipeline)
388387
axis tight;
389388

390389
for i=1:numel(pipeline)
391-
390+
392391
label = makelabel(pipeline(i), cfg.showinfo);
393-
392+
394393
% dublicate backslashes to escape tex interpreter (in case of windows filenames)
395394
label = strrep(label, '\', '\\');
396395
label = strrep(label, '{\\bf', '{\bf'); % undo for bold formatting
397-
396+
398397
% escape underscores
399398
label = strrep(label, '_', '\_');
400-
399+
401400
% strip blank line if present and not needed
402401
if strcmp(label{end},'')
403402
label(end) = [];
404403
end
405-
404+
406405
% compute width and height of each box, note that axis Units are set to Normalized
407406
boxsize = 1./[maxwidth+1 maxheight+3];
408-
407+
409408
% create the 4 corners for our patch, close the patch by returning to the start point
410409
x = ([0 1 1 0 0]-0.5) .* boxsize(1);
411410
y = ([0 0 1 1 0]-0.5) .* boxsize(2);
412-
411+
413412
% position the patch
414413
location = pipeline(i).position([2 1]);
415414
location(1) = (location(1)-0.5)/maxwidth;
416415
location(2) = (location(2)-0.5)/maxheight;
417-
416+
418417
% the location specifies the center of the patch
419418
x = x + location(1);
420419
y = y + location(2);
421-
420+
422421
p = patch(x', y', 0);
423422
set(p, 'Facecolor', [1 1 0.6])
424-
423+
425424
pipeline(i).x = x;
426425
pipeline(i).y = y;
427426
guidata(fig, pipeline);
428-
427+
429428
if length(label)==1
430429
textloc = location;
431430
l = text(textloc(1), textloc(2), label);
@@ -438,18 +437,18 @@ function pipeline2matlabfigure(cfg, pipeline)
438437
textloc = location;
439438
textloc(1) = textloc(1)-boxsize(1)/2;
440439
textloc(2) = textloc(2)+boxsize(2)/2;
441-
440+
442441
l = text(textloc(1), textloc(2), label);
443442
set(l, 'HorizontalAlignment', 'left');
444443
set(l, 'VerticalAlignment', 'top');
445444
set(l, 'fontUnits', 'points');
446445
set(l, 'fontSize', cfg.fontsize);
447446
set(l, 'interpreter', 'tex');
448447
end
449-
448+
450449
% draw an arrow if appropriate
451450
n = length(pipeline(i).parent);
452-
451+
453452
for j=1:n
454453
[parentlocation(2), parentlocation(1)] = ind2sub([maxheight, maxwidth], find(strcmp(layout(:), pipeline(i).parent{j}), 1, 'first'));
455454
% parentlocation = info(find(strcmp({pipeline.this}, analysis.parent{j}), 1, 'first')).position;
@@ -467,8 +466,8 @@ function pipeline2matlabfigure(cfg, pipeline)
467466
end
468467
arrow(base, tip, 'length', 8, 'lineWidth', 1);
469468
end
470-
471-
469+
470+
472471
end % for numel(info)
473472

474473
set(fig, 'WindowButtonUpFcn', @button);
@@ -499,77 +498,77 @@ function pipeline2matlabfigure(cfg, pipeline)
499498
label = {};
500499
for k = 1:numel(showinfo)
501500
switch showinfo{k}
502-
501+
503502
case 'functionname'
504503
% label{end+1} = ['{\bf ' pipeline(i).name '}'];
505504
label{end+1} = pipeline.name;
506505
if k == 1 % add blank line if function name is on top, looks nice
507506
label{end+1} = '';
508507
end
509-
508+
510509
case 'revision'
511510
if isfield(pipeline.cfg, 'version') && isfield(pipeline.cfg.version, 'id')
512511
label{end+1} = pipeline.cfg.version.id;
513512
else
514513
label{end+1} = '<revision unknown>';
515514
end
516-
515+
517516
case 'matlabversion'
518517
if isfield(pipeline.cfg, 'callinfo') && isfield(pipeline.cfg.callinfo, 'matlab')
519518
label{end+1} = ['MATLAB ' pipeline.cfg.callinfo.matlab];
520519
else
521520
label{end+1} = '<MATLAB version unknown>';
522521
end
523-
522+
524523
case 'computername'
525524
if isfield(pipeline.cfg, 'callinfo') && isfield(pipeline.cfg.callinfo, 'hostname')
526525
label{end+1} = ['Hostname: ' pipeline.cfg.callinfo.hostname];
527526
else
528527
label{end+1} = '<hostname unknown>';
529528
end
530-
529+
531530
case 'architecture'
532531
if isfield(pipeline.cfg, 'callinfo') && isfield(pipeline.cfg.callinfo, 'hostname')
533532
label{end+1} = ['Architecture: ' pipeline.cfg.callinfo.computer];
534533
else
535534
label{end+1} = '<architecture unknown>';
536535
end
537-
536+
538537
case 'username'
539538
if isfield(pipeline.cfg, 'callinfo') && isfield(pipeline.cfg.callinfo, 'user')
540539
label{end+1} = ['Username: ' pipeline.cfg.callinfo.user];
541540
else
542541
label{end+1} = '<username unknown>';
543542
end
544-
543+
545544
case 'calltime'
546545
if isfield(pipeline.cfg, 'callinfo') && isfield(pipeline.cfg.callinfo, 'calltime')
547546
label{end+1} = ['Function called at ' datestr(pipeline.cfg.callinfo.calltime)];
548547
else
549548
label{end+1} = '<function call time unknown>';
550549
end
551-
550+
552551
case 'timeused'
553552
if isfield(pipeline.cfg, 'callinfo') && isfield(pipeline.cfg.callinfo, 'proctime')
554553
label{end+1} = sprintf('Function call required %d seconds', round(pipeline.cfg.callinfo.proctime));
555554
else
556555
label{end+1} = '<processing time unknown>';
557556
end
558-
557+
559558
case 'memused'
560559
if isfield(pipeline.cfg, 'callinfo') && isfield(pipeline.cfg.callinfo, 'procmem')
561560
label{end+1} = sprintf('Function call required %d MB', round(pipeline.cfg.callinfo.procmem/1024/1024));
562561
else
563562
label{end+1} = '<memory requirement unknown>';
564563
end
565-
564+
566565
case 'workingdir'
567566
if isfield(pipeline.cfg, 'callinfo') && isfield(pipeline.cfg.callinfo, 'pwd')
568567
label{end+1} = sprintf('Working directory was %s', pipeline.cfg.callinfo.pwd);
569568
else
570569
label{end+1} = '<working directory unknown>';
571570
end
572-
571+
573572
case 'scriptpath'
574573
if isfield(pipeline.cfg, 'version') && isfield(pipeline.cfg.version, 'name')
575574
label{end+1} = sprintf('Full path to script was %s', pipeline.cfg.version.name);
@@ -587,9 +586,7 @@ function pipeline2matlabscript(cfg, pipeline)
587586
[p, f, x] = fileparts(cfg.filename);
588587
filename = fullfile(p, [f '.m']);
589588

590-
if istrue(cfg.feedback)
591-
fprintf('exporting MATLAB script to file ''%s''\n', filename);
592-
end
589+
fprintf('exporting MATLAB script to file ''%s''\n', filename);
593590

594591
varname = {};
595592
varhash = {};
@@ -637,9 +634,7 @@ function pipeline2dotfile(cfg, pipeline)
637634
[p, f, x] = fileparts(cfg.filename);
638635
filename = fullfile(p, [f '.dot']);
639636

640-
if istrue(cfg.feedback)
641-
fprintf('exporting DOT file to ''%s''\n', filename);
642-
end
637+
fprintf('exporting DOT file to ''%s''\n', filename);
643638

644639
% write the complete script to file
645640
fid = fopen(filename, 'wb');
@@ -680,46 +675,41 @@ function pipeline2htmlfile(cfg, pipeline)
680675
[p, f, x] = fileparts(cfg.filename);
681676
filename = fullfile(p, [f '.html']);
682677

683-
if istrue(cfg.feedback)
684-
fprintf('exporting HTML file to ''%s''\n', filename);
685-
end
678+
% skip the data-like fields and the fields that probably were not added by the user himself
679+
skipfields = {'previous', 'grid', 'headmodel', 'event', 'warning', 'progress', 'trackconfig', 'checkconfig', 'checksize', 'showcallinfo', 'debug', 'outputfilepresent', 'trackcallinfo', 'trackdatainfo'};
680+
681+
fprintf('exporting HTML file to ''%s''\n', filename);
686682

687683
html = '';
688684
totalproctime = 0;
689685

690-
if istrue(cfg.feedback)
691-
ft_progress('init', 'text', 'serialising cfg-structures...');
692-
else
693-
ft_progress('init', 'none');
694-
end
686+
ft_progress('init', cfg.feedback, 'serialising cfg-structures...');
695687

696688
for k = 1:numel(pipeline)
697-
ft_progress(k/numel(pipeline));
698-
689+
ft_progress(k/numel(pipeline), 'serialising cfg-structure %d from %d', k, numel(pipeline));
690+
699691
% strip away the cfg.previous fields, and all data-like fields
700-
tmpcfg = removefields(pipeline(k).cfg,...
701-
{'previous', 'grid', 'headmodel', 'event', 'warning'});
702-
692+
tmpcfg = removefields(pipeline(k).cfg, skipfields);
693+
703694
usercfg = [];
704-
695+
705696
% record the usercfg and proctime if present
706697
if isfield(tmpcfg, 'callinfo')
707698
if isfield(tmpcfg.callinfo, 'usercfg')
708-
usercfg = removefields(tmpcfg.callinfo.usercfg,...
709-
{'previous', 'grid', 'headmodel', 'event', 'warning'});
710-
699+
usercfg = removefields(tmpcfg.callinfo.usercfg, skipfields);
700+
711701
% avoid processing usercfg twice
712702
tmpcfg.callinfo = rmfield(tmpcfg.callinfo, 'usercfg');
713703
end
714-
704+
715705
if isfield(tmpcfg.callinfo, 'proctime')
716706
totalproctime = totalproctime + tmpcfg.callinfo.proctime;
717707
end
718708
end
719-
709+
720710
html = [html sprintf('nodes["%s"] = {"id": "%s", "name": "%s", "cfg": "%s", "usercfg": "%s", "parentIds": [',...
721711
pipeline(k).this, pipeline(k).this, pipeline(k).name, escapestruct(tmpcfg), escapestruct(usercfg))];
722-
712+
723713
if ~isempty(pipeline(k).parent)
724714
for j = 1:numel(pipeline(k).parent)
725715
html = [html '"' pipeline(k).parent{j} '"'];
@@ -728,14 +718,14 @@ function pipeline2htmlfile(cfg, pipeline)
728718
end
729719
end
730720
end
731-
721+
732722
html = [html sprintf(']};\n')];
733-
723+
734724
if k == numel(pipeline)
735725
% we are at the single leaf node
736726
html = [html sprintf('var leafId = "%s";\n', pipeline(k).this)];
737727
end
738-
728+
739729
end
740730
ft_progress('close');
741731

@@ -780,24 +770,3 @@ function pipeline2htmlfile(cfg, pipeline)
780770
cfghtml = strrep(cfghtml, sprintf('\t'), '\t');
781771
cfghtml = strrep(cfghtml, '"', '\"');
782772

783-
784-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
785-
% SUBFUNCTION
786-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
787-
function h = struct2hash(s)
788-
% both of these are from Mathworks file exchange
789-
try
790-
% this one uses a mex file
791-
h = CalcMD5(mxSerialize(s));
792-
catch
793-
% this one uses Java
794-
h = DataHash(s);
795-
end
796-
797-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
798-
% SUBFUNCTION
799-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
800-
function b = istrue(s)
801-
false_list = {'no' 'false' 'off' 'n' 'none'};
802-
b = ~any(strcmp(s, false_list));
803-

test/private/isequalfigure.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@
2121
status = isequal(mata, matb);
2222

2323
% the following is too sensitive
24-
% md5a = CalcMD5(fna);
25-
% md5b = CalcMD5(fnb);
24+
% md5a = ft_hash(fna);
25+
% md5b = ft_hash(fnb);
2626
% status = isequal(md5a, md5b);

utilities/ft_hash.m

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
function h = ft_hash(s)
2+
3+
% Compute a MD5 hash from a variable or a MATLAB structure
4+
%
5+
% It will first try a hashing algorithm implemented as a mex file.
6+
% If that fails, it falls back to a slower one that is based on Java.
7+
8+
% both CalcMD5 and DataHash are from Mathworks file exchange
9+
ft_hastoolbox('fileexchange', 1);
10+
11+
try
12+
% this one uses a mex file
13+
if isstruct(s) || iscell(s)
14+
h = CalcMD5(mxSerialize(s));
15+
else
16+
h = CalcMD5(s);
17+
end
18+
catch
19+
% this one uses Java
20+
h = DataHash(s);
21+
end

0 commit comments

Comments
 (0)