Can I generalize a folder lookup location so that data can be accessed in more than one way?
Show older comments
I'm finding this issue difficult to describe, so bear with me...
So a colleague created a structure that looks for files in specified location, for example if I call the structure.get_image() function, it looks in folder '/media/Server/user/...'
However, the data itself has been copied to different servers and is accessed from different workstations (sometimes with different operating systems), and not all of those workstations and servers have the same file structure. For example, in one it may need to look in '/data/DifferentServer/user...' - everything past the server name should be the same for everyone and every type of access, but not so with the server name itself and what comes before it. It may also need to look in a windows computer at a location like Z:\media\Server\user\...' which possibly poses a different problem.
So my question is, is there a way to generalize the folder location to work in all of these cases?
Structure example (omitted some things, but all of the omitted things are universal regardless of how the data is accessed):
algorithm: 'omitted'
parameters: [1×1 struct]
nJobs: 2
comment: []
folder: '/media/Cube/omitted/' % The problem
refScan: 1
uuid: (leaving this out)
patient: []
study: [1×1 study]
sliceFolder: '/media/Cube/omitted/omitted/' % Also the problem
% There is a lot more to these structures, but I think this gives the idea
Error received if accessed from a server that instead has the folder located at '/data/Sphere/omitted/':
Cannot find file
"/media/Cube/omitted/01_deformed.nii".
Currently people have to go in and do this if they want to use the data from another location:
set(thing.subThing,'folder','data/Sphere/omitted');
set(thing.subThing,'sliceFolder','data/Sphere/omitted');
% About 5 more things to set in addition to these, but you get the idea
And then they can't save those changes or else it may mess it up for other people accessing the data. And sometimes we want to work with dozens of these structures at once so we would have to do these sets for each one (and the "omitted" part is different for each one, so it can't be looped). Ideally the folder location would be instead something like:
folder: '.../omitted/'
% Same for sliceFolder and all the other paths
Where the "..." is determined based on where "omitted" is actually located for that user.
Is this sort of generalized pointer possible?
Apologies again for how difficult this has been for me to explain, please let me know if I can provide any additional clarification!
6 Comments
"Is this sort of generalized pointer possible?"
One simple approach is to get the user to browse to that location and then use relative filepaths:
Ryan Andosca
on 9 Aug 2023
Hardcoding the specific server name with the rest of the folder is the root of the problem; those should be two different pieces of building the full file name, not one. Then, build the path via fullfile using the particular user's server path.
Ideally, the app should have a separate configuration file for each user that lets them customize their install to match their workstation and work habits. Then, the data aren't global and wouldn't have the server embedded into a dataset that subsequently gets moved somewhere else.
One temporary workaround while working out a more generic solution that would alleviate a fair amount of the issues raised above would be to put the file browsing call inside a try...catch block that only prods the user to browse to the correct location if the present server path doesn't exist or the file isn't at that location. Then those for which the present is correct won't know there's anything different.
And, of course, if one does have to change the server, then there should be a script/function/app that does all the needed changes given the updated server info. That would/will be much simpler once you get the server specification into its own datafield.
It should be essentially transparent as to OS with MATLAB IFF you use fullfile() and other high-level MATLAB functions to do the filename manipulations; it handles the format for each OS. You may have some issues with existing names hardcoded into the data files; I'm not sure how forgiving Linux is on the forward vs backwards slash and other details...
"Ideally, the app should have a separate configuration file for each user"
Another option is to specify the root path as a function input argument, and let the user manage their own path as they see fit. If you have competent MATLAB users than that might be a good option.
In any case, one way or another if every computer has the required data stored in a different location then you need to have a way to provide that location to your code. What approach you use is more of a design decision than a technical one: what user experience do you want to offer your users? Does your code already use a configuration file? What skill level do your users have? Do you provide your code as functions/methods to be called by the user, or as a turn-key blackbox? Is the code installed along with other code (possibly with other languages/apps) or consists of MATLAB functions only?
"I'm not sure how forgiving Linux is on the forward vs backwards slash and other details"
Not at all forgiving: linux accepts only the /, whereas windows accepts both / and \. So to write paths that work on both, use /.
Steven Lord
on 10 Aug 2023
So to write paths that work on both, use /.
To write folder paths that work on Linux, Windows, and Mac use fullfile. Or if you can't for some reason, use filesep.
Ryan Andosca
on 10 Aug 2023
Edited: Ryan Andosca
on 10 Aug 2023
Answers (1)
Jan
on 10 Aug 2023
The core of the problem is a messed up storage of files in a bunch of folders. Collecting the files in one shared folder would be much cleaner. A database is even better to store a bunch of data.
It is not hard to write a workaround: The folders needed by a user are defined in a function e.g. called "PersonalDataFolder" in the personal user path. Then calling the sharded function "FindFile" requests the list of the folders and searchs for the specific file in the set of given folders:
function File = FindFile(FileName)
FolderList = PersonalDataFolder(); % E.g. {'/media/Server1/user1', '/media/server2/user1'}
File = '$File not found$';
for iFolder = 1:numel(FolderList)
aFile = fullfile(FolderList{iFolder}, FileName);
if isfile(aFile)
File = aFile;
break;
end
% Alternatively: Check the other folders also to find ambiguities
end
end
1 Comment
Ryan Andosca
on 10 Aug 2023
Edited: Ryan Andosca
on 10 Aug 2023
Categories
Find more on Share and Distribute Software in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!