1#!/usr/local/bin/python3.8
2
3# python3 status: compatible
4
5# system libraries
6import sys, os, glob
7
8# AFNI libraries
9from afnipy import option_list as OL
10from afnipy import afni_util as UTIL
11from afnipy import lib_afni1D as LD
12
13# ----------------------------------------------------------------------
14# globals
15
16g_help_string = """
17=============================================================================
18read_matlab_files.py    - describe or convert MATLAB files (to 1D)
19
20   Describe the contents of matlab files, and possibly convert them to 1D.
21
22   Using only -infiles, all file objects (names not starting with '__') will
23   be reported.  With the addition of -prefix, all numpy matrices will be
24   converted to 1D format.
25
26------------------------------------------
27examples:
28
29   1. Describe the contents of all matlab files.
30
31      read_matlab_files.py -infiles *.mat
32
33   1. Convert all matlab files in the current directory to test.*.1D
34
35      read_matlab_files.py -infiles *.mat -prefix test
36
37------------------------------------------
38terminal options:
39
40   -help                : show this help
41   -hist                : show the revision history
42   -ver                 : show the version number
43
44------------------------------------------
45process options:
46
47   -infiles             : specify input files
48   -overwrite           : overwrite any output file
49   -prefix PREFIX       : prefix for output file names
50
51        Using -prefix, output files will have the naming format:
52
53           PREFIX.INDEX.KEY.1D
54
55              PREFIX    : as specified with -prefix
56              INDEX     : 1-based index of objects found in file
57              KEY       : key (label) corresponding to the given object
58
59------------------------------------------
60R Reynolds    January 2015
61=============================================================================
62"""
63
64g_todo = """
65   todo list:
66"""
67
68g_history = """
69   read_matlab_files.py history:
70
71   0.0  Jan 14, 2015 - initial version
72   0.1  May 11, 2020 - python3 compatible
73"""
74
75g_version = "read_matlab_files.py version 0.1, May 11, 2020"
76
77
78class MyInterface:
79   """interface class for MyLibrary (whatever that is)
80   """
81   def __init__(self, verb=1):
82      # main variables
83      self.valid_opts      = None
84      self.user_opts       = None
85
86      # control
87      self.verb            = 1
88
89      # process vars
90      self.infiles         = []
91      self.prefix          = ''
92      self.overwrite       = 0
93
94      # initialize valid_opts
95      self.valid_opts = self.get_valid_opts()
96
97   def get_valid_opts(self):
98      vopts = OL.OptionList('valid opts')
99
100      # short, terminal arguments
101      vopts.add_opt('-help', 0, [], helpstr='display program help')
102      vopts.add_opt('-hist', 0, [], helpstr='display the modification history')
103      vopts.add_opt('-ver', 0, [], helpstr='display the current version number')
104
105      # general options
106      vopts.add_opt('-infiles', -1, [],
107                    helpstr='specify input files')
108      vopts.add_opt('-overwrite', 0, [],
109                    helpstr='flag to overwrite any existing output files')
110      vopts.add_opt('-prefix', 1, [],
111                    helpstr='specify prefix for output files')
112      vopts.add_opt('-verb', 1, [], helpstr='set the verbose level (def=1)')
113
114      vopts.sort()
115
116      return vopts
117
118   def process_options(self):
119      """return  1 on valid and exit
120         return  0 on valid and continue
121         return -1 on invalid
122      """
123
124      argv = sys.argv
125
126      # process any optlist_ options
127      self.valid_opts.check_special_opts(argv)
128
129      # process terminal options without the option_list interface
130      # (so that errors are not reported)
131
132      # if no arguments are given, do default processing
133      if '-help' in argv or len(argv) < 2:
134         print(g_help_string)
135         return 1
136
137      if '-hist' in argv:
138         print(g_history)
139         return 1
140
141      if '-show_valid_opts' in argv:
142         self.valid_opts.show('', 1)
143         return 1
144
145      if '-ver' in argv:
146         print(g_version)
147         return 1
148
149      # ============================================================
150      # read options specified by the user
151      self.user_opts = OL.read_options(argv, self.valid_opts)
152      uopts = self.user_opts            # convenience variable
153      if not uopts: return -1           # error condition
154
155      # ------------------------------------------------------------
156      # process verb first
157
158      val, err = uopts.get_type_opt(int, '-verb')
159      if val != None and not err: self.verb = val
160
161      # ------------------------------------------------------------
162      # process options sequentially, to make them like a script
163      errs = 0
164      for opt in self.user_opts.olist:
165         # check for anything to skip
166         if opt.name == '-verb': pass
167
168         elif opt.name == '-infiles':
169            self.infiles, err = uopts.get_string_list('', opt=opt)
170            if self.infiles == None or err:
171               print('** failed to read -infiles list')
172               errs +=1
173
174         elif opt.name == '-overwrite':
175            self.overwrite = 1
176
177         elif opt.name == '-prefix':
178            val, err = uopts.get_string_opt(opt=opt)
179            if val != None and not err: self.prefix = val
180
181      # allow early and late error returns
182      if errs: return -1
183
184      # ------------------------------------------------------------
185      # apply any trailing logic
186
187      # if here, require input files
188      if len(self.infiles) < 1:
189         print('** missing -infiles option')
190         errs += 1
191
192      # if no -prefix and no -verb, default verb to 2
193      if not self.user_opts.find_opt('-verb') and self.prefix == '':
194         self.verb = 2
195
196      if errs: return -1
197
198      return 0
199
200   def process_matlab_file(self, fname, index=0):
201      """process matlab files
202      """
203
204      try:
205         import scipy.io
206         import numpy
207      except:
208         print('** missing library: scipy.io')
209         print('   (please install scipy)')
210         return 1
211
212      if not os.path.isfile(fname):
213         print("** missing file '%s'" % fname)
214         return 1
215
216      mfile = scipy.io.loadmat(fname)
217      if mfile == None: return 1
218
219      # prepare output prefix
220      prefix = self.prefix
221      if prefix != '' and len(self.infiles) > 1:
222         prefix = '%s.%02d' % (self.prefix, index+1)
223
224      klist = [key for key in list(mfile.keys()) if key[0:2] != '__']
225      maxlen = max([len(key) for key in klist])
226
227      if self.verb:
228         if self.verb > 1: print()
229         print('-- file %s has %d key(s)' % (fname, len(klist)))
230
231      for key in klist:
232         obj = mfile[key]
233         if self.verb > 1:
234            if type(obj) is numpy.ndarray:
235               shstr = ' shape %s' % str(obj.shape)
236            else:
237               shstr = ''
238            print('   %-*s %s%s' % (maxlen, key, type(obj), shstr))
239
240         # maybe write any numpy data
241         if prefix != '' and type(obj) is numpy.ndarray:
242            # convert object to Afni1D and transpose
243            olist = obj.tolist()
244            adata = LD.Afni1D(from_mat=1, matrix=olist, verb=self.verb)
245            adata.transpose()
246
247            # if model, break apart, else just write
248            if key == 'model' and adata.nvec >= 2:
249               self.write_model_files(adata, prefix)
250            else:
251               ofile = '%s.%s.1D'%(prefix,key)
252               print('++ writing ndarry to %s' % ofile)
253               adata.write(ofile, overwrite=self.overwrite)
254
255      return 0
256
257   def write_model_files(self, adata, prefix):
258      """break model matrix into many 1D files
259      """
260
261      if adata.nvec < 1: return 0
262
263      for ind in range(adata.nvec):
264         avec = LD.Afni1D(from_mat=1, matrix=[adata.mat[ind]], verb=self.verb)
265         ofile = '%s.model.%02d.1D' % (prefix, ind+1)
266         print('++ writing model file %s' % ofile)
267         avec.write(ofile, overwrite=self.overwrite)
268
269      return 0
270
271   def process_files(self):
272      """process matlab files
273      """
274
275      print('-- have %d files to process' % len(self.infiles))
276
277      # check file existence first
278      for ind, ifile in enumerate(self.infiles):
279         if self.process_matlab_file(ifile, ind): return 1
280
281      return 0
282
283def main():
284   me = MyInterface()
285   if not me: return 1
286
287   rv = me.process_options()
288   if rv > 0: return 0  # exit with success
289   if rv < 0:           # exit with error status
290      print('** failed to process options...')
291      return 1
292
293   if me.process_files(): return 1
294
295   return 0
296
297if __name__ == '__main__':
298   sys.exit(main())
299
300
301