docs: def propagate_all_tables_info(self, write=True):
          """
          Find any items (specimens, samples, sites, or locations) from
          tables other than measurements and make sure they each have a
          row in their own table.  For example, if a site name is in
          the samples table but not in the sites table, create a row
          for it in the sites table.
          """
          for table_name in ["specimens", "samples", "sites", "locations"]:
              if not table_name in self.tables:
                  continue
              df = self.tables[table_name].df
              parent_name, child_name = self.get_parent_and_child(table_name)
              if parent_name:
                  if parent_name[:-1] in df.columns:
                      parents = sorted(set(df[parent_name[:-1]].dropna().values.astype(str)))
                      if parent_name in self.tables: # if there is a parent table, update it
                          parent_df = self.tables[parent_name].df
                          missing_parents = set(parents) - set(parent_df.index)
                          if missing_parents: # add any missing values
                              print("-I- Updating {} table with values from {} table".format(parent_name, table_name))
                              for item in missing_parents:
                                  self.add_item(parent_name, {parent_name[:-1]: item}, label=item)
                              # save any changes to file
                              if write:
                                  self.write_table_to_file(parent_name)
  
                      else:  # if there is no parent table, create it if necessary
                          if parents:
                              # create a parent_df with the names you got from the child
                              print("-I- Creating new {} table with data from {} table".format(parent_name, table_name))
                              # add in the grandparent if available
                              grandparent_name = self.get_parent_and_child(parent_name)[0]
                              if grandparent_name:
                                  grandparent = ""
                                  if grandparent_name in df.columns:
                                      grandparent = df[df[parent_name] == item][grandparent_name].values[0]
                                  columns = [parent_name[:-1]]#, grandparent_name[:-1]]
                              else:
                                  columns = [parent_name[:-1]]
  
                              parent_df = pd.DataFrame(columns=columns, index=parents)
                              parent_df[parent_name[:-1]] = parent_df.index
                              if grandparent_name:
                                  if grandparent_name[:-1] in df.columns:
                                      parent_df = pd.merge(df[[parent_name[:-1], grandparent_name[:-1]]], parent_df, on=parent_name[:-1])
                              self.tables[parent_name] = MagicDataFrame(dtype=parent_name,
                                                                        df=parent_df)
                              if write:
                                  # save new table to file
                                  self.write_table_to_file(parent_name)
              if child_name:
                  if child_name in df.columns:
                      raw_children = df[child_name].dropna().str.split(':')
                      # create dict of all children with parent info
                      parent_of_child = {}
                      for parent, children in raw_children.items():
                          for child in children:
                              # remove whitespace
                              child = child.strip()
                              old_parent = parent_of_child.get(child)
                              if old_parent and parent and (old_parent != parent):
                                  print('-I- for {} {}, replacing: {} with: {}'.format(child_name[:-1], child,
                                                                                       old_parent, parent))
                              parent_of_child[child] = parent
                      # old way:
                      # flatten list, ignore duplicates
                      #children = sorted(set([item.strip() for sublist in raw_children for item in sublist]))
                      if child_name in self.tables: # if there is already a child table, update it
                          child_df = self.tables[child_name].df
                          missing_children = set(parent_of_child.keys()) - set(child_df.index)
                          if missing_children: # add any missing values
                              print("-I- Updating {} table with values from {} table".format(child_name, table_name))
                              for item in missing_children:
                                  data = {child_name[:-1]: item, table_name[:-1]: parent_of_child[item]}
                                  self.add_item(child_name, data, label=item)
                              if write:
                                  # save any changes to file
                                  self.write_table_to_file(child_name)
                      else: # if there is no child table, create it if necessary
                          if children:
                              # create a child_df with the names you got from the parent
                              print("-I- Creating new {} table with data from {} table".format(child_name, table_name))
                              # old way to make new table:
                              #child_df = pd.DataFrame(columns=[table_name[:-1]], index=children)
                              # new way to make new table
                              children_list = sorted(parent_of_child.keys())
                              children_data = [[child_name, parent_of_child[c_name]] for c_name in children_list]
                              child_df = pd.DataFrame(index=children_list, columns=[child_name[:-1], table_name[:-1]], data=children_data)
  
                              self.tables[child_name] = MagicDataFrame(dtype=child_name, df=child_df)
                              if write:
                                  # save new table to file
                                  self.write_table_to_file(child_name)