docs: def create_participant(worker_id, hit_id, assignment_id, mode):
      """Create a participant.
  
      This route is hit early on. Any nodes the participant creates will be
      defined in reference to the participant object. You must specify the
      worker_id, hit_id, assignment_id, and mode in the url.
      """
      # Lock the table, triggering multiple simultaneous accesses to fail
      try:
          session.connection().execute("LOCK TABLE participant IN EXCLUSIVE MODE NOWAIT")
      except exc.OperationalError as e:
          e.orig = TransactionRollbackError()
          raise e
  
      missing = [p for p in (worker_id, hit_id, assignment_id) if p == "undefined"]
      if missing:
          msg = "/participant POST: required values were 'undefined'"
          return error_response(error_type=msg, status=403)
  
      fingerprint_hash = request.args.get("fingerprint_hash")
      try:
          fingerprint_found = models.Participant.query.filter_by(
              fingerprint_hash=fingerprint_hash
          ).one_or_none()
      except MultipleResultsFound:
          fingerprint_found = True
  
      if fingerprint_hash and fingerprint_found:
          db.logger.warning("Same browser fingerprint detected.")
  
          if mode == "live":
              return error_response(
                  error_type="/participant POST: Same participant dectected.", status=403
              )
  
      already_participated = models.Participant.query.filter_by(
          worker_id=worker_id
      ).one_or_none()
  
      if already_participated:
          db.logger.warning("Worker has already participated.")
          return error_response(
              error_type="/participant POST: worker has already participated.", status=403
          )
  
      duplicate = models.Participant.query.filter_by(
          assignment_id=assignment_id, status="working"
      ).one_or_none()
  
      if duplicate:
          msg = """
              AWS has reused assignment_id while existing participant is
              working. Replacing older participant {}.
          """
          app.logger.warning(msg.format(duplicate.id))
          q.enqueue(worker_function, "AssignmentReassigned", None, duplicate.id)
  
      # Count working or beyond participants.
      nonfailed_count = (
          models.Participant.query.filter(
              (models.Participant.status == "working")
              | (models.Participant.status == "overrecruited")
              | (models.Participant.status == "submitted")
              | (models.Participant.status == "approved")
          ).count()
          + 1
      )
  
      recruiter_name = request.args.get("recruiter", "undefined")
      if not recruiter_name or recruiter_name == "undefined":
          recruiter = recruiters.from_config(_config())
          if recruiter:
              recruiter_name = recruiter.nickname
  
      # Create the new participant.
      participant = models.Participant(
          recruiter_id=recruiter_name,
          worker_id=worker_id,
          assignment_id=assignment_id,
          hit_id=hit_id,
          mode=mode,
          fingerprint_hash=fingerprint_hash,
      )
  
      exp = Experiment(session)
  
      overrecruited = exp.is_overrecruited(nonfailed_count)
      if overrecruited:
          participant.status = "overrecruited"
  
      session.add(participant)
      session.flush()  # Make sure we know the id for the new row
      result = {"participant": participant.__json__()}
  
      # Queue notification to others in waiting room
      if exp.quorum:
          quorum = {"q": exp.quorum, "n": nonfailed_count, "overrecruited": overrecruited}
          db.queue_message(WAITING_ROOM_CHANNEL, dumps(quorum))
          result["quorum"] = quorum
  
      # return the data
      return success_response(**result)