docs: def parse(self):
          """Parse our string and return a Survey object, None, or raise :exc:`ParseException`"""
          if not self.survey_str:
              return None
          lines = self.survey_str.splitlines()
          if len(lines) < 10:
              raise ParseException("Expected at least 10 lines in a Compass Survey, only found %d!\nlines=%s" % (len(lines), lines))
  
          # undelimited Cave Name may be empty string and "skipped"
          first_line = lines.pop(0).strip()
          if first_line.startswith('SURVEY NAME:'):
              cave_name = ''
              name = first_line.strip('SURVEY NAME:').strip()
          else:
              cave_name = first_line
              name = lines.pop(0).split('SURVEY NAME:', 1)[1].strip()
  
          # Date and Comment on one line, Comment may be missing
          date_comment_toks = lines.pop(0).split('SURVEY DATE:', 1)[1].split('COMMENT:')
          date = CompassSurveyParser._parse_date(date_comment_toks[0])
          comment = date_comment_toks[1].strip() if len(date_comment_toks) > 1 else ''
  
          lines.pop(0)  # SURVEY TEAM:\n (actual team members are on the next line)
          team = [member.strip() for member in lines.pop(0).split(',')]  # We're already decoding from windows-1252 codec so we have unicode for names like 'Tanya Pietra\xdf'
  
          # TODO: implement format (units!), instrument correction(s)
          dec_fmt_corr = lines.pop(0)
          declination, fmt, corrections, corrections2 = CompassSurveyParser._parse_declination_line(dec_fmt_corr)
  
          lines.pop(0)
          shot_header = lines.pop(0).split()
          val_count = len(shot_header) - 2 if 'FLAGS' in shot_header else len(shot_header)  # 1998 vintage data has no FLAGS, COMMENTS at end
          lines.pop(0)
  
          survey = Survey(name=name, date=date, comment=comment, team=team, cave_name=cave_name,
                          shot_header=shot_header, declination=declination,
                          file_format=fmt, corrections=corrections, corrections2=corrections2)
  
          shots = []
          shot_lines = lines
          for shot_line in shot_lines:
              shot_vals = shot_line.split(None, val_count)
  
              if len(shot_vals) > val_count:  # last two spare columns are FLAGS and COMMENTS, either value may be missing
                  flags_comment = shot_vals.pop()
                  if not flags_comment.startswith('#|'):
                      flags, comment = '', flags_comment
                  else:
                      try:
                          flags, comment = flags_comment.split('#|', 1)[1].split('#', 1)
                      except ValueError:
                          raise ParseException('Invalid flags in %s survey: %s' % (name, flags_comment))  # A 2013 bug in Compass inserted corrupt binary garbage into FLAGS column, causes parse to barf
                  shot_vals += [flags, comment.strip()]
  
              shot_vals = [(header, self._coerce(header, val)) for (header, val) in zip(shot_header, shot_vals)]
              shot = Shot(shot_vals)
              survey.add_shot(shot)
  
          #log.debug("Survey: name=%s shots=%d length=%0.1f date=%s team=%s\n%s", name, len(shots), survey.length, date, team, '\n'.join([str(shot) for shot in survey.shots]))
  
          return survey