docs: def excel_main(file):
      """
      Parse data from Excel spreadsheets into LiPD files.
  
      :param dict file: File metadata (source, name, etc)
      :return str dsn: Dataset name
      """
  
      os.chdir(file["dir"])
      name_ext = file["filename_ext"]
  
      # Filename without extension
      name = file["filename_no_ext"]
      # remove foreign characters to prevent wiki uploading errors
      name = normalize_name(name)
      print("processing: {}".format(name_ext))
      logger_excel.info("processing: {}".format(name_ext))
  
      pending_csv = []
      final = OrderedDict()
      logger_excel.info("variables initialized")
  
      # Create a temporary folder and set paths
      dir_tmp = create_tmp_dir()
  
      """
      EACH DATA TABLE WILL BE STRUCTURED LIKE THIS
      "paleo_chron": "paleo",
      "pc_idx": 1,
      "model_idx": "",
      "table_type": "measurement",
      "table_idx": 1,
      "name": sheet,
      "filename": sheet,
      "data": { column data }
      """
  
      # Open excel workbook with filename
      try:
          workbook = xlrd.open_workbook(name_ext)
          logger_excel.info("opened XLRD workbook")
  
      except Exception as e:
          # There was a problem opening a file with XLRD
          print("Failed to open Excel workbook: {}".format(name))
          workbook = None
          logger_excel.debug("excel: xlrd failed to open workbook: {}, {}".format(name, e))
  
      if workbook:
  
          # Build sheets, but don't make full filenames yet. Need to parse metadata sheet first to get datasetname.
          sheets, ct_paleo, ct_chron, metadata_str = _get_sheet_metadata(workbook, name)
  
          # METADATA WORKSHEETS
          # Parse Metadata sheet and add to output dictionary
          if metadata_str:
              logger_excel.info("parsing worksheet: {}".format(metadata_str))
              final = cells_dn_meta(workbook, metadata_str, 0, 0, final)
  
          # Now that we have the dataSetName, we can use it to build filenames
          dsn = __get_datasetname(final, name)
          filename = str(dsn) + ".lpd"
          sheets = __set_sheet_filenames(sheets, dsn)
  
          dir_bag = os.path.join(dir_tmp, "bag")
          dir_data = os.path.join(dir_bag, 'data')
  
          # Make folders in tmp
          os.mkdir(os.path.join(dir_bag))
          os.mkdir(os.path.join(dir_data))
  
          # PALEO AND CHRON SHEETS
          for sheet in sheets:
              logger_excel.info("parsing data worksheet: {}".format(sheet["new_name"]))
              sheet_meta, sheet_csv = _parse_sheet(workbook, sheet)
              if sheet_csv and sheet_meta:
                  pending_csv.append(sheet_csv)
                  sheet["data"] = sheet_meta
  
          # create the metadata skeleton where we will place the tables. dynamically add empty table blocks for data.
          skeleton_paleo, skeleton_chron = _create_skeleton_1(sheets)
  
          # Reorganize sheet metadata into LiPD structure
          d_paleo, d_chron = _place_tables_main(sheets, skeleton_paleo, skeleton_chron)
  
          # Add organized metadata into final dictionary
          final['paleoData'] = d_paleo
          final['chronData'] = d_chron
  
          # OUTPUT
  
          # Create new files and dump data in dir_data
          os.chdir(dir_data)
  
          # WRITE CSV
          _write_data_csv(pending_csv)
  
          # JSON-LD
          # Invoke DOI Resolver Class to update publisher data
          try:
              logger_excel.info("invoking doi resolver")
              final = DOIResolver(file["dir"], name, final).main()
          except Exception as e:
              print("Error: doi resolver failed: {}".format(name))
              logger_excel.debug("excel: doi resolver failed: {}, {}".format(name, e))
  
          # Dump final_dict to a json file.
          final["lipdVersion"] = 1.2
          final["createdBy"] = "excel"
          write_json_to_file(final)
  
          # Move files to bag root for re-bagging
          # dir : dir_data -> dir_bag
          logger_excel.info("start cleanup")
          dir_cleanup(dir_bag, dir_data)
  
          # Create a bag for the 3 files
          finish_bag(dir_bag)
  
          # dir: dir_tmp -> dir_root
          os.chdir(file["dir"])
  
          # Check if same lpd file exists. If so, delete so new one can be made
          if os.path.isfile(filename):
              os.remove(filename)
  
          # Zip dir_bag. Creates in dir_root directory
          logger_excel.info("re-zip and rename")
          zipper(root_dir=dir_tmp, name="bag", path_name_ext=os.path.join(file["dir"], filename))
  
      # Move back to dir_root for next loop.
      os.chdir(file["dir"])
  
      # Cleanup and remove tmp directory
      shutil.rmtree(dir_tmp)
  
      return dsn