Skip to content

Commit

Permalink
Merge pull request #2964 from GreenAsJade/ai_stats_for_cm_analysis
Browse files Browse the repository at this point in the history
Make the AI stats table show up in Reports view
  • Loading branch information
anoek authored Feb 19, 2025
2 parents d14184b + a18fd92 commit 5e0326e
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 71 deletions.
11 changes: 2 additions & 9 deletions src/components/Player/Player.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { PlayerIcon } from "@/components/PlayerIcon";
import * as player_cache from "@/lib/player_cache";
import * as preferences from "@/lib/preferences";
import online_status from "@/lib/online_status";
import { ReportContext } from "@/contexts/ReportContext";

/* There are cases where what we are handed is some odd looking dirty data. We
* should probably start warning about remaining uses of these fields and then
Expand Down Expand Up @@ -73,14 +74,6 @@ export interface PlayerProperties {
forceShowRank?: boolean;
}

type ShowPlayersInReportContextType = {
reporter: player_cache.PlayerCacheEntry;
reported: player_cache.PlayerCacheEntry;
};

export const ShowPlayersInReportContext =
React.createContext<ShowPlayersInReportContextType | null>(null);

export function Player(props: PlayerProperties): React.ReactElement {
const user = data.get("user");
const player_id: number =
Expand All @@ -107,7 +100,7 @@ export function Player(props: PlayerProperties): React.ReactElement {
const base = player || historical;
const combined = base ? Object.assign({}, base, historical ? historical : {}) : null;

const viewReportContext = React.useContext(ShowPlayersInReportContext);
const viewReportContext = React.useContext(ReportContext);

React.useEffect(() => {
if (!props.disableCacheUpdate) {
Expand Down
18 changes: 18 additions & 0 deletions src/contexts/ReportContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright (C) Online-Go.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*/
import * as React from "react";
import * as player_cache from "@/lib/player_cache";

type ReportContextType = {
reporter: player_cache.PlayerCacheEntry;
reported: player_cache.PlayerCacheEntry;
moderator_powers: number;
};

export const ReportContext = React.createContext<ReportContextType | null>(null);
156 changes: 98 additions & 58 deletions src/views/Game/AIReview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,14 @@ import {
Goban,
encodeMoves,
encodeMove,
GobanRenderer,
} from "goban";
import { game_control } from "./game_control";
import { alert } from "@/lib/swal_config";
import { GobanContext } from "./goban_context";
import { ReportContext } from "@/contexts/ReportContext";
import { MODERATOR_POWERS } from "@/lib/moderation";

export interface AIReviewEntry {
move_number: number;
win_rate: number;
Expand All @@ -59,6 +63,8 @@ interface AIReviewProperties {
game_id: number;
hidden: boolean;
onAIReviewSelected: (ai_review: JGOFAIReview) => void;
reportContext?: React.ContextType<typeof ReportContext>;
gobanContext?: React.ContextType<typeof GobanContext>;
}

interface AIReviewState {
Expand All @@ -69,24 +75,39 @@ interface AIReviewState {
selected_ai_review?: JGOFAIReview;
update_count: number;
worst_moves_shown: number;
table_set: boolean;
hide_table: boolean;
table_hidden: boolean;
}

export class AIReview extends React.Component<AIReviewProperties, AIReviewState> {
// this will be the full ai review we are working with, as opposed to
// selected_ai_review which will just contain some metadata from the
// postgres database
// We need this wrapped because we want to access two contexts,
// and we can't do that in a function component.
// This wil be a lot cleaner when we convert it to a function component.
export function AIReview(props: AIReviewProperties) {
return (
<ReportContext.Consumer>
{(reportContext) => (
<GobanContext.Consumer>
{(gobanContext) => (
<AIReviewClass
{...props}
reportContext={reportContext}
gobanContext={gobanContext}
/>
)}
</GobanContext.Consumer>
)}
</ReportContext.Consumer>
);
}

class AIReviewClass extends React.Component<AIReviewProperties, AIReviewState> {
ai_review?: JGOFAIReview;
table_rows!: string[][];
avg_score_loss!: number[];
median_score_loss!: number[];
moves_pending!: number;
max_entries!: number;

static contextType = GobanContext;
declare context: React.ContextType<typeof GobanContext>;

constructor(props: AIReviewProperties) {
super(props);
const state: AIReviewState = {
Expand All @@ -98,7 +119,7 @@ export class AIReview extends React.Component<AIReviewProperties, AIReviewState>
// TODO: allow users to view more than 3 of these key moves
// See https://forums.online-go.com/t/top-3-moves-score-a-better-metric/32702/15
worst_moves_shown: 6,
table_set: false,
hide_table: false,
table_hidden: preferences.get("ai-summary-table-show"),
};
this.state = state;
Expand All @@ -113,17 +134,24 @@ export class AIReview extends React.Component<AIReviewProperties, AIReviewState>
this.median_score_loss = ai_table_out.median_score_loss;
this.moves_pending = ai_table_out.moves_pending;
this.max_entries = ai_table_out.max_entries;
if (!data.get("user").is_moderator) {
this.setState({
table_set: true,
});
}

const user = data.get("user");
const canViewTable =
user.is_moderator ||
((this.props.reportContext?.moderator_powers ?? 0) &
MODERATOR_POWERS.ASSESS_AI_REPORTS) !==
0;

this.setState({
hide_table: !canViewTable,
});
}

componentDidUpdate(prevProps: AIReviewProperties) {
if (this.getGameId() !== this.getGameId(prevProps)) {
this.getAIReviewList();
}
if (!this.state.table_set) {
if (!this.state.hide_table) {
const ai_table_out = this.AiSummaryTableRowList();
this.table_rows = ai_table_out.ai_table_rows;
this.avg_score_loss = ai_table_out.avg_score_loss;
Expand Down Expand Up @@ -209,7 +237,7 @@ export class AIReview extends React.Component<AIReviewProperties, AIReviewState>
.catch(errorLogger);
}

private static handicapOffset(goban: Goban): number {
private static handicapOffset(goban: GobanRenderer): number {
if (
goban &&
goban.engine &&
Expand Down Expand Up @@ -269,7 +297,7 @@ export class AIReview extends React.Component<AIReviewProperties, AIReviewState>
this.updateAIReviewMetadata(ai_review);
this.setState({
selected_ai_review: ai_review,
table_set: false,
hide_table: false,
});
this.props.onAIReviewSelected(ai_review);
this.syncAIReview();
Expand Down Expand Up @@ -433,7 +461,7 @@ export class AIReview extends React.Component<AIReviewProperties, AIReviewState>
throw new Error("ai_review not set");
}

const goban = this.context;
const goban = this.props.gobanContext;

if (!goban) {
throw new Error("goban not set");
Expand Down Expand Up @@ -747,7 +775,7 @@ export class AIReview extends React.Component<AIReviewProperties, AIReviewState>
}

private requestAnalysisOfVariation(cur_move: MoveTree, trunk_move: MoveTree): boolean {
const goban = this.context;
const goban = this.props.gobanContext;
if (!goban) {
return false;
}
Expand Down Expand Up @@ -810,7 +838,7 @@ export class AIReview extends React.Component<AIReviewProperties, AIReviewState>
throw new Error("ai_review not set");
}

const goban = this.context;
const goban = this.props.gobanContext;

if (!goban) {
throw new Error("goban not set");
Expand Down Expand Up @@ -890,7 +918,7 @@ export class AIReview extends React.Component<AIReviewProperties, AIReviewState>
}

private AiSummaryTableRowList() {
const goban = this.context;
const goban = this.props.gobanContext;
if (!goban) {
throw new Error("goban not set");
}
Expand Down Expand Up @@ -929,7 +957,7 @@ export class AIReview extends React.Component<AIReviewProperties, AIReviewState>

if (!this.ai_review?.engine.includes("katago")) {
this.setState({
table_set: true,
hide_table: true,
});
return {
ai_table_rows: default_table_rows,
Expand All @@ -942,10 +970,10 @@ export class AIReview extends React.Component<AIReviewProperties, AIReviewState>

const handicap = goban.engine.handicap;
//only useful when there's free placement, handicap = 1 no offset needed.
let h_offset = AIReview.handicapOffset(goban);
let h_offset = AIReviewClass.handicapOffset(goban);
h_offset = h_offset === 1 ? 0 : h_offset;
const b_player = h_offset > 0 || handicap > 1 ? 1 : 0;
const move_player_list = AIReview.getPlayerColorsMoveList(goban);
const move_player_list = AIReviewClass.getPlayerColorsMoveList(goban);

if (this.ai_review?.type === "fast") {
const scores = this.ai_review?.scores;
Expand Down Expand Up @@ -1065,7 +1093,7 @@ export class AIReview extends React.Component<AIReviewProperties, AIReviewState>
}

this.setState({
table_set: true,
hide_table: true,
});

return {
Expand Down Expand Up @@ -1212,7 +1240,7 @@ export class AIReview extends React.Component<AIReviewProperties, AIReviewState>

if (!check1 && !check2) {
this.setState({
table_set: true,
hide_table: true,
});
}

Expand All @@ -1239,7 +1267,7 @@ export class AIReview extends React.Component<AIReviewProperties, AIReviewState>
return null;
}

const goban = this.context;
const goban = this.props.gobanContext;

if (!goban || !goban.engine) {
return null;
Expand Down Expand Up @@ -1527,23 +1555,31 @@ export class AIReview extends React.Component<AIReviewProperties, AIReviewState>
/>
<div className="worst-moves-summary-toggle-container">
{this.renderWorstMoveList(worst_move_list)}
{(user.is_moderator || null) && (
<div className="ai-summary-toggler">
<span>
<i className="fa fa-table"></i>
</span>
<span>
<Toggle
checked={this.state.table_hidden}
onChange={(b) => {
preferences.set("ai-summary-table-show", b);
this.setState({ table_hidden: b });
//console.log(this.state.table_hidden);
}}
/>
</span>
</div>
)}
{(user.is_moderator ||
(this.props.reportContext &&
(data.get("user").moderator_powers &
MODERATOR_POWERS.ASSESS_AI_REPORTS) !==
0)) &&
this.ai_review?.engine.includes("katago") && (
<div className="ai-summary-toggler">
<span>
<i className="fa fa-table"></i>
</span>
<span>
<Toggle
checked={this.state.table_hidden}
onChange={(b) => {
preferences.set(
"ai-summary-table-show",
b,
);
this.setState({ table_hidden: b });
//console.log(this.state.table_hidden);
}}
/>
</span>
</div>
)}
</div>
{this.ai_review.scores && (
<div className="win-score-toggler">
Expand Down Expand Up @@ -1657,25 +1693,29 @@ export class AIReview extends React.Component<AIReviewProperties, AIReviewState>
</span>
</div>
)*/}
{data.get("user").is_moderator && this.ai_review?.engine.includes("katago") && (
<div>
<AiSummaryTable
heading_list={[_("Type"), _("Black"), "%", _("White"), "%"]}
body_list={this.table_rows}
avg_loss={this.avg_score_loss}
median_score_loss={this.median_score_loss}
table_hidden={this.state.table_hidden}
pending_entries={this.moves_pending}
max_entries={this.max_entries}
/>
</div>
)}
{(data.get("user").is_moderator ||
(this.props.reportContext &&
(data.get("user").moderator_powers & MODERATOR_POWERS.ASSESS_AI_REPORTS) !==
0)) &&
this.ai_review?.engine.includes("katago") && (
<div>
<AiSummaryTable
heading_list={[_("Type"), _("Black"), "%", _("White"), "%"]}
body_list={this.table_rows}
avg_loss={this.avg_score_loss}
median_score_loss={this.median_score_loss}
table_hidden={this.state.table_hidden}
pending_entries={this.moves_pending}
max_entries={this.max_entries}
/>
</div>
)}
</div>
);
}

public renderWorstMoveList(lst: AIReviewWorstMoveEntry[]): React.ReactElement | null {
const goban = this.context;
const goban = this.props.gobanContext;
if (!goban?.engine.move_tree || !this.ai_review) {
return null;
}
Expand Down
13 changes: 9 additions & 4 deletions src/views/ReportsCenter/ViewReport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { report_manager } from "@/lib/report_manager";
import { Report } from "@/lib/report_util";
import { AutoTranslate } from "@/components/AutoTranslate";
import { interpolate, _, pgettext, llm_pgettext } from "@/lib/translate";
import { Player, ShowPlayersInReportContext } from "@/components/Player";
import { Player } from "@/components/Player";
import { Link } from "react-router-dom";
import { post } from "@/lib/requests";
import { PlayerCacheEntry } from "@/lib/player_cache";
Expand All @@ -44,6 +44,7 @@ import * as DynamicHelp from "react-dynamic-help";
import { MODERATOR_POWERS } from "@/lib/moderation";
import { KBShortcut } from "@/components/KBShortcut";
import { GobanRenderer } from "goban";
import { ReportContext } from "@/contexts/ReportContext";

interface ViewReportProps {
reports: Report[];
Expand Down Expand Up @@ -331,8 +332,12 @@ export function ViewReport({ report_id, reports, onChange }: ViewReportProps): R
<KBShortcut shortcut="left" action={nav_prev} />
<KBShortcut shortcut="right" action={nav_next} />

<ShowPlayersInReportContext.Provider
value={{ reporter: report.reporting_user, reported: report.reported_user }}
<ReportContext.Provider
value={{
reporter: report.reporting_user,
reported: report.reported_user,
moderator_powers: user.moderator_powers,
}}
>
<div id="ViewReport" className="show-players-in-report">
{isAnnulQueueModalOpen && (
Expand Down Expand Up @@ -795,7 +800,7 @@ export function ViewReport({ report_id, reports, onChange }: ViewReportProps): R
)}
</ErrorBoundary>
</div>
</ShowPlayersInReportContext.Provider>
</ReportContext.Provider>
</div>
);
}
Expand Down

0 comments on commit 5e0326e

Please sign in to comment.