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
93 changes: 67 additions & 26 deletions code/+nansen/+metadata/@MetaTable/MetaTable.m
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@

MetaTableKey = '';
MetaTableName = '';
MetaTableClass = '';
MetaTableIdVarname = '';

% MetaTableMembers - cell array of character vectors representing
% unique identifiers for all entries of the table
Expand All @@ -66,6 +64,8 @@

properties (SetAccess = private)
ItemClassName = '';
MetaTableClass = '';
MetaTableIdVarname = '';
end

properties (Dependent)
Expand Down Expand Up @@ -765,18 +765,20 @@ function checkIfMetaTableComplete(obj, options)
obj.MetaTableVariables = obj.entries.Properties.VariableNames;
end

function addMissingVarsToMetaTable(obj, metaTableType)
function addMissingVarsToMetaTable(obj, metaTableType, options)
%addMissingVarsToMetaTable Add variable to table if it is missing.
%
% If a table is present in the table variable definitions, but
% missing from the table, this functions adds a new variable to
% If a table variable is present in the table variable definitions,
% but missing from the table, this functions adds a new variable to
% the table and initializes with the default value based on the
% table variable definition.

if nargin < 2
metaTableType = 'session';
end


arguments
obj (1,1) nansen.metadata.MetaTable
metaTableType = 'session'
options.AutoUpdateValues (1,1) logical = true
end

tableVarNames = obj.entries.Properties.VariableNames;

currentProject = nansen.getCurrentProject();
Expand Down Expand Up @@ -811,13 +813,15 @@ function addMissingVarsToMetaTable(obj, metaTableType)
defaultValue = fcnResult;
end
obj.addTableVariable(thisName, defaultValue)

if refVariableAttributes{thisRowIndex, 'HasUpdateFunction'}
% Update for all items of the metatable
tableRowInd = 1:height(obj.entries);
updateFcnName = refVariableAttributes{thisRowIndex, 'UpdateFunctionName'}{1};
wasUpdated = obj.updateTableVariable(thisName, tableRowInd, str2func(updateFcnName)); %#ok<NASGU>
% Todo: Show warning if any fails to update?

if options.AutoUpdateValues
if refVariableAttributes{thisRowIndex, 'HasUpdateFunction'}
% Update for all items of the metatable
tableRowInd = 1:height(obj.entries);
updateFcnName = refVariableAttributes{thisRowIndex, 'UpdateFunctionName'}{1};
wasUpdated = obj.updateTableVariable(thisName, tableRowInd, str2func(updateFcnName)); %#ok<NASGU>
% Todo: Show warning if any fails to update?
end
end
end
if not( isempty(obj.filepath) )
Expand Down Expand Up @@ -966,7 +970,7 @@ function appendTable(obj, T)
obj.addTable(T)
end

function addTable(obj, T)
function addTable(obj, T, options)
%addTable Add table rows to the MetaTable
%
% addTable(obj, T) adds rows from a table directly to the
Expand All @@ -975,6 +979,13 @@ function addTable(obj, T)
% from external sources or merging MetaTables.

% Set MetaTable class if this is the first time entries are added

arguments
obj (1,1) nansen.metadata.MetaTable
T (:,:) table
options.AutoUpdateValues (1,1) logical = true
end

if isempty(obj.MetaTableMembers)
if isempty(obj.MetaTableClass) % Don't override if already set
obj.MetaTableClass = 'table';
Expand All @@ -993,11 +1004,11 @@ function addTable(obj, T)
end

% Use common append logic
obj.appendTableRows(T);
obj.appendTableRows(T, "AutoUpdateValues", options.AutoUpdateValues);
end

% Add entry/entries to MetaTable table
function addEntries(obj, newEntries)
function addEntries(obj, newEntries, options)
%addEntries Add schema object entries to the MetaTable
%
% addEntries(obj, newEntries) adds one or more schema objects
Expand All @@ -1006,6 +1017,13 @@ function addEntries(obj, newEntries)
% then converted to a table and appended.

% Make sure entries are based on the MetadataEntity class.

arguments
obj (1,1) nansen.metadata.MetaTable
newEntries
options.AutoUpdateValues (1,1) logical = true
end

isValid = isa(newEntries, 'nansen.metadata.abstract.MetadataEntity');
message = 'MetaTable entries must inherit from the MetadataEntity class';

Expand All @@ -1028,7 +1046,7 @@ function addEntries(obj, newEntries)
newTableRows = newEntries.makeTable();

% Use common append logic
obj.appendTableRows(newTableRows);
obj.appendTableRows(newTableRows, "AutoUpdateValues", options.AutoUpdateValues);
end

function entries = getEntry(obj, listOfEntryIds)
Expand Down Expand Up @@ -1516,7 +1534,7 @@ function updateEntries(obj, listOfEntryIds)
end
end

function appendTableRows(obj, newTableRows)
function appendTableRows(obj, newTableRows, options)
%appendTableRows Append table rows to MetaTable with duplicate checking
%
% This is a private helper method that consolidates the common
Expand All @@ -1528,7 +1546,13 @@ function appendTableRows(obj, newTableRows)
% - Sorting by ID
%
% This method is called by both addEntries and addTable.


arguments
obj (1,1) nansen.metadata.MetaTable
newTableRows
options.AutoUpdateValues (1,1) logical = true
end

if isempty(newTableRows)
return
end
Expand All @@ -1554,19 +1578,25 @@ function appendTableRows(obj, newTableRows)
if isempty(newEntryIds)
return; % Nothing to add
end

% Temporarily create a new MetaTable and add missing table
% variables
tempMetaTable = nansen.metadata.MetaTable.newLike(newTableRows, obj);
tempMetaTable.addMissingVarsToMetaTable(tempMetaTable.MetaTableClass, ...
"AutoUpdateValues", options.AutoUpdateValues);

% Todo:
% Todo (alternative to creating a temp table):
% - expand entries if table has dynamic table variables
% % updateEntries(obj, listOfEntryIds) [Not implemented]

% Concatenate tables
try
% Try direct concatenation
obj.entries = [obj.entries; newTableRows];
obj.entries = [obj.entries; tempMetaTable.entries];
catch
% Fallback: convert to struct, concatenate, then back to table
obj.entries = struct2table([table2struct(obj.entries); ...
table2struct(newTableRows)]);
table2struct(tempMetaTable.entries)]);
end

% Update member list
Expand Down Expand Up @@ -1822,6 +1852,17 @@ function openMetaTableFromName(obj, inputName)
end

methods (Static)
function metaTable = newLike(entries, metaTable)
arguments
entries table
metaTable (1,1) nansen.metadata.MetaTable
end

metaTable = nansen.metadata.MetaTable(entries, ...
"ItemClassName", metaTable.ItemClassName, ...
"MetaTableClass", metaTable.MetaTableClass, ...
"MetaTableIdVarname", metaTable.MetaTableIdVarname);
end

function metaTable = new(varargin)
%NEW Create a new MetaTable
Expand Down
3 changes: 3 additions & 0 deletions tests/+nansen/+unittest/+metadata/MetaTableAdvancedTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ function setupTestEnvironment(~)
error('MetaTable class not found on path');
end
end
function setupProject(testCase)
testCase.applyFixture(nansen.fixture.ProjectFixture)
end
end

methods (TestMethodSetup)
Expand Down
3 changes: 3 additions & 0 deletions tests/+nansen/+unittest/+metadata/MetaTableTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ function setupTestEnvironment(~)
error('MetaTable class not found on path');
end
end
function setupProject(testCase)
testCase.applyFixture(nansen.fixture.ProjectFixture)
end
end

methods (TestMethodSetup)
Expand Down
5 changes: 4 additions & 1 deletion tests/+nansen/+unittest/+metadata/MetadataEntityTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@ function testGetSessionObject(testCase)
sourceFile = fullfile(sourceFile.folder, sourceFile.name);
copyfile(sourceFile, targetFolder)

% Pause for a second to to give some time for internal
% registration of the new table variable.
% Todo: this should be immediately updated
pause(1)
sessionObject.addDynamicTableVariables()

sessionObject.updateDynamicVariable('BrainRegion')
%keyboard
end

function testAddTableVariable(testCase)
Expand Down