BZWC6XMOUXEPOI7P65MREJZ45TSQRFMMR2SQ7LML7RMCKUL57VHAC 7QS7R2ORWCZMTCE3SD5PJ5ZL27N3RS7YCWBHXH3JDPTHDQ3KBAMQC B6QIBRKFDFZKHHHT6F6M2NST2CYXNK4FCCYL3JOIAYDZYX3FQLRAC N7TZV5WJKLYKSG6SIVAID4ZCC76YHZU5U5WWQGG6TFKAV6CE3Y6AC B7W4Q522DLB6DKH2TFDOCTSZFZLFTOLCT6CCZEOC3V3UUMSAOOFAC GGREOYIRZE2GH62X7YXQA7IUSSB25ENLQ5OKVJYWPNI462DP37FAC HXRDRHIVGSBEAMTKJFZK43MSB53BKON6F77AARUQNWFUPSTZSBPAC Q4BYGYNX3UZXX47EMX2GWX6M6ICSMIMCLQ7HVOYS4HB4RHYI4NEQC 62OPHDLT2IIHK2OEH76NWB6V7E2Z5USUBNHB4X3DP2XLDI3YHY7QC 3QZU4ZIBA24ZXKPBGMTWX4S2GF6AOXJMPD2753CCTU7QUL2WMDXAC }#[derive(Debug, Insertable)]#[table_name = "encounter_stats"]pub struct EncounterStats {pub team: i32,pub encounter_id: i32,pub difficulty: i32,pub kill_log: Option<i32>,pub kill_week: Option<i32>,pub ilvl_min: f32,pub ilvl_max: f32,pub pull_count: i32,pub prog_time: i32,
}#[derive(Debug, Insertable, Queryable)]#[table_name = "encounter_stats"]pub struct EncounterStats {pub team: i32,pub encounter_id: i32,pub difficulty: i32,pub kill_log: Option<i32>,pub kill_week: Option<i32>,pub ilvl_min: f32,pub ilvl_max: f32,pub pull_count: i32,pub prog_time: i32,
#[Object]impl EncounterStats {async fn team(&self) -> i32 {self.team}async fn encounter_id(&self) -> i32 {self.encounter_id}async fn difficulty(&self) -> i32 {self.difficulty}async fn kill_log(&self, ctx: &Context<'_>) -> Result<Option<Log>> {use crate::schema::logs::dsl as logs;if self.kill_log.is_none() {return Ok(None);}let Data { conpool, .. } = ctx.data()?;let log = logs::logs.filter(logs::iid.eq(self.kill_log.unwrap())).first(&*conpool.get()?)?;Ok(Some(log))}async fn kill_week(&self) -> Option<i32> {self.kill_week}async fn ilvl_min(&self) -> f32 {self.ilvl_min}async fn ilvl_max(&self) -> f32 {self.ilvl_max}async fn pull_count(&self) -> i32 {self.pull_count}async fn prog_time(&self) -> i32 {self.prog_time}}
Ok(logs)
match include_farm {Some(true) => Ok(logs),_ => Ok(prog_logs(&*conpool.get()?, encounter_id, difficulty, logs)?.logs),}}async fn stats_for_encounter(&self,ctx: &Context<'_>,team_id: i32,encounter_id: i32,difficulty: i32,) -> Result<Option<EncounterStats>> {use crate::schema::encounter_stats::dsl as stats;let Data { conpool, .. } = ctx.data()?;Ok(stats::encounter_stats.filter(stats::team.eq(team_id).and(stats::encounter_id.eq(encounter_id)).and(stats::difficulty.eq(difficulty)),).select((stats::team,stats::encounter_id,stats::difficulty,stats::kill_log,stats::kill_week,stats::ilvl_min,stats::ilvl_max,stats::pull_count,stats::prog_time,)).first(&*conpool.get()?).optional()?)
generate_analysis(&*con, guild_id.id() as i32)?;
match force_analysis {Some(true) => generate_analysis(&*con, guild_id.id() as i32)?,_ => {for (encounter, difficulty) in new_encounters {if let Some(difficulty) = difficulty {generate_encounter_analysis(&*con,guild_id.id() as i32,encounter as i32,difficulty as i32,)?}}}}
background_import(client.clone(), token.clone(), con, tx, guild_id, tag, zone).inspect_err(|e| {error!("an error occurred during import: {:?}", e);}),
background_import(client.clone(),token.clone(),con,tx,guild_id,tag,zone,force_analysis,).inspect_err(|e| {error!("an error occurred during import: {:?}", e);}),
// Generate the analysis entries for a team on the given encounter and difficulty. This overwites// any existing analysis for that 3-tuple.pub fn generate_encounter_analysis(
pub struct ProgData {pub logs: Vec<Log>,pub kill: bool,pub fights: HashMap<i32, Vec<Fight>>,}pub fn prog_logs(
Ok(ProgData {logs,kill,fights: fightmap,})}// Generate the analysis entries for a team on the given encounter and difficulty. This overwites// any existing analysis for that 3-tuple.pub fn generate_encounter_analysis(con: &PgConnection,team_id: i32,encounter_id: i32,difficulty: i32,) -> Result<()> {let raw_logs = logs_for_encounter(con, team_id, encounter_id, difficulty)?;let region = team_region(con, team_id)?;debug!("generating analysis for {} {} {}",team_id, encounter_id, difficulty);let ProgData {kill,logs,fights: fightmap,} = prog_logs(con, encounter_id, difficulty, raw_logs)?;
}}`;export const Q_STATS = gql`query Stats($team: Int!, $encounterId: Int!, $difficulty: Int!) {statsForEncounter(teamId: $team, encounterId: $encounterId, difficulty: $difficulty) {team, encounterId, difficulty, killLog { iid, startTime }, killWeek, pullCount, progTime, ilvlMin, ilvlMax
<span className="block -mb-2">{DIFFICULTY_NAMES[props.difficulty] || props.difficulty}</span><span className="text-5xl">{ENCOUNTER_NAMES[props.encounterId] || props.encounterId}</span>
<span className="block">{DIFFICULTY_NAMES[props.difficulty] || props.difficulty}</span><span className="text-5xl" style={{lineHeight: 0.75}}>{ENCOUNTER_NAMES[props.encounterId] || props.encounterId}</span>
export function encounter_stats(props: StatProps): EncounterStats {const killLog = props.logs.find(log => log.fights.some(({ kill }) => kill));const ilvlRange = props.logs.reduce((acc, log) => {return log.fights.reduce(([min, max], fight) => ([Math.min(min, fight.avgIlvl), Math.max(max, fight.avgIlvl)]), acc);}, [1000, 0]);const pullCount = props.logs.reduce((count, log) => count + log.fights.length, 0);
function EncounterStatColumn(props: StatProps) {const { loading, error, data } = useQuery(Q_STATS, { variables: props });
const progTime = props.logs.reduce((time, log) => {const duration_ms = log.fights[log.fights.length - 1].endTime - log.fights[0].startTime;return time + duration_ms;}, 0);const killWeek = killLog ? tierWeek(killLog.zone, killLog.startTime, props.difficulty === 5, props.region) : undefined;return {encounterId: props.logs[0].fights[0].encounterId,killLog, ilvlRange: ilvlRange as [number, number], pullCount, progTime, killWeek
if (loading) {return <>Loading...</>;
useEffect(() => {if(dispatch) {dispatch(stats);}// the empty dep list is intentional. we do this once on load.// eslint-disable-next-line}, []);
const { killLog, ilvlMin, ilvlMax, pullCount, progTime, killWeek } = data.statsForEncounter as EncounterStats;
import EncounterOverview, { EncounterStats } from './EncounterOverview';import { StatBlocksDispatch, ZONE_ENCOUNTERS, toRegion, ENCOUNTER_NAMES, DIFFICULTY_NAMES } from './model';
import EncounterOverview, { Q_STATS } from './EncounterOverview';import { ZONE_ENCOUNTERS, toRegion, ENCOUNTER_NAMES, DIFFICULTY_NAMES } from './model';
function StatTable({ statblocks, encounters }: { statblocks: StatBlocks, encounters: number[] }) {
if(data && data.statsForEncounter) {const stats = data.statsForEncounter;return (<tr><td className="text-left pr-2 border-r border-gray-200">{ENCOUNTER_NAMES[stats.encounterId]}</td><td className="pl-2 pr-2 text-center">{(stats && stats.killLog) ? formatShortDate(new Date(stats.killLog.startTime)) : ''}</td><td className="pl-2 pr-2 text-center">{(stats && stats.killWeek) ? `Week ${stats.killWeek}` : ''}</td><td className="pl-2 pr-2 border-l border-gray-200">{stats ? stats.pullCount : ''}</td><td className="pl-2 pr-2">{stats ? formatDuration(stats.progTime, ' hrs') : ''}</td><td className="pl-2 pr-2 border-l border-gray-200">{stats ? stats.ilvlMin.toFixed(1) : ''}</td><td className="pl-2 pr-2">{stats ? stats.ilvlMax.toFixed(1) : ''}</td></tr>);} else {return null;}}function StatTable({ difficulty, team, encounters }: { team: number; encounters: number[]; difficulty: number; }) {
{encounters.map(id => {const stats = statblocks[id];return (<tr key={id}><td className="text-left pr-2 border-r border-gray-200">{ENCOUNTER_NAMES[id]}</td><td className="pl-2 pr-2 text-center">{(stats && stats.killLog) ? formatShortDate(stats.killLog.startTime) : ''}</td><td className="pl-2 pr-2 text-center">{(stats && stats.killWeek) ? `Week ${stats.killWeek}` : ''}</td><td className="pl-2 pr-2 border-l border-gray-200">{stats ? stats.pullCount : ''}</td><td className="pl-2 pr-2">{stats ? formatDuration(stats.progTime, ' hrs') : ''}</td><td className="pl-2 pr-2 border-l border-gray-200">{stats ? stats.ilvlRange[0].toFixed(1) : ''}</td><td className="pl-2 pr-2">{stats ? stats.ilvlRange[1].toFixed(1) : ''}</td></tr>);})}
{encounters.map(id => <StatRow key={id} encounterId={id} difficulty={difficulty} team={team} />)}
<StatBlocksDispatch.Provider value={dispatch} key={encounter}><EncounterOverview encounterId={encounter} difficulty={5} team={team.id} region={toRegion(region)} /></StatBlocksDispatch.Provider>
<EncounterOverview key={encounter} encounterId={encounter} difficulty={5} team={team.id} region={toRegion(region)} />