diff --git a/fcn_core/CreateID.m b/+PlotID/CreateID.m similarity index 51% rename from fcn_core/CreateID.m rename to +PlotID/CreateID.m index 94d0df2216f9732540f21ae68b7eaccc9c8a2c8a..570a187ba40d94354edb54e8742bc100b62e3fca 100644 --- a/fcn_core/CreateID.m +++ b/+PlotID/CreateID.m @@ -1,18 +1,24 @@ -function [ID] = CreateID(method) -%CreateID Creates an Identifier (char) -% Creates an (sometimes unique) identifier based on the selected method -% if no method is selected method 1 will be the default method -arguments - method (1,1) {mustBeNumeric} = 1 -end - -switch method - case 1 % UNIX Time in seconds as HEX - ID = posixtime(datetime('now')); %get current Unix time - ID = dec2hex(int32(ID)); % get it as Hex - pause(0.5); %Pausing for unique IDs - otherwise - error('The requested method is not defined yet'); -end - -end +function [ID] = CreateID(method) +%CreateID Creates an identifier (char) +% Creates an (sometimes unique) identifier based on the selected method +% if no method is selected method 1 will be the default method +arguments + method (1,1) {mustBeNumeric} = 1 +end + +switch method + case 1 % UNIX Time in seconds as HEX + ID = posixtime(datetime('now')); %get current Unix time + ID = dec2hex(int32(ID)); % get it as Hex + pause(1); %Pausing for unique IDs + case 2 % random UUID from Java 128 bit + %Static factory to retrieve a type 4 (pseudo randomly generated) UUID. + % The UUID is generated using a cryptographically strong pseudo + % random number generator. + temp = java.util.UUID.randomUUID; + ID = temp.toString; + otherwise + error('The requested method is not defined yet'); +end + +end diff --git a/+PlotID/Publish.m b/+PlotID/Publish.m new file mode 100644 index 0000000000000000000000000000000000000000..fcd7772b6dc55615ee24f92ea8e2d3823b3e4ddb --- /dev/null +++ b/+PlotID/Publish.m @@ -0,0 +1,190 @@ +function Publish(DataPaths, ID, figure, options) +%Publishes saves plot, data and measuring script +% Location sets the storage location. 'local' sets the storage location +% to the current folder (an export folder will be created), 'server' is a +% remote path, that is defined in the config file. +% Two Methods are implemented 'individual' stores the data for +% each plot while 'centralized' uses a data folder and uses reference links +% to the original data (hdf5 only). +% ParentFolder is the folder Name where the exported data is stored if an +% path is used, PlotId will use this path a storagePath + +arguments + DataPaths + ID (1,:) {mustBeNonzeroLengthText} % ID must be provided + figure (1,:) {mustBeFigure} % Checks if figure is a figure object + options.Location {mustBeMember(options.Location ,{'local','server','manual','CI-Test'})} = 'local' % storage path + options.Method {mustBeMember(options.Method ,{'individual','centralized'})} = 'individual' + options.ParentFolder (1,:) {mustBeText} = 'export' + options.CopyUserFCN (1,1) {mustBeNumericOrLogical} = true + options.CSV (1,1) {mustBeNumericOrLogical} = false +end + +%catch multiple figures in fig +if numel(figure) > 1 + figure = figure(1); + msg = ['Publish is designed for handeling one figure at once' newline,... + '- publishes uses only the first figure.' newline , ... + 'Consider an external for loop for multi figure export as provided in example.m']; + warning(msg); +end + +%% read config file +try + txt = fileread('config.json'); + config = jsondecode(txt); + configError = false; +catch + msg = ['Error while reading the config file' newline,... + ' publishing on server not possible']; + warning(msg); + configError = true; +end + +%% storage location +switch options.Location + case 'local' + if contains(options.ParentFolder, {'/','\'}) + storPath = options.ParentFolder; + else + % use the script path as export path + scriptPath = fileparts(DataPaths.script); + storPath = fullfile(scriptPath,options.ParentFolder); + end + case 'server' %from config File + storPath = config.ServerPath; + case 'manual' %UI + storPath = uigetdir(); + case 'CI-Test' + storPath = fullfile(pwd,'CI_files',options.ParentFolder); +end +folderName = char(ID); + +%% Create Data-Directory +if isfolder(fullfile(storPath,folderName)) + error(['Folder ',folderName, ' exists - Plot was already published ']); +elseif mkdir(fullfile(storPath,folderName)) +else + error('Directory could not be created - check remote path and permissions'); +end +disp(['publishing of ', ID, ' started']); + +%% Create a Copy of the script and user functions(optional) +% script +PlotID.createFileCopy({[DataPaths.script,'.m']},folderName,storPath,ID, 'script'); + +% user functions +[fList,pList] = matlab.codetools.requiredFilesAndProducts(DataPaths.script); +if options.CopyUserFCN + fList = fList(~ismember(fList,[DataPaths.script,'.m'])); % rmv script from list + fList = fList(~contains(fList,'config.json')); % rmv config.json from list + fList = PlotID.removePltIdFiles(fList); % Do not copy files that are part of plot ID + if ~isempty(fList) + PlotID.createFileCopy(fList,folderName,storPath,ID,'userFcn'); + end +end + +%% Research data handeling +switch options.Method + case 'centralized' + DataFolderName = 'data'; + % check if data folder exists + if ~isfolder(fullfile(storPath,DataFolderName)) + mkdir(fullfile(storPath,DataFolderName)); + end + % to get relative Paths + currentPath = fullfile(storPath); + + %list all files + fList = dir(fullfile(storPath,DataFolderName, '**\*.*')); + %get list of files and folders in any subfolder + fList = fList(~[fList.isdir]); %remove folders from list + fList = struct2table(fList); + + % Check if the new plot is based on the original data-set + % copy the data(once) + for i=1:numel(DataPaths.rdata) + % check if identical file exists (status = 1) + [~, idx] = PlotID.fileCompare(DataPaths.rdata{i},fList); + % create Linked HDF5 files for identical files + if any(idx) + fList.path = fullfile(fList.folder,fList.name); + sourcePath = fList{idx,'path'}; + relativeSourcePath = strrep(sourcePath,currentPath,''); + + if contains(sourcePath,{'.h5','.hdf5'}) % Linking only for HDF5 + PlotID.createLinkedHDF5(relativeSourcePath{1,1},storPath,ID); + end + else % no identical file exists + %Copy the file in data and create the links (if hdf5) + [dataPath] = PlotID.createFileCopy(DataPaths.rdata{i},'data',storPath,ID,'dataCentral'); + relativeDataPath = strrep(dataPath,currentPath,''); + %WIP + if contains(DataPaths.rdata{i},{'.h5','.hdf5'}) % Linking only for HDF5 + % and create also linked files in the plot folder + PlotID.createLinkedHDF5(relativeDataPath,storPath,ID); + end %if + end %if + end %for + clear DataFolderName + case 'individual' + % Create a copy of the research data + PlotID.createFileCopy(DataPaths.rdata,folderName,storPath,ID, 'data'); +end +%% Write Config File + +if ~configError %config File must exist + % copy config file + configPath = PlotID.createFileCopy('config.json',folderName,... + storPath,ID, 'data'); +else + configPath = fullpath(storPath,folderName, 'config.json'); + config = struct(); + if ispc + config.author = getenv('USERNAME'); + end +end +% add further Metadata +config.ProjectID = ID; +config.CreationDate = datestr(now); +config.MatlabVersion = version; +config.ToolboxVersions = pList; + +%write config +fid = fopen(char(configPath),'w'); +txt = jsonencode(config,'PrettyPrint',true); +fprintf(fid,txt); +fclose(fid); + + +%% Export the Plot +try + PlotName = [ID,'_plot']; % plotname + RemotePath = fullfile(storPath ,folderName, PlotName); + % Matlab figure + savefig(figure,RemotePath); + % the png should only be a preview + exportgraphics(figure,[RemotePath,'.png'],'Resolution',300); +catch + warning('Plot export was not successful') +end + +disp(['publishing of ', ID , ' done']); + +% CSV EXport +if options.CSV + T = table(); + T.research_Data = DataPaths.rdata'; T.PlotID(:) = {ID}; + T.Script_Name(:) = {[DataPaths.script,'.m']}; + T.Storage_Location(:) = {storPath}; + T.Date(:) = {datestr(now)}; + T = movevars(T,'PlotID','before',1); + writetable(T, fullfile(storPath, 'overview_table.csv'),'WriteMode','append'); +end + +end %function + +function tf = mustBeFigure(h) +%checks if input is a figure object + tf = strcmp(get(h, 'type'), 'figure') & isa(h, 'matlab.ui.Figure'); +end diff --git a/+PlotID/TagPlot.m b/+PlotID/TagPlot.m new file mode 100644 index 0000000000000000000000000000000000000000..bcf84b912018f4da1a084a530a7e19bc87d31819 --- /dev/null +++ b/+PlotID/TagPlot.m @@ -0,0 +1,91 @@ +function [figs, IDs] = TagPlot(figs, options) +%TagPlot adds IDs to figures +% The ID is placed visual on the figure window and as Tag (property of figure) +% TagPlot can tag multiple figures at once. +% If a single Plot is taged IDs is a char, otherwise it is a cell array of +% chars +% options.ProjectID is the project number (string or char), if empty the ID from the +% config file is used +% +% The ID is placed on the 'east' per default, if you want it somwhere +% else, use the 'Location' option. 'north','east','south','west' are +% predefined, otherwise use the 'custom' option and provide the desired +% 'Position' (relative to your x- and y-axis limits) +arguments + figs (1,:) {mustBeFigure} + options.ProjectID (1,:) {mustBeText}= '' + options.Fontsize (1,1) {mustBeInteger} = 8 + options.Location (1,:) {mustBeText} = 'east' + options.Position (1,2) {mustBeVector} = [1,0.4] % default for east + options.Rotation (1,1) {mustBeInteger} = 0 +end + +if isempty(options.ProjectID) + try + txt = fileread('config.json'); + config = jsondecode(txt); + catch + config =struct; + config.ProjectID = ''; + warning("No ProjectID was definded and no config.json could be found"); + + end + + if ~isempty(config.ProjectID) + options.ProjectID = config.ProjectID; + else + warning('no project options.ProjectID defined') + end +end + +switch options.Location + case 'north' + options.Position = [0.4,0.95]; + options.Rotation = 0; + case 'east' + options.Position = [1,0.4]; + options.Rotation = 90; + case 'south' + options.Position = [0.4,.02]; + options.Rotation = 0; + case 'west' + options.Position = [.05,0.4]; + options.Rotation = 90; + case 'custom' + % Check if Position is valid + if ~all(0 <= options.Position & options.Position <= 1) + options.Position = [1,0.4]; + warning('options.Position is not valid, TagPlot uses default values instead'); + end + otherwise % set default position + warning([options.Location, ' is not a defined location, TagPlot uses location east instead']); + options.Location = 'east'; options.Position = [1,0.4]; +end + +IDs = cell(numel(figs),1); + +for n = 1:numel(figs) + IDs{n} = PlotID.CreateID; % Create ID + IDs{n} = [options.ProjectID,'-',IDs{n}]; % add options.ProjectID + axes = get(figs(n),'CurrentAxes'); % Axes object for text annotation + % Limits for relative Positioning + ylim =get(axes,'YLim'); + xlim =get(axes,'XLim'); + %ID + position = [options.Position(1)*xlim(2), options.Position(2)*ylim(2)]; + text(axes,position(1),position(2), IDs{n},'Fontsize',options.Fontsize,... + 'Rotation',options.Rotation, 'VerticalAlignment','bottom','Color',... + 0.65*[1 1 1],'BackgroundColor','w'); + set(figs(n),'Tag', IDs{n}); +end + +if numel(figs) == 1 + % use char instead of a cell arry for a single ID + IDs = IDs{1}; +end + +end + +function tf = mustBeFigure(h) %checks if input is a figure object + tf = strcmp(get(h, 'type'), 'figure') & isa(h, 'matlab.ui.Figure'); +end diff --git a/+PlotID/createFileCopy.m b/+PlotID/createFileCopy.m new file mode 100644 index 0000000000000000000000000000000000000000..061f10903343ccde4ea33ea666f93b19f414cb79 --- /dev/null +++ b/+PlotID/createFileCopy.m @@ -0,0 +1,79 @@ +function [storagePaths] = createFileCopy(filePaths,folderName,storPath,ID,type) +% Creates a copy of the files (can be used for multiple paths in a cell array) +% folderName is the name of the exporting folder +% returns the storage paths were files were stored + +if ~iscell(filePaths) + %fixes Issue if Filepath is a char and not a cell array + filePaths = {filePaths}; +end + +try + storagePaths = cell(numel(filePaths,1)); + for i = 1:numel(filePaths) + FileNameAndLocation = filePaths{i}; + [~,name,ext] = fileparts(filePaths{i}); % get the extension + + switch type + case 'data' + newfile = sprintf([name,ext]); %keep original name + %old behaviour + %sufix = '_data'; + %newfile = sprintf([ID, sufix, '_' , num2str(i) ,ext]); + case 'dataCentral' + %keep original name + newfile = sprintf([name,ext]); + case 'script' + sufix = '_script'; + newfile = sprintf([ID, sufix ,ext]); + case 'userFcn' + %keep original name + newfile = sprintf([name,ext]); + otherwise + error([type,' is not a valid type for createFileCopy']) + end %switch + + RemotePath = fullfile(storPath,folderName, newfile); + +% Check if remote file already exists + count = 0; + while isfile(RemotePath) && ismember(type,{'data','dataCentral'}) + % Add a Sufix number to new file name + % TODO add more inteligent way then a simple sufix + count = count + 1; + [~,name,ext] = fileparts(RemotePath); + if count < 2 + RemotePath = fullfile(storPath,folderName,... + [name,'_',num2str(count),ext]); + else + RemotePath = fullfile(storPath,folderName,... + [name(1:end-length(num2str(count))),num2str(count),ext]); + end + [~, name, ~] = fileparts(RemotePath); + + + msg = ['Filename ',name,... + ' already exists in the data folder' newline,... + ' PlotID will add an suffix if you continue.' newline,... + ' This can cause serious confusions.']; + warning(msg); + m = input('Do you want to continue, Y/N [Y]:','s'); + + if ismember(m,{'N','n'}) + errorMSG = ['Filename already exists in data folder.' newline,... + ' Rename the File and restart PlotID.']; + error(); + end + end + copyfile(FileNameAndLocation,RemotePath); + storagePaths{i} = RemotePath; + end + disp([type, ' sucessfully published']); +catch + warning([type,' export was not sucessful']) + if exist('errorMSG') + error(errorMSG); + end +end %try +end %function + diff --git a/+PlotID/createLinkedHDF5.m b/+PlotID/createLinkedHDF5.m new file mode 100644 index 0000000000000000000000000000000000000000..08415500f0a169dff3c293bd89afb68f9ee25fb4 --- /dev/null +++ b/+PlotID/createLinkedHDF5.m @@ -0,0 +1,32 @@ +function [status] = createLinkedHDF5(SourceFile,TargetPath,ID) +%createLinkedHDF5 creates a HDF file that references the Sourcefile +% TargetPath is the storage location, ID the foldername +% Status returns true if the function was sucessfull + +plist_id = 'H5P_DEFAULT'; + +%catches error of wrong file type +if iscell(SourceFile) + SourceFile = SourceFile{1}; +end + +[~,filename,ext] = fileparts(SourceFile); + +% try + %old + %fid = H5F.create(fullfile(TargetPath,ID,[ID,'_data.h5'])); + + fid = H5F.create(fullfile(TargetPath,ID,[filename,ext])); + %create External Link to Sourcefile in the Group linkToExternal + H5L.create_external(['..\',SourceFile],'/',fid, SourceFile ,plist_id,plist_id); + %H5L.create_external(['..\data\',SourceFile],'/',fid, SourceFile ,plist_id,plist_id); %original + H5F.close(fid); + disp([fullfile(TargetPath,ID,[filename,ext]),' created']); + status = 1; +% catch +% warning('No linked HDF file was created'); +% status = 0; +% end + +end + diff --git a/+PlotID/fileCompare.m b/+PlotID/fileCompare.m new file mode 100644 index 0000000000000000000000000000000000000000..8317328dc5e5d0bd815171898c99ad281ef4d131 --- /dev/null +++ b/+PlotID/fileCompare.m @@ -0,0 +1,56 @@ +function [status, id] = fileCompare(filename,fileList) +%% fileCompare checks if file1 is (binary) identical to a file in filelist +% it returns a status and the id of the identical file +% the functions uses the java libraries from matlab for windows systems +% or the unix function diff +% because of performance issues with windows system calls in matlab + +if isempty(fileList) + % no comparison necessary + status =false; + id = 0; + return +end + +[~,~,ext1] = fileparts(filename); +id = zeros(height(fileList),1); + +for i=1:height(fileList) + [~,~,ext2] = fileparts(fileList{i,'name'}); + + if ~isequal(ext1,ext2) + %warning('File extension are not identical'); + status = false; + continue + end + + filepath = fullfile(fileList{i,'folder'},fileList{i,'name'}); + + %% comparison with java function + % TODO test paths with spaces + status = comparejava(filename, char(filepath)); + + if status == 1 + id(i) = 1; + id =logical(id); %bugfix + return; + else + % Status can also be any other number e.g. 2 + id(i) = 0; + end + + id =logical(id); %bugfix +end + +end + +function is_equal = comparejava(file1, file2) +%% Utilizing java functionalities for inter-os comparison of binary files +% file1 and file2 should be full filepaths + +file1 = javaObject('java.io.File',file1); +file2 = javaObject('java.io.File',file2); +is_equal = javaMethod('contentEquals','org.apache.commons.io.FileUtils',file1,file2); + +is_equal = double(is_equal); % Conversion for compatibility reasons +end diff --git a/fcn_help/FriendlyID.m b/+PlotID/friendlyID.m similarity index 91% rename from fcn_help/FriendlyID.m rename to +PlotID/friendlyID.m index e18834b71304e4fa575cef824a78c1e195d16c8f..cdac106b417794cf287ea9cab706741a4bb25b9c 100644 --- a/fcn_help/FriendlyID.m +++ b/+PlotID/friendlyID.m @@ -1,4 +1,4 @@ -function [IDf,PrjID, Str] = FriendlyID(ID) +function [IDf,PrjID, Str] = friendlyID(ID) %FriendlyID Changes the Hex Number to a human friendly datetime and dateStr % IDf ID as DateTime Object, PrjID returns the ProjectID, Str returns the % timestamp as String diff --git a/+PlotID/removePltIdFiles.m b/+PlotID/removePltIdFiles.m new file mode 100644 index 0000000000000000000000000000000000000000..98e83b340440da6ca0ca9d2f9b5e843d30dd928d --- /dev/null +++ b/+PlotID/removePltIdFiles.m @@ -0,0 +1,19 @@ +function [fListClean] = removePltIdFiles(fList) +%removePltIdFiles removes functions that are part of PlotID out of flist +% Detailed explanation goes here +%addpath('..\fcn_core'); + +[~,names,ext] = fileparts(fList); +names = strcat(names, ext); % add ext for comparison + +% Get a list of all .m files that are part of Plot id +PltID_flist = struct2table(dir('+PlotID')); %get list of files +[~,~,PltID_flist.ext(:)] = fileparts(PltID_flist.name(:)); % add ext column + +PltID_flist = PltID_flist(strcmp(PltID_flist.ext,'.m'),:); + +% Comparison and filter +fListClean = fList(~ismember(names,PltID_flist.name)); + +end + diff --git a/+PlotID/replaceLinkedHDF5.m b/+PlotID/replaceLinkedHDF5.m new file mode 100644 index 0000000000000000000000000000000000000000..e84e49f6e3aeed36d24f59b50288984fbe4d3998 --- /dev/null +++ b/+PlotID/replaceLinkedHDF5.m @@ -0,0 +1,61 @@ +function replaceLinkedHDF5(linkedFilepath) +% replaceLinkedHDF5 replaces the HDF5, that contains only a link to another +% HDF5 File with a file containing the actual Data, thus turning it into an +% independent data file. It essentially turns the plotID option +% 'centralized' into 'individual'. +% Filepath is the location of the File that contains the link. The +% filepath to the linked data has to be present in the file itself. + + + +%% Check whether the only Objects present are links +h5inf = h5info(linkedFilepath); + +% default behaviour dont overwrite +overwrite = false; + +if isempty([h5inf.Groups, h5inf.Datasets, h5inf.Datatypes, h5inf.Attributes]) && ... + all(size(h5inf.Links) == [1 1]) + % There are no other known objects present and + % there is only one linked file + overwrite = true; +end + +%% Create Copies of files +for i = 1:size(h5inf.Links, 2) + % This is the file with full data + linkTargetPath = h5inf.Links(i).Value{1,1}; + % This is the empty file + % TODO filename decision + linkSourcePath = strcat(linkedFilepath,'_',num2str(i)); + + % Create an absolute path from the relative linking path + absoluteTP = regexp(linkSourcePath, filesep, 'split'); + TargetPathSplit = regexp(linkTargetPath, filesep, 'split'); + % How many levels do we need to go up in the relative path + % This is merely counting the occurances of '..' within the path, but + % should not cause issues if the realtive path was created by the + % linking process + levelsUp = sum(contains(TargetPathSplit,'..')); + absoluteTP = absoluteTP(1:(end-(1+levelsUp)) ); %Remove filename + levels + linkTargetPath = strjoin([absoluteTP, TargetPathSplit(~contains(TargetPathSplit,'..'))],filesep); + + if ~isfile(linkTargetPath) + warning(strcat("Link destination """,linkTargetPath,""" does not exist or relative path does not fit, no copy created")) + end + + if overwrite % can only be for i=1, and size = 1 + linkSourcePath = linkedFilepath; + end + disp('Copying file from ', linkSourcePath, ' to ', linkTargetPath) + copyfile(linkTargetPath,linkSourcePath); + +end + + + +end + + + + diff --git a/.gitignore b/.gitignore index b0afd4835bf581456c0cf7f6dc7bcd8139cddf34..708bea6b2032d70640c74a197d183a115ff0bdda 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# config file changes +config.json + # Windows default autosave extension *.asv @@ -26,7 +29,18 @@ codegen/ # Personal test files test*.m +test123_data.h5 + + +# files that are created in example.m +testdata_2.h5 +testdata2.h5 +test_data.mat export/* # Octave session info octave-workspace +test_data.mat + +#logs +log.txt diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..7901609ef3b830a97e409b23c51890ace703b737 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,10 @@ +#Based on STFS Workshop +stages: + - Run +Test Code: + stage: Run + tags: + - matlab + script: + - ./CI_files/runtest.ps1 + - cat log2.txt diff --git a/CI_files/default_test.m b/CI_files/default_test.m new file mode 100644 index 0000000000000000000000000000000000000000..505c360ea8fde4390434772eeece053120d15f0e --- /dev/null +++ b/CI_files/default_test.m @@ -0,0 +1,73 @@ +function [result] = default_test() +%UNTITLED2 This is a simple test if Plot ID works for the default settings +% Detailed explanation goes here + +clear; clc; close all; + +% clean up, if previous run failed +try + delete CI_files/export/* CI_files/*.mat CI_files/*.h5 + rmdir('CI_files/export','s'); +end + +ProjectID = 'Test01'; +%% Data +% some random data +x = linspace(0,7); +y = rand(1,100)+2; +dataset1 = 'test_data.mat'; +save('CI_files/test_data.mat','x','y'); +% some data as .h5 +x1 = linspace(0,2*pi); +y1 = sin(x1)+2; + +% define file path & name +fpath = "CI_files/testdata_2.h5"; +dataset2 = 'testdata_2.h5'; + +% create hdf5 file and dataset > write data to hdf5 file / dataset +h5create(fpath, "/x1", size(x1), "Datatype", class(x1)) +h5create(fpath, "/y1", size(y1), "Datatype", class(y1)) +h5write(fpath, "/x1", x1) +h5write(fpath, "/y1", y1) + +%% Plotting + +fig(1) =figure('visible','off'); +plot(x,y,'-k'); +hold on +plot(x1,y1,'-r'); + +%% Tag the plot +try + [figs, ID] = PlotID.TagPlot(fig,'ProjectID', ProjectID); + + %% call a dummy function + a=1; + a = example_fcn(a); + + %% publishing + + % The functions needs the file location, the location of the data and the + % figure + path.script = mfilename('fullpath'); % filename of the m.script + + % file name of the data + path.rdata = {dataset1,dataset2} ; % don't forget the extension + + PlotID.Publish(path, ID, figs, 'Location', 'CI-Test') + result = true; + + % clean up + delete CI_files/export/* CI_files/*.mat CI_files/*.h5 + rmdir('CI_files/export','s'); + + clear; clc; + +catch + result = false; + warning('simple_test failed'); +end + +end + diff --git a/CI_files/runner_test.m b/CI_files/runner_test.m new file mode 100644 index 0000000000000000000000000000000000000000..496afd8558f128e08826f25e62472d6b3cb15f62 --- /dev/null +++ b/CI_files/runner_test.m @@ -0,0 +1,7 @@ +function [result] = runner_test() +%RUNNER_TEST testing function to test, if the runner is set up properly +result = true; + +exit(result); +end + diff --git a/CI_files/runtest.ps1 b/CI_files/runtest.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..3f4764e18b95a71beb523253c20ec44a577db389 --- /dev/null +++ b/CI_files/runtest.ps1 @@ -0,0 +1,15 @@ +# runtests.ps1 +$LOGFILE="log2.txt" + +$arguments = "-nosplash","-minimize","-wait", "-logfile $LOGFILE","-r","runner_test" +$Returnvalue = & 'C:\Program Files\MATLAB\R2021b\bin\matlab.exe' $arguments + +CODE=$? + +cat $Returnvalue; +cat $LOGFILE + +# It doesnt look as if matlab gives an exitcode upon successfully stopping, and otherwise it just doesnt stop. +# Would need a catch / try in the test script - if it is called correctly. +exit Code +#exit $LASTEXITCODE diff --git a/CI_files/runtest.sh b/CI_files/runtest.sh new file mode 100644 index 0000000000000000000000000000000000000000..1cf54b3d54028e97a117b3a9f0ebbd7ae1222f8b --- /dev/null +++ b/CI_files/runtest.sh @@ -0,0 +1,9 @@ +# runtests.sh +LOGFILE=log2.txt + +"C:\Program Files\MATLAB\R2021b\bin\matlab.exe" -nodesktop -nosplash -minimize -wait -logfile "$LOGFILE" -r 'runtests'; +CODE=$? + +cat "$LOGFILE" + +exit $CODE diff --git a/CI_files/test.sh b/CI_files/test.sh new file mode 100644 index 0000000000000000000000000000000000000000..72bc42c3ce832ba8c9c12c61f5f72af7face5975 --- /dev/null +++ b/CI_files/test.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +#LOGFILE=log.txt + +#"C:\Program Files\MATLAB\R2021b\bin\matlab.exe" -nodesktop -nosplash -nodesktop -logfile "$LOGFILE" -r "runner_test;exit(ans);" +"C:\Program Files\MATLAB\R2021b\bin\matlab.exe" -nodesktop -nosplash -nodesktop -r "runner_test;exit(ans);" + +exitstatus=$? + +# cat $LOGFILE + +if [[ $exitstatus -eq '0' ]] +then + echo "matlab succed. Exit status: $exitstatus" + exit $exitstatus +else + echo "matlab failed. Exit status: $exitstatus" + exit $exitstatus +fi + diff --git a/PlotID_Demo.m b/PlotID_Demo.m new file mode 100644 index 0000000000000000000000000000000000000000..766ad17cfb60feb31820c5382e61b6f3cc7d413b --- /dev/null +++ b/PlotID_Demo.m @@ -0,0 +1,74 @@ +%% Example Script +% This Script is meant to demonstrate the capabilities of the PlotID tool. + +%% Clear Environment +clear; clc; close all; +addpath('CI_files'); % Test scripts +try + delete testdata2.h5; +end + +%% Set ProjectID +% ProjectID can also be set in the config file +% Leave empty for using the ID from the config file +ProjectID = 'FST01'; + +%% Data +% Creating Random Data to use as data-file +x = linspace(0,7); +y = rand(1,100)+2; +dataset1 = 'test_data.mat'; +% use absolute paths for good practise +dataset1 = fullfile(pwd, dataset1); +save(dataset1,'x','y'); + +% some data as .h5 +x1 = linspace(0,2*pi); +y1 = sin(x1)+.5*sin(2*x1)+2; + +% define file path & name + +fpath = fullfile(pwd,"./testdata2.h5"); +dataset2 = fullfile(pwd,'testdata2.h5'); + +% create hdf5 file and dataset > write data to hdf5 file / dataset +h5create(fpath, "/x1", size(x1), "Datatype", class(x1)) +h5create(fpath, "/y1", size(y1), "Datatype", class(y1)) +h5write(fpath, "/x1", x1) +h5write(fpath, "/y1", y1) + +%% Plotting +% This is still part of a normal script to produce plots. +% Make sure to save each figure in a variable to pass to PlotID-functions. +fig(1) = figure; +set(gcf,'Units','centimeters','PaperUnits','centimeters','PaperSize',[9 7],... +'Position',[5 5 9 7]); +plot(x,y,'Color',0.5*[1 1 1]); +box off; hold on; +plot(x1,y1,'-k'); +set(gca, 'TickDir', 'out', 'YLim', [0,4],'YTick',[0:1:4],'XLim',[0,6]); +%fstplt.setfiguresize('1/2ppt16:9'); +%fstplt.pimpplot; + +%% Example 1: single plot based on two data-sets + +%% Tag the plot +% PlotID Implementation starts here. +% TagPlot adds a visible ID to the figure(s) and to the figures property +% 'Tag' +[fig, ID] = PlotID.TagPlot(fig, 'ProjectID', ProjectID); + +%% Publishing +% Second part of plotID +% The functions needs the file location, the location of the data and the +% figure and can take several options. +path.script = mfilename('fullpath'); % filename of the m.script +% file names of the datasets +path.rdata = {dataset1,dataset2} ; % don't forget the extension + +PlotID.Publish(path, ID, fig(1), 'Location', 'local' ,'Method','individual') + + + +%% End + diff --git a/README.md b/README.md index 7ee43f258a5fcbe1d062e490ed88da91f88ee267..a9e827036777707cfd3e4722c16b3dd8fddf3695 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,111 @@ -# Plot_Identifier +**PlotID** + +`PlotID` is a toolkit that labels your plots and figures and copies all associated data (research data, plotting script, user-functions and a copy of the figure) to the desired location. + +This version of `PlotID` is build for `MATLAB`. + +`PlotID` provides two core functions **TagPlot** and **Publish** which are described in detail below. + +Feel free to give feedback and feature requests or to take part in the development process. + + [TOC] + +# Quick User Guide +`PlotID` works in two Steps: + +1. tagging the plot +`[figs, IDs] = plotId.TagPlot(figs, options)` + + +2. publishing the plot and the associated data +`plotID.Publish(DataPaths, ID, figure, options)` + +`DataPaths.script = mfilename('fullpath');` contains the filename of the m.script + +`DataPaths.rdata = {dataset1, dataset2};` file names of the datasets (they most be at the path) + +# PlotID.TagPlot() +`[figs, IDs] = TagPlot(figs, options)` +**TagPlot** adds IDs to figures +The ID is placed visual on the figure window and as Tag (property of the figure) +**TagPlot** can tag multiple figures at once. If a single Plot is tagged, IDs is a char, otherwise it is a cell array of chars . + +<details><summary>detailed description</summary> + +_options_ \ +you find the options for TagPlot below. The data type is in curled braces and the default value follows the equal sign. + +- ProjectID {mustBeText}= '' +- Fontsize {mustBeInteger} = 8 +- Location {mustBeText} = 'east' +- Position {mustBeVector} = [1,0.4] +- Rotation {mustBeInteger} = 0 + +`options.ProjectID` is the project number (string or char), if empty the ID from the config file is used +The ID is placed on the 'east' of the figure per default, if you want it somwhere else, use the `'Location'` option. 'north', 'east', 'south', 'west' are predefined, otherwise use the `'custom'` option and provide the desired 'Position' as a vector relative to your x- and y-axis limits `[relX, relY]` . +</details> + +## CreateID() +`function [ID] = CreateID(method)` \ +CreateID Creates an identifier (char). It creates an (sometimes unique) identifier based on the selected method, if no method is selected method 1 will be the default method. + +1. **UNIX Time in seconds as HEX** \ +This is used by default due to its simplicity and it is human readable. +2. **random UUID from Java 128 bit.**\ +Static factory to retrieve a type 4 (pseudo randomly generated) UUID. The UUID is generated using a cryptographically strong pseudo random number generator. + +## friendlyID() +`[IDf,PrjID, Str] = friendlyID(ID)` \ +FriendlyID Changes the Hex Number to a human friendly *datetime* and *dateStr*. `IDf` returns the ID as DateTime Object, `PrjID` returns the ProjectID, `Str` returns the timestamp as String. \ +**This only works with ID method 1.** + + +# PlotID.Publish() +`Publish(DataPaths, ID, figure, options)` \ +Publishes saves plot, data and measuring script +Location sets the storage location. 'local' sets the storage location to the current folder (an export folder will be created), 'server' is a remote path, that is defined in the config file. Two Methods are implemented 'individual' stores the data for each plot while 'centralized' uses a data folder and uses reference links to the original data (hdf5 only). ParentFolder is the folder Name where the exported data is stored if a path is used, PlotId will use this path a storagePath + +<details><summary>detailed description</summary> + +_options_ \ +you find the options for publish below. The data is in curled braces and the default value follows the equal sign. + +- Location {mustBeMember(options.Location ,{'local','server','manual','CI-Test'})} = 'local' \ +'local' creates the folder in your current working path, 'server' on a remote path that needs to be specified in the config file. 'manual' opens an UI Window where the path must be choosen, 'CI-Test' is for CI testing only. +- Method {mustBeMember(options.Method ,{'individual','centraliced'})} = 'individual'\ +'individual' each folder contains the assigned data files. (recommended for small file sizes and for sharing). +'centralized' one central data folder is used for saving the research data files, the subfolders contain linked hdf5-files (if hdf5 is used). This is recommended, if many plots are made from the same data set. Attention, the linked HDF5 will not work when a subfolder was moved or the data folder was deleted. +- ParentFolder {mustBeText} = 'export'\ +An individual folden name can be set with this option. +- CopyUserFCN {mustBeNumericOrLogical} = true \ +All user functions that are used by script that calls publish will be exported per default as well. Set this to false (not recommended) to prevent this. +- CSV {mustBeNumericOrLogical} = false \ +An overview of all published plots and the associated data will be stored in a csv file. +</details> + +## createFileCopy () +`[] = createFileCopy(filePaths,folderName,storPath,ID,type)`\ +Creates a copy of the files (can be used for multiple paths in a cell array). folderName is the name of the exporting folder +## createLinkedHDF () +`[status] = createLinkedHDF5(SourceFile,TargetPath,ID)` \ +createLinkedHDF5 creates a HDF file that references the Sourcefile. TargetPath is the storage location, ID the foldername, Status returns true if the function was sucessfull. +## fileCompare() +`[status, id] = fileCompare(filename,fileList)` \ +fileCompare checks if file1 is (binary) identical to a file in filelist, it returns a sttus and the id of the identical file. The function uses the windows system function fc or the unix function diff (not tested). +## removePltIdFiles() +`[fListClean] = removePltIdFiles(fList)` \ +removePltIdFiles removes functions that are part of PlotID out of flist. + +# How to use the .config file +The config file is a JSON-File. At the moment two options can be set in the config file, the project ID and the remote Path. + +``` +{ + "ProjectID": "your ProjectID", + "ServerPath": "\\\\Server\\Folder1\\Subfolder" +} +``` + +# Known Issues +# FAQs -Organisation und Beispiele aus den beiden Pilotprojekten zu Plotidentifiern diff --git a/copiedFile.h5 b/copiedFile.h5 deleted file mode 100644 index 4ddca81c276e204d2476b044f5a2fda057105080..0000000000000000000000000000000000000000 Binary files a/copiedFile.h5 and /dev/null differ diff --git a/example-config.json b/example-config.json new file mode 100644 index 0000000000000000000000000000000000000000..9df3f40d142af73340a15197d7787e9485809211 --- /dev/null +++ b/example-config.json @@ -0,0 +1,5 @@ +{ + "Author": "Example Author" + "ProjectID": "AB01", + "ServerPath": "\\\\Server\\path\\folder" +} \ No newline at end of file diff --git a/example.m b/example.m index e25950de4b3bf0e7bb0edb41c76791a8d3992e30..04aa8c11aebc102d0810c7e23681ad1ba473d943 100644 --- a/example.m +++ b/example.m @@ -1,28 +1,37 @@ -% test skript +%% Example Script +% This Script is meant to demonstrate the capabilities of the PlotID tool. + +%% Clear Environment clear; clc; close all; -addpath('fcn_core','fcn_help'); try - delete testdata_2.h5; + delete testdata2.h5; end -ProjectID = 'JL01'; +%% Set ProjectID +% ProjectID can also be set in the config file + +% Leave empty for using the ID from the config file +ProjectID = 'Example'; + %% Data -% some random data +% only necessary for this example -x = linspace(0,7); -y = rand(1,100)+2; +% Creating Random Data to use as data-file +x = linspace(0,7); y = rand(1,100)+2; dataset1 = 'test_data.mat'; -save('test_data.mat','x','y'); +% Use absolute paths for good practise +dataset1 = fullfile(pwd,dataset1); +save(dataset1,'x','y'); -% some data as .h5 -x1 = linspace(0,2*pi); -y1 = sin(x1)+2; +% some data for the .h5 file +x1 = linspace(0,2*pi); y1 = sin(x1)+2; -% define file path & name -fpath = "./testdata_2.h5"; -dataset2 = 'testdata_2.h5'; +% define filepath & name +dataset2 = 'testdata2.h5'; +dataset2 = fullfile(pwd,dataset2); +fpath = dataset2; % create hdf5 file and dataset > write data to hdf5 file / dataset h5create(fpath, "/x1", size(x1), "Datatype", class(x1)) @@ -30,30 +39,79 @@ h5create(fpath, "/y1", size(y1), "Datatype", class(y1)) h5write(fpath, "/x1", x1) h5write(fpath, "/y1", y1) -%% Plotting +%% function calls +% Place for post-processing of the data, or additional related code. +% example_fcn is a dummy function to show the functionality +a = 1; a = example_fcn(a); +p = betacdf(0.5,1,1); % to test toolboxes -fig(1) =figure; +%% Plotting +% This is still part of a normal script to produce plots. +% Make sure to save each figure in a variable +% to pass it to PlotID-functions. +fig(1) = figure; plot(x,y,'-k'); -box off -set(gca, 'TickDir', 'out', 'YLim', [0,4]); - -hold on -%fig(2) =figure; +box off; hold on; plot(x1,y1,'-r'); set(gca, 'TickDir', 'out', 'YLim', [0,4]); -% Tag the plot -[figs, ID] = TagPlot(fig, ProjectID); +%% Example 1: single plot based on two data-sets + +%% Tag the plot +% PlotID Implementation starts here. +% TagPlot adds a visible ID to the figure(s) and to the figures property +% 'Tag' +[fig, ID] = PlotID.TagPlot(fig, 'ProjectID', ProjectID); -%% publishing +%% Publishing +% Second part of plotID +% The functions needs the file location of the script, the location of the +% data and the figure and can take several options (see readme). -% The functions needs the file location, the location of the data and the -% figure -path.script = mfilename('fullpath') % filename of the m.script +path.script = mfilename('fullpath'); % filename of the m.script +% file names of the datasets -% file name of the data (should be extended to handle arrays) +%(defined above:) dataset1 = 'test_data.mat'; dataset2 = 'testdata2.h5' path.rdata = {dataset1,dataset2} ; % don't forget the extension +%call publishing +PlotID.Publish(path, ID, fig(1), 'Location', 'local' ,'Method','individual') + +%% Example 2: multiple plots plot, all based on dataset2 (hdf5) +% for individual data-sets, use an appropriate array + +fig(2) = figure; +plot(x,y,'-b'); +box off; hold on; +plot(x1,y1,'--k'); +set(gca, 'TickDir', 'out', 'YLim', [0,4]); + +% tag both plots +[fig, IDs] = PlotID.TagPlot(fig,'ProjectID', ProjectID); + +% data locations +path.script = mfilename('fullpath'); % filename of the m.script +% file names of the datasets +path.rdata = {dataset2} ; % don't forget the extension + +% publsihing via a for-loop +for i=1: numel(fig) + PlotID.Publish(path, IDs{i}, fig(i), 'Location', 'local',... + 'Method','individual'); +end + +%% Second Plot with identical data to test centralized method +% A central data folder is used for saving the research data files, the +% subfolders contain linked hdf5-files (if hdf5 is used). This is +% recommended, if many plots are made from the same data set. Attention, +% the linked HDF5 will not work when a subfolder was moved or the data +% folder was deleted + +fig2 =figure; +plot(x,y,'-k'); +hold on +plot(x1,y1,'-r'); -Publish(path, ID, figs, 'Location', 'local','Method','individual') +[fig2, ID] = PlotID.TagPlot(fig2,'ProjectID', ProjectID); +PlotID.Publish(path, ID, fig2, 'Location', 'local','Method','centralized') diff --git a/example_fcn.m b/example_fcn.m new file mode 100644 index 0000000000000000000000000000000000000000..418f15ca734ac8423bdda5a6ead2c32af6820e22 --- /dev/null +++ b/example_fcn.m @@ -0,0 +1,6 @@ +function [outputArg1] = example_fcn(inputArg1) +%TEST_2 just a dummy function +outputArg1 = inputArg1; + +end + diff --git a/fcn_core/Publish.m b/fcn_core/Publish.m deleted file mode 100644 index eb0c428aebddba8a2311df6acb31d39c4af097ce..0000000000000000000000000000000000000000 --- a/fcn_core/Publish.m +++ /dev/null @@ -1,109 +0,0 @@ -function Publish(DataPaths, ID, figures, options) -%Publishes Saves Plot, Data and Measuring script -% Detailed explanation goes here -% Location sets the storage location. local is in the current folder (an -% export folder will be created), server is a remote Path -% two Methods are implemented individual stores the data for each plot -% while centralized uses a data folder and uses reference links to the -% original data - -arguments - DataPaths - ID (1,:) {mustBeNonzeroLengthText} % ID must be provided - figures (1,:) {mustBeFigure} % Checks if Figures are figures - options.Location {mustBeMember(options.Location ,['local','server'])} = 'local' % storage path - options.Method {mustBeMember(options.Method ,['individual','centraliced'])} = 'individual' -end - -switch options.Location - case 'local' - storPath = fullfile(pwd,'export'); - case 'server' - storPath = '\\FST-220\Jans-50GB-SMB\Lemmer'; -end -folderName = char(ID); - -%% Create Data-Directory -addpath(storPath); -if mkdir(fullfile(storPath,folderName)) -else - error('Directory could not be created - check remote path and permissions'); -end - -disp('Publishing started'); - -%% Create a Copy of the script - -try - %FileNameAndLocation=['C:\Users\Lemmer\Documents\GitLab\plot_identifier\MATLAB\test']; - FileNameAndLocation= DataPaths.script; - newbackup=sprintf([ID,'_script.m']); - currentfile=strcat(FileNameAndLocation, '.m'); - % Write a Copy of the Plotting Script - RemotePath = fullfile(storPath,folderName, newbackup); - copyfile(currentfile,RemotePath); - disp('Script sucessfully published'); -catch - warning('Script export was not sucessful') -end -%% Research data handeling -switch options.Method - case 'centraliced' % Work in progresss!!! - %check if data folder exists - if ~isfolder(fullfile(storPath,'data')) - mkdir(fullfile(storPath,'data')); - end - % Check if the new plot is based on the original data-set - % copy the data(once) - createFileCopy(DataPaths.rdata,'data',storPath,ID); - % create a linked (soft) copy - - - - case 'individual' - % Create a copy of the research data - createFileCopy(DataPaths.rdata,folderName,storPath,ID); -end -%% Export the Plots -%try - for i=1:numel(figures) - fig = figures(i); - PlotName = [ID,'_plot',num2str(i)]; % Der Plotnamen - RemotePath = fullfile(storPath ,folderName, PlotName); - % Matlab figure - savefig(fig,RemotePath); - exportgraphics(fig,[RemotePath,'.png'],'Resolution',300); - disp([num2str(i),' of ',num2str(numel(figures)),' figures exported']); - end -%catch - %warning('Plot export was not sucessful') -%end - -disp(['Publishing of ', ID , ' done']); - -end %function - -function [] = createFileCopy(filePaths,folderName,storPath,ID) -% Creates a copy of the files (can used for multiple paths in a cell array) -% folderName is the name of the exporting folder - disp('Start to copy research data ...'); - try - for i = 1:numel(filePaths) - FileNameAndLocation = filePaths{i}; - [~,~,ext] = fileparts(filePaths{i}); % get the extension - newbackup = sprintf([ID,'_data',ext]); - - % Write the copied file - RemotePath = fullfile(storPath,folderName, newbackup); - copyfile(FileNameAndLocation,RemotePath); - end - disp('Research data sucessfully published'); - catch - warning('Research data export was not sucessful') - end %try -end - -function tf = mustBeFigure(h) -%checks if input is a figure object - tf = strcmp(get(h, 'type'), 'figure') & isa(h, 'matlab.ui.Figure'); -end diff --git a/fcn_core/TagPlot.m b/fcn_core/TagPlot.m deleted file mode 100644 index 3c426266624a4a9dd64e93d970428c2fe08837a0..0000000000000000000000000000000000000000 --- a/fcn_core/TagPlot.m +++ /dev/null @@ -1,32 +0,0 @@ -function [figs, ID] = TagPlot(figs, prefix) -%TagPlot adds IDs to figures -% The ID is placed visual on the figure window and as Tag (Property of figure) -% TagPlot can tag multiple figures at once -arguments - figs (1,:) {mustBeFigure} - prefix (1,:) {mustBeText}= '' -end - -if isempty(prefix) - warning('no project prefix defined') -end - -for n = 1:numel(figs) - ID = CreateID; % Create ID - ID = [prefix,'-',ID]; % add Prefix - axes = get(figs(n),'CurrentAxes'); % Axes object for text annotation - % Limits for relative Positioning - ylim =get(axes,'YLim'); - xlim =get(axes,'XLim'); - %ID - text(axes,xlim(2),0.4*ylim(2), ID,'Fontsize',8,'Rotation',90,'VerticalAlignment','bottom',... - 'Color', 0.65*[1 1 1],'BackgroundColor','w'); - set(figs(n),'Tag', ID); - -end - -end - -function tf = mustBeFigure(h) %checks if input is a figure object - tf = strcmp(get(h, 'type'), 'figure') & isa(h, 'matlab.ui.Figure'); -end diff --git a/fcn_help/FileCompare.m b/fcn_help/FileCompare.m deleted file mode 100644 index aa5adfe3cbce82b1627760697908143b11894e53..0000000000000000000000000000000000000000 --- a/fcn_help/FileCompare.m +++ /dev/null @@ -1,29 +0,0 @@ -function [status] = fileCompare(filename1,filename2) -%UNTITLED2 Summary of this function goes here -% Detailed explanation goes here - -[~,~,ext1] = fileparts(filename1); -[~,~,ext2] = fileparts(filename2); - -if ~isequal(ext1,ext2) - warning('File extension are not identical'); - status = false; - return -end - -if ispc - filename2 = 'export\JL01-60DB3572\JL01-60DB3572_data.h5'; - filename1 = 'export\JL01-60DB3576\JL01-60DB3576_data.h5'; - [status,result] = system(['fc ' filename1 ' ' filename2]); - -elseif isunix %untested! - filename2 = 'export/JL01-60DB3572/JL01-60DB3572_data.h5'; - filename1 = 'export/JL01-60DB3576/JL01-60DB3576_data.h5'; - [status,result] = system(['diff ' filename1 ' ' filename2]); -else - warning('Platform not supported') -end - - -end - diff --git a/fcn_help/createLinkedHDF5.m b/fcn_help/createLinkedHDF5.m deleted file mode 100644 index 5b1e9716ccd5219e8756ae0af60b23c0d6ac282c..0000000000000000000000000000000000000000 --- a/fcn_help/createLinkedHDF5.m +++ /dev/null @@ -1,20 +0,0 @@ -function [status] = createLinkedHDF5(SourceFile,TargetPath,ID) -%UNTITLED4 Summary of this function goes here -% Detailed explanation goes here - -plist_id = 'H5P_DEFAULT'; - -% Work in Progress... Problem mit dem Pfad... - -try - fid = H5F.create(fullfile(TargetPath,[ID,'_data.h5'])); - %create External Link to Sourcefile in the Group linkToExternal - H5L.create_external(SourceFile,'/',fid,'linkToExternal',plist_id,plist_id); - H5F.close(fid); - status = 1; -catch - warning('No linked HDF file was created'); - status = 0; -end -end - diff --git a/test_data.mat b/test_data.mat deleted file mode 100644 index 06ca1639f8c3febb925fb5bbdc67b1953a894ab6..0000000000000000000000000000000000000000 Binary files a/test_data.mat and /dev/null differ diff --git a/testdata_2.h5 b/testdata_2.h5 deleted file mode 100644 index efc071f84eaf0f39c147f2e74f80c4666ff22467..0000000000000000000000000000000000000000 Binary files a/testdata_2.h5 and /dev/null differ