Skip to content
Draft
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
21 changes: 6 additions & 15 deletions code/+nansen/+config/+varmodel/VariableModel.m
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,6 @@ function updateDefaultValues(obj)
dlModel = nansen.DataLocationModel();
end

fileAdapterList = nansen.dataio.listFileAdapters();

for i = 1:numel(obj.Data)
if isempty( obj.Data(i).FileAdapter ) || strcmp(obj.Data(i).FileAdapter, str)
obj.Data(i).FileAdapter = 'Default';
Expand All @@ -179,15 +177,7 @@ function updateDefaultValues(obj)
if ~isfield(obj.Data, 'DataType') % Infer from file adapter
[obj.Data(:).DataType] = deal('');
for i = 1:numel(obj.Data)
if ~strcmp(obj.Data(i).FileAdapter, 'Default')
isMatch = strcmp({fileAdapterList.FileAdapterName}, obj.Data(i).FileAdapter);
if any(isMatch)
fileAdapterFcn = str2func(fileAdapterList(isMatch).FunctionName);
obj.Data(i).DataType = fileAdapterFcn().DataType;
else
% pass
end
end
obj.Data(i) = obj.updateVariableDataType(obj.Data(i));
end
else
% Normalize existing DataType to char. Todo: support string
Expand Down Expand Up @@ -659,15 +649,16 @@ function disableNotifications(obj)

methods (Static) % Todo: Should be moved to a data variable class
function variableItem = updateVariableDataType(variableItem)
% The list holds the data type of every file adapter, also of a
% function-based one, which is not a class that can be created
fileAdapterList = nansen.dataio.listFileAdapters();
if ~strcmp(variableItem.FileAdapter, 'Default')
isMatch = strcmp({fileAdapterList.FileAdapterName}, variableItem.FileAdapter);
if any(isMatch)
fileAdapterFcn = str2func(fileAdapterList(isMatch).FunctionName);

variableItem.DataType = fileAdapterFcn().DataType;
variableItem.DataType = char(fileAdapterList(find(isMatch, 1)).DataType);
else
% pass
% An adapter that is not in the current project keeps
% the data type the item has
end
end
end
Expand Down
8 changes: 6 additions & 2 deletions code/+nansen/+dataio/DynamicFileAdapter.m
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,15 @@
end

methods (Access = protected)
function data = readData(obj)
function data = readData(obj, varargin)
% Session.loadData passes the variable name and name-value
% options to load; the read function takes the file only
data = obj.ReadFunction(obj.Filename);
end

function writeData(obj, data)
function writeData(obj, data, varargin)
% Session.saveData passes the variable name; the write
% function takes the file and the data only
if ~isempty(obj.WriteFunction)
obj.WriteFunction(obj.Filename, data)
else
Expand Down
12 changes: 11 additions & 1 deletion code/+nansen/+metadata/+type/Session.m
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,17 @@ function updateSessionFolder(obj, dataLocationName, folderPath) %#ok<INUSD>
error('NANSEN:Session:MultipleFileAdapters', 'This is a bug. Please report')
end

fileAdapterFcn = str2func(fileAdapterList(isMatch).FunctionName);
fileAdapterInfo = fileAdapterList(isMatch);
if isfield(fileAdapterInfo, 'IsDynamic') && isequal(fileAdapterInfo.IsDynamic, true)
% A function-based file adapter is a folder with a
% fileadapter.json and read and write functions, not a
% class, and DynamicFileAdapter wraps it, as nansen.load does
functionName = fileAdapterInfo.FunctionName;
fileAdapterFcn = @(filePath, varargin) ...
nansen.dataio.DynamicFileAdapter(functionName, filePath);
else
fileAdapterFcn = str2func(fileAdapterInfo.FunctionName);
end
end

function viewDataVariable(obj, varName)
Expand Down
65 changes: 65 additions & 0 deletions tests/+nansen/+unittest/+session/FunctionBasedFileAdapterTest.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
classdef FunctionBasedFileAdapterTest < matlab.unittest.TestCase
%FunctionBasedFileAdapterTest - Data variables whose file adapter is function-based
%
% A function-based file adapter is a folder with a fileadapter.json and
% read and write functions, such as MatFile of the core module. The mock
% project gets the variable Values, with the adapter MatFile, and a .mat
% file for it in the first session folder.
%
% Run tests:
% runtests('nansen.unittest.session.FunctionBasedFileAdapterTest')

properties
Project
Session
end

methods (TestClassSetup)
function setupProject(testCase)
testCase.applyFixture(nansen.fixture.ProjectFixture)
testCase.Project = nansen.getCurrentProject();

% The new item copies the fields of the mock project's item,
% which has fields that a default item lacks
variableModel = testCase.Project.VariableModel;
item = variableModel.Data(1);
item.VariableName = 'Values';
item.Uuid = nansen.util.getuuid();
item.FilePathPattern = '';
item.FileNameExpression = 'values';
item.FileType = '.mat';
item.FileAdapter = 'MatFile';
item.DataType = '';
variableModel.insertItem(item);
variableModel.save()

testCase.Session = testCase.Project.getSessionObjects( ...
testCase.Project.MetaTableCatalog.getMasterMetaTable('session').entries.sessionID{1});
Values = magic(3);
save(fullfile(testCase.Session.getSessionFolder('MockData'), 'values.mat'), 'Values')
end
end

methods (Test)
function testInsertedVariableGetsDataTypeOfAdapter(testCase)
item = testCase.Project.VariableModel.getItem('Values');

testCase.verifyEqual(item.DataType, 'struct')
end

function testLoadDataReadsWithFunctionBasedAdapter(testCase)
data = testCase.Session.loadData('Values');

testCase.verifyEqual(data.Values, magic(3))
end

function testSaveDataWritesWithFunctionBasedAdapter(testCase)
testCase.addTeardown(@() testCase.Session.saveData('Values', struct('Values', magic(3))))

testCase.Session.saveData('Values', struct('Values', magic(4)))

data = testCase.Session.loadData('Values');
testCase.verifyEqual(data.Values, magic(4))
end
end
end
Loading