Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions code/+nansen/+internal/+template/copyCss.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
function copyCss(htmlFolder)
cssFilepath = fullfile(nansen.toolboxdir, 'resources', 'templates', 'helpwin.css');
copyfile(cssFilepath, htmlFolder)
end
154 changes: 154 additions & 0 deletions code/+nansen/+internal/+template/fillTemplate.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
function renderedHTML = fillTemplate(templatePath, outputPath, data)
% fillTemplate - Populates a template with data and writes the output to a file.
%
% Syntax: renderedHTML = fillTemplate(templatePath, outputPath, data)
%
% Inputs:
% templatePath - Path to the template file (string)
% outputPath - Path to save the rendered HTML output (string)
% data - Struct containing data to populate the template
%
% Outputs:
% renderedHTML - Rendered HTML as a string

% Read the template file
template = fileread(templatePath);

% Replace top-level fields
fields = fieldnames(data);
for i = 1:numel(fields)
if ~isstruct(data.(fields{i})) && ~iscell(data.(fields{i}))
placeholder = sprintf('{{ %s }}', fields{i});
template = strrep(template, placeholder, string(data.(fields{i})));
end
end

% Process repeating sections for option presets
if isfield(data, 'option_presets')
template = processForLoop(template, 'option_presets', data.option_presets);
end

% Process repeating sections for parameters
if isfield(data, 'parameters')
template = processForLoop(template, 'parameters', data.parameters);
end

% Write the rendered HTML to the output file
renderedHTML = template;
renderedHTML = replaceUrlsWithHyperlinks(renderedHTML);

pattern = upper(data.helptopic);
%patterns = sprintf('^%s | %s | %s\n', pattern, pattern, pattern);
%replacePattern = sprintf('<span class="helptopic">%s</span>', pattern);
%renderedHTML = regexprep(renderedHTML, patterns, replacePattern);

% Define the pattern to match the target text without removing spaces
patterns = sprintf('(^|\\s)%s(\\s|$)', pattern);

% Define the replacement pattern, ensuring the match is replaced without altering spaces
replacePattern = sprintf('$1<span class="helptopic">%s</span>$2', pattern);

% Perform the replacement
renderedHTML = regexprep(renderedHTML, patterns, replacePattern);


fid = fopen(outputPath, 'w');
if fid == -1
error('Unable to open output file: %s', outputPath);
end
fprintf(fid, '%s', renderedHTML);
fclose(fid);
end

function template = processForLoop(template, sectionName, loopParams)
% processForLoop - Replaces a for-loop section in the template with repeated items
%
% Inputs:
% template - Template string
% sectionName - Section name in the template
% dataList - Struct array containing the data
% itemTemplate - Template for individual items
%
% Output:
% template - Updated template string

template = char(template);
template_lines = splitlines(template);
trimmed_lines = strtrim(template_lines);

% Define loop directive placeholders
startPlaceholder = sprintf('{%% for .* %s %%}', sectionName);
endPlaceholder = sprintf('{%% endfor %%}');

% Locate the loop section
lineMatch = regexp(trimmed_lines, startPlaceholder, 'once');
startIdx = find(cellfun(@(c) ~isempty(c), lineMatch));
lineMatch = regexp(trimmed_lines(startIdx:end), endPlaceholder, 'once');
endIdx = find(cellfun(@(c) ~isempty(c), lineMatch), 1, 'first') + startIdx - 1;

if isempty(startIdx) || isempty(endIdx)
return; % No loop section found
end

if isempty(loopParams)
if strcmp(sectionName, 'parameters')
loopContentFinal = ['<tr class="summary-item empty-row">', ...
'<td class="m-help" colspan="3">No parameters are exposed for this method.</td>', ...
'</tr>'];
else
loopContentFinal = ['<tr class="summary-item empty-row">', ...
'<td class="m-help" colspan="2">No option presets are available for this method.</td>', ...
'</tr>'];
end
else
% Extract everything between the loop directives
loopContent = template_lines(startIdx+1:endIdx-1);
loopContent = strjoin(loopContent, newline);

placeholderPattern = '\{\{\s*([a-zA-Z0-9_\.]+)\s*\}\}';
% Extract unique placeholders
tokens = regexp(loopContent, placeholderPattern, 'tokens');
tokens = string(tokens);
placeholders = compose('{{ %s }}', tokens);

loopTemplate = regexprep(loopContent, placeholders, '%s');

% Filter loopParams by detected tokens:
A = squeeze( split(tokens, '.') );
fieldNames = A(:,2);
allFieldNames = fieldnames(loopParams);
loopParams = rmfield(loopParams, setdiff(fieldNames, allFieldNames));
loopParams = orderfields(loopParams, fieldNames);

strValues = squeeze( string( struct2cell(loopParams) ))';

% Build array of replace values for compose
loopContentFinal = compose(loopTemplate, strValues);
loopContentFinal = strjoin(loopContentFinal, newline);
end

% Replace the loop section in the template
template = strjoin( cat(1, ...
template_lines(1:startIdx-1), ...
loopContentFinal, ...
template_lines(endIdx+1:end)), newline );
end

function result = replaceUrlsWithHyperlinks(inputText)
% replaceUrlsWithHyperlinks - Detects URLs in the input text and replaces them with HTML hyperlinks
%
% Inputs:
% inputText - A string containing the text with URLs
%
% Outputs:
% result - The input text with URLs replaced by HTML hyperlinks

% Regular expression to detect URLs
urlPattern = '(https?://[^\s<]+)';

% Replacement pattern to create hyperlinks
replacementPattern = '<a href="$1">$1</a>';

% Replace URLs with HTML hyperlinks
result = regexprep(inputText, urlPattern, replacementPattern, 'ignorecase');
end
108 changes: 104 additions & 4 deletions code/+nansen/+manage/OptionsManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,40 @@
error('This method is not implemented yet.')

end

function metadata = getPresetMetadata(obj)
metadata = struct('name', {}, 'description', {});

options = [obj.PresetOptions_, obj.CustomOptions_];

if numel(options) == 1

Check notice

Code scanning / Code Analyzer

To improve performance, use 'isscalar' instead of length comparison. Note

To improve performance, use 'isscalar' instead of length comparison.
return
end

defaultOptionsName = obj.getReferenceOptionsName('Default');

for i = 1:numel(options)
if strcmp(options(i).Name, defaultOptionsName)
metadata(i).name = obj.formatDefaultName(options(i).Name);
else
metadata(i).name = options(i).Name;
end

metadata(i).description = options(i).Description;
end
end

function optionsDescriptions = getOptionDescriptions(obj)

if obj.FunctionTypeIdx == 1 || obj.FunctionTypeIdx == 2
optionsDescriptions = obj.findOptionDescriptionsFromFunction();

elseif obj.FunctionTypeIdx == 3 || obj.FunctionTypeIdx == 4
optionsDescriptions = [];
else
error('Something went wrong!')
end
end
end

methods % Set/get
Expand Down Expand Up @@ -889,7 +923,7 @@
names = {optionsEntry.Name};
end
end

function optionsEntry = findPresetsFromFunction(obj)
%findPresetsFromFunction Find preset options from a function
%
Expand All @@ -915,7 +949,6 @@

optionsEntry = obj.createOptionsStructForSaving(opts, name, ...
sprintf('Default preset options for %s', obj.FunctionName) );

end

function optionsEntry = findPresetsFromOptionsMixinClass(obj)
Expand All @@ -928,13 +961,20 @@
fcnName = strcat(obj.FunctionName, '.getDefaultOptions');
fcnHandle = str2func(fcnName);

methodNameFcnName = strcat(obj.FunctionName, '.MethodName');

% Return as options entry (struct)
opts = fcnHandle();
name = 'Preset Options';

try
methodName = eval(methodNameFcnName);
catch
methodName = obj.FunctionName;
end

optionsEntry = obj.createOptionsStructForSaving(opts, name, ...
sprintf('Default preset options for %s', obj.FunctionName) );

sprintf('Default preset options for %s', methodName) );
end

function tf = inheritOptionsFromSuperclass(obj)
Expand Down Expand Up @@ -1066,6 +1106,12 @@
isMatch = strcmp(obj.PresetOptionNames, optionsName);
S = obj.PresetOptions_(isMatch).Options;
end

function descriptions = findOptionDescriptionsFromFunction(obj)

functionFilePath = which(obj.FunctionName);
descriptions = extractStructDescriptions(functionFilePath);
end
end

methods (Access = private) % Methods for listing option set names
Expand Down Expand Up @@ -1116,6 +1162,7 @@
names(isPreferred) = obj.formatDefaultName(names(isPreferred));

end

end

methods (Access = private) % Methods for file interaction
Expand Down Expand Up @@ -1538,3 +1585,56 @@
end
end
end

function descriptions = extractStructDescriptions(filePath)
% extractStructDescriptions - Extract descriptions for struct fields in
% the getDefaultParameters function from a MATLAB file.
%
% Inputs:
% filePath - Path to the MATLAB file containing the getDefaultParameters function.
%
% Outputs:
% descriptions - Struct array with fields 'name', 'default_value', and 'description'.

% Read the file content
fileContent = fileread(filePath);

% Locate the getDefaultParameters function
functionPattern = 'function\s+[^\=]+\=\s*getDefaultParameters\s*\(\)';
startIdx = regexp(fileContent, functionPattern, 'start');
if isempty(startIdx)
error('Function "getDefaultParameters" not found in the file.');
end

% Extract the content of the getDefaultParameters function
structStart = regexp(fileContent, 'params\s*=\s*struct\(\);', 'end', 'once');
if isempty(structStart)
descriptions = struct('name', {}, 'default_value', {}, 'description', {});
return; % Return an empty struct if the struct is not defined
end

structContent = fileContent(structStart:end);
structEnd = regexp(structContent, 'end', 'start', 'once');
if isempty(structEnd)
error('Function "getDefaultParameters" does not have an "end".');
end
structContent = structContent(1:structEnd - 1);

% Match struct field assignments with comments
pattern = 'params\.(\w+)\s*=\s*([^;]+);\s*%[^\n]*';
matches = regexp(structContent, pattern, 'tokens', 'lineanchors');

% Parse matched tokens into a struct array
descriptions = struct('name', {}, 'default_value', {}, 'description', {});
for i = 1:numel(matches)
descriptions(i).name = matches{i}{1};
descriptions(i).default_value = strtrim(matches{i}{2});
% Extract the comment (after %)
commentMatch = regexp(structContent, ['params\.' matches{i}{1} '.*?%\s*(.+?)\n'], 'tokens', 'once');
if ~isempty(commentMatch)
descriptions(i).description = strtrim(commentMatch{1});
else
descriptions(i).description = '';
end
end
end
11 changes: 7 additions & 4 deletions code/+nansen/+session/SessionMethod.m
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ function usePreset(obj, presetName)
S.BatchMode = 'serial';
S.IsQueueable = true;
S.Alternatives = {};
S.MethodName = '';

% Pick out default options from inputs or init to empty struct
if ~isempty(varargin) && isstruct(varargin{1})
Expand Down Expand Up @@ -257,10 +258,12 @@ function usePreset(obj, presetName)

% Get name of calling function:
% Todo: Get this from varargin if provided.
fcnName = nansen.session.SessionMethod.getCallingFunction();
fcnNameSplit = strsplit(fcnName, '.');
S.MethodName = utility.string.varname2label(fcnNameSplit{end});

if isempty(S.MethodName)
fcnName = nansen.session.SessionMethod.getCallingFunction();
fcnNameSplit = strsplit(fcnName, '.');
S.MethodName = utility.string.varname2label(fcnNameSplit{end});
end

attributes = S;

end
Expand Down
Loading
Loading