docs: def orientation_magic(or_con=1, dec_correction_con=1, dec_correction=0, bed_correction=True,
                        samp_con='1', hours_from_gmt=0, method_codes='', average_bedding=False,
                        orient_file='orient.txt', samp_file='samples.txt', site_file='sites.txt',
                        output_dir_path='.', input_dir_path='', append=False, data_model=3):
      """
      use this function to convert tab delimited field notebook information to MagIC formatted tables (er_samples and er_sites)
  
      INPUT FORMAT
          Input files must be tab delimited and have in the first line:
  tab  location_name
          Note: The "location_name" will facilitate searching in the MagIC database. Data from different
              "locations" should be put in separate files.  The definition of a "location" is rather loose.
               Also this is the word 'tab' not a tab, which will be indicated by '\t'.
          The second line has the names of the columns (tab delimited), e.g.:
  site_name sample_name mag_azimuth field_dip date lat long sample_lithology sample_type sample_class shadow_angle hhmm stratigraphic_height bedding_dip_direction bedding_dip GPS_baseline image_name image_look image_photographer participants method_codes site_description sample_description GPS_Az, sample_igsn, sample_texture, sample_cooling_rate, cooling_rate_corr, cooling_rate_mcd
  
  
        Notes:
          1) column order doesn't matter but the NAMES do.
          2) sample_name, sample_lithology, sample_type, sample_class, lat and long are required.  all others are optional.
          3) If subsequent data are the same (e.g., date, bedding orientation, participants, stratigraphic_height),
              you can leave the field blank and the program will fill in the last recorded information. BUT if you really want a blank stratigraphic_height, enter a '-1'.    These will not be inherited and must be specified for each entry: image_name, look, photographer or method_codes
          4) hhmm must be in the format:  hh:mm and the hh must be in 24 hour time.
      date must be mm/dd/yy (years < 50 will be converted to  20yy and >50 will be assumed 19yy).  hours_from_gmt is the number of hours to SUBTRACT from hh to get to  GMT.
          5) image_name, image_look and image_photographer are colon delimited lists of file name (e.g., IMG_001.jpg) image look direction and the name of the photographer respectively.  If all images had same look and photographer, just enter info once.  The images will be assigned to the site for which they were taken - not at the sample level.
          6) participants:  Names of who helped take the samples.  These must be a colon delimited list.
          7) method_codes:  Special method codes on a sample level, e.g., SO-GT5 which means the orientation is has an uncertainty of >5 degrees
               for example if it broke off before orienting....
          8) GPS_Az is the place to put directly determined GPS Azimuths, using, e.g., points along the drill direction.
          9) sample_cooling_rate is the cooling rate in K per Ma
          10) int_corr_cooling_rate
          11) cooling_rate_mcd:  data adjustment method code for cooling rate correction;  DA-CR-EG is educated guess; DA-CR-PS is percent estimated from pilot samples; DA-CR-TRM is comparison between 2 TRMs acquired with slow and rapid cooling rates.
  is the percent cooling rate factor to apply to specimens from this sample, DA-CR-XX is the method code
  
  
      defaults:
      orientation_magic(or_con=1, dec_correction_con=1, dec_correction=0, bed_correction=True, samp_con='1', hours_from_gmt=0, method_codes='', average_bedding=False, orient_file='orient.txt', samp_file='er_samples.txt', site_file='er_sites.txt', output_dir_path='.', input_dir_path='', append=False):
      orientation conventions:
          [1] Standard Pomeroy convention of azimuth and hade (degrees from vertical down)
               of the drill direction (field arrow).  lab arrow azimuth= sample_azimuth = mag_azimuth;
               lab arrow dip = sample_dip =-field_dip. i.e. the lab arrow dip is minus the hade.
          [2] Field arrow is the strike  of the plane orthogonal to the drill direction,
               Field dip is the hade of the drill direction.  Lab arrow azimuth = mag_azimuth-90
               Lab arrow dip = -field_dip
          [3] Lab arrow is the same as the drill direction;
               hade was measured in the field.
               Lab arrow azimuth = mag_azimuth; Lab arrow dip = 90-field_dip
          [4] lab azimuth and dip are same as mag_azimuth, field_dip : use this for unoriented samples too
          [5] Same as AZDIP convention explained below -
              azimuth and inclination of the drill direction are mag_azimuth and field_dip;
              lab arrow is as in [1] above.
              lab azimuth is same as mag_azimuth,lab arrow dip=field_dip-90
          [6] Lab arrow azimuth = mag_azimuth-90; Lab arrow dip = 90-field_dip
          [7] see http://earthref.org/PmagPy/cookbook/#field_info for more information.  You can customize other format yourself, or email ltauxe@ucsd.edu for help.
  
  
      Magnetic declination convention:
          [1] Use the IGRF value at the lat/long and date supplied [default]
          [2] Will supply declination correction
          [3] mag_az is already corrected in file
          [4] Correct mag_az but not bedding_dip_dir
  
      Sample naming convention:
          [1] XXXXY: where XXXX is an arbitrary length site designation and Y
              is the single character sample designation.  e.g., TG001a is the
              first sample from site TG001.    [default]
          [2] XXXX-YY: YY sample from site XXXX (XXX, YY of arbitary length)
          [3] XXXX.YY: YY sample from site XXXX (XXX, YY of arbitary length)
          [4-Z] XXXX[YYY]:  YYY is sample designation with Z characters from site XXX
          [5] site name = sample name
          [6] site name entered in site_name column in the orient.txt format input file  -- NOT CURRENTLY SUPPORTED
          [7-Z] [XXX]YYY:  XXX is site designation with Z characters from samples  XXXYYY
          NB: all others you will have to either customize your
              self or e-mail ltauxe@ucsd.edu for help.
  
  
      """
      # initialize some variables
      # bed_correction used to be BedCorr
      # dec_correction_con used to be corr
      # dec_correction used to be DecCorr
      # meths is now method_codes
      # delta_u is now hours_from_gmt
  
      input_dir_path, output_dir_path = pmag.fix_directories(input_dir_path, output_dir_path)
      or_con, dec_correction_con, dec_correction = int(
          or_con), int(dec_correction_con), float(dec_correction)
      hours_from_gmt = float(hours_from_gmt)
      stratpos = ""
      # date of sampling, latitude (pos North), longitude (pos East)
      date, lat, lon = "", "", ""
      bed_dip, bed_dip_dir = "", ""
      Lats, Lons = [], []  # list of latitudes and longitudes
      # lists of Sample records and Site records
      SampOuts, SiteOuts, ImageOuts = [], [], []
      samplelist, sitelist, imagelist = [], [], []
      Z = 1
      newbaseline, newbeddir, newbeddip = "", "", ""
      fpars = []
      sclass, lithology, sample_type = "", "", ""
      newclass, newlith, newtype = '', '', ''
      BPs = []  # bedding pole declinations, bedding pole inclinations
      image_file = "er_images.txt"
      #
      # use 3.0. default filenames when in 3.0.
      # but, still allow for custom names
      data_model = int(data_model)
      if data_model == 3:
          if samp_file == "er_samples.txt":
              samp_file = "samples.txt"
          if site_file == "er_sites.txt":
              site_file = "sites.txt"
          image_file = "images.txt"
      orient_file = pmag.resolve_file_name(orient_file, input_dir_path)
      if not os.path.exists(orient_file):
          return False, "No such file: {}.  If the orientation file is not in your current working directory, make sure you have specified the correct input directory.".format(orient_file)
      samp_file = os.path.join(output_dir_path, samp_file)
      site_file = os.path.join(output_dir_path, site_file)
      image_file = os.path.join(output_dir_path, image_file)
  
      # validate input
      if '4' in samp_con[0]:
          pattern = re.compile('[4][-]\d')
          result = pattern.match(samp_con)
          if not result:
              raise Exception(
                  "If using sample naming convention 4, you must provide the number of characters with which to distinguish sample from site. [4-Z] XXXX[YYY]:  YYY is sample designation with Z characters from site XXX)")
  
      if '7' in samp_con[0]:
          pattern = re.compile('[7][-]\d')
          result = pattern.match(samp_con)
          if not result:
              raise Exception(
                  "If using sample naming convention 7, you must provide the number of characters with which to distinguish sample from site.  [7-Z] [XXX]YYY:  XXX is site designation with Z characters from samples  XXXYYY")
  
      if dec_correction_con == 2 and not dec_correction:
          raise Exception(
              "If using magnetic declination convention 2, you must also provide a declincation correction in degrees")
  
      SampRecs, SiteRecs, ImageRecs = [], [], []
      SampRecs_sorted, SiteRecs_sorted = {}, {}
  
      if append:
          try:
              SampRecs, file_type = pmag.magic_read(samp_file)
              # convert 3.0. sample file to 2.5 format
              if data_model == 3:
                  SampRecs3 = SampRecs
                  SampRecs = []
                  for samp_rec in SampRecs3:
                      rec = map_magic.mapping(
                          samp_rec, map_magic.samp_magic3_2_magic2_map)
                      SampRecs.append(rec)
              # magic_data dictionary sorted by sample_name
              SampRecs_sorted = pmag.sort_magic_data(SampRecs, 'er_sample_name')
              print('sample data to be appended to: ', samp_file)
          except Exception as ex:
              print(ex)
              print('problem with existing file: ',
                    samp_file, ' will create new.')
          try:
              SiteRecs, file_type = pmag.magic_read(site_file)
              # convert 3.0. site file to 2.5 format
              if data_model == 3:
                  SiteRecs3 = SiteRecs
                  SiteRecs = []
                  for site_rec in SiteRecs3:
                      SiteRecs.append(map_magic.mapping(
                          site_rec, map_magic.site_magic3_2_magic2_map))
              # magic_data dictionary sorted by site_name
              SiteRecs_sorted = pmag.sort_magic_data(SiteRecs, 'er_site_name')
              print('site data to be appended to: ', site_file)
          except Exception as ex:
              print(ex)
              print('problem with existing file: ',
                    site_file, ' will create new.')
          try:
              ImageRecs, file_type = pmag.magic_read(image_file)
              # convert from 3.0. --> 2.5
              if data_model == 3:
                  ImageRecs3 = ImageRecs
                  ImageRecs = []
                  for image_rec in ImageRecs3:
                      ImageRecs.append(map_magic.mapping(
                          image_rec, map_magic.image_magic3_2_magic2_map))
              print('image data to be appended to: ', image_file)
          except:
              print('problem with existing file: ',
                    image_file, ' will create new.')
      #
      # read in file to convert
      #
      OrData, location_name = pmag.magic_read(orient_file)
      if location_name == "demag_orient":
          location_name = ""
      #
      # step through the data sample by sample
      #
      # use map_magic in here...
  
      for OrRec in OrData:
          if 'mag_azimuth' not in list(OrRec.keys()):
              OrRec['mag_azimuth'] = ""
          if 'field_dip' not in list(OrRec.keys()):
              OrRec['field_dip'] = ""
          if OrRec['mag_azimuth'] == " ":
              OrRec["mag_azimuth"] = ""
          if OrRec['field_dip'] == " ":
              OrRec["field_dip"] = ""
          if 'sample_description' in list(OrRec.keys()):
              sample_description = OrRec['sample_description']
          else:
              sample_description = ""
          if 'cooling_rate_corr' in list(OrRec.keys()):
              if 'cooling_rate_mcd' not in list(OrRec.keys()):
                  OrRec['cooling_rate_mcd'] = 'DA-CR'
          sample_orientation_flag = 'g'
          if 'sample_orientation_flag' in list(OrRec.keys()):
              if OrRec['sample_orientation_flag'] == 'b' or OrRec["mag_azimuth"] == "":
                  sample_orientation_flag = 'b'
          methcodes = method_codes  # initialize method codes
          if methcodes:
              if 'method_codes' in list(OrRec.keys()) and OrRec['method_codes'].strip() != "":
                  methcodes = methcodes + ":" + \
                      OrRec['method_codes']  # add notes
          else:
              if 'method_codes' in list(OrRec.keys()) and OrRec['method_codes'].strip() != "":
                  methcodes = OrRec['method_codes']  # add notes
  
          codes = methcodes.replace(" ", "").split(":")
          sample_name = OrRec["sample_name"]
          # patch added by rshaar 7/2016
          # if sample_name already exists in er_samples.txt:
          # merge the new data colmuns calculated by orientation_magic with the existing data colmuns
          # this is done to make sure no previous data in er_samples.txt and
          # er_sites.txt is lost.
  
          if sample_name in list(SampRecs_sorted.keys()):
              Prev_MagRec = SampRecs_sorted[sample_name][-1]
              MagRec = Prev_MagRec
          else:
              Prev_MagRec = {}
              MagRec = {}
          MagRec["er_citation_names"] = "This study"
  
          # the following keys were calculated or defined in the code above:
          for key in ['sample_igsn', 'sample_texture', 'sample_cooling_rate',
                      'cooling_rate_corr', 'cooling_rate_mcd']:
              val = OrRec.get(key, '')
              if val:
                  MagRec[key] = val
              elif key in list(Prev_MagRec.keys()):
                  MagRec[key] = Prev_MagRec[key]
              else:
                  MagRec[key] = ""
  
              if location_name != "":
                  MagRec["er_location_name"] = location_name
              elif "er_location_name" in list(Prev_MagRec.keys()):
                  MagRec["er_location_name"] = Prev_MagRec["er_location_name"]
              else:
                  MagRec["er_location_name"] = ""
  
          # the following keys are taken directly from OrRec dictionary:
          for key in ["sample_height", "er_sample_alternatives", "sample_orientation_flag"]:
              if key in list(OrRec.keys()) and OrRec[key] != "":
                  MagRec[key] = OrRec[key]
              elif key in list(Prev_MagRec.keys()):
                  MagRec[key] = Prev_MagRec[key]
              else:
                  MagRec[key] = ""
  
          # the following keys, if blank, used to be defined here as "Not Specified" :
  
          for key in ["sample_class", "sample_lithology", "sample_type"]:
              if key in list(OrRec.keys()) and OrRec[key] != "" and OrRec[key] != "Not Specified":
                  MagRec[key] = OrRec[key]
              elif key in list(Prev_MagRec.keys()) and Prev_MagRec[key] != "" and Prev_MagRec[key] != "Not Specified":
                  MagRec[key] = Prev_MagRec[key]
              else:
                  MagRec[key] = ""  # "Not Specified"
  
          # (rshaar) From here parse new information and replace previous, if exists:
      #
      # parse information common to all orientation methods
      #
          MagRec["er_sample_name"] = OrRec["sample_name"]
          if "IGSN" in list(OrRec.keys()):
              MagRec["sample_igsn"] = OrRec["IGSN"]
          else:
              MagRec["sample_igsn"] = ""
          # MagRec["sample_height"],MagRec["sample_bed_dip_direction"],MagRec["sample_bed_dip"]="","",""
          MagRec["sample_bed_dip_direction"], MagRec["sample_bed_dip"] = "", ""
          # if "er_sample_alternatives" in OrRec.keys():
          #    MagRec["er_sample_alternatives"]=OrRec["sample_alternatives"]
          sample = OrRec["sample_name"]
          if OrRec['mag_azimuth'] == "" and OrRec['field_dip'] != "":
              OrRec['mag_azimuth'] = '999'
          if OrRec["mag_azimuth"] != "":
              labaz, labdip = pmag.orient(
                  float(OrRec["mag_azimuth"]), float(OrRec["field_dip"]), or_con)
              if labaz < 0:
                  labaz += 360.
          else:
              labaz, labdip = "", ""
          if OrRec['mag_azimuth'] == '999':
              labaz = ""
          if "GPS_baseline" in list(OrRec.keys()) and OrRec['GPS_baseline'] != "":
              newbaseline = OrRec["GPS_baseline"]
          if newbaseline != "":
              baseline = float(newbaseline)
          MagRec['er_scientist_mail_names'] = OrRec.get('participants', '')
          newlat = OrRec["lat"]
          if newlat != "":
              lat = float(newlat)
          if lat == "":
              print("No latitude specified for ! ", sample,
                    ". Latitude is required for all samples.")
              return False, "No latitude specified for ! " + sample + ". Latitude is required for all samples."
          MagRec["sample_lat"] = '%11.5f' % (lat)
          newlon = OrRec["long"]
          if newlon != "":
              lon = float(newlon)
          if lon == "":
              print("No longitude specified for ! ", sample,
                    ". Longitude is required for all samples.")
              return False, str("No longitude specified for ! " + sample + ". Longitude is required for all samples.")
          MagRec["sample_lon"] = '%11.5f' % (lon)
          if 'bedding_dip_direction' in list(OrRec.keys()):
              newbeddir = OrRec["bedding_dip_direction"]
          if newbeddir != "":
              bed_dip_dir = OrRec['bedding_dip_direction']
          if 'bedding_dip' in list(OrRec.keys()):
              newbeddip = OrRec["bedding_dip"]
          if newbeddip != "":
              bed_dip = OrRec['bedding_dip']
          MagRec["sample_bed_dip"] = bed_dip
          MagRec["sample_bed_dip_direction"] = bed_dip_dir
          # MagRec["sample_type"]=sample_type
          if labdip != "":
              MagRec["sample_dip"] = '%7.1f' % labdip
          else:
              MagRec["sample_dip"] = ""
          if "date" in list(OrRec.keys()) and OrRec["date"] != "":
              newdate = OrRec["date"]
              if newdate != "":
                  date = newdate
              mmddyy = date.split('/')
              yy = int(mmddyy[2])
              if yy > 50:
                  yy = 1900 + yy
              else:
                  yy = 2000 + yy
              decimal_year = yy + old_div(float(mmddyy[0]), 12)
              sample_date = '%i:%s:%s' % (yy, mmddyy[0], mmddyy[1])
              time = OrRec['hhmm']
              if time:
                  sample_date += (':' + time)
              MagRec["sample_date"] = sample_date.strip(':')
          if labaz != "":
              MagRec["sample_azimuth"] = '%7.1f' % (labaz)
          else:
              MagRec["sample_azimuth"] = ""
          if "stratigraphic_height" in list(OrRec.keys()):
              if OrRec["stratigraphic_height"] != "":
                  MagRec["sample_height"] = OrRec["stratigraphic_height"]
                  stratpos = OrRec["stratigraphic_height"]
              elif OrRec["stratigraphic_height"] == '-1':
                  MagRec["sample_height"] = ""   # make empty
              elif stratpos != "":
                  # keep last record if blank
                  MagRec["sample_height"] = stratpos
  #
          # get magnetic declination (corrected with igrf value)
          if dec_correction_con == 1 and MagRec['sample_azimuth'] != "":
              x, y, z, f = pmag.doigrf(lon, lat, 0, decimal_year)
              Dir = pmag.cart2dir((x, y, z))
              dec_correction = Dir[0]
          if "bedding_dip" in list(OrRec.keys()):
              if OrRec["bedding_dip"] != "":
                  MagRec["sample_bed_dip"] = OrRec["bedding_dip"]
                  bed_dip = OrRec["bedding_dip"]
              else:
                  MagRec["sample_bed_dip"] = bed_dip
          else:
              MagRec["sample_bed_dip"] = '0'
          if "bedding_dip_direction" in list(OrRec.keys()):
              if OrRec["bedding_dip_direction"] != "" and bed_correction == 1:
                  dd = float(OrRec["bedding_dip_direction"]) + dec_correction
                  if dd > 360.:
                      dd = dd - 360.
                  MagRec["sample_bed_dip_direction"] = '%7.1f' % (dd)
                  dip_dir = MagRec["sample_bed_dip_direction"]
              else:
                  MagRec["sample_bed_dip_direction"] = OrRec['bedding_dip_direction']
          else:
              MagRec["sample_bed_dip_direction"] = '0'
          if average_bedding:
              if str(MagRec["sample_bed_dip_direction"]) and str(MagRec["sample_bed_dip"]):
                  BPs.append([float(MagRec["sample_bed_dip_direction"]),
                              float(MagRec["sample_bed_dip"]) - 90., 1.])
          if MagRec['sample_azimuth'] == "" and MagRec['sample_dip'] == "":
              MagRec["sample_declination_correction"] = ''
              methcodes = methcodes + ':SO-NO'
          MagRec["magic_method_codes"] = methcodes
          MagRec['sample_description'] = sample_description
      #
      # work on the site stuff too
          if 'site_name' in list(OrRec.keys()) and OrRec['site_name'] != "":
              site = OrRec['site_name']
          elif 'site_name' in list(Prev_MagRec.keys()) and Prev_MagRec['site_name'] != "":
              site = Prev_MagRec['site_name']
          else:
              # parse out the site name
              site = pmag.parse_site(OrRec["sample_name"], samp_con, Z)
          MagRec["er_site_name"] = site
          site_description = ""  # overwrite any prior description
          if 'site_description' in list(OrRec.keys()) and OrRec['site_description'] != "":
              site_description = OrRec['site_description'].replace(",", ";")
          if "image_name" in list(OrRec.keys()):
              images = OrRec["image_name"].split(":")
              if "image_look" in list(OrRec.keys()):
                  looks = OrRec['image_look'].split(":")
              else:
                  looks = []
              if "image_photographer" in list(OrRec.keys()):
                  photographers = OrRec['image_photographer'].split(":")
              else:
                  photographers = []
              for image in images:
                  if image != "" and image not in imagelist:
                      imagelist.append(image)
                      ImageRec = {}
                      ImageRec['er_image_name'] = image
                      ImageRec['image_type'] = "outcrop"
                      ImageRec['image_date'] = sample_date
                      ImageRec['er_citation_names'] = "This study"
                      ImageRec['er_location_name'] = location_name
                      ImageRec['er_site_name'] = MagRec['er_site_name']
                      k = images.index(image)
                      if len(looks) > k:
                          ImageRec['er_image_description'] = "Look direction: " + looks[k]
                      elif len(looks) >= 1:
                          ImageRec['er_image_description'] = "Look direction: " + looks[-1]
                      else:
                          ImageRec['er_image_description'] = "Look direction: unknown"
                      if len(photographers) > k:
                          ImageRec['er_photographer_mail_names'] = photographers[k]
                      elif len(photographers) >= 1:
                          ImageRec['er_photographer_mail_names'] = photographers[-1]
                      else:
                          ImageRec['er_photographer_mail_names'] = "unknown"
                      ImageOuts.append(ImageRec)
          if site not in sitelist:
              sitelist.append(site)  # collect unique site names
              # patch added by rshaar 7/2016
              # if sample_name already exists in er_samples.txt:
              # merge the new data colmuns calculated by orientation_magic with the existing data colmuns
              # this is done to make sure no previous data in er_samples.txt and
              # er_sites.txt is lost.
  
              if site in list(SiteRecs_sorted.keys()):
                  Prev_MagRec = SiteRecs_sorted[site][-1]
                  SiteRec = Prev_MagRec
              else:
                  Prev_MagRec = {}
                  SiteRec = {}
              SiteRec["er_citation_names"] = "This study"
              SiteRec["er_site_name"] = site
              SiteRec["site_definition"] = "s"
  
              if "er_location_name" in SiteRec and SiteRec.get("er_location_name"):
                  pass
              elif key in list(Prev_MagRec.keys()) and Prev_MagRec[key] != "":
                  SiteRec[key] = Prev_MagRec[key]
              else:
                  print('setting location name to ""')
                  SiteRec[key] = ""
  
              for key in ["lat", "lon", "height"]:
                  if "site_" + key in list(Prev_MagRec.keys()) and Prev_MagRec["site_" + key] != "":
                      SiteRec["site_" + key] = Prev_MagRec["site_" + key]
                  else:
                      SiteRec["site_" + key] = MagRec["sample_" + key]
  
              # SiteRec["site_lat"]=MagRec["sample_lat"]
              # SiteRec["site_lon"]=MagRec["sample_lon"]
              # SiteRec["site_height"]=MagRec["sample_height"]
  
              for key in ["class", "lithology", "type"]:
                  if "site_" + key in list(Prev_MagRec.keys()) and Prev_MagRec["site_" + key] != "Not Specified":
                      SiteRec["site_" + key] = Prev_MagRec["site_" + key]
                  else:
                      SiteRec["site_" + key] = MagRec["sample_" + key]
  
              # SiteRec["site_class"]=MagRec["sample_class"]
              # SiteRec["site_lithology"]=MagRec["sample_lithology"]
              # SiteRec["site_type"]=MagRec["sample_type"]
  
              if site_description != "":  # overwrite only if site_description has something
                  SiteRec["site_description"] = site_description
              SiteOuts.append(SiteRec)
          if sample not in samplelist:
              samplelist.append(sample)
              if MagRec['sample_azimuth'] != "":  # assume magnetic compass only
                  MagRec['magic_method_codes'] = MagRec['magic_method_codes'] + ':SO-MAG'
                  MagRec['magic_method_codes'] = MagRec['magic_method_codes'].strip(
                      ":")
              SampOuts.append(MagRec)
              if MagRec['sample_azimuth'] != "" and dec_correction_con != 3:
                  az = labaz + dec_correction
                  if az > 360.:
                      az = az - 360.
                  CMDRec = {}
                  for key in list(MagRec.keys()):
                      CMDRec[key] = MagRec[key]  # make a copy of MagRec
                  CMDRec["sample_azimuth"] = '%7.1f' % (az)
                  CMDRec["magic_method_codes"] = methcodes + ':SO-CMD-NORTH'
                  CMDRec["magic_method_codes"] = CMDRec['magic_method_codes'].strip(
                      ':')
                  CMDRec["sample_declination_correction"] = '%7.1f' % (
                      dec_correction)
                  if dec_correction_con == 1:
                      CMDRec['sample_description'] = sample_description + \
                          ':Declination correction calculated from IGRF'
                  else:
                      CMDRec['sample_description'] = sample_description + \
                          ':Declination correction supplied by user'
                  CMDRec["sample_description"] = CMDRec['sample_description'].strip(
                      ':')
                  SampOuts.append(CMDRec)
              if "mag_az_bs" in list(OrRec.keys()) and OrRec["mag_az_bs"] != "" and OrRec["mag_az_bs"] != " ":
                  SRec = {}
                  for key in list(MagRec.keys()):
                      SRec[key] = MagRec[key]  # make a copy of MagRec
                  labaz = float(OrRec["mag_az_bs"])
                  az = labaz + dec_correction
                  if az > 360.:
                      az = az - 360.
                  SRec["sample_azimuth"] = '%7.1f' % (az)
                  SRec["sample_declination_correction"] = '%7.1f' % (
                      dec_correction)
                  SRec["magic_method_codes"] = methcodes + \
                      ':SO-SIGHT-BACK:SO-CMD-NORTH'
                  SampOuts.append(SRec)
      #
      # check for suncompass data
      #
              # there are sun compass data
              if "shadow_angle" in list(OrRec.keys()) and OrRec["shadow_angle"] != "":
                  if hours_from_gmt == "":
                      #hours_from_gmt=raw_input("Enter hours to subtract from time for  GMT: [0] ")
                      hours_from_gmt = 0
                  SunRec, sundata = {}, {}
                  shad_az = float(OrRec["shadow_angle"])
                  if not OrRec["hhmm"]:
                      print('If using the column shadow_angle for sun compass data, you must also provide the time for each sample.  Sample ',
                            sample, ' has shadow_angle but is missing the "hh:mm" column.')
                  else:  # calculate sun declination
                      sundata["date"] = '%i:%s:%s:%s' % (
                          yy, mmddyy[0], mmddyy[1], OrRec["hhmm"])
                      sundata["delta_u"] = hours_from_gmt
                      sundata["lon"] = lon  # do not truncate!
                      sundata["lat"] = lat  # do not truncate!
                      sundata["shadow_angle"] = OrRec["shadow_angle"]
                      # now you can truncate
                      sundec = '%7.1f' % (pmag.dosundec(sundata))
                      for key in list(MagRec.keys()):
                          SunRec[key] = MagRec[key]  # make a copy of MagRec
                      SunRec["sample_azimuth"] = sundec  # do not truncate!
                      SunRec["sample_declination_correction"] = ''
                      SunRec["magic_method_codes"] = methcodes + ':SO-SUN'
                      SunRec["magic_method_codes"] = SunRec['magic_method_codes'].strip(
                          ':')
                      SampOuts.append(SunRec)
      #
      # check for differential GPS data
      #
              # there are diff GPS data
              if "prism_angle" in list(OrRec.keys()) and OrRec["prism_angle"] != "":
                  GPSRec = {}
                  for key in list(MagRec.keys()):
                      GPSRec[key] = MagRec[key]  # make a copy of MagRec
                      prism_angle = float(OrRec["prism_angle"])
                      sundata["shadow_angle"] = OrRec["shadow_angle"]
                      sundec = pmag.dosundec(sundata)
                      for key in list(MagRec.keys()):
                          SunRec[key] = MagRec[key]  # make a copy of MagRec
                      SunRec["sample_azimuth"] = '%7.1f' % (sundec)
                      SunRec["sample_declination_correction"] = ''
                      SunRec["magic_method_codes"] = methcodes + ':SO-SUN'
                      SunRec["magic_method_codes"] = SunRec['magic_method_codes'].strip(
                          ':')
                      SampOuts.append(SunRec)
      #
      # check for differential GPS data
      #
              # there are diff GPS data
              if "prism_angle" in list(OrRec.keys()) and OrRec["prism_angle"] != "":
                  GPSRec = {}
                  for key in list(MagRec.keys()):
                      GPSRec[key] = MagRec[key]  # make a copy of MagRec
                  prism_angle = float(OrRec["prism_angle"])
                  laser_angle = float(OrRec["laser_angle"])
                  if OrRec["GPS_baseline"] != "":
                      baseline = float(OrRec["GPS_baseline"])  # new baseline
                  gps_dec = baseline + laser_angle + prism_angle - 90.
                  while gps_dec > 360.:
                      gps_dec = gps_dec - 360.
                  while gps_dec < 0:
                      gps_dec = gps_dec + 360.
                  for key in list(MagRec.keys()):
                      GPSRec[key] = MagRec[key]  # make a copy of MagRec
                  GPSRec["sample_azimuth"] = '%7.1f' % (gps_dec)
                  GPSRec["sample_declination_correction"] = ''
                  GPSRec["magic_method_codes"] = methcodes + ':SO-GPS-DIFF'
                  SampOuts.append(GPSRec)
              # there are differential GPS Azimuth data
              if "GPS_Az" in list(OrRec.keys()) and OrRec["GPS_Az"] != "":
                  GPSRec = {}
                  for key in list(MagRec.keys()):
                      GPSRec[key] = MagRec[key]  # make a copy of MagRec
                  GPSRec["sample_azimuth"] = '%7.1f' % (float(OrRec["GPS_Az"]))
                  GPSRec["sample_declination_correction"] = ''
                  GPSRec["magic_method_codes"] = methcodes + ':SO-GPS-DIFF'
                  SampOuts.append(GPSRec)
          if average_bedding != "0" and fpars:
              fpars = pmag.fisher_mean(BPs)
              print('over-writing all bedding with average ')
      Samps = []
      for rec in SampOuts:
          if average_bedding != "0" and fpars:
              rec['sample_bed_dip_direction'] = '%7.1f' % (fpars['dec'])
              rec['sample_bed_dip'] = '%7.1f' % (fpars['inc'] + 90.)
              Samps.append(rec)
          else:
              Samps.append(rec)
      for rec in SampRecs:
          if rec['er_sample_name'] not in samplelist:  # overwrite prior for this sample
              Samps.append(rec)
      for rec in SiteRecs:
          if rec['er_site_name'] not in sitelist:  # overwrite prior for this sample
              SiteOuts.append(rec)
      for rec in ImageRecs:
          if rec['er_image_name'] not in imagelist:  # overwrite prior for this sample
              ImageOuts.append(rec)
      print('saving data...')
      SampsOut, keys = pmag.fillkeys(Samps)
      Sites, keys = pmag.fillkeys(SiteOuts)
      if data_model == 3:
          SampsOut3 = []
          Sites3 = []
          for samp_rec in SampsOut:
              new_rec = map_magic.mapping(
                  samp_rec, map_magic.samp_magic2_2_magic3_map)
              SampsOut3.append(new_rec)
          for site_rec in Sites:
              new_rec = map_magic.mapping(
                  site_rec, map_magic.site_magic2_2_magic3_map)
              Sites3.append(new_rec)
          wrote_samps = pmag.magic_write(samp_file, SampsOut3, "samples")
          wrote_sites = pmag.magic_write(site_file, Sites3, "sites")
      else:
          wrote_samps = pmag.magic_write(samp_file, SampsOut, "er_samples")
          wrote_sites = pmag.magic_write(site_file, Sites, "er_sites")
      if wrote_samps:
          print("Data saved in ", samp_file, ' and ', site_file)
      else:
          print("No data found")
      if len(ImageOuts) > 0:
          # need to do conversion here 3.0. --> 2.5
          Images, keys = pmag.fillkeys(ImageOuts)
          image_type = "er_images"
          if data_model == 3:
              # convert 2.5 --> 3.0.
              image_type = "images"
              Images2 = Images
              Images = []
              for image_rec in Images2:
                  Images.append(map_magic.mapping(
                      image_rec, map_magic.image_magic2_2_magic3_map))
          pmag.magic_write(image_file, Images, image_type)
          print("Image info saved in ", image_file)
      return True, None