in sourcecode/scoring/note_status_history.py [0:0]
def _update_single_note_status_history(mergedNote, currentTimeMillis, newScoredNotesSuffix="_sn"):
"""For a single row (representing one note) in note status history, which contains fields
merged between the old note status history and the note's current status, compute the new
note's status history.
Args:
mergedNote: row of a pd.DataFrame, result of joining noteStatusHistory with scoredNotes
currentTimeMillis: the timestamp to use for the note's new status in note_status_history
newScoredNotesSuffix (str, optional): Defaults to "_sn". Merge suffix to distinguish
fields from previous noteStatusHistory vs new note status.
Returns:
row of pd.DataFrame
"""
# This TS will be set by run_combine_scoring_outputs.
mergedNote[c.timestampMinuteOfFinalScoringOutput] = np.nan
# TODO(jiansongc): remove after new column is in prod.
if c.timestampMillisOfFirstNmrDueToMinStableCrhTimeKey not in mergedNote:
mergedNote[c.timestampMillisOfFirstNmrDueToMinStableCrhTimeKey] = np.nan
if not pd.isna(mergedNote[c.updatedTimestampMillisOfNmrDueToMinStableCrhTimeKey]):
mergedNote[c.timestampMillisOfNmrDueToMinStableCrhTimeKey] = mergedNote[
c.updatedTimestampMillisOfNmrDueToMinStableCrhTimeKey
]
if pd.isna(mergedNote[c.timestampMillisOfFirstNmrDueToMinStableCrhTimeKey]):
mergedNote[c.timestampMillisOfFirstNmrDueToMinStableCrhTimeKey] = mergedNote[
c.timestampMillisOfNmrDueToMinStableCrhTimeKey
]
if mergedNote[c.finalRatingStatusKey] != mergedNote[c.currentLabelKey]:
# Changed status vs. previous run:
mergedNote[c.timestampMillisOfMostRecentStatusChangeKey] = currentTimeMillis
else:
# No change in status vs. previous run
# If the note has not changed status (since the launch of this feature on 2024/07/02),
# then the timestamp of the most recent status change should be set to -1 by default.
if c.timestampMillisOfMostRecentStatusChangeKey not in mergedNote.index:
mergedNote[c.timestampMillisOfMostRecentStatusChangeKey] = -1
elif pd.isna(mergedNote[c.timestampMillisOfMostRecentStatusChangeKey]):
mergedNote[c.timestampMillisOfMostRecentStatusChangeKey] = -1
# Update the current status in accordance with this scoring run.
assert not pd.isna(mergedNote[c.finalRatingStatusKey])
mergedNote[c.currentLabelKey] = mergedNote[c.finalRatingStatusKey]
mergedNote[c.currentCoreStatusKey] = mergedNote[c.coreRatingStatusKey]
mergedNote[c.currentExpansionStatusKey] = mergedNote[c.expansionRatingStatusKey]
mergedNote[c.currentGroupStatusKey] = mergedNote[c.groupRatingStatusKey]
mergedNote[c.currentDecidedByKey] = mergedNote[c.decidedByKey]
mergedNote[c.currentModelingGroupKey] = mergedNote[c.modelingGroupKey]
mergedNote[c.timestampMillisOfNoteCurrentLabelKey] = currentTimeMillis
mergedNote[c.currentMultiGroupStatusKey] = mergedNote[c.multiGroupRatingStatusKey]
mergedNote[c.currentModelingMultiGroupKey] = mergedNote[c.modelingMultiGroupKey]
# Lock notes which are (1) not already locked, (2) old enough to lock and (3)
# were decided by logic which has global display impact. Criteria (3) guarantees
# that any CRH note which is shown to all users will have the status locked, but
# also means that if a note is scored as NMR by all trusted models and CRH by an
# experimental model we will avoid locking the note to NMR even though the note was
# in scope for a trusted model and scored as NMR at the time of status locking.
# The note will continue to be CRH only as long as an experimental model continues
# scoring the note as CRH (or a core model scores the note as CRH). If at any point
# both experimental models and core models score the note as NMR, then the note will
# lock to NMR.
# Check whether the note has already been locked.
notAlreadyLocked = pd.isna(mergedNote[c.lockedStatusKey])
# Check whether the note is old enough to be eligible for locking.
lockEligible = c.noteLockMillis < (currentTimeMillis - mergedNote[c.createdAtMillisKey])
# Check whether the note was decided by a rule which displays globally
trustedRule = mergedNote[c.decidedByKey] in {
rule.get_name() for rule in RuleID if rule.value.lockingEnabled
}
if notAlreadyLocked and lockEligible and trustedRule:
mergedNote[c.lockedStatusKey] = mergedNote[c.finalRatingStatusKey]
mergedNote[c.timestampMillisOfStatusLockKey] = currentTimeMillis
# Clear timestampMillisOfNmrDueToMinStableCrhTimeKey if the note is locked.
if pd.notna(mergedNote[c.lockedStatusKey]):
mergedNote[c.timestampMillisOfNmrDueToMinStableCrhTimeKey] = -1
if pd.isna(mergedNote[c.createdAtMillisKey + newScoredNotesSuffix]):
# note used to be scored but isn't now; just retain old info
return mergedNote
# If the notes created before the deleted note cutfoff. Retain
# the old data.
if mergedNote[c.createdAtMillisKey] < c.deletedNoteTombstonesLaunchTime:
return mergedNote
if np.invert(pd.isna(mergedNote[c.createdAtMillisKey])) & np.invert(
pd.isna(mergedNote[c.createdAtMillisKey + newScoredNotesSuffix])
):
assert (
mergedNote[c.createdAtMillisKey] == mergedNote[c.createdAtMillisKey + newScoredNotesSuffix]
)
if pd.isna(mergedNote[c.createdAtMillisKey]):
raise Exception("This should be impossible, we already called add new notes")
if mergedNote[c.finalRatingStatusKey] != c.needsMoreRatings:
if pd.isna(mergedNote[c.firstNonNMRLabelKey]):
# first time note has a status
mergedNote[c.firstNonNMRLabelKey] = mergedNote[c.finalRatingStatusKey]
mergedNote[c.timestampMillisOfNoteFirstNonNMRLabelKey] = currentTimeMillis
mergedNote[c.mostRecentNonNMRLabelKey] = mergedNote[c.finalRatingStatusKey]
mergedNote[c.timestampMillisOfNoteMostRecentNonNMRLabelKey] = currentTimeMillis
if mergedNote[c.finalRatingStatusKey] != mergedNote[c.mostRecentNonNMRLabelKey]:
# NOTE: By design, this branch captures label flips between CRH and CRNH but not NMR.
mergedNote[c.mostRecentNonNMRLabelKey] = mergedNote[c.finalRatingStatusKey]
mergedNote[c.timestampMillisOfNoteMostRecentNonNMRLabelKey] = currentTimeMillis
return mergedNote