docs: def create_entry_line_from_text(self, text):
          """
          Try to parse the given text line and extract and entry. Return an :class:`~taxi.timesheet.lines.Entry`
          object if parsing is successful, otherwise raise :exc:`~taxi.exceptions.ParseError`.
          """
          split_line = re.match(self.entry_line_regexp, text)
  
          if not split_line:
              raise ParseError("Line must have an alias, a duration and a description")
  
          alias = split_line.group('alias')
          start_time = end_time = None
  
          if split_line.group('start_time') is not None:
              if split_line.group('start_time'):
                  try:
                      start_time = create_time_from_text(split_line.group('start_time'))
                  except ValueError:
                      raise ParseError("Start time is not a valid time, it must be in format hh:mm or hhmm")
              else:
                  start_time = None
  
          if split_line.group('end_time') is not None:
              if split_line.group('end_time') == '?':
                  end_time = None
              else:
                  try:
                      end_time = create_time_from_text(split_line.group('end_time'))
                  except ValueError:
                      raise ParseError("End time is not a valid time, it must be in format hh:mm or hhmm")
  
          if split_line.group('duration') is not None:
              duration = float(split_line.group('duration'))
          elif start_time or end_time:
              duration = (start_time, end_time)
          else:
              duration = (None, None)
  
          description = split_line.group('description')
  
          # Parse and set line flags
          if split_line.group('flags'):
              try:
                  flags = self.extract_flags_from_text(split_line.group('flags'))
              # extract_flags_from_text will raise `KeyError` if one of the flags is not recognized. This should never
              # happen though as the list of accepted flags is bundled in self.entry_line_regexp
              except KeyError as e:
                  raise ParseError(*e.args)
          else:
              flags = set()
  
          # Backwards compatibility with previous notation that allowed to end the alias with a `?` to ignore it
          if alias.endswith('?'):
              flags.add(Entry.FLAG_IGNORED)
              alias = alias[:-1]
  
          if description == '?':
              flags.add(Entry.FLAG_IGNORED)
  
          line = (
              split_line.group('flags') or '',
              split_line.group('spacing1') or '',
              split_line.group('alias'),
              split_line.group('spacing2'),
              split_line.group('time'),
              split_line.group('spacing3'),
              split_line.group('description'),
          )
  
          entry_line = Entry(alias, duration, description, flags=flags, text=line)
  
          return entry_line