diff --git a/+PlotID/@config/config.m b/+PlotID/@config/config.m index db8d1110bc5fcfa28700d1178dcbe9035d6e18ef..efb0f4e3ca3a1e232375e2c04dad7cbfde6a2271 100644 --- a/+PlotID/@config/config.m +++ b/+PlotID/@config/config.m @@ -1,8 +1,7 @@ classdef config < handle - %CONFIG class handles methoths and attributes related to the config + %CONFIG class handles methods and attributes related to the config %file - % Detailed explanation goes here - % handle class used for dynamicy property updates + %handle class used for dynamicy property updates properties (SetAccess = private) mandatoryFields = {'Author', 'ProjectID'} @@ -19,7 +18,8 @@ classdef config < handle methods function obj = config(configFileName) %CONFIG Construct an instance of this class - % Detailed explanation goes here + % reads config data from config file, if an error occurs the + % wizard is started by the catch block obj.configFileName = configFileName; try txt = fileread(obj.configFileName); @@ -62,18 +62,12 @@ classdef config < handle end function configData = addPublishOptions(obj,mode) - %writeConfig writes the config file to path - % TODo; + %addPublishOptions adds the publsih options to the config + %object depending on the mode obj.configData.options = obj.plotID_options(mode); cpath = fileparts(obj.configPath); obj.writeConfig(cpath); - end - - function outputArg = method1(obj,inputArg) - %METHOD1 Summary of this method goes here - % Detailed explanation goes here - outputArg = obj.Property1 + inputArg; - end + end end diff --git a/+PlotID/@config/plotID_options.m b/+PlotID/@config/plotID_options.m index 235f1cb791ef0484873d32a2883aae5daf387406..aaa2f918802ae73342e723c7304de88a9200750a 100644 --- a/+PlotID/@config/plotID_options.m +++ b/+PlotID/@config/plotID_options.m @@ -1,6 +1,6 @@ function [options] = plotID_options(input) -%PLOTID_OPTIONS Summary of this function goes here -% Detailed explanation goes here +%PLOTID_OPTIONS gives you a struct with all the options defined in publish +% Two modes are implemented default and debug options = struct(); switch input diff --git a/+PlotID/@userDLG/userDLG.m b/+PlotID/@userDLG/userDLG.m index 0052a88804903b22c9bcf3f7b2267380ce41e818..7b82f79cd84dfb8102cdd84412971710b8ad6765 100644 --- a/+PlotID/@userDLG/userDLG.m +++ b/+PlotID/@userDLG/userDLG.m @@ -38,8 +38,7 @@ classdef userDLG end function obj = set.msg(obj,message) - %setmsg Summary of this method goes here - % Detailed explanation goes here + %setmsg sets a message text obj.msg = message; end @@ -56,7 +55,7 @@ classdef userDLG obj.error(errorMessage) end end - + function [] = userMSG(obj,message) %userMSG user message without priority % MSG will only be displaye if ShowMessages is true @@ -83,12 +82,22 @@ classdef userDLG error(message); end end - - function outputArg = method1(obj,inputArg) - %METHOD1 Summary of this method goes here - % Detailed explanation goes here - outputArg = obj.Property1 + inputArg; - end end -end + + methods(Static) + function [status] = userQuestion(dialogMessage) + %userQuestion displays Y/N user input + m = ''; + while ~ismember(m,{'Y','y','n','N'}) + m = input([dialogMessage,', Y/N [Y]? '],'s'); + end + if ismember(m,{'Y','y'}) + status = true; + else + status = false; + end + end + end %static + +end % class diff --git a/+PlotID/Publish.m b/+PlotID/Publish.m index 8e276389a3f60faa1c7e261896b78f1a35215155..f134cc04ea24c06c6e8b92b3072cdd93627d1f23 100644 --- a/+PlotID/Publish.m +++ b/+PlotID/Publish.m @@ -4,18 +4,26 @@ function Publish(DataPaths,scriptPath, figure, options) % DataPaths contains the path(s) to the research data, for multiple files % use a cell array % scriptPath contains the path to the script, this can be provided with -% the simple call of scriptPath = [mfilename('fullpath'),'.m'], note that -% the extension is mandatory +% the simple call of scriptPath = [mfilename('fullpath'),'.m'] % % Options: % Location sets the storage location. 'local' sets the storage location -% to the current folder (an export folder will be created), 'server' is a +% to the current folder (an export folder will be created), 'manual' opens +% a explorer window, where you can choose the folder. If you define a export +% path in the config file, this will used per default. % 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 +% 'ParentFolder' is the folder Name where the exported data is stored if an % path is used, PlotId will use this path a storagePath +% 'ConfigFileName' is needed for handling multiple config files (see example) +% 'CSV' turns a summary table of all exports on or off +% +% DebugOptions: +% 'ShowMessage' and 'ForcePublish' are mainly for debugging. +% 'ShowMessage' will display Messages for every step of PlotID, +% 'ForcePublish' will publish even if one step was not successful (not recommended) arguments DataPaths {mustBeDataPath} % location of the data-file(s) @@ -23,11 +31,11 @@ arguments 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.ParentFolder (1,:) {mustBeText} = 'PlotID_export' % name of the ParentFolder options.ConfigFileName (1,:) {mustBeText} = 'config.json' %individual config names possible options.CopyUserFCN (1,1) {mustBeNumericOrLogical} = true - options.CSV (1,1) {mustBeNumericOrLogical} = false - options.ShowMessages(1,1) {mustBeNumericOrLogical} = true + options.CSV (1,1) {mustBeNumericOrLogical} = true + options.ShowMessages(1,1) {mustBeNumericOrLogical} = false options.ForcePublish (1,1) {mustBeNumericOrLogical} = false %publish anyway end @@ -38,7 +46,7 @@ if ~iscell(DataPaths) DataPaths = {DataPaths}; %Cell array end -% strings will cause problems, we therfore use chars +% strings will cause problems, therefore chars are used for i=1:numel(DataPaths) if isstring(DataPaths{i}) DataPaths{i} = char(DataPaths{i}); @@ -52,7 +60,7 @@ end %catch multiple figures in fig if numel(figure) > 1 figure = figure(1); - msg = ['Publish is designed for handeling one figure at once' newline,... + msg = ['Publish is designed for handling 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); @@ -68,7 +76,7 @@ end ID = figure.Tag; if isempty(ID) - % no ID found, User dialog for Folder name + % no ID found, user dialog for folder name ID = inputdlg(['No ID defined- ' newline,... 'Please enter a folder name to continue:'],'Please enter a folder name'); ID = ID{1}; @@ -80,7 +88,7 @@ end %% read config file % there is only one config Object (handleClass) configObj = PlotID.config(options.ConfigFileName); -% add user options +% read user options from config and set them for Publish if isfield(configObj.configData, 'options') fldnames = fieldnames(configObj.configData.options); for ii=1:numel(fldnames) @@ -89,10 +97,9 @@ if isfield(configObj.configData, 'options') end end -% Error and MSG handeling +% error and MSG handling object dlgObj = PlotID.userDLG(ID,options); - %% storage location switch options.Location case 'local' @@ -103,7 +110,7 @@ switch options.Location scriptLocation = fileparts(scriptPath); storPath = fullfile(scriptLocation,options.ParentFolder); end - case 'exportPath' %legacy + case 'exportPath' if isfolder(configObj.exportPath) storPath = configObj.exportPath; else @@ -120,16 +127,16 @@ switch options.Location storPath = configObj.configData.ServerPath; case 'manual' %UI storPath = uigetdir(); - case 'CI-Test' + case 'CI-Test' %only for CI tests storPath = fullfile(pwd,'CI_files',options.ParentFolder); end -folderName = ['.',char(ID)]; %hidden folder +folderName = ['.',char(ID)]; % hidden folder folderNameV = char(ID); %visible Folder %% Create data directory overwriteDir = false; -% if invisible Folder exists, delete it (publish was not succesfull before) +% if invisible Folder exists, delete it (publish was not successful before) if isfolder(fullfile(storPath,folderName)) rmdir(fullfile(storPath,folderName),'s') end @@ -138,9 +145,14 @@ if isfolder(fullfile(storPath,folderNameV)) msg = ['Folder ',folderNameV, ' exists - Plot was already published ']; disp(msg); dialogMsg = 'Do you want to overwrite the existing files'; - overwriteDir = dlgObj.userDialog(dialogMsg, msg); + overwriteDir = dlgObj.userQuestion(dialogMsg); + if ~overwriteDir % USR does not want to overwrite + warning('PlotID has finished without an export'); + disp('rerun TagPlot if you need a new ID or consider overwriting.'); + return; %terminate + end end -% create folder +% create the folder if ~mkdir(fullfile(storPath,folderName)) dlgObj.error('Directory could not be created - check remote path and permissions'); end @@ -157,13 +169,13 @@ dlgObj.userMSG(msg); if options.CopyUserFCN fList = fList(~ismember(fList,scriptPath)); % 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 + fList = PlotID.removePltIdFiles(fList); % Do not copy files that are part of PlotID if ~isempty(fList) PlotID.createFileCopy(fList,folderName,storPath,ID,'userFcn'); end end -%% Research data handeling +%% Research data handling switch options.Method case 'centralized' DataFolderName = 'data'; @@ -224,7 +236,7 @@ exportPath = fullfile(storPath,folderName); configObj.writeConfig(exportPath); % add further Metadata -meta =struct(); +meta = struct(); if ispc meta.author = getenv('USERNAME'); end @@ -254,12 +266,12 @@ catch dlgObj.softError('Plot export was not successful'); end -%% final renaming and error/warning handeling -% if no error orcurred or if force publish is activated, rename the hidden +%% final renaming and error/warning handling +% if no error occurred or if force publish is activated, rename the hidden % folder to a non hidden one, otherwise delete it. if dlgObj.success || options.ForcePublish oldPath = fullfile(storPath,folderName); - newPath = strrep(oldPath,'.',''); %remov dot + newPath = strrep(oldPath,'.',''); %remove dot if overwriteDir rmdir(newPath,'s'); dlgObj.userMSG(['old export ', folderNameV, ' deleted']); @@ -267,8 +279,7 @@ if dlgObj.success || options.ForcePublish status = movefile(oldPath,newPath); %rename directory end -%% CSV EXport -% ToDo exclude in function +%% CSV export if options.CSV T = table(); T.research_Data = DataPaths'; T.PlotID(:) = {ID}; @@ -281,9 +292,8 @@ if options.CSV end if status - disp(['publishing of ', ID , ' to ', newPath, ' done']); %always displayed onsucess -else % publish was not successfull! - %replace with error from userDLG Class + disp(['publishing of ', ID , ' to ', newPath, ' done']); %always displayed on success +else % publish was not successful! dlgObj.error(['publishing of ', ID , ' failed']) end diff --git a/+PlotID/TagPlot.m b/+PlotID/TagPlot.m index fc72278b5ededd18f954e69b64834e8d4af9bba8..d940a5dc29e9cc7b4869e9f86eab8721b37437ed 100644 --- a/+PlotID/TagPlot.m +++ b/+PlotID/TagPlot.m @@ -2,15 +2,17 @@ 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 +% If a single Plot is tagged 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 +% The ID is placed on the 'east' per default, if you want it somewhere % 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) +% 'ConfigFileName' is the config-file which is used for the ProjectID +% If you use multiple config files you need to use this option. arguments figs (1,:) {mustBeFigure} options.ProjectID (1,:) {mustBeText}= '' @@ -76,7 +78,6 @@ for n = 1:numel(figs) ylim =get(axes,'YLim'); xlim =get(axes,'XLim'); %ID - position = [options.Position(1), options.Position(2)]; text(axes,position(1),position(2), IDs{n},'Fontsize',options.Fontsize,... 'Rotation',Rotation, 'VerticalAlignment','bottom','Color',... diff --git a/+PlotID/createFileCopy.m b/+PlotID/createFileCopy.m index c2a857b171facd2566ad78070de52fda3563c028..9787b19dcd37e842f18440b1ad1cc3ff7e5f4b0d 100644 --- a/+PlotID/createFileCopy.m +++ b/+PlotID/createFileCopy.m @@ -4,7 +4,7 @@ function [storagePaths, status, msg] = createFileCopy(filePaths,folderName,storP % returns the storage paths were files were stored if ~iscell(filePaths) - %fixes Issue if Filepath is a char and not a cell array + %fixes Issue if filepath is a char and not a cell array filePaths = {filePaths}; end diff --git a/CITATION.cff b/CITATION.cff index 6c8e17a380a65867c2de3888abcc23f8d09d4f8f..54312fbdd41a9d06ba6cc453bb3d2cd71a5474fb 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -16,3 +16,4 @@ authors: family-names: Hock email: martin.hock@fst.tu-darmstadt.de affiliation: 'Chair of Fluid Systems, TU Darmstadt' + orcid: ' https://orcid.org/0000-0001-9917-3152' diff --git a/Examples/createExampleData.m b/Examples/createExampleData.m index 1f98926fd7404afe282a266eb238b2017b4d17ad..d2a1359313f054f289352c0aa85d265278fad5ad 100644 --- a/Examples/createExampleData.m +++ b/Examples/createExampleData.m @@ -1,6 +1,8 @@ function [x,y, fpath] = createExampleData(option) -%CREATEEXAMPLEDATA creates x,y Data needed for all PlotID Examples -% fpath contains the file path of the example data +%CREATEEXAMPLEDATA creates x,y data needed for the PlotID examples +% fpath contains the file path of the example data +% there are two options, the option 'matlab' creates a matlab file and the +% hdf option a hdf5 file switch option case 'hdf' @@ -22,7 +24,7 @@ switch option h5write(fpath, "/x", x) h5write(fpath, "/y", y) case 'matlab' - % Creating Random Data to use as data-file + % Creating random data to use as data-file x = linspace(0,7); y = rand(1,100)+2; dataset = 'test_data.mat'; % Use absolute paths for good practise diff --git a/PlotID_example.m b/PlotID_example.m index d4def8646cd7632f6923ae5fbf8cca7bf02e4598..92099efa2997dd5dba4fd14bde1bb89f31314199 100644 --- a/PlotID_example.m +++ b/PlotID_example.m @@ -1,7 +1,8 @@ %% Example Script -% This Script is meant to demonstrate the capabilities of the PlotID tool. -% please run Initilisation.m before first time usage -%% Clear an set Environment +% This Script is mend to demonstrate the capabilities of the PlotID tool. +% please run Initilisation.m before first time use + +%% Clear and set environment clear; clc; close all; addpath('Examples'); @@ -14,9 +15,9 @@ addpath('Examples'); fig1 = figure; plot(x,y,'-k'); box off; set(gca, 'TickDir', 'out', 'YLim', [0,4]); -%% Basic Usage -% PlotID Implementation starts here. -%% 1. Tag the plot +% -------------- PlotID Implementation starts here. ------------------------- + +%% ----- 1. Tag the plot ----- % TagPlot adds visible ID(s) to the figure(s) and to the figures property 'Tag' % every time you run tagPlot you will get an new ID @@ -39,9 +40,9 @@ fig2 = figure; plot(x,y,'-k'); box off; set(gca, 'TickDir', 'out', 'YLim', [0,4] % You can find the description to all options in the readme -%% 2. Publishing -% Second part of plotID -% Exports your Plot, the research Data and the associated scripts to a +%% ---- 2. Publishing ----- +% Second part of PlotID +% Exports your plot, the research data and the associated scripts to a % folder named with your ID % The functions needs the file location of the script, the location of the @@ -54,19 +55,20 @@ fig2 = figure; plot(x,y,'-k'); box off; set(gca, 'TickDir', 'out', 'YLim', [0,4] % Path of the m.script that you use for creating your plot. scriptPath = [mfilename('fullpath'),'.m']; -% file paths of the datasets used for the plot (don't forget the extension) -% datapath = test_data.mat +% file paths of the datasets used for the plot (don't forget the extension) +% note: use absolute paths for best practice +% datapath = 'test_data.mat'; locations = datapath; %call publish PlotID.Publish(locations,scriptPath, fig2) -% your plot, script and all the data that your provide are now published - +% Your plot, script and all the data that your provide are now published. +% --------------------------------- %% Further examples and help % You find more examples in the Examples folder: -% Advanced Usage -% Working with HDF5-files -% Using a central data folder -% How to use advanced config-files \ No newline at end of file +% - Advanced usage +% - Working with HDF5-files +% - Using a central data folder +% - How to use advanced config-files \ No newline at end of file diff --git a/README.md b/README.md index e8609c5e75c7c066faffac97a8545abef1300879..929ac878932aafd14616caa66ecc8e6cfceb1796 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ **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` 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. @@ -13,19 +11,25 @@ Feel free to give feedback and feature requests or to take part in the developme # Quick User Guide **Requirements:** MATLAB R2021a or newer -`PlotID` works in two Steps: +**First use:** +1. Put your PlotID folder in you [userpath](https://uk.mathworks.com/help/matlab/ref/userpath.html) on Windows this is +`C:\Users\USERNAME\Documents\MATLAB`. \ +When using a diffrent folder, make sure the folder is on your [search path](https://uk.mathworks.com/help/matlab/matlab_env/add-remove-or-reorder-folders-on-the-search-path.html). -1. tagging the plot -`[figs, IDs] = plotId.TagPlot(figs, options)` -You should either set a ProjectID in the config.json (copy & rename the 'example-config.json' to 'config.json'), or pass it as option 'ProjectID' to the TagPlot function. +2. Run `Initialisation.m` and follow the command line instructions.\ +The **ProjectID** is your custom project number, it well be the prefix of the ID created by `PlotID` (e.g. JL-001). Save your config file. -2. publish the plot and the associated data -`plotID.Publish(DataPaths,scriptPath, figure, options)` +**Intended use:**\ +`PlotID` works in two Steps: + +1. Tag the plot +`[figs, IDs] = PlotId.TagPlot(figs, options)` -`scriptPath` contains the path to the script, this can be provided with the simple call of `scriptPath = [mfilename('fullpath'),'.m']`, note that - **the extension is mandatory.** +2. Publish the plot and the associated data +`PlotID.Publish(DataPaths,scriptPath, figure, options)` -`DataPaths` contains the path(s) to the research data, for multiple files you can use a cell arrays (they must be at the path). +`scriptPath` contains the path to the script, this can be provided with the simple call of `scriptPath = [mfilename('fullpath'),'.m']` \ +`DataPaths` contains the path(s) to the research data, for multiple files you can use a cell arrays (they must be at the path). \ `figure` is the figure that should be published. @@ -102,15 +106,55 @@ fileCompare checks if file1 is (binary) identical to a file in filelist, it retu 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. +The config file is a JSON-File. The config file stores user data and options of `PlotID`. It will be created when you use PlotID for the first time. Follow the Instructions to create the file. _Note:_ for basic usage no manual changes on the config file are required. \ + +**Note:** If you mess up your config-file simply delete it, PlotID will create a new one the next time you use it. + +## Define an export path +If you define an export path in the config.json this path is used as location for the exported data. + +## Store custom options for Publish +**add custom publish options to the config file** \ +you can define your custom options for publish in the config file. **Important:** They will always have priority against Name-Value pairs! + + +``` +configFileName = 'config.json'; +configObj = PlotID.config(configFileName); +``` +This method adds the default config to your config file +``` +configObj.addPublishOptions('default'); +``` + +You can change the options in the json file, but be carefull no argumentValidation will be done on options from the config file! + +## Working with multiple (project dependend) config files + +<details><summary> Expand </summary> +It is possible to use individual config files for each project (configP1.json, configP2.json, ...) \ + +1. Create a new config file programatically +``` +customConfigFileName = 'configP1.json'; +configObj = PlotID.config(customConfigFileName); +configObj.addPublishOptions('default'); +``` + +2. Use the custom Config +you need to provide the name of your custom config file to tagplot as well as to publish by using the Name-Value Pair: +` 'ConfigFileName','YOURNAME.json'` : ``` -{ - "ProjectID": "your ProjectID", - "ServerPath": "\\\\Server\\Folder1\\Subfolder" -} + [figs, IDs] = PlotID.TagPlot(figs, 'ConfigFileName','configP1.json'); + Publish(DataPaths,scriptPath, 'ConfigFileName','configP1.json') ``` +</details> + +# Aknowledgements +The authors would like to thank the Federal Government and the Heads of Government of the Länder, as well as the Joint Science Conference (GWK), for their funding and support within the framework of the NFDI4Ing consortium. Funded by the German Research Foundation (DFG) - project number 442146713. + # Known Issues # FAQs