package fr.titionfire.ffsaf.domain.service; import fr.titionfire.ffsaf.data.model.*; import fr.titionfire.ffsaf.data.repository.*; 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.hibernate.reactive.panache.common.WithSession; import io.quarkus.runtime.annotations.RegisterForReflection; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.unchecked.Unchecked; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import lombok.Builder; import org.hibernate.reactive.mutiny.Mutiny; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; @WithSession @ApplicationScoped public class ResultService { @Inject CompetitionRepository compRepository; @Inject RegisterRepository registerRepository; @Inject MembreService membreService; @Inject ClubRepository clubRepository; @Inject CompetitionGuestRepository competitionGuestRepository; @Inject CategoryRepository categoryRepository; @Inject MatchRepository matchRepository; private static final ResourceBundle BUNDLE = ResourceBundle.getBundle("lang.String"); private static final HashMap combTempIds = new HashMap<>(); private static String getCombTempId(Long key) { synchronized (combTempIds) { if (!combTempIds.containsKey(key)) { combTempIds.put(key, UUID.randomUUID().toString()); } return combTempIds.get(key); } } private static Long getCombTempId(String value) { synchronized (combTempIds) { for (Map.Entry entry : combTempIds.entrySet()) { if (entry.getValue().equals(value)) { return entry.getKey(); } } return null; } } private static final HashMap clubTempIds = new HashMap<>(); private static Long getClubTempId(String key) { synchronized (clubTempIds) { if (!clubTempIds.containsKey(key)) { clubTempIds.put(key, (System.currentTimeMillis() + clubTempIds.size()) * -1); } return clubTempIds.get(key); } } private static String getClubTempId(Long value) { synchronized (clubTempIds) { for (Map.Entry entry : clubTempIds.entrySet()) { if (entry.getValue().equals(value)) { return entry.getKey(); } } return null; } } public Uni> getList(SecurityCtx securityCtx) { return membreService.getByAccountId(securityCtx.getSubject()) .chain(m -> registerRepository.list("membre = ?1", m)) .onItem().transformToMulti(Multi.createFrom()::iterable) .onItem().call(r -> Mutiny.fetch(r.getCompetition())) .onItem().transform(r -> new Object[]{r.getCompetition().getUuid(), r.getCompetition().getName(), r.getCompetition().getDate()}) .collect().asList(); } public Uni> getCategoryList(String uuid) { return categoryRepository.list("compet.uuid = ?1", uuid) .map(categoryModels -> { HashMap map = new HashMap<>(); categoryModels.stream() .sorted(Comparator.comparing(CategoryModel::getName)) .forEachOrdered(categoryModel -> map.put(categoryModel.getName(), categoryModel.getId())); return map; }); } public Uni getCategory(String uuid, long poule, SecurityCtx securityCtx) { return hasAccess(uuid, securityCtx).chain(r -> matchRepository.list("category.compet.uuid = ?1 AND category.id = ?2", uuid, poule) .call(list -> Mutiny.fetch(list.get(0).getCategory().getTree())) .map(list -> getData(list, r.getMembre()))); } public Uni getCategory(String uuid, long poule) { return matchRepository.list("category.compet.uuid = ?1 AND category.id = ?2", uuid, poule) .call(list -> Mutiny.fetch(list.get(0).getCategory().getTree())) .map(list -> getData(list, null)); } private ResultCategoryData getData(List matchModels, MembreModel membreModel) { ResultCategoryData out = new ResultCategoryData(); CategoryModel categoryModel = matchModels.get(0).getCategory(); out.setName(categoryModel.getName()); out.setType(categoryModel.getType()); out.setLiceName(categoryModel.getLiceName() == null ? new String[]{} : categoryModel.getLiceName().split(";")); out.setGenTime(System.currentTimeMillis()); getArray2(matchModels, membreModel, out); getTree(categoryModel.getTree(), membreModel, out); return out; } private void getArray2(List matchModels_, MembreModel membreModel, ResultCategoryData out) { List matchModels = matchModels_.stream().filter(o -> o.getCategory_ord() >= 0).toList(); HashMap> matchMap = new HashMap<>(); for (MatchModel model : matchModels) { char g = model.getPoule(); if (!matchMap.containsKey(g)) matchMap.put(g, new ArrayList<>()); matchMap.get(g).add(model); } matchMap.forEach((c, matchEntities) -> { List matchs = matchEntities.stream() .sorted(Comparator.comparing(MatchModel::getCategory_ord)) .map(o -> ResultCategoryData.PouleArrayData.fromModel(o, membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS)) .toList(); List rankArray = matchEntities.stream() .flatMap(m -> Stream.of(m.getC1_id(), m.getC2_id(), m.getC1_guest(), m.getC2_guest())) .distinct() .filter(Objects::nonNull) .map(comb -> { AtomicInteger w = new AtomicInteger(0); AtomicInteger pointMake = new AtomicInteger(0); AtomicInteger pointTake = new AtomicInteger(0); matchEntities.stream() .filter(m -> m.isEnd() && (m.isC1(comb) || m.isC2(comb))) .forEach(matchModel -> { int win = matchModel.win(); if ((matchModel.isC1(comb) && win > 0) || matchModel.isC2(comb) && win < 0) w.getAndIncrement(); for (ScoreEmbeddable score : matchModel.getScores()) { if (score.getS1() <= -900 || score.getS2() <= -900) continue; if (matchModel.isC1(comb)) { pointMake.addAndGet(score.getS1()); pointTake.addAndGet(score.getS2()); } else { pointMake.addAndGet(score.getS2()); pointTake.addAndGet(score.getS1()); } } }); float pointRate = (pointTake.get() == 0) ? pointMake.get() : (float) pointMake.get() / pointTake.get(); return new ResultCategoryData.RankArray(0, comb.getName(membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS), w.get(), pointMake.get(), pointTake.get(), pointRate); }) .sorted(Comparator .comparing(ResultCategoryData.RankArray::getWin) .thenComparing(ResultCategoryData.RankArray::getPointRate).reversed()) .toList(); out.getMatchs().put(c, matchs); int lastWin = -1; float pointRate = 0; int rank = 0; for (ResultCategoryData.RankArray rankArray1 : rankArray) { if (rankArray1.getWin() != lastWin || pointRate != rankArray1.getPointRate()) { lastWin = rankArray1.getWin(); pointRate = rankArray1.getPointRate(); rank++; } rankArray1.setRank(rank); } out.getRankArray().put(c, rankArray); }); } private static void convertTree(TreeModel src, TreeNode dst, MembreModel membreModel, ResultPrivacy privacy) { dst.setData(ResultCategoryData.TreeData.from(src.getMatch(), membreModel, privacy)); if (src.getLeft() != null) { dst.setLeft(new TreeNode<>()); convertTree(src.getLeft(), dst.getLeft(), membreModel, privacy); } if (src.getRight() != null) { dst.setRight(new TreeNode<>()); convertTree(src.getRight(), dst.getRight(), membreModel, privacy); } } private void getTree(List treeModels, MembreModel membreModel, ResultCategoryData out) { ArrayList> trees = new ArrayList<>(); treeModels.stream().filter(t -> t.getLevel() != 0).forEach(treeModel -> { TreeNode root = new TreeNode<>(); convertTree(treeModel, root, membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS); trees.add(root); }); out.setTrees(trees); } public Uni getAllCombArray(String uuid, SecurityCtx securityCtx) { return hasAccess(uuid, securityCtx) .chain(r -> getAllCombArray_(uuid, r.getMembre())); } public Uni getAllCombArrayPublic(String uuid) { return getAllCombArray_(uuid, null); } private Uni getAllCombArray_(String uuid, MembreModel membreModel) { return registerRepository.list("competition.uuid = ?1", uuid) .chain(registers -> matchRepository.list("category.compet.uuid = ?1", uuid) .map(matchModels -> new Pair<>(registers, matchModels))) .map(pair -> { List registers = pair.getKey(); List matchModels = pair.getValue(); CombsArrayData.CombsArrayDataBuilder builder = CombsArrayData.builder(); List combs = matchModels.stream() .flatMap(m -> Stream.of(m.getC1_id(), m.getC2_id(), m.getC1_guest(), m.getC2_guest())) .filter(Objects::nonNull) .distinct() .map(comb -> { var builder2 = CombsArrayData.CombsData.builder(); AtomicInteger w = new AtomicInteger(0); AtomicInteger l = new AtomicInteger(0); AtomicInteger pointMake = new AtomicInteger(); AtomicInteger pointTake = new AtomicInteger(); makeStat(matchModels, comb, w, l, pointMake, pointTake); Categorie categorie = null; String clubName = null; Optional register = registers.stream() .filter(r -> Objects.equals(r.getMembre(), comb)).findFirst(); if (register.isPresent()) { categorie = register.get().getCategorie2(); ClubModel club = register.get().getClub2(); clubName = (club == null) ? BUNDLE.getString("no.licence") : club.getName(); } else if (comb instanceof CompetitionGuestModel guestModel) { categorie = guestModel.getCategorie(); clubName = guestModel.getClub(); } else if (comb instanceof MembreModel model) { categorie = model.getCategorie(); clubName = (model.getClub() == null) ? BUNDLE.getString( "no.licence") : model.getClub().getName(); } builder2.cat((categorie == null) ? "---" : categorie.getName(BUNDLE)); builder2.name(comb.getName(membreModel, ResultPrivacy.REGISTERED_ONLY)); builder2.w(w.get()); builder2.l(l.get()); builder2.ratioVictoire((l.get() == 0) ? w.get() : (float) w.get() / l.get()); builder2.club(clubName); builder2.pointMake(pointMake.get()); builder2.pointTake(pointTake.get()); builder2.ratioPoint( (pointTake.get() == 0) ? pointMake.get() : (float) pointMake.get() / pointTake.get()); return builder2.build(); }) .sorted(Comparator.comparing(CombsArrayData.CombsData::name)) .toList(); builder.nb_insc(combs.size()); builder.tt_match((int) matchModels.stream().filter(MatchModel::isEnd).count()); builder.point(combs.stream().mapToInt(CombsArrayData.CombsData::pointMake).sum()); builder.combs(combs); return builder.build(); }); } public Uni> getCombList(String uuid, ResultPrivacy privacy) { return registerRepository.list("competition.uuid = ?1 AND membre.resultPrivacy <= ?2", uuid, privacy) .map(models -> { HashMap map = new HashMap<>(); models.forEach( r -> map.put(Utils.getFullName(r.getMembre()), getCombTempId(r.getMembre().getId()))); return map; }) .chain(map -> competitionGuestRepository.list("competition.uuid = ?1", uuid) .map(models -> { models.forEach(guestModel -> map.put(Utils.getFullName(guestModel), getCombTempId(guestModel.getId() * -1))); return map; }) ); } public Uni getCombArrayPublic(String uuid, String combTempId, ResultPrivacy privacy) { CombArrayData.CombArrayDataBuilder builder = CombArrayData.builder(); Long id = getCombTempId(combTempId); if (id == null) { return Uni.createFrom().failure(new DForbiddenException("Comb not found")); } Uni> uni; if (id >= 0) { uni = registerRepository.find("membre.id = ?1 AND competition.uuid = ?2 AND membre.resultPrivacy <= ?3", id, uuid, privacy).firstResult() .chain(Unchecked.function(registerModel -> { if (registerModel == null) throw new DBadRequestException("Combattant non inscrit"); builder.name(Utils.getFullName(registerModel.getMembre())); builder.club((registerModel.getClub2() == null) ? BUNDLE.getString( "no.licence") : registerModel.getClub2().getName()); builder.cat((registerModel.getCategorie2() == null) ? "---" : registerModel.getCategorie2() .getName(BUNDLE)); return matchRepository.list("category.compet.uuid = ?1 AND (c1_id = ?2 OR c2_id = ?2)", uuid, registerModel.getMembre()); })); } else { uni = competitionGuestRepository.find("id = ?1 AND competition.uuid = ?2", -id, uuid).firstResult() .chain(guestModel -> { builder.name(Utils.getFullName(guestModel)); builder.club(guestModel.getClub()); builder.cat((guestModel.getCategorie() == null) ? "---" : guestModel.getCategorie() .getName(BUNDLE)); return matchRepository.list("category.compet.uuid = ?1 AND (c1_guest = ?2 OR c2_guest = ?2)", uuid, guestModel); }); } return uni.invoke(matchModels -> { List pouleModels = matchModels.stream().map(MatchModel::getCategory).distinct() .toList(); List matchs = new ArrayList<>(); AtomicInteger sumW = new AtomicInteger(); AtomicInteger sumPointMake = new AtomicInteger(0); AtomicInteger sumPointTake = new AtomicInteger(0); for (MatchModel matchModel : matchModels) { if ((matchModel.getC1_id() == null && matchModel.getC1_guest() == null) || (matchModel.getC2_id() == null && matchModel.getC2_guest() == null)) continue; var builder2 = CombArrayData.MatchsData.builder(); builder2.date(matchModel.getDate()); builder2.poule(pouleModels.stream().filter(p -> p.equals(matchModel.getCategory())) .map(CategoryModel::getName).findFirst().orElse("")); AtomicInteger pointMake = new AtomicInteger(); AtomicInteger pointTake = new AtomicInteger(); if (matchModel.isC1(id)) { builder2.adv(Utils.getFullName(matchModel.getC2_id(), matchModel.getC2_guest())); if (matchModel.isEnd()) { matchModel.getScores().stream() .filter(s -> s.getS1() > -900 && s.getS2() > -900) .forEach(scoreEntity -> { pointMake.addAndGet(scoreEntity.getS1()); pointTake.addAndGet(scoreEntity.getS2()); }); builder2.score(matchModel.getScores().stream() .map(s -> new Integer[]{s.getS1(), s.getS2()}).toList()); } else { builder2.score(new ArrayList<>()); } builder2.win(matchModel.win() > 0); } else { builder2.adv(Utils.getFullName(matchModel.getC1_id(), matchModel.getC1_guest())); if (matchModel.isEnd()) { matchModel.getScores().stream() .filter(s -> s.getS1() > -900 && s.getS2() > -900) .forEach(scoreEntity -> { pointMake.addAndGet(scoreEntity.getS2()); pointTake.addAndGet(scoreEntity.getS1()); }); builder2.score(matchModel.getScores().stream() .map(s -> new Integer[]{s.getS2(), s.getS1()}).toList()); } else { builder2.score(new ArrayList<>()); } builder2.win(matchModel.win() < 0); } builder2.ratio( (pointTake.get() == 0) ? pointMake.get() : (float) pointMake.get() / pointTake.get()); sumPointMake.addAndGet(pointMake.get()); sumPointTake.addAndGet(pointTake.get()); matchs.add(builder2.build()); if (builder2.win) sumW.getAndIncrement(); } builder.totalWin(sumW.get()); builder.pointRatio( (sumPointTake.get() == 0) ? sumPointMake.get() : (float) sumPointMake.get() / sumPointTake.get()); builder.pointMake(sumPointMake.get()); builder.pointTake(sumPointTake.get()); matchs.sort(Comparator.comparing(CombArrayData.MatchsData::poule) .thenComparing(CombArrayData.MatchsData::adv)); builder.matchs(matchs); }) .map(__ -> builder.build()); } @Builder @RegisterForReflection public static record CombsArrayData(int nb_insc, int tt_match, long point, List combs) { @Builder @RegisterForReflection public static record CombsData(String cat, String club, String name, int w, int l, float ratioVictoire, float ratioPoint, int pointMake, int pointTake) { } } @Builder @RegisterForReflection public static record CombArrayData(String name, String club, String cat, int totalWin, float pointRatio, int pointMake, int pointTake, List matchs) { @Builder @RegisterForReflection public static record MatchsData(Date date, String poule, String adv, List score, float ratio, boolean win) { } } public Uni> getClubList(String uuid) { return registerRepository.list("competition.uuid = ?1", uuid) .map(registers -> { HashMap registerMap = new HashMap<>(); registers.stream().map(RegisterModel::getClub2).distinct().filter(Objects::nonNull) .forEach(registerClub -> registerMap.put(registerClub.getName(), registerClub.getId())); return registerMap; }) .chain(map -> competitionGuestRepository.list("competition.uuid = ?1", uuid) .map(guests -> { guests.stream().map(CompetitionGuestModel::getClub).distinct() .filter(Objects::nonNull) .forEach(guestClub -> map.putIfAbsent(guestClub, getClubTempId(guestClub))); return map; }) ); } public Uni getClubArray(String uuid, Long id, SecurityCtx securityCtx) { return hasAccess(uuid, securityCtx).chain(cm_register -> getClubArray2(uuid, id, cm_register.getMembre())); } public Uni getClubArray2(String uuid, Long id, MembreModel membreModel) { if (id < 0) { String clubName = getClubTempId(id); if (clubName == null) { return Uni.createFrom().failure(new DForbiddenException("Club not found")); } return competitionGuestRepository.list("competition.uuid = ?1 AND club = ?2", uuid, clubName) .call(list -> { if (list.isEmpty()) return Uni.createFrom().failure(new DBadRequestException("Club not found")); return Uni.createFrom().voidItem(); }) .chain(guests -> matchRepository.list( "category.compet.uuid = ?1 AND (c1_guest IN ?2 OR c2_guest IN ?2)", uuid, guests) .map(matchModels -> getClubArray2(clubName, guests.stream().map(o -> (CombModel) o).toList(), matchModels, new ArrayList<>(), membreModel))); } else { return clubRepository.findById(id).chain(clubModel -> registerRepository.list("competition.uuid = ?1 AND membre.club = ?2", uuid, clubModel) .chain(registers -> matchRepository.list("category.compet.uuid = ?1", uuid) .map(matchModels -> getClubArray2(clubModel.getName(), registers.stream().map(o -> (CombModel) o.getMembre()).toList(), matchModels, registers, membreModel)))); } } private ClubArrayData getClubArray2(String name, List combs, List matchModels, List registers, MembreModel membreModel) { ClubArrayData.ClubArrayDataBuilder builder = ClubArrayData.builder(); builder.name(name); builder.nb_insc(combs.size()); AtomicInteger tt_win = new AtomicInteger(0); AtomicInteger tt_match = new AtomicInteger(0); List combData = combs.stream().map(comb -> { var builder2 = ClubArrayData.CombData.builder(); AtomicInteger w = new AtomicInteger(0); AtomicInteger l = new AtomicInteger(0); AtomicInteger pointMake = new AtomicInteger(); AtomicInteger pointTake = new AtomicInteger(); makeStat(matchModels, comb, w, l, pointMake, pointTake); Categorie categorie = null; Optional register = registers.stream() .filter(r -> Objects.equals(r.getMembre(), comb)).findFirst(); if (register.isPresent()) { categorie = register.get().getCategorie2(); } else if (comb instanceof CompetitionGuestModel guestModel) { categorie = guestModel.getCategorie(); } else if (comb instanceof MembreModel model) { categorie = model.getCategorie(); } builder2.cat((categorie == null) ? "---" : categorie.getName(BUNDLE)); builder2.name(comb.getName(membreModel, ResultPrivacy.REGISTERED_ONLY)); builder2.w(w.get()); builder2.l(l.get()); builder2.ratioVictoire((l.get() == 0) ? w.get() : (float) w.get() / l.get()); builder2.pointMake(pointMake.get()); builder2.pointTake(pointTake.get()); builder2.ratioPoint((pointTake.get() == 0) ? pointMake.get() : (float) pointMake.get() / pointTake.get()); tt_win.addAndGet(w.get()); tt_match.addAndGet(w.get() + l.get()); return builder2.build(); }) .sorted(Comparator.comparing(ClubArrayData.CombData::name)) .toList(); builder.nb_match(tt_match.get()); builder.match_w(tt_win.get()); builder.ratioVictoire((float) combData.stream().filter(c -> c.l + c.w != 0) .mapToDouble(ClubArrayData.CombData::ratioVictoire).average().orElse(0L)); builder.pointMake(combData.stream().mapToInt(ClubArrayData.CombData::pointMake).sum()); builder.pointTake(combData.stream().mapToInt(ClubArrayData.CombData::pointTake).sum()); builder.ratioPoint((float) combData.stream().filter(c -> c.l + c.w != 0) .mapToDouble(ClubArrayData.CombData::ratioPoint).average().orElse(0L)); builder.combs(combData); return builder.build(); } private static void makeStat(List matchModels, CombModel comb, AtomicInteger w, AtomicInteger l, AtomicInteger pointMake, AtomicInteger pointTake) { matchModels.stream() .filter(m -> m.isEnd() && (m.isC1(comb) || m.isC2(comb))) .forEach(matchModel -> { int win = matchModel.win(); if ((matchModel.isC1(comb) && win > 0) || matchModel.isC2(comb) && win < 0) { w.getAndIncrement(); } else { l.getAndIncrement(); } matchModel.getScores().stream() .filter(s -> s.getS1() > -900 && s.getS2() > -900) .forEach(score -> { if (matchModel.isC1(comb)) { pointMake.addAndGet(score.getS1()); pointTake.addAndGet(score.getS2()); } else { pointMake.addAndGet(score.getS2()); pointTake.addAndGet(score.getS1()); } }); }); } @Builder @RegisterForReflection public static record ClubArrayData(String name, int nb_insc, int nb_match, int match_w, float ratioVictoire, float ratioPoint, int pointMake, int pointTake, List combs) { @Builder @RegisterForReflection public static record CombData(String cat, String name, int w, int l, float ratioVictoire, float ratioPoint, int pointMake, int pointTake) { } } private Uni hasAccess(String uuid, SecurityCtx securityCtx) { return registerRepository.find("membre.userId = ?1 AND competition.uuid = ?2", securityCtx.getSubject(), uuid) .firstResult() .invoke(Unchecked.consumer(o -> { if (o == null) throw new DForbiddenException("Access denied"); })); } private Uni hasAccess(Long compId, SecurityCtx securityCtx) { return registerRepository.find("membre.userId = ?1 AND competition.id = ?2", securityCtx.getSubject(), compId) .firstResult() .invoke(Unchecked.consumer(o -> { if (o == null) throw new DForbiddenException("Access denied"); })); } }