Skip to content
This repository has been archived by the owner on Nov 30, 2023. It is now read-only.

Commit

Permalink
Add Stat entity to get the count of games creations by days on a period
Browse files Browse the repository at this point in the history
  • Loading branch information
GuillaumeKESTEMAN committed Aug 19, 2022
1 parent b391718 commit d5923ef
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 36 deletions.
27 changes: 13 additions & 14 deletions admin/src/dashboard/dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import NbResource from "./nbResource.tsx";
// @ts-ignore
import PendingLots from "./pendingLots.tsx";
import {Game} from '../types/Game';
import {Stat_Game} from '../types/Stat_Game';
import {PlayerRaRecord} from '../types/Player';
import {LotRaRecord} from '../types/Lot';
import {useMediaQuery, Theme} from '@mui/material';
Expand All @@ -21,7 +22,7 @@ interface State {
nbLots: number;
pendingLots?: LotRaRecord[];
nbGames: number;
recentGames?: Game[];
gamesCount?: LotRaRecord[];
nbPlayers: number;
}

Expand Down Expand Up @@ -60,35 +61,33 @@ const Dashboard = () => {
};
}, [lots, totalLots]);

const {data: games = [], total: totalGames = 0} = useGetList<Game>('games', {
filter: {playDate: aSubDaysAgoForGames.toISOString()},
sort: {field: 'playDate', order: 'DESC'},
pagination: {page: 1, perPage: 100},
const {data: gamesCountStats = []} = useGetList<Stat_Game>(`stats/games/after/${aSubDaysAgoForGames.toISOString()}`);
const {total: totalGames = 0} = useGetList<Game>('games', {
pagination: {page: 1, perPage: 1},
});

// @ts-ignore
const gamesAggregation = useMemo<State>(() => {
return {
recentGames: games,
gamesCount: gamesCountStats,
nbGames: totalGames,
};
}, [games, totalGames]);

}, [gamesCountStats, totalGames]);

const {total: totalPlayers = 0} = useGetList<PlayerRaRecord>('players', {
filter: {"exists[lastPlayDate]": true},
pagination: {page: 1, perPage: 1},
});

// @ts-ignore

const playersAggregation = useMemo<State>(() => {
return {
nbPlayers: totalPlayers,
};
}, [totalPlayers]);

const {recentGames, nbGames} = gamesAggregation;
const {gamesCount, nbGames} = gamesAggregation;
const {nbPlayers} = playersAggregation;

const {nbLots, pendingLots} = lotsAggregation;

return isXSmall ? (
Expand All @@ -111,7 +110,7 @@ const Dashboard = () => {
icon={VideogameAssetIcon}
title={translate('pos.dashboard.games.total')}
/>
<GameChart games={recentGames} nbrMaxGamesDatesToShow={nbrMaxGamesDatesToShow}/>
<GameChart gamesCount={gamesCount} nbrMaxGamesDatesToShow={nbrMaxGamesDatesToShow}/>
</Card>
) : isSmall ? (
<Card>
Expand Down Expand Up @@ -143,7 +142,7 @@ const Dashboard = () => {
</div>
</div>
</div>
<GameChart games={recentGames} nbrMaxGamesDatesToShow={nbrMaxGamesDatesToShow}/>
<GameChart gamesCount={gamesCount} nbrMaxGamesDatesToShow={nbrMaxGamesDatesToShow}/>
</Card>
) : (
<Card>
Expand All @@ -162,7 +161,7 @@ const Dashboard = () => {
title={translate('pos.dashboard.games.total')}
/>
<br/>
<GameChart games={recentGames} nbrMaxGamesDatesToShow={nbrMaxGamesDatesToShow}/>
<GameChart gamesCount={gamesCount} nbrMaxGamesDatesToShow={nbrMaxGamesDatesToShow}/>
</div>
</div>
<div style={styles.rightCol}>
Expand Down
30 changes: 14 additions & 16 deletions admin/src/dashboard/gameChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import {
import {useTranslate} from 'react-admin';
import {format, subDays, addDays} from 'date-fns';

import {Game} from '../types/Game';
import {Stat_Game} from '../types/Stat_Game';


const GameChart = (props: { games?: Game[], nbrMaxGamesDatesToShow?: number }) => {
const {games, nbrMaxGamesDatesToShow} = props;
const GameChart = (props: { gamesCount?: Stat_Game[], nbrMaxGamesDatesToShow?: number }) => {
const {gamesCount, nbrMaxGamesDatesToShow} = props;
const translate = useTranslate();

const lastDay = new Date();
Expand All @@ -26,34 +26,32 @@ const GameChart = (props: { games?: Game[], nbrMaxGamesDatesToShow?: number }) =
const dateFormatter = (date: number): string =>
new Date(date).toLocaleDateString();

const aggregateGamesByDay = (games: Game[]): { [key: string]: number } =>
games
const aggregateGamesByDay = (gamesCount: Stat_Game[]): { [key: string]: number } =>
gamesCount
.reduce((acc, curr) => {
const day = format(new Date(curr.playDate), 'yyyy-MM-dd');
if (!acc[day]) {
acc[day] = 0;
}
acc[day]++;
const day = format(new Date(curr.date), 'yyyy-MM-dd');
acc[day] = curr.nbrGames;
return acc;
}, {} as { [key: string]: number });

const getNbGamesPerDay = (games: Game[]): TotalByDay[] => {
const daysWithGames = aggregateGamesByDay(games);
const getNbGamesPerDay = (gamesCount: Stat_Game[]): TotalByDay[] => {
const daysWithGames = aggregateGamesByDay(gamesCount);
return lastMonthDays.map(date => ({
date: date.getTime(),
total: daysWithGames[format(new Date(date), 'yyyy-MM-dd')] || 0,
}));
};

if (!games) return (<CardHeader title={translate('pos.dashboard.games.no_games')}/>);
if (!gamesCount || (Array.isArray(gamesCount) && gamesCount.length === 0)) return (<CardHeader title={translate('pos.dashboard.games.no_games')}/>);

return (
<Card>
<CardHeader title={translate('pos.dashboard.games.month_history').replace('%nbr_jours%', nbrMaxGamesDatesToShow.toString())}/>
<CardHeader
title={translate('pos.dashboard.games.month_history').replace('%nbr_jours%', nbrMaxGamesDatesToShow.toString())}/>
<CardContent>
<div style={{width: '100%', height: 300}}>
<ResponsiveContainer>
<BarChart width={600} height={600} data={getNbGamesPerDay(games)}>
<BarChart width={600} height={600} data={getNbGamesPerDay(gamesCount)}>
<CartesianGrid stroke="#ccc"/>
<Bar dataKey="total" fill="#288690"/>
<XAxis
Expand All @@ -62,7 +60,7 @@ const GameChart = (props: { games?: Game[], nbrMaxGamesDatesToShow?: number }) =
type="number"
scale="time"
domain={[
addDays(aMonthAgo, 1).getTime(),
addDays(aMonthAgo, 0).getTime(),
new Date().getTime(),
]}
tickFormatter={dateFormatter}
Expand Down
6 changes: 6 additions & 0 deletions admin/src/types/Stat_Game.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { RaRecord } from 'react-admin';

export interface Stat_Game extends RaRecord {
nbrGames?: number;
date?: Date;
}
1 change: 1 addition & 0 deletions api/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"ext-zlib": "*",
"abraham/twitteroauth": "^4.0",
"api-platform/core": "^2.7@dev",
"beberlei/doctrineextensions": "^1.3",
"doctrine/annotations": "^1.0",
"doctrine/doctrine-bundle": "^2.7",
"doctrine/doctrine-migrations-bundle": "^3.2",
Expand Down
59 changes: 58 additions & 1 deletion api/composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions api/config/packages/doctrine.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ doctrine:
auto_generate_proxy_classes: true
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
auto_mapping: true
dql:
string_functions:
DATE: DoctrineExtensions\Query\Postgresql\Date
mappings:
App:
is_bundle: false
Expand Down
4 changes: 4 additions & 0 deletions api/src/Entity/Stat.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@
paginationClientItemsPerPage: true
)
]
<<<<<<< HEAD
final class Stat
=======
class Stat
>>>>>>> 0caddfb... Add Stat entity to get the count of games creations by days on a period (#1)
{

public int $nbrGames = 0;
Expand Down
30 changes: 25 additions & 5 deletions api/src/Repository/GameRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
namespace App\Repository;

use App\Entity\Game;
use App\Entity\Stat;
use DateTime;
use Doctrine\DBAL\Result;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\Persistence\ManagerRegistry;
use Psr\Log\LoggerInterface;
Expand All @@ -16,6 +20,8 @@
* @method Game|null findOneBy(array $criteria, array $orderBy = null)
* @method Game[] findAll()
* @method Game[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
* @ORM\Entity
* @ORM\Table(name="game_repository")
*/
final class GameRepository extends CommonRepository
{
Expand All @@ -29,10 +35,10 @@ public function __construct(ManagerRegistry $registry, private readonly LoggerIn
*/
public function findOneByPlayer($player): ?Game
{
return $this->createQueryBuilder('p')
->andWhere('p.player = :val')
return $this->createQueryBuilder('g')
->andWhere('g.player = :val')
->setParameter('val', $player)
->orderBy('p.playDate', 'DESC')
->orderBy('g.playDate', 'DESC')
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
Expand All @@ -43,11 +49,25 @@ public function findOneByPlayer($player): ?Game
*/
public function findOneById(int $id): ?Game
{
return $this->createQueryBuilder('p')
->andWhere('p.id = :val')
return $this->createQueryBuilder('g')
->andWhere('g.id = :val')
->setParameter('val', $id)
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
}

public function getDaysCount(DateTime $afterDate, DateTime $beforeDate = new DateTime()): array
{
return $this->createQueryBuilder('g')
->select('count(g.id) as nbrGames, DATE(g.playDate) as date')
->andWhere('g.playDate <= :beforeDate')
->andWhere('g.playDate >= :afterDate')
->setParameter('beforeDate', $beforeDate)
->setParameter('afterDate', $afterDate)
->groupBy('date')
->orderBy('date', 'DESC')
->getQuery()
->getScalarResult();
}
}
31 changes: 31 additions & 0 deletions api/src/State/StatGamesCountProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace App\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Entity\Stat;
use App\Repository\GameRepository;
use DateTime;
use Exception;
use Psr\Log\LoggerInterface;
use RuntimeException;
use Symfony\Component\Serializer\SerializerInterface;

final class StatGamesCountProvider implements ProviderInterface
{
public function __construct(private readonly GameRepository $gameRepository, private readonly LoggerInterface $logger, private readonly SerializerInterface $serializer)
{
}

/**
* {@inheritDoc}
* @throws Exception
*/
public function provide(Operation $operation, array $uriVariables = [], array $context = []): array
{
return $this->serializer->denormalize($this->gameRepository->getDaysCount(new DateTime($uriVariables['id'])), Stat::class . '[]');
}
}
4 changes: 4 additions & 0 deletions api/tests/Api/StatTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;

<<<<<<< HEAD
final class StatTest extends ApiTestCase
=======
class StatTest extends ApiTestCase
>>>>>>> 0caddfb... Add Stat entity to get the count of games creations by days on a period (#1)
{
/**
* @throws RedirectionExceptionInterface
Expand Down

0 comments on commit d5923ef

Please sign in to comment.