docs: def zeq_magic(meas_file='measurements.txt', spec_file='',crd='s',input_dir_path='.', angle=0,
                n_plots=5, save_plots=True, fmt="svg", interactive=False, specimen="",
                samp_file='samples.txt', contribution=None,fignum=1):
      """
      zeq_magic makes zijderveld and equal area plots for magic formatted measurements files.
      Parameters
      ----------
      meas_file : str
          input measurement file
      spec_file : str
          input specimen interpretation file
      samp_file : str
          input sample orientations file
      crd : str
          coordinate system [s,g,t] for specimen, geographic, tilt corrected
          g,t options require a sample file with specimen and bedding orientation
      input_dir_path : str
          input directory of meas_file, default "."
      angle : float
          angle of X direction with respect to specimen X
      n_plots : int, default 5
          maximum number of plots to make
          if you want to make all possible plots, specify "all"
      save_plots : bool, default True
          if True, create and save all requested plots
      fmt : str, default "svg"
          format for figures, [svg, jpg, pdf, png]
      interactive : bool, default False
          interactively plot and display for each specimen
          (this is best used on the command line only)
      specimen : str, default ""
          specimen name to plot
      samp_file : str, default 'samples.txt'
          name of samples file
      contribution : cb.Contribution, default None
          if provided, use Contribution object instead of reading in
          data from files
      fignum : matplotlib figure number
      """
  
      def plot_interpretations(ZED, spec_container, this_specimen, this_specimen_measurements, datablock):
          if cb.is_null(spec_container) or cb.is_null(this_specimen_measurements) or cb.is_null(datablock):
              return ZED
          if 'method_codes' not in spec_container.df.columns:
              return ZED
          prior_spec_data = spec_container.get_records_for_code(
              'LP-DIR', strict_match=False)  # look up all prior directional interpretations
          prior_specimen_interpretations=[]
          if not len(prior_spec_data):
              return ZED
          mpars = {"specimen_direction_type": "Error"}
          if len(prior_spec_data):
              prior_specimen_interpretations = prior_spec_data[prior_spec_data['specimen'].astype(str) == this_specimen]  #.str.match(this_specimen) == True]
          if len(prior_specimen_interpretations):
              if len(prior_specimen_interpretations)>0:
                  beg_pcas = pd.to_numeric(
                      prior_specimen_interpretations.meas_step_min.values).tolist()
                  end_pcas = pd.to_numeric(
                      prior_specimen_interpretations.meas_step_max.values).tolist()
                  spec_methods = prior_specimen_interpretations.method_codes.tolist()
              # step through all prior interpretations and plot them
                  for ind in range(len(beg_pcas)):
                      spec_meths = spec_methods[ind].split(':')
                      for m in spec_meths:
                          if 'DE-BFL' in m:
                              calculation_type = 'DE-BFL'  # best fit line
                          if 'DE-BFP' in m:
                              calculation_type = 'DE-BFP'  # best fit plane
                          if 'DE-FM' in m:
                              calculation_type = 'DE-FM'  # fisher mean
                          if 'DE-BFL-A' in m:
                              calculation_type = 'DE-BFL-A'  # anchored best fit line
                      treatments = pd.to_numeric(this_specimen_measurements.treatment).tolist()
  
                      if len(beg_pcas)!=0:
                          try:
                              # getting the starting and ending points
                              start, end = treatments.index(beg_pcas[ind]), treatments.index(end_pcas[ind])
                              mpars = pmag.domean(
                                  datablock, start, end, calculation_type)
                          except ValueError as ex:
                              mpars['specimen_direction_type'] = "Error"
                              try:
                                  if beg_pcas[ind] == 0:
                                      start = 0
                                  else:
                                      start = treatments.index(beg_pcas[ind])
                                  if end_pcas[ind] == 0:
                                      end = 0
                                  else:
                                      end = treatments.index(end_pcas[ind])
                                  mpars = pmag.domean(
                                      datablock, start, end, calculation_type)
                              except ValueError:
                                  mpars['specimen_direction_type'] = "Error"
                      # calculate direction/plane
                          if mpars["specimen_direction_type"] != "Error":
                              # put it on the plot
                              pmagplotlib.plot_dir(ZED, mpars, datablock, angle)
                              #if interactive:
                              #    pmagplotlib.draw_figs(ZED)
                          else:
                              print('\n-W- Specimen {} record contains invalid start/stop bounds:'.format(this_specimen))
                              print(prior_spec_data.loc[this_specimen][['meas_step_min', 'meas_step_max']])
                              print('\n    Measurement records:')
                              cols = list(set(['treat_ac_field', 'treat_temp']).intersection(this_specimen_measurements.columns))
                              print(this_specimen_measurements[cols])
                              print('\n    Data will be plotted without interpretations\n')
  
          return ZED
  
  
      def make_plots(spec, cnt, meas_df, spec_container, samp_container=None):
          # get sample data for orientation
          if spec_container:
              try:
                  samps = spec_container.df.loc[spec, 'sample']
              except KeyError:
                  samps = ""
                  samp_df = []
              if isinstance(samps, int) or isinstance(samps, float) or isinstance(samps, np.int64):
                  if np.isnan(samps):
                      samp = ""
                      samp_df = []
                  else:
                      samp = str(samps)
                      samp_container.df.index = samp_container.df.index.astype(str)
                      samp_df = samp_container.df[samp_container.df.index == samp]
              elif isinstance(samps, type(None)):
                  samp = ""
                  samp_df = []
              elif len(samps):
                  if isinstance(samps, str):
                      samp = samps
                  else:
                      samp = samps.iloc[0]
                  samp_df = samp_container.df[samp_container.df.index == samp]
          else:
              samp_df = []
          # we can make the figure dictionary that pmagplotlib likes:
          ZED = {'eqarea': cnt, 'zijd': cnt+1, 'demag': cnt+2}  # make datablock
          # get the relevant data
          spec_df = meas_df[meas_df.specimen == s]
          # remove ARM data
          spec_df = spec_df[- spec_df.method_codes.str.contains(
              'LP-*[\w]*-ARM')]
          # split data into NRM, thermal, and af dataframes
          spec_df_nrm = spec_df[spec_df.method_codes.str.contains(
              'LT-NO')]  # get the NRM data
          spec_df_th = spec_df[spec_df.method_codes.str.contains(
              'LT-T-Z')]  # zero field thermal demag steps
          try:
              cond = spec_df.method_codes.str.contains('(^|[\s\:])LT-PTRM')
              spec_df_th = spec_df_th[-cond]  # get rid of some pTRM steps
          except ValueError:
              keep_inds = []
              n = 0
              for ind, row in spec_df_th.copy().iterrows():
                  if 'LT-PTRM' in row['method_codes'] and 'ALT-PTRM' not in row['method_codes']:
                      keep_inds.append(n)
                  else:
                      pass
                  n += 1
              if len(keep_inds) < n:
                  spec_df_th = spec_df_th.iloc[keep_inds]
          spec_df_af = spec_df[spec_df.method_codes.str.contains('LT-AF-Z')]
          this_spec_meas_df = None
          datablock = None
          if (not len(spec_df_th.index) > 1) and (not len(spec_df_af.index) > 1):
              return
          if len(spec_df_th.index) > 1:  # this is a thermal run
              this_spec_meas_df = pd.concat([spec_df_nrm, spec_df_th])
              # make sure all decs/incs are filled in
              n_rows = len(this_spec_meas_df)
              this_spec_meas_df = this_spec_meas_df.dropna(how='any', subset=['dir_dec', 'dir_inc', 'magn_moment'])
              if n_rows > len(this_spec_meas_df):
                  print('-W- Some dec/inc/moment data were missing for specimen {}, so {} measurement row(s) were excluded'.format(s, n_rows - len(this_spec_meas_df)))
              # geographic transformation
              if coord != "-1" and len(samp_df):
                  this_spec_meas_df = transform_to_geographic(this_spec_meas_df, samp_df, samp, coord)
              units = 'K'  # units are kelvin
              try:
                  this_spec_meas_df['magn_moment'] = this_spec_meas_df['magn_moment'].astype(float)
                  this_spec_meas_df['treat_temp'] = this_spec_meas_df['treat_temp'].astype(float)
              except:
                  print('-W- There are malformed or missing data for specimen {}, skipping'.format(spec))
                  return
              datablock = this_spec_meas_df[['treat_temp', 'dir_dec', 'dir_inc',
                                   'magn_moment', 'blank', 'quality']].values.tolist()
              ZED = pmagplotlib.plot_zed(ZED, datablock, angle, s, units)
          if len(spec_df_af.index) > 1:  # this is an af run
              this_spec_meas_df = pd.concat([spec_df_nrm, spec_df_af])
              # make sure all decs/incs are filled in
              n_rows = len(this_spec_meas_df)
              this_spec_meas_df = this_spec_meas_df.dropna(how='any', subset=['dir_dec', 'dir_inc', 'magn_moment'])
              if n_rows > len(this_spec_meas_df):
                  print('-W- Some dec/inc/moment data were missing for specimen {}, so {} measurement row(s) were excluded'.format(s, n_rows - len(this_spec_meas_df)))
              # geographic transformation
              if coord != "-1" and len(samp_df):
                  this_spec_meas_df = transform_to_geographic(this_spec_meas_df, samp_df, samp, coord)
              units = 'T'  # these are AF data
              try:
                  this_spec_meas_df['magn_moment'] = this_spec_meas_df['magn_moment'].astype(float)
                  this_spec_meas_df['treat_ac_field'] = this_spec_meas_df['treat_ac_field'].astype(float)
              except:
                  print('-W- There are malformed or missing data for specimen {}, skipping'.format(spec))
                  return
              datablock = this_spec_meas_df[['treat_ac_field', 'dir_dec', 'dir_inc',
                                   'magn_moment', 'blank', 'quality']].values.tolist()
              ZED = pmagplotlib.plot_zed(ZED, datablock, angle, s, units)
          return plot_interpretations(ZED, spec_container, s, this_spec_meas_df, datablock)
  
      if interactive:
          save_plots = False
      # read in MagIC formatted data if contribution object not provided
      if not isinstance(contribution, cb.Contribution):
          input_dir_path = os.path.realpath(input_dir_path)
          file_path = pmag.resolve_file_name(meas_file, input_dir_path)
          # read in magic formatted data
          if not os.path.exists(file_path):
              print('No such file:', file_path)
              return False, []
          custom_filenames = {'measurements': file_path, 'specimens': spec_file, 'samples': samp_file}
          contribution = cb.Contribution(input_dir_path, custom_filenames=custom_filenames,
                                         read_tables=['measurements', 'specimens',
                                                      'contribution', 'samples'])
      if pmagplotlib.isServer:
          try:
              contribution.propagate_location_to_samples()
              contribution.propagate_location_to_specimens()
              contribution.propagate_location_to_measurements()
          except KeyError as ex:
              pass
      meas_container = contribution.tables['measurements']
      meas_df = contribution.tables['measurements'].df #
      #meas_df=pd.read_csv(file_path, sep='\t', header=1)
      spec_container = contribution.tables.get('specimens', None)
      samp_container = contribution.tables.get('samples', None)
      #if not spec_file:
      #    spec_file = os.path.join(os.path.split(file_path)[0], "specimens.txt")
      #if os.path.exists(spec_file):
      #    spec_container = cb.MagicDataFrame(spec_file, dtype="specimens")
      #else:
      #    spec_container = None
      meas_df['blank'] = ""  # this is a dummy variable expected by plotZED
      if 'treat_ac_field' in meas_df.columns:
          # create 'treatment' column.
          # uses treat_temp if treat_ac_field is missing OR zero.
          # (have to take this into account for plotting later)
          if 'treat_temp' in meas_df.columns:
              meas_df['treatment'] = meas_df['treat_ac_field'].where(
                  cond=meas_df['treat_ac_field'].astype(bool), other=meas_df['treat_temp'])
          else:
              meas_df['treatment'] = meas_df['treat_ac_field']
      else:
          meas_df['treatment'] = meas_df['treat_temp']
  
      if crd == "s":
          coord = "-1"
      elif crd == "t":
          coord = "100"
      else:
          coord = "0"
  
      specimens = meas_df.specimen.unique()  # list of specimen names
      if len(specimens) == 0:
          print('there are no data for plotting')
          return False, []
      # check measurement table for req'd fields
      missing = []
      reqd_cols_present = meas_df.columns.intersection(['dir_dec', 'dir_inc', 'magn_moment'])
      for col in ['dir_dec', 'dir_inc', 'magn_moment']:
          if col not in reqd_cols_present:
              missing.append(col)
          if missing:
              print('-W- Missing required column(s) {}, cannot run zeq_magic'.format(', '.join(missing)))
              return False, []
  
      cnt = fignum
      if n_plots != "all":
          if len(specimens) > n_plots:
              specimens = specimens[:n_plots]
      saved = []
      if specimen:
          specimens = [specimen]
      for s in specimens:
          ZED = make_plots(s, cnt, meas_df, spec_container, samp_container)
          if not ZED:
              if pmagplotlib.verbose:
                  print('No plots could be created for specimen:', s)
                  continue
          titles = {key: s + "_" + key + "." + fmt for key in ZED}
          if pmagplotlib.isServer:
              titles = {}
              titles['eqarea'] = 'Equal Area Plot'
              titles['zijd'] = 'Zijderveld Plot'
              titles['demag'] = 'Demagnetization Plot'
              con_id = ""
              if 'contribution' in contribution.tables:
                  if 'id' in contribution.tables['contribution'].df.columns:
                      con_id = contribution.tables['contribution'].df['id'].values[0]
              pmagplotlib.add_borders(ZED, titles, con_id=con_id)
              for title in titles:
                  # try to get the full hierarchy for plot names
                  df_slice = meas_container.df[meas_container.df['specimen'] == s]
                  location = str(meas_container.get_name('location', df_slice))
                  site = str(meas_container.get_name('site', df_slice))
                  sample = str(meas_container.get_name('sample', df_slice))
                  # add coord here!
                  filename = 'LO:_'+location+'_SI:_'+site+'_SA:_'+sample + \
                      '_SP:_'+str(s)+'_CO:_' + '_TY:_'+title+'_.png'
                  titles[title] = filename
          if save_plots:
              saved.extend(pmagplotlib.save_plots(ZED, titles))
          elif interactive:
              pmagplotlib.draw_figs(ZED)
              ans = pmagplotlib.save_or_quit()
              if ans == 'a':
                  saved.extend(pmagplotlib.save_plots(ZED, titles))
              else:
                  continue
          else:
              cnt += 3
  
      return True, saved