""" Contains MV audit related sheets. """ from abc import ABC from functools import cache import pandas as pd from ...... import logger from ......constants import AuditTypes, DBColumns from ......constants import FlagResolutions as Resolutions from ......constants import Flags, MiscLower from ......constants import RowReportColors as Colors from ......constants import RowReportCols as Cols from ......constants import RowReportColsHuman as ColsHuman from ......constants import RowReportTabs as Tabs from ......constants import RowReportTexts as Texts from ......constants import RowTypes from ......logic.audit.flags import conditions from ......typings import Row, Rows from .base import BaseSheet, Column from .sr import AudioAudit logger = logger.new_logger(__name__) YES = Texts.YES NO = Texts.NO class BaseSheetMV(BaseSheet, ABC): filterAuditTypes = {AuditTypes.MV} columns = [ Column(key=key, label=label, is_hyperlink=is_hyperlink) for key, label, is_hyperlink in [ (Cols.LABEL_NAME, ColsHuman.LABEL_NAME, False), (Cols.CHANNEL_NAME, ColsHuman.CHANNEL_NAME, False), (Cols.TIME_UPLOADED, ColsHuman.UPLOAD_DATE, False), (Cols.VIDEO_TITLE, ColsHuman.VIDEO_TITLE, False), (Cols.VIDEO_LINK, ColsHuman.VIDEO_LINK, True), ] ] @staticmethod def _extract_rows_with_third_party_claim(rows: Rows) -> Rows: """Get indices of rows that have a third party claim. Use pandas for this, to leverage the conditions.has_third_party_claim function, which is the same check applied during the audit flagging. """ df = pd.DataFrame(rows) condition = conditions.has_third_party_claim(df, other_claimants=True) matching_row_indices = df[condition].index.tolist() return [rows[i] for i in matching_row_indices] class VideoNextSteps(BaseSheetMV): """This sheet should only show the videos that have a 3rd Party Claim.""" title = Tabs.VIDEO_NEXT_STEPS columns = [ *BaseSheetMV.columns, *[ Column(key=key, label=label, color_label_bkg=Colors.FFF5C343) for key, label in [ (Cols.ISSUE, ColsHuman.ISSUE), (Cols.ACTION_REQUIRED, ColsHuman.ACTION_REQUIRED), (Cols.OTHER_OWNERS_CLAIMING, ColsHuman.THIRD_PARTY_CLAIMANT), ] ], Column( key=Cols.LABEL_COMMENTS, label=ColsHuman.LABEL_COMMENTS, color_label_bkg=Colors.FFF5C343, is_dropdown=True, dropdown_default=False, # Empty cell value on default ), ] def __init__(self, rows: Rows, flags) -> None: super().__init__(rows, flags=flags) def handle_rows(self) -> None: label_comments_options = ";".join( [ Texts.THIRD_PARTY_CLAIM_VALID, Texts.THIRD_PARTY_CLAIM_INVALID, ] ) third_party_claim_rows = self._extract_rows_with_third_party_claim(self.rows) for row in third_party_claim_rows: row[Cols.ISSUE] = Texts.THIRD_PARTY_CLAIM row[Cols.ACTION_REQUIRED] = Texts.VIDEO_CLAIMED_BY_THIRD_PARTY row[Cols.LABEL_COMMENTS] = label_comments_options row[Cols.OTHER_OWNERS_CLAIMING] = ", ".join( sorted(row[Cols.OTHER_OWNERS_CLAIMING]) ) self.rows = third_party_claim_rows class VideoAudit(BaseSheetMV, AudioAudit): """Sheet for the Music Video audit.""" title = Tabs.VIDEO_AUDIT columns = [ *BaseSheetMV.columns, *[ Column(key=key, label=label) for key, label in [ (Cols.VIEW_COUNT, ColsHuman.VIEW_COUNT), (Cols.IS_CLAIMED_BY_ORCHARD, ColsHuman.IS_CLAIMED_BY_ORCHARD), (Cols.IS_OWNERSHIP_CORRECT, ColsHuman.IS_OWNERSHIP_CORRECT), (Cols.IS_ACTIVE_REFERENCE_FILE, ColsHuman.IS_ACTIVE_REFERENCE_FILE), ( Cols.IS_UGC_MATCH_POLICY_CORRECT, ColsHuman.IS_UGC_MATCH_POLICY_CORRECT, ), (Cols.ACTIONS_TAKEN_BY_ORCHARD, ColsHuman.ACTIONS_TAKEN_BY_ORCHARD), (Cols.ISSUE, ColsHuman.ISSUE), (Cols.ADDITIONAL_NOTES, ColsHuman.ADDITIONAL_NOTES), ] ], Column( key=Cols.ACTION_REQUIRED, label=ColsHuman.ACTION_REQUIRED, color_label_bkg=Colors.FFF5C343, ), ] # These are flag columns that will be set to YES or NO based on the audit flags. flag_columns: list[Cols] = [ Cols.IS_CLAIMED_BY_ORCHARD, Cols.IS_OWNERSHIP_CORRECT, Cols.IS_ACTIVE_REFERENCE_FILE, Cols.IS_UGC_MATCH_POLICY_CORRECT, ] _all_flag_columns_no: dict[Cols, str] = {col: NO for col in flag_columns} _all_flag_columns_yes: dict[Cols, str] = {col: YES for col in flag_columns} _no_active_references_resolutions = ( { Resolutions.REACTIVATE_REFERENCE: { None: { Cols.IS_ACTIVE_REFERENCE_FILE: NO, Cols.IS_UGC_MATCH_POLICY_CORRECT: NO, Cols.ACTIONS_TAKEN_BY_ORCHARD: Texts.REACTIVATED_CORRECTED, } }, Resolutions.INELIGIBLE_REFERENCE: { Resolutions.REFERENCE_INSUFFICIENT_LENGTH: { Cols.IS_ACTIVE_REFERENCE_FILE: NO, Cols.IS_UGC_MATCH_POLICY_CORRECT: NO, Cols.ISSUE: Texts.INACTIVE_REFERENCE_FILE, Cols.ADDITIONAL_NOTES: Texts.REFERENCE_INSUFFICIENT_LENGTH_VIDEO, }, Resolutions.REFERENCE_EXCLUDED: { Cols.IS_ACTIVE_REFERENCE_FILE: NO, Cols.IS_UGC_MATCH_POLICY_CORRECT: NO, Cols.ISSUE: Texts.INACTIVE_REFERENCE_FILE, Cols.ADDITIONAL_NOTES: Texts.REFERENCE_EXCLUDED, }, Resolutions.REFERENCE_DEACTIVATED_BY_OWNER: { Cols.IS_ACTIVE_REFERENCE_FILE: NO, Cols.IS_UGC_MATCH_POLICY_CORRECT: NO, Cols.ISSUE: Texts.INACTIVE_REFERENCE_FILE, Cols.ADDITIONAL_NOTES: Texts.REFERENCE_DEACTIVATED_BY_OWNER, }, Resolutions.REFERENCE_OVERLAP: { Cols.IS_ACTIVE_REFERENCE_FILE: NO, Cols.IS_UGC_MATCH_POLICY_CORRECT: NO, Cols.ISSUE: Texts.INACTIVE_REFERENCE_FILE, Cols.ADDITIONAL_NOTES: Texts.REFERENCE_OVERLAP, }, Resolutions.NO_FINGERPRINTING_RIGHTS_VIDEO: { Cols.IS_ACTIVE_REFERENCE_FILE: NO, Cols.IS_UGC_MATCH_POLICY_CORRECT: NO, Cols.ISSUE: Texts.REFERENCE_DEACTIVATED, Cols.ADDITIONAL_NOTES: Texts.NO_FINGERPRINTING_RIGHTS, }, Resolutions.NO_FINGERPRINTING_RIGHTS_CHANNEL: { Cols.IS_ACTIVE_REFERENCE_FILE: NO, Cols.IS_UGC_MATCH_POLICY_CORRECT: NO, Cols.ISSUE: Texts.REFERENCE_DEACTIVATED, Cols.ADDITIONAL_NOTES: Texts.NO_FINGERPRINTING_RIGHTS, }, }, Resolutions.IGNORE: { Resolutions.REFERENCE_FILE_ACTIVE: {}, }, }, ) _third_party_claim_column_values = { **_all_flag_columns_no, Cols.ISSUE: Texts.THIRD_PARTY_CLAIM, Cols.ACTION_REQUIRED: Texts.REVIEW_VIDEO_NEXT_STEPS, } audit_columns = { Flags.NO_ASSET_ID: { Resolutions.CLAIM_VIDEO: { Resolutions.CLAIM_VIA_BACON: { **_all_flag_columns_no, Cols.ACTIONS_TAKEN_BY_ORCHARD: Texts.CLAIMED, }, Resolutions.MANUALLY_CLAIM: { **_all_flag_columns_no, Cols.ACTIONS_TAKEN_BY_ORCHARD: Texts.CLAIMED, }, }, Resolutions.IGNORE: { Resolutions.THIRD_PARTY_CLAIM: { **_all_flag_columns_no, Cols.ISSUE: Texts.THIRD_PARTY_CLAIM, }, Resolutions.CLAIM_RELEASED: { **_all_flag_columns_no, Cols.ISSUE: Texts.CLAIM_RELEASED_PER_LABEL_REQUEST, }, }, }, Flags.TERRITORIES_MISSING: { Resolutions.UPDATE_OWNERSHIP: { None: { Cols.IS_OWNERSHIP_CORRECT: NO, Cols.ACTIONS_TAKEN_BY_ORCHARD: Texts.OWNERSHIP_UPDATED, } }, Resolutions.IGNORE: {Resolutions.TERRITORY_RIGHTS_CORRECT: {}}, }, Flags.OWNERSHIP_INCOMPLETE: { Resolutions.UPDATE_OWNERSHIP: { None: { Cols.IS_OWNERSHIP_CORRECT: NO, Cols.ACTIONS_TAKEN_BY_ORCHARD: Texts.OWNERSHIP_UPDATED, } }, Resolutions.IGNORE: {Resolutions.TERRITORY_RIGHTS_CORRECT: {}}, }, Flags.CAN_REACTIVATE: { Resolutions.REACTIVATE_REFERENCE: { None: { Cols.IS_ACTIVE_REFERENCE_FILE: NO, Cols.IS_UGC_MATCH_POLICY_CORRECT: NO, Cols.ACTIONS_TAKEN_BY_ORCHARD: Texts.REACTIVATED_CORRECTED, } }, Resolutions.IGNORE: { Resolutions.REFERENCE_FILE_ACTIVE: {}, }, }, Flags.NO_ACTIVE_REFERENCES: _no_active_references_resolutions, Flags.NO_ACTIVE_REFERENCES_REASONS_ORCHARD: _no_active_references_resolutions, Flags.NO_ACTIVE_REFERENCES_REASONS_THIRD_PARTY: _no_active_references_resolutions, Flags.BAD_MATCH_POLICY: { Resolutions.CORRECT_MATCH_POLICY: { None: { Cols.IS_UGC_MATCH_POLICY_CORRECT: NO, Cols.ACTIONS_TAKEN_BY_ORCHARD: Texts.MATCH_POLICY_CORRECTED, } }, Resolutions.IGNORE: {Resolutions.MATCH_POLICY_CORRECT: {}}, }, Flags.MUST_CLAIM: { Resolutions.CLAIM_VIDEO: { Resolutions.CLAIM_VIA_BACON: { **_all_flag_columns_no, Cols.ACTIONS_TAKEN_BY_ORCHARD: Texts.CLAIMED, }, Resolutions.MANUALLY_CLAIM: { **_all_flag_columns_no, Cols.ACTIONS_TAKEN_BY_ORCHARD: Texts.CLAIMED, }, }, Resolutions.IGNORE: { Resolutions.ALREADY_CLAIMED: { **_all_flag_columns_yes, }, Resolutions.CLAIM_RELEASED: { **_all_flag_columns_no, Cols.ADDITIONAL_NOTES: Texts.CLAIM_RELEASED_PER_LABEL_REQUEST, }, }, }, Flags.THIRD_PARTY_CLAIM: { Resolutions.IGNORE: { Resolutions.THIRD_PARTY_CLAIM: _third_party_claim_column_values, }, }, Flags.ASSET_MISSING_ISRC: { Resolutions.CORRECT_METADATA: { Resolutions.CORRECT_VIA_BACON: {}, Resolutions.MANUALLY_CORRECT: {}, }, Resolutions.IGNORE: { Resolutions.METADATA_CORRECT: {}, }, }, } def handle_rows(self) -> None: filtered_rows = [] rows = self.rows third_party_claim_rows_unique_row_ids = { self._get_unique_row_id(row) for row in self._extract_rows_with_third_party_claim(self.rows) } while rows: row = rows.pop() row_handled = self._handle_row(row, third_party_claim_rows_unique_row_ids) filtered_rows.append(row_handled) self.rows = filtered_rows def _handle_row( self, row: Row, third_party_claim_rows_unique_row_ids: set[str] = None ) -> Row: # Set all flag columns to YES by default self._set_all_flag_columns_default(row) row_audit_id = row[DBColumns.ID] audit_flags = self.flags.get(row_audit_id, {}) unique_row_id = self._get_unique_row_id(row) if unique_row_id in third_party_claim_rows_unique_row_ids: self._apply_flag_column_values(row, self._third_party_claim_column_values) else: self._handle_flag_columns(row, audit_flags) return row @staticmethod def _get_unique_row_id(row: Row) -> str: """Return a unique identifier for the row, which is a combination of the audit ID and the row index in the audit. """ return f"{row[DBColumns.ID]}|{row[DBColumns.ROW_IDX]}" class VideoMonetizationRestrictions(BaseSheetMV): """This sheet should only show the videos that have a monetization restriction.""" title = Tabs.VIDEO_MONETIZATION_RESTRICTIONS columns = [ *BaseSheetMV.columns, *[ Column(key=key, label=label, color_label_bkg=Colors.FFF5C343) for key, label in [ (Cols.ISSUE, ColsHuman.ISSUE), (Cols.ACTION_REQUIRED, ColsHuman.ACTION_REQUIRED), ] ], Column( key=Cols.LABEL_COMMENTS, label=ColsHuman.LABEL_COMMENTS, color_label_bkg=Colors.FFF5C343, is_dropdown=True, dropdown_default=False, # Empty cell value on default ), ] def __init__(self, rows: Rows) -> None: super().__init__(rows) def handle_rows(self) -> None: label_comments_options = ";".join( [ Texts.REQUEST_REVIEW, Texts.SUBMIT_APPEAL, Texts.REQUEST_REVIEW_AND_SUBMIT_APPEAL, Texts.IGNORE, ] ) for row in self.rows: row[Cols.ACTION_REQUIRED] = Texts.REVIEW_VIDEO_RESTRICTION row[Cols.LABEL_COMMENTS] = label_comments_options class VideoPrivateUnlisted(BaseSheetMV): """This sheet will show all of the videos that are Private or Unlisted.""" filterRowTypes = {RowTypes.PRIVATE, RowTypes.UNLISTED} title = Tabs.VIDEO_PRIVATE_UNLISTED columns = [ *BaseSheetMV.columns, *[ Column(key=key, label=label) for key, label in [ (Cols.VIEW_COUNT, ColsHuman.VIEW_COUNT), (Cols.VIDEO_STATUS, ColsHuman.VIDEO_STATUS), ] ], *[ Column(key=key, label=label, color_label_bkg=Colors.FFF5C343) for key, label in [ (Cols.HAS_THIRD_PARTY_CLAIM, ColsHuman.HAS_THIRD_PARTY_CLAIM), (Cols.OTHER_OWNERS_CLAIMING, ColsHuman.THIRD_PARTY_CLAIMANT), ] ], ] def __init__(self, rows: Rows, flags) -> None: super().__init__(rows, flags=flags) def handle_rows(self) -> None: allowed_statuses = { status.lower() for status in [MiscLower.PRIVATE, MiscLower.UNLISTED] } @cache def _is_private_or_unlisted(status: str) -> bool: return status.lower() in allowed_statuses rows = [ row for row in self.rows if _is_private_or_unlisted(row[Cols.VIDEO_STATUS]) ] for row in rows: row_third_party_claimants = _row_third_party_claimants(row) row[Cols.OTHER_OWNERS_CLAIMING] = ( ", ".join(sorted(row_third_party_claimants)) if row_third_party_claimants else None ) row[Cols.HAS_THIRD_PARTY_CLAIM] = ( YES if _row_has_third_party_claim(row) else NO ) rows = sorted( rows, key=lambda row: row[Cols.VIDEO_STATUS], ) self.rows = rows def _row_third_party_claimants(row: Row) -> list[str] | None: return row[Cols.OTHER_OWNERS_CLAIMING] def _row_has_third_party_claim(row: Row) -> bool: return bool(_row_third_party_claimants(row))