def compute_general_helpfulness_scores(
  scoredNotes: pd.DataFrame,
  validRatings: pd.DataFrame,
  minMeanNoteScore: float,
  minCRHVsCRNHRatio: float,
  minRaterAgreeRatio: float,
  ratings: Optional[pd.DataFrame] = None,
  tagConsensusHarassmentAbuseNotes: Optional[pd.DataFrame] = None,
  tagConsensusHarassmentHelpfulRatingPenalty=10,
  multiplyPenaltyByHarassmentScore: bool = True,
  minimumHarassmentScoreToPenalize: float = 2.0,
) -> pd.DataFrame:
  """Given notes scored by matrix factorization, compute helpfulness scores.
  Author helpfulness scores are based on the scores of the notes you wrote.
  Rater helpfulness scores are based on how the ratings you made match up with note scores.
  See https://twitter.github.io/communitynotes/contributor-scores/.

  Args:
      scoredNotes: one row per note, containing preliminary note statuses.
      validRatings: ratings to use.
      minMeanNoteScore: minimum average intercept for notes written.
      minCRHVsCRNHRatio: threshold applied to crhCrnhRatioDifference, a weighted measure
        comparing how often an author produces CRH / CRNH notes.  See author_helpfulness.
      minRaterAgreeRatio: minimum standard for how often a rater must predict the
        eventual outcome when rating before a note is assigned status.
      ratings: all ratings (to check if tag-consensus harassment/abuse notes were rated helpful)
  Returns:
      helpfulness_scores pandas.DataFrame: 1 row per user, with helpfulness scores as columns.
  """
  # don't consider any notes which we didn't score
  scoredNotes = scoredNotes[~pd.isna(scoredNotes[c.internalNoteInterceptKey])]
  authorCounts = author_helpfulness(scoredNotes, c.internalNoteInterceptKey)
  raterCounts = _rater_helpfulness(validRatings)

  helpfulnessScores = (
    authorCounts.join(
      raterCounts,
      how="outer",
      lsuffix="_author",
      rsuffix="_rater",
      unsafeAllowed={
        c.defaultIndexKey,
        c.currentlyRatedHelpfulBoolKey,
        c.currentlyRatedNotHelpfulBoolKey,
        c.noteCountKey,
        c.ratingAgreesWithNoteStatusKey,
        # ratingCountKey was added with the migration to Pandas 2.2.2 because type checking showed
        # a new conversion from int64 to float64.  Given the outer join and hte data involved, that
        # type conversion is actually expected.  Additionally, we already have an exception for an
        # int64 to float64 type conversion for ratingAgreesWithNoteStatusKey, which suggests the only
        # reason we didn't see warnings for ratingCountKey before was that they type may have already
        # been float64 going into the join.
        c.ratingCountKey,
        c.successfulRatingNotHelpfulCount,
        c.successfulRatingHelpfulCount,
        c.unsuccessfulRatingNotHelpfulCount,
        c.unsuccessfulRatingHelpfulCount,
      },
    )
    .reset_index()
    .rename({"index": c.raterParticipantIdKey}, axis=1)[
      [
        c.raterParticipantIdKey,
        c.crhCrnhRatioDifferenceKey,
        c.meanNoteScoreKey,
        c.raterAgreeRatioKey,
        c.ratingAgreesWithNoteStatusKey,
        c.ratingCountKey,
        c.successfulRatingNotHelpfulCount,
        c.successfulRatingHelpfulCount,
        c.unsuccessfulRatingNotHelpfulCount,
        c.unsuccessfulRatingHelpfulCount,
      ]
    ]
  )

  if (ratings is None) or (tagConsensusHarassmentAbuseNotes is None):
    helpfulnessScores[c.totalHelpfulHarassmentRatingsPenaltyKey] = 0
  else:
    filteredAbuseNotes = tagConsensusHarassmentAbuseNotes[
      tagConsensusHarassmentAbuseNotes[c.harassmentNoteInterceptKey]
      >= minimumHarassmentScoreToPenalize
    ]
    helpfulRatingsOnBadNotes = ratings[ratings[c.helpfulNumKey] == 1].merge(
      filteredAbuseNotes, on=c.noteIdKey
    )

    helpfulRatingsOnBadNotes[
      c.totalHelpfulHarassmentRatingsPenaltyKey
    ] = tagConsensusHarassmentHelpfulRatingPenalty
    if multiplyPenaltyByHarassmentScore:
      helpfulRatingsOnBadNotes[c.totalHelpfulHarassmentRatingsPenaltyKey] *= (
        helpfulRatingsOnBadNotes[c.harassmentNoteInterceptKey] / minimumHarassmentScoreToPenalize
      )

    helpfulRatingsOnBadNotesCount = (
      helpfulRatingsOnBadNotes[[c.raterParticipantIdKey, c.totalHelpfulHarassmentRatingsPenaltyKey]]
      .groupby(c.raterParticipantIdKey)[[c.totalHelpfulHarassmentRatingsPenaltyKey]]
      .sum()
      .reset_index()
    )
    helpfulnessScores = helpfulnessScores.merge(
      helpfulRatingsOnBadNotesCount,
      on=c.raterParticipantIdKey,
      how="left",
      unsafeAllowed=c.totalHelpfulHarassmentRatingsPenaltyKey,
    )
    helpfulnessScores[c.totalHelpfulHarassmentRatingsPenaltyKey].fillna(0, inplace=True)

  helpfulnessScores[c.raterAgreeRatioWithHarassmentAbusePenaltyKey] = (
    helpfulnessScores[c.ratingAgreesWithNoteStatusKey]
    - helpfulnessScores[c.totalHelpfulHarassmentRatingsPenaltyKey]
  ) / helpfulnessScores[c.ratingCountKey]

  helpfulnessScores[c.aboveHelpfulnessThresholdKey] = (
    (
      (
        (helpfulnessScores[c.crhCrnhRatioDifferenceKey] >= minCRHVsCRNHRatio)
        & (helpfulnessScores[c.meanNoteScoreKey] >= minMeanNoteScore)
      )
      | (
        pd.isna(helpfulnessScores[c.crhCrnhRatioDifferenceKey])
        & pd.isna(helpfulnessScores[c.meanNoteScoreKey])
      )
      | (
        pd.isna(helpfulnessScores[c.crhCrnhRatioDifferenceKey])
        & helpfulnessScores[c.meanNoteScoreKey]
        >= minMeanNoteScore
      )
    )
    & (helpfulnessScores[c.raterAgreeRatioKey] >= minRaterAgreeRatio)
    & (helpfulnessScores[c.raterAgreeRatioWithHarassmentAbusePenaltyKey] >= minRaterAgreeRatio)
  )

  helpfulnessScores.drop(columns=[c.ratingCountKey, c.ratingAgreesWithNoteStatusKey], inplace=True)
  return helpfulnessScores
Given notes scored by matrix factorization, compute helpfulness scores.
  Author helpfulness scores are based on the scores of the notes you wrote.
  Rater helpfulness scores are based on how the ratings you made match up with note scores.
  See https://twitter.github.io/communitynotes/contributor-scores/.

  Args:
      scoredNotes: one row per note, containing preliminary note statuses.
      validRatings: ratings to use.
      minMeanNoteScore: minimum average intercept for notes written.
      minCRHVsCRNHRatio: threshold applied to crhCrnhRatioDifference, a weighted measure
        comparing how often an author produces CRH / CRNH notes.  See author_helpfulness.
      minRaterAgreeRatio: minimum standard for how often a rater must predict the
        eventual outcome when rating before a note is assigned status.
      ratings: all ratings (to check if tag-consensus harassment/abuse notes were rated helpful)
  Returns:
      helpfulness_scores pandas.DataFrame: 1 row per user, with helpfulness scores as columns.
  
sourcecode/scoring/helpfulness_scores.py