diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/ResultService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/ResultService.java index a9cc7bd..d1ed494 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/ResultService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/ResultService.java @@ -7,6 +7,8 @@ import fr.titionfire.ffsaf.rest.data.ResultCategoryData; import fr.titionfire.ffsaf.rest.exception.DBadRequestException; import fr.titionfire.ffsaf.rest.exception.DForbiddenException; import fr.titionfire.ffsaf.utils.*; +import io.quarkus.cache.Cache; +import io.quarkus.cache.CacheName; import io.quarkus.hibernate.reactive.panache.common.WithSession; import io.quarkus.runtime.annotations.RegisterForReflection; import io.smallrye.mutiny.Multi; @@ -15,6 +17,7 @@ import io.smallrye.mutiny.unchecked.Unchecked; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import lombok.Builder; +import lombok.Data; import org.hibernate.reactive.mutiny.Mutiny; import java.util.*; @@ -52,6 +55,10 @@ public class ResultService { @Inject TradService trad; + @Inject + @CacheName("club-classement") + Cache cacheClubClassement; + private static final HashMap combTempIds = new HashMap<>(); private static String getCombTempId(Long key) { @@ -214,7 +221,7 @@ public class ResultService { } public void getClassementArray(CategoryModel categoryModel, MembreModel membreModel, List cards, - ResultCategoryData out) { + ResultCategoryData out) { if ((categoryModel.getType() & 2) != 0) { AtomicInteger rank = new AtomicInteger(0); categoryModel.getTree().stream() @@ -231,7 +238,8 @@ public class ResultService { } else { for (List list : out.getRankArray().values()) { for (ResultCategoryData.RankArray r : list) { - out.getClassement().add(new ResultCategoryData.ClassementData(r.getRank(), r.getComb(), r.getName())); + out.getClassement() + .add(new ResultCategoryData.ClassementData(r.getRank(), r.getComb(), r.getName())); } } } @@ -724,6 +732,100 @@ public class ResultService { } } + public Uni> getAllClubArray(String uuid) { + return getAllClubArray(uuid, true); + } + + public Uni> getAllClubArray(String uuid, SecurityCtx securityCtx) { + return hasAccess(uuid, securityCtx).chain(membreModel -> getAllClubArray(uuid, true)); + } + + public Uni> getAllClubArray(String uuid, boolean cache) { + List cards = new java.util.ArrayList<>(); + + //noinspection unchecked + cacheClubClassement.invalidateIf( + (p) -> ((Pair>) p).getKey() > System.currentTimeMillis()); + + if (!cache) + cacheClubClassement.invalidate(uuid); + + return cacheClubClassement.getAsync(uuid, k -> cardRepository.list("competition.uuid = ?1", uuid) + .invoke(__ -> System.out.println("Cache miss for club classement with uuid " + uuid)) + .invoke(cards::addAll) + .chain(__ -> matchRepository.list("category.compet.uuid = ?1", uuid)) + .chain(matchs -> { + HashMap> map = new HashMap<>(); + for (MatchModel match : matchs) { + if (!map.containsKey(match.getCategory())) + map.put(match.getCategory(), new java.util.ArrayList<>()); + map.get(match.getCategory()).add(match); + } + + return Multi.createFrom().iterable(map.entrySet()) + .onItem().call(entry -> Mutiny.fetch(entry.getKey().getTree())) + .map(entry -> { + ResultCategoryData tmp = new ResultCategoryData(); + + getArray2(entry.getValue().stream().map(m -> new MatchModelExtend(m, cards)).toList(), + null, tmp); + getClassementArray(entry.getKey(), null, cards, tmp); + + return tmp; + }) + .collect().asList(); + }) + .map(categoryData -> { + HashMap clubMap = new HashMap<>(); + + categoryData.forEach(c -> c.getClassement().forEach(classementData -> { + if (classementData.rank() > 3) + return; + + if (classementData.comb() != null) { + long clubId = 0L; + String clubName = ""; + if (classementData.comb() instanceof MembreModel membreModel2) { + clubId = (membreModel2.getClub() != null) ? membreModel2.getClub().getId() : 0; + clubName = (membreModel2.getClub() != null) ? membreModel2.getClub().getName() : ""; + } else if (classementData.comb() instanceof CompetitionGuestModel guestModel) { + if (guestModel.getClub() != null && !guestModel.getClub().isBlank()) + clubId = getClubTempId(guestModel.getClub()); + clubName = guestModel.getClub(); + } + + if (clubId != 0) { + clubMap.putIfAbsent(clubId, new ClubClassement(clubName)); + ClubClassement entity = clubMap.get(clubId); + entity.score[classementData.rank() - 1]++; + entity.tt_score += 4 - classementData.rank(); + } + } + })); + + return clubMap.values().stream() + .sorted(Comparator.comparingInt((ClubClassement c) -> c.tt_score) + .thenComparingInt(c -> c.score[0]) + .thenComparingInt(c -> c.score[1]) + .thenComparingInt(c -> c.score[2]).reversed()) + .toList(); + }) + .map(l -> new Pair<>(System.currentTimeMillis() + 60 * 1000L, l)) + ).map(Pair::getValue); + } + + @Data + @RegisterForReflection + public static class ClubClassement { + String name; + Integer[] score = new Integer[]{0, 0, 0}; + int tt_score = 0; + + public ClubClassement(String name) { + this.name = name; + } + } + @RegisterForReflection public static class CombStat { public int w; diff --git a/src/main/java/fr/titionfire/ffsaf/rest/ExternalResultEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/ExternalResultEndpoints.java index 259c3bb..5ba0b13 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/ExternalResultEndpoints.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/ExternalResultEndpoints.java @@ -8,6 +8,7 @@ import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; import java.util.HashMap; +import java.util.List; @Path("api/public/result/{id}") public class ExternalResultEndpoints { @@ -73,6 +74,13 @@ public class ExternalResultEndpoints { return resultService.getClubList(id); } + @GET + @Path("/club/classement") + @Produces(MediaType.APPLICATION_JSON) + public Uni> clubClassement() { + return resultService.getAllClubArray(id); + } + @GET @Path("/club/data") @Produces(MediaType.APPLICATION_JSON) diff --git a/src/main/java/fr/titionfire/ffsaf/rest/ResultEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/ResultEndpoints.java index 25e1a6e..b4d0184 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/ResultEndpoints.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/ResultEndpoints.java @@ -47,6 +47,12 @@ public class ResultEndpoints { return resultService.getClubList(uuid, securityCtx); } + @GET + @Path("{uuid}/club/classement") + public Uni> getClubClassement(@PathParam("uuid") String uuid) { + return resultService.getAllClubArray(uuid, securityCtx); + } + @GET @Path("{uuid}/club/{id}") public Uni getClub(@PathParam("uuid") String uuid, @PathParam("id") long id) { @@ -64,7 +70,7 @@ public class ResultEndpoints { public Uni getCombList(@PathParam("uuid") String uuid, @PathParam("id") String id) { return resultService.getCombArrayPublic(uuid, id, securityCtx); } - + @GET @Path("{uuid}/comb") public Uni getComb(@PathParam("uuid") String uuid) { diff --git a/src/main/webapp/public/competition.js b/src/main/webapp/public/competition.js index 28cde4b..c9295a6 100644 --- a/src/main/webapp/public/competition.js +++ b/src/main/webapp/public/competition.js @@ -39,6 +39,9 @@ function setSubPage(name) { case 'club': clubPage(location); break; + case 'clubRank': + clubRankPage(); + break; case 'all': combsPage(); break; @@ -54,6 +57,7 @@ function homePage() {
  • ${i18next.t('parCatégorie')}
  • ${i18next.t('parCombattant')}
  • ${i18next.t('parClub')}
  • +
  • ${i18next.t('classementClub')}
  • ${i18next.t('tousLesCombattants')}
  • ` @@ -62,6 +66,7 @@ function homePage() { document.getElementById('pouleLink').addEventListener('click', () => setSubPage('poule')); document.getElementById('combLink').addEventListener('click', () => setSubPage('comb')); document.getElementById('clubLink').addEventListener('click', () => setSubPage('club')); + document.getElementById('clubClassement').addEventListener('click', () => setSubPage('clubRank')); document.getElementById('allLink').addEventListener('click', () => setSubPage('all')); } @@ -640,6 +645,60 @@ function clubPage(location) { rootDiv.append(content) } +function buildClubsView(clubs) { + const pouleDiv = document.createElement('div'); + let arrayContent = ` +

    ${i18next.t('classementClub')} :

    + +
    + + + + + + + + + ` + for (const club of clubs) { + arrayContent += ` + + + + + + + ` + } + arrayContent += `
    ${i18next.t('club')}${i18next.t('1er')}${i18next.t('2eme')}${i18next.t('3eme')}${i18next.t('scores')}
    ${club.name}${club.score[0]}${club.score[1]}${club.score[2]}${club.tt_score}
    ` + pouleDiv.innerHTML = arrayContent; + return pouleDiv; +} + +function clubRankPage() { + rootDiv.innerHTML = `

    ${i18next.t('résultatDeLaCompétition')} :

    ${i18next.t('back')}`; + document.getElementById('homeLink').addEventListener('click', () => setSubPage('home')); + + const content = document.createElement('div'); + content.style.marginTop = '1em'; + + const dataContainer = document.createElement('div'); + dataContainer.id = 'data-container'; + + const loading = startLoading(content); + fetch(`${apiUrlRoot}/club/classement`) + .then(response => response.json()) + .then(clubs => { + console.log(clubs); + dataContainer.replaceChildren(buildClubsView(clubs)); + }) + .catch(() => dataContainer.replaceChildren(new Text(i18next.t('erreurDeChargementDeLaListe')))) + .finally(() => stopLoading(loading)); + + content.append(dataContainer); + rootDiv.append(content) +} + function buildCombsView(combs) { const pouleDiv = document.createElement('div'); let arrayContent = ` diff --git a/src/main/webapp/public/locales/en/result.json b/src/main/webapp/public/locales/en/result.json index 4de482a..bd9ba68 100644 --- a/src/main/webapp/public/locales/en/result.json +++ b/src/main/webapp/public/locales/en/result.json @@ -2,6 +2,9 @@ "--sélectionnerUnClub--": "--Select a club--", "--sélectionnerUnCombattant--": "--Select a fighter--", "--sélectionnerUneCatégorie--": "--Select a category--", + "1er": "Gold medal", + "2eme": "Silver medal", + "3eme": "Bronze medal", "abs.": "abs.", "adversaire": "Opponent", "aujourdhuià": "Today at {{time}}", @@ -11,7 +14,8 @@ "catégorie": "Category", "chargement": "Loading", "classement": "Ranking", - "classementFinal": "Final standings", + "classementClub": "Club ranking", + "classementFinal": "Final ranking", "club": "Club", "combattant": "Fighter", "combattants": "Fighters", diff --git a/src/main/webapp/public/locales/fr/result.json b/src/main/webapp/public/locales/fr/result.json index 115350d..d14a7b0 100644 --- a/src/main/webapp/public/locales/fr/result.json +++ b/src/main/webapp/public/locales/fr/result.json @@ -2,6 +2,9 @@ "--sélectionnerUnClub--": "--Sélectionner un club--", "--sélectionnerUnCombattant--": "--Sélectionner un combattant--", "--sélectionnerUneCatégorie--": "--Sélectionner une catégorie--", + "1er": "Médaille d'or", + "2eme": "Médaille d'argent", + "3eme": "Médaille de bronze", "abs.": "abs.", "adversaire": "Adversaire", "aujourdhuià": "Aujourd'hui à {{time}}", @@ -11,6 +14,7 @@ "catégorie": "Catégorie", "chargement": "Chargement", "classement": "Classement", + "classementClub": "Classement club", "classementFinal": "Classement final", "club": "Club", "combattant": "Combattant", diff --git a/src/main/webapp/src/pages/result/ResultView.jsx b/src/main/webapp/src/pages/result/ResultView.jsx index 5557842..e3c933e 100644 --- a/src/main/webapp/src/pages/result/ResultView.jsx +++ b/src/main/webapp/src/pages/result/ResultView.jsx @@ -39,7 +39,8 @@ export function ResultView() { {resultShow && resultShow === "cat" && || resultShow && resultShow === "club" && || resultShow && resultShow === "comb" && - || resultShow && resultShow === "combs" && } + || resultShow && resultShow === "combs" && + || resultShow && resultShow === "clubs" && } @@ -66,6 +67,10 @@ function MenuBar({resultShow, setResultShow}) { setResultShow("combs")}>{t('combattants')} +
  • + setResultShow("clubs")}>{t('classementClub')} +
  • /* @@ -450,6 +455,43 @@ function CombResult({uuid, combId}) { } +function ClubsResult({uuid}) { + const setLoading = useLoadingSwitcher() + const {data, error} = useFetch(`/result/${uuid}/club/classement`, setLoading, 1) + const {t} = useTranslation('result'); + + return <> + {data ? <> +

    {t('classementClub')} :

    + + + + + + + + + + + + + {data.map((club, idx) => + + + + + + )} + +
    {t('club')}{t('1er')}{t('2eme')}{t('3eme')}{t('scores')}
    {club.name}{club.score[0]}{club.score[1]}{club.score[2]}{club.tt_score}
    + + : error + ? + : + } + +} + function CombsResult({uuid}) { const setLoading = useLoadingSwitcher() const {data, error} = useFetch(`/result/${uuid}/comb`, setLoading, 1)