Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replay DB and Global Stats #364

Draft
wants to merge 39 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
84c566a
Persist replay stats data to sqlite for faster access (#126)
alvarosevilla95 May 31, 2021
a50f8d3
feat: allow importing sql files as text
vinceau May 31, 2021
87d62c6
feat: move table creation sql into separate file
vinceau May 31, 2021
fe83520
revert: stop storing stats in database
vinceau May 31, 2021
58d620a
refactor: remove old code
vinceau May 31, 2021
759810c
Merge branch 'refactor/rewrite' into rewrite/db
vinceau May 31, 2021
dc1acc3
fix: merge conflicts
vinceau May 31, 2021
74646d5
Merge branch 'main' into rewrite/db
vinceau May 29, 2022
d176cc6
feat: rename db worker
vinceau May 29, 2022
a122cd1
fix: build issues
vinceau May 29, 2022
cc8006f
fix: loading worker from a worker
vinceau May 29, 2022
bc6314c
just declare sql directly for now
vinceau May 29, 2022
fa1f95b
Merge branch 'main' into rewrite/db
vinceau Jun 25, 2022
d0179b7
wip
vinceau Jun 25, 2022
ac2e48c
fix database stuff
vinceau Jun 25, 2022
5256648
Merge branch 'main' into rewrite/db
vinceau Jun 25, 2022
080e0ea
fix: progress indicator not working
vinceau Jun 25, 2022
84ac953
feat: use test.db when developing
vinceau Jun 25, 2022
6fc10e2
fix: incorrect file size count
vinceau Jun 25, 2022
1cf28f6
fix: clean up the subscription after processing
vinceau Jun 25, 2022
e36136c
fix: incorrect error file count
vinceau Jun 25, 2022
e4920d2
refactor: clean up load replays function
vinceau Jun 25, 2022
02401e5
Merge branch 'main' into rewrite/db
alvarosevilla95 Oct 17, 2022
b1972d8
Global stats caching
alvarosevilla95 Dec 14, 2022
8f3a984
Modify ReplayBrowser to support GlobalStats
alvarosevilla95 Dec 14, 2022
8ef8f65
Add recharts lib
alvarosevilla95 Dec 14, 2022
e64714d
Add some Global Stats components
alvarosevilla95 Dec 14, 2022
d6bb31f
Improve stats components
alvarosevilla95 Dec 15, 2022
6771ed9
Make stats caching robust
alvarosevilla95 Dec 15, 2022
896cbb3
Refactor show stats button
alvarosevilla95 Dec 15, 2022
894254d
Keep state of global stats loading
alvarosevilla95 Dec 16, 2022
eb2e388
Rename PlayerProfile to GeneralStats
alvarosevilla95 Dec 16, 2022
6bde45e
Move Global Stats to own module
alvarosevilla95 Dec 16, 2022
a61faaa
Create stats subcomponents
alvarosevilla95 Dec 16, 2022
deb0237
Refactoring
alvarosevilla95 Dec 16, 2022
24bdacb
Improve stats components
alvarosevilla95 Dec 20, 2022
5eec869
Refactor stats computer
alvarosevilla95 Dec 25, 2022
7f7f1c7
Move global stats computing to backend thread
alvarosevilla95 Dec 25, 2022
315b3b4
Increase overscan on replay list
alvarosevilla95 Dec 25, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"@teamsupercell/typings-for-css-modules-loader": "^2.5.1",
"@testing-library/jest-dom": "^5.16.1",
"@testing-library/react": "^12.1.2",
"@types/better-sqlite3": "^7.5.0",
"@types/compare-func": "^1.3.0",
"@types/jest": "^27.4.1",
"@types/lodash": "^4.14.178",
Expand Down Expand Up @@ -152,6 +153,7 @@
"@mui/icons-material": "^5.5.1",
"@mui/material": "^5.5.1",
"@slippi/slippi-js": "^6.3.0",
"@types/recharts": "^1.8.24",
"compare-func": "^2.0.0",
"cross-fetch": "^3.1.5",
"dmg": "^0.1.0",
Expand Down Expand Up @@ -185,6 +187,7 @@
"react-twitter-embed": "^4.0.4",
"react-virtualized-auto-sizer": "^1.0.3",
"react-window": "^1.8.6",
"recharts": "^2.2.0",
"wget-improved": "^3.3.1",
"zustand": "^3.2.0"
}
Expand Down
1 change: 1 addition & 0 deletions release/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"postinstall": "yarn run electron-rebuild && yarn run link-modules"
},
"dependencies": {
"better-sqlite3": "^7.5.3",
"obs-websocket-js": "^5.0.1",
"threads": "^1.6.5",
"websocket": "^1.0.34"
Expand Down
373 changes: 317 additions & 56 deletions release/app/yarn.lock

Large diffs are not rendered by default.

174 changes: 174 additions & 0 deletions src/renderer/containers/GlobalStats/GlobalStats.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import { css } from "@emotion/react";
import styled from "@emotion/styled";
import ArrowBackIosIcon from "@mui/icons-material/ArrowBackIos";
import ErrorIcon from "@mui/icons-material/Error";
import { Button, IconButton } from "@mui/material";
import type { FileResult } from "@replays/types";
import _ from "lodash";
import React from "react";

import { BasicFooter } from "@/components/Footer";
import { IconMessage } from "@/components/Message";
import { useGlobalStats } from "@/lib/hooks/useGlobalStats";

import { LoadingBox } from "../ReplayBrowser/ReplayBrowser";
import { StatSection } from "../ReplayFileStats/GameProfile";
import { AnalysisStats } from "./analysis/AnalysisStats";
import { GeneralStats } from "./general/GeneralStats";
import { ProgressionStats } from "./progression/ProgressionStats";
import { RandomStats } from "./random/RandomStats";

const Outer = styled.div<{
backgroundImage?: any;
}>`
position: relative;
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
&::before {
z-index: -1;
position: absolute;
height: 100%;
width: 100%;
content: "";
background-size: cover;
background-position: center center;
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.6) 0 100%),
${(p) => (p.backgroundImage ? `url("${p.backgroundImage}")` : "")};
box-shadow: inset 0 0 2000px rgba(255, 255, 255, 0.2);
filter: blur(10px);
}
`;

const Content = styled.div`
display: flex;
flex: 1;
overflow: auto;
`;

export interface GlobalStatsProps {
files: FileResult[];
onClose: () => void;
}

export const GlobalStats: React.FC<GlobalStatsProps> = ({ onClose }) => {
const setActive = useGlobalStats((store) => store.setActiveView);
const activeView = useGlobalStats((store) => store.active);
const isLoading = useGlobalStats((store) => store.loading);
const stats = useGlobalStats((store) => store.stats);
const progressionStats = useGlobalStats((store) => store.progression);
// const filters = {
// characters: [],
// opponentCharacters: [],
// opponents: [],
// stages: [],
// };
const error = undefined;

const getButtonVariant = (view: string) => (view == activeView ? "contained" : "outlined");

return (
<Outer>
<BasicFooter>
<div
css={css`
display: flex;
flex-direction: column;
margin-left: 10px;
padding-right: 20px;
`}
>
<div
css={css`
font-weight: bold;
text-transform: uppercase;
color: white;
`}
>
<IconButton onClick={onClose} disabled={false} css={css``} size="large">
<ArrowBackIosIcon fontSize="small" />
</IconButton>
Back
</div>
</div>
<div
css={css`
margin-left: auto;
margin-right: 50px;
`}
>
<Button css={css``}>Filter</Button>
</div>
</BasicFooter>
<Content>
{error ? (
<IconMessage Icon={ErrorIcon} label={`Error: ${error ?? JSON.stringify(error, null, 2)}`} />
) : isLoading || !stats ? (
<LoadingBox />
) : (
<>
<div style={{ flex: "1", margin: "auto", maxWidth: 1500 }}>
<StatSection>
<div
css={css`
display: flex;
flex-direction: row;
flex: 1;
align-items: center;
`}
>
<Button
onClick={() => setActive("general")}
variant={getButtonVariant("general")}
css={css`
margin: auto;
`}
>
General
</Button>
<Button
onClick={() => setActive("progression")}
variant={getButtonVariant("progression")}
css={css`
margin: auto;
`}
>
Progression
</Button>
<Button
onClick={() => setActive("analysis")}
variant={getButtonVariant("analysis")}
css={css`
margin: auto;
`}
>
Analysis
</Button>
<Button
onClick={() => setActive("random")}
variant={getButtonVariant("random")}
css={css`
margin: auto;
`}
>
Random
</Button>
</div>
</StatSection>
{activeView == "general" ? (
<GeneralStats player={"EAST#312"!} stats={stats}></GeneralStats>
) : activeView == "progression" ? (
<ProgressionStats player={"EAST#312"!} stats={progressionStats}></ProgressionStats>
) : activeView == "analysis" ? (
<AnalysisStats player={"EAST#312"!} stats={stats}></AnalysisStats>
) : activeView == "random" ? (
<RandomStats player={"EAST#312"!} stats={progressionStats}></RandomStats>
) : null}
</div>
</>
)}
</Content>
</Outer>
);
};
90 changes: 90 additions & 0 deletions src/renderer/containers/GlobalStats/analysis/AnalysisStats.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { colors } from "@common/colors";
import { css } from "@mui/material";
import React from "react";
import { PolarAngleAxis, PolarGrid, Radar, RadarChart, ResponsiveContainer } from "recharts";
import type { GlobalStats } from "stats/stats";

import { StatSection } from "@/containers/ReplayFileStats/GameProfile";

import { ThrowsTable } from "./ThrowsTable";

interface AnalysisStatsProps {
player: string;
stats: GlobalStats;
}

export const AnalysisStats: React.FC<AnalysisStatsProps> = (props) => {
const stats = props.stats;
const techs = stats.actions.groundTechCount;
const throws = stats.actions.throwCount;

const techData = [
{ subject: "neutral", value: techs.neutral },
{ subject: "in", value: techs.in },
{ subject: "fail", value: techs.fail },
{ subject: "away", value: techs.away },
];

const throwData = [
{ subject: "up", value: throws.up },
{ subject: "forward", value: throws.forward },
{ subject: "down", value: throws.down },
{ subject: "back", value: throws.back },
];

// TODO
// stack area chart for techs / aerials / throws

return (
<>
<StatSection title="Progression" align="center">
<ResponsiveContainer>
<div
css={css`
display: flex;
flex-wrap: wrap;
flex-direction: row;
align-items: top;
height: 400px;
margin: auto;
`}
>
<RadarChart outerRadius={90} width={400} height={400} data={throwData} style={{ margin: "auto" }}>
<PolarGrid />
<PolarAngleAxis dy={5} dataKey="subject" />
<Radar name="" dataKey="value" stroke={colors.greenDark} fill={colors.greenDark} fillOpacity={0.6} />
</RadarChart>
<ThrowsTable stats={stats} />
<ThrowsTable stats={stats} />
</div>
</ResponsiveContainer>
</StatSection>
<StatSection title="Progression" align="center">
<ResponsiveContainer>
<div
css={css`
display: flex;
flex-wrap: wrap;
flex-direction: row;
align-items: center;
height: 400px;
margin: auto;
`}
>
<RadarChart outerRadius={90} width={400} height={400} data={techData} style={{ margin: "auto" }}>
<PolarGrid />
<PolarAngleAxis dy={5} dataKey="subject" />
<Radar name="" dataKey="value" stroke={colors.greenDark} fill={colors.greenDark} fillOpacity={0.6} />
</RadarChart>
</div>
</ResponsiveContainer>
</StatSection>

<div
css={css`
min-height: 50px;
`}
/>
</>
);
};
40 changes: 40 additions & 0 deletions src/renderer/containers/GlobalStats/analysis/ThrowsTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { css } from "@mui/material";
import React from "react";
import type { GlobalStats } from "stats/general";

import * as T from "@/containers/ReplayFileStats/TableStyles";

interface ThrowsTableProps {
stats: GlobalStats;
}

const generatePunishRow = () => {
return (
<T.TableRow style={{ textAlign: "center" }} key={`${1}-${1}-punish-${1}`}>
<T.TableCell>Hello</T.TableCell>
<T.TableCell>Hello</T.TableCell>
<T.TableCell>Hello</T.TableCell>
<T.TableCell>Hello</T.TableCell>
</T.TableRow>
);
};

export const ThrowsTable: React.FC<ThrowsTableProps> = () => {
return (
<div
css={css`
margin-left: auto;
margin-right: auto;
margin-top: 50px;
`}
>
<T.Table>
<thead></thead>
<tbody>
{generatePunishRow()}
{generatePunishRow()}
</tbody>
</T.Table>
</div>
);
};
Loading