docs: def read_upload(up_file, data_model=None):
      """
      take a file that should be ready for upload
      using the data model, check that all required columns are full,
      and that all numeric data is in fact numeric.
      print out warnings for any validation problems
      return True if there were no problems, otherwise return False
      """
      print("-I- Running validation for your upload file")
  
      ## Read file
      f = open(up_file)
      lines = f.readlines()
      f.close()
      data = split_lines(lines)
      data_dicts = get_dicts(data)
      ## initialize
      invalid_data = {}
      missing_data = {}
      non_numeric = {}
      bad_vocab = {}
      bad_coords = {}
      invalid_col_names = {}
      missing_file_type = False
      ## make sure you have the data model
      if not data_model:
          data_model = get_data_model()
      reqd_file_types = ['er_locations']
      provided_file_types = set()
      if not data_model:
          return False, None
      ## Iterate through data
      # each dictionary is one tab delimited line in a csv file
      for dictionary in data_dicts:
          for k, v in list(dictionary.items()):
              if k == "file_type": # meta data
                  provided_file_types.add(v)
                  continue
              file_type = dictionary['file_type']
              # need to deal with pmag_criteria type file, too
              item_type = file_type.split('_')[1][:-1]
              if item_type == 'criteria':
                  item_name = dictionary.get('criteria_definition')
              elif item_type == 'result':
                  item_name = dictionary.get('pmag_result_name', None)
              elif item_type in ('specimen', 'sample', 'site', 'location'):
                  item_name = dictionary.get('er_' + item_type + '_name', None)
              elif item_type == 'age':
                  # get the lowest level er_*_name column that is filled in
                  for dtype in ('specimen', 'sample', 'site', 'location'):
                      item_name = dictionary.get('er_' + dtype + '_name', None)
                      if item_name:
                          break
              elif item_type == 'measurement':
                  exp_name = dictionary.get('magic_experiment_name')
                  meas_num = dictionary.get('measurement_number')
                  item_name = exp_name + '_' + str(meas_num)
              else:
                  item_name = None
  
              if file_type not in list(data_model.keys()):
                  continue
              specific_data_model = data_model[file_type]
  
              ## Function for building problems list
              def add_to_invalid_data(item_name, item_type, invalid_data,
                                      validation, problem_type):
                  """
                  correctly create or add to the dictionary of invalid values
                  """
                  if item_name:
                      if item_type not in invalid_data:
                          invalid_data[item_type] = {}
                      if item_name not in invalid_data[item_type]:
                          invalid_data[item_type][item_name] = {}
                      if problem_type not in invalid_data[item_type][item_name]:
                          invalid_data[item_type][item_name][problem_type] = []
                      invalid_data[item_type][item_name][problem_type].append(validation)
  
              ## Validate for each problem type
  
              # check if column header is in the data model
              invalid_col_name = validate_for_recognized_column(k, v, specific_data_model)
              if invalid_col_name:
                  if item_type not in list(invalid_col_names.keys()):
                      invalid_col_names[item_type] = set()
                  invalid_col_names[item_type].add(invalid_col_name)
                  # skip to next item, as additional validations won't work
                  # (key is not in the data model)
  
                  ## new style
                  add_to_invalid_data(item_name, item_type, invalid_data,
                                      invalid_col_name, 'invalid_col')
                  # skip to next item, as additional validations won't work
                  # (key is not in the data model)
                  continue
  
              # make a list of missing, required data
              missing_item = validate_for_presence(k, v, specific_data_model)
              #print 'k, v', k, v
              if missing_item:
                  if item_type not in list(missing_data.keys()):
                      missing_data[item_type] = set()
                  missing_data[item_type].add(missing_item)
                  if item_name:
                      # don't double count if a site is missing its parent location
                      if item_type == 'age' and missing_item == 'er_location_name':
                          pass
                      # ignore er_synthetic_name (data model is incorrect here)
                      if missing_item == 'er_synthetic_name':
                          pass
                      else:
                          add_to_invalid_data(item_name, item_type, invalid_data,
                                              missing_item, 'missing_data')
  
              # vocabulary problems
              vocab_problem = validate_for_controlled_vocab(k, v, specific_data_model)
              if vocab_problem:
                  if item_type not in list(bad_vocab.keys()):
                      bad_vocab[item_type] = set()
                  bad_vocab[item_type].add(vocab_problem)
                  add_to_invalid_data(item_name, item_type, invalid_data,
                                      vocab_problem, 'vocab_problem')
  
              # illegal coordinates
              coord_problem = validate_for_coordinates(k, v, specific_data_model)
              if coord_problem:
                  if item_type not in list(bad_coords.keys()):
                      bad_coords[item_type] = set()
                  bad_coords[item_type].add(coord_problem)
                  add_to_invalid_data(item_name, item_type, invalid_data,
                                      coord_problem, 'coordinates')
  
              # make a list of data that should be numeric, but aren't
              number_fail = validate_for_numericality(k, v, specific_data_model)
              if number_fail:
                  if item_type not in list(non_numeric.keys()):
                      non_numeric[item_type] = set()
                  non_numeric[item_type].add(number_fail)
                  add_to_invalid_data(item_name, item_type, invalid_data,
                                      number_fail, 'number_fail')
  
      ## Print out all issues
  
      for file_type, invalid_names in list(invalid_col_names.items()):
          print("-W- In your {} file, you are using the following unrecognized columns: {}".format(file_type, ', '.join(invalid_names)))
  
      for file_type, wrong_cols in list(non_numeric.items()):
          print("-W- In your {} file, you must provide only valid numbers, in the following columns: {}".format(file_type, ', '.join(wrong_cols)))
  
      for file_type, empty_cols in list(missing_data.items()):
          print("-W- In your {} file, you are missing data in the following required columns: {}".format(file_type, ', '.join(empty_cols)))
  
      for file_type in reqd_file_types:
          if file_type not in provided_file_types:
              print("-W- You have not provided a(n) {} type file, which is required data".format(file_type))
              missing_file_type = True
  
      for file_type, vocab_types in list(bad_vocab.items()):
          print("-W- In your {} file, you are using an unrecognized value for these controlled vocabularies: {}".format(file_type, ', '.join(vocab_types)))
  
      for file_type, coords in list(bad_coords.items()):
          print("-W- In your {} file, you are using an illegal value for these columns: {}.  (Latitude must be between -90 and +90)".format(file_type, ', '.join(coords)))
  
  
      if any((invalid_col_names, non_numeric, missing_data, missing_file_type, bad_vocab, bad_coords)):
          return False, invalid_data
      else:
          print("-I- validation was successful")
          return True, None