feat: add podium PDF
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 7m28s
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 7m28s
This commit is contained in:
parent
ed5d73c25f
commit
2f390b03e2
@ -1,9 +1,11 @@
|
||||
package fr.titionfire.ffsaf.data.model;
|
||||
|
||||
import fr.titionfire.ffsaf.utils.Categorie;
|
||||
import fr.titionfire.ffsaf.utils.ResultPrivacy;
|
||||
|
||||
public interface CombModel {
|
||||
Long getCombId();
|
||||
String getName();
|
||||
String getName(MembreModel model, ResultPrivacy privacy);
|
||||
Categorie getCategorie();
|
||||
}
|
||||
|
||||
@ -188,6 +188,7 @@ public class ResultService {
|
||||
comb.getName(membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS), stat.score, stat.w,
|
||||
stat.pointMake, stat.pointTake, stat.getPointRate());
|
||||
})
|
||||
.filter(r -> r.getPointMake() > 0 || r.getPointTake() > 0)
|
||||
.sorted(Comparator
|
||||
.comparing(ResultCategoryData.RankArray::getScore)
|
||||
.thenComparing(ResultCategoryData.RankArray::getWin)
|
||||
@ -212,7 +213,7 @@ public class ResultService {
|
||||
});
|
||||
}
|
||||
|
||||
private void getClassementArray(CategoryModel categoryModel, MembreModel membreModel, List<CardModel> cards,
|
||||
public void getClassementArray(CategoryModel categoryModel, MembreModel membreModel, List<CardModel> cards,
|
||||
ResultCategoryData out) {
|
||||
if ((categoryModel.getType() & 2) != 0) {
|
||||
AtomicInteger rank = new AtomicInteger(0);
|
||||
@ -258,7 +259,7 @@ public class ResultService {
|
||||
.add(new ResultCategoryData.ClassementData(rank.incrementAndGet(), m.getC1(),
|
||||
m.getC1Name(membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS)));
|
||||
out.getClassement()
|
||||
.add(new ResultCategoryData.ClassementData(rank.getAndIncrement(), m.getC2(),
|
||||
.add(new ResultCategoryData.ClassementData(rank.get(), m.getC2(),
|
||||
m.getC2Name(membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS)));
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -54,6 +54,9 @@ public class CompetitionWS {
|
||||
@Inject
|
||||
RState rState;
|
||||
|
||||
@Inject
|
||||
RPDF rpdf;
|
||||
|
||||
@Inject
|
||||
SecurityCtx securityCtx;
|
||||
|
||||
@ -99,6 +102,7 @@ public class CompetitionWS {
|
||||
getWSReceiverMethods(RCard.class, rCard);
|
||||
getWSReceiverMethods(RTeam.class, rTeam);
|
||||
getWSReceiverMethods(RState.class, rState);
|
||||
getWSReceiverMethods(RPDF.class, rpdf);
|
||||
|
||||
executor = notifyExecutor;
|
||||
}
|
||||
|
||||
107
src/main/java/fr/titionfire/ffsaf/ws/recv/RPDF.java
Normal file
107
src/main/java/fr/titionfire/ffsaf/ws/recv/RPDF.java
Normal file
@ -0,0 +1,107 @@
|
||||
package fr.titionfire.ffsaf.ws.recv;
|
||||
|
||||
import fr.titionfire.ffsaf.data.model.CardModel;
|
||||
import fr.titionfire.ffsaf.data.model.CategoryModel;
|
||||
import fr.titionfire.ffsaf.data.model.MatchModel;
|
||||
import fr.titionfire.ffsaf.data.repository.CardRepository;
|
||||
import fr.titionfire.ffsaf.data.repository.CategoryRepository;
|
||||
import fr.titionfire.ffsaf.data.repository.CompetitionRepository;
|
||||
import fr.titionfire.ffsaf.data.repository.MatchRepository;
|
||||
import fr.titionfire.ffsaf.domain.entity.MatchModelExtend;
|
||||
import fr.titionfire.ffsaf.domain.service.ResultService;
|
||||
import fr.titionfire.ffsaf.domain.service.TradService;
|
||||
import fr.titionfire.ffsaf.rest.data.ResultCategoryData;
|
||||
import fr.titionfire.ffsaf.utils.Categorie;
|
||||
import fr.titionfire.ffsaf.ws.PermLevel;
|
||||
import io.quarkus.hibernate.reactive.panache.common.WithSession;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import io.quarkus.websockets.next.WebSocketConnection;
|
||||
import io.smallrye.mutiny.Multi;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.hibernate.reactive.mutiny.Mutiny;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@WithSession
|
||||
@ApplicationScoped
|
||||
@RegisterForReflection
|
||||
public class RPDF {
|
||||
|
||||
@Inject
|
||||
CompetitionRepository competitionRepository;
|
||||
|
||||
@Inject
|
||||
MatchRepository matchRepository;
|
||||
|
||||
@Inject
|
||||
CardRepository cardRepository;
|
||||
|
||||
@Inject
|
||||
CategoryRepository categoryRepository;
|
||||
|
||||
@Inject
|
||||
ResultService resultService;
|
||||
|
||||
@Inject
|
||||
TradService trad;
|
||||
|
||||
@Transactional
|
||||
@WSReceiver(code = "getPodium", permission = PermLevel.VIEW)
|
||||
public Uni<List<PodiumEntity>> getPodium(WebSocketConnection connection, Object o) {
|
||||
List<CardModel> cards = new java.util.ArrayList<>();
|
||||
|
||||
return cardRepository.list("competition.uuid = ?1", connection.pathParam("uuid"))
|
||||
.invoke(cards::addAll)
|
||||
.chain(__ -> matchRepository.list("category.compet.uuid = ?1", connection.pathParam("uuid")))
|
||||
.chain(matchs -> {
|
||||
HashMap<CategoryModel, List<MatchModel>> 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();
|
||||
|
||||
double cmoy = entry.getValue().stream().flatMap(m -> Stream.of(m.getC1(), m.getC2()))
|
||||
.filter(c -> c != null && c.getCategorie() != null)
|
||||
.mapToInt(c -> c.getCategorie().ordinal())
|
||||
.average().orElse(0);
|
||||
Categorie categorie_moy = Categorie.values()[(int) Math.ceil(cmoy)];
|
||||
|
||||
resultService.getArray2(
|
||||
entry.getValue().stream().map(m -> new MatchModelExtend(m, cards)).toList(),
|
||||
null, tmp);
|
||||
resultService.getClassementArray(entry.getKey(), null, cards, tmp);
|
||||
|
||||
String source = "";
|
||||
if ((entry.getKey().getType() & 2) != 0) {
|
||||
if (entry.getKey().isTreeAreClassement())
|
||||
source = trad.t("podium.source.classement", connection);
|
||||
else
|
||||
source = trad.t("podium.source.tree", connection);
|
||||
} else if ((entry.getKey().getType() & 1) != 0)
|
||||
source = trad.t("podium.source.poule", connection);
|
||||
|
||||
|
||||
return new PodiumEntity(entry.getKey().getName(), source, categorie_moy,
|
||||
tmp.getClassement());
|
||||
})
|
||||
.collect().asList();
|
||||
});
|
||||
}
|
||||
|
||||
@RegisterForReflection
|
||||
public static record PodiumEntity(String poule_name, String source, Categorie categorie,
|
||||
List<ResultCategoryData.ClassementData> podium) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -89,3 +89,6 @@ carton.non.trouver=Card not found
|
||||
card.cannot.be.added=Unable to add the card
|
||||
configuration.non.supportee=Unsupported configuration
|
||||
err.match.termine=Error, a placement match has already been played
|
||||
podium.source.classement=Ranking
|
||||
podium.source.tree=Tournaments
|
||||
podium.source.poule=Pool
|
||||
|
||||
@ -85,3 +85,6 @@ carton.non.trouver=Carton introuvable
|
||||
card.cannot.be.added=Impossible d'ajouter le carton
|
||||
configuration.non.supportee=Configuration non supportée
|
||||
err.match.termine=Erreur, un match de classement a déjà été joué
|
||||
podium.source.classement=Classement
|
||||
podium.source.tree=Tournois
|
||||
podium.source.poule=Poule
|
||||
@ -97,6 +97,7 @@
|
||||
"individuelle": "Individual",
|
||||
"informationCatégorie": "Category information",
|
||||
"inscrit": "Registered",
|
||||
"jusquauRang": "Up to the rank",
|
||||
"leTournoiServiraDePhaseFinaleAuxPoules": "The tournament will serve as the final phase for the group stage.",
|
||||
"lesCombattantsEnDehors": "Fighters not participating in the tournament will have a ranking match.",
|
||||
"lesCombattantsEnDehors2": "Fighters outside the ranking tournament will have a ranking match",
|
||||
@ -143,6 +144,7 @@
|
||||
"select.sélectionnerDesCombatants": "Select fighters",
|
||||
"select.à": "to",
|
||||
"serveur": "Server",
|
||||
"source": "Source",
|
||||
"suivant": "Next",
|
||||
"supprimer": "Delete",
|
||||
"supprimerUn": "Delete one",
|
||||
|
||||
@ -97,6 +97,7 @@
|
||||
"individuelle": "Individuelle",
|
||||
"informationCatégorie": "Information catégorie",
|
||||
"inscrit": "Inscrit",
|
||||
"jusquauRang": "Jusqu'au rang",
|
||||
"leTournoiServiraDePhaseFinaleAuxPoules": "Le tournoi servira de phase finale aux poules",
|
||||
"lesCombattantsEnDehors": "Les combattants en dehors du tournoi auront un match de classement",
|
||||
"lesCombattantsEnDehors2": "Les combattants en dehors du tournoi de classement auront un match de classement",
|
||||
@ -143,6 +144,7 @@
|
||||
"select.sélectionnerDesCombatants": "Sélectionner des combatants",
|
||||
"select.à": "à",
|
||||
"serveur": "Serveur",
|
||||
"source": "Source",
|
||||
"suivant": "Suivant",
|
||||
"supprimer": "Supprimer",
|
||||
"supprimerUn": "Supprimer un",
|
||||
|
||||
@ -405,13 +405,23 @@ function PrintModal({menuActions}) {
|
||||
const [presetEmpty, setPresetEmpty] = useState(false);
|
||||
const [allCat, setAllCat] = useState(false);
|
||||
const [allCatEmpty, setAllCatEmpty] = useState(false);
|
||||
const [podium, setPodium] = useState(false);
|
||||
const [podiumRank, setPodiumRank] = useState(4);
|
||||
|
||||
const [presetSelect, setPresetSelect] = useState(-1)
|
||||
|
||||
const {welcomeData} = useWS();
|
||||
const {sendRequest, welcomeData} = useWS();
|
||||
const {getComb} = useCombs();
|
||||
const {t} = useTranslation("cm");
|
||||
|
||||
const podiumPromise = (podiumRank_) => {
|
||||
return sendRequest("getPodium", {}).then(data => {
|
||||
return [welcomeData?.name + " - " + "Podium", [
|
||||
{type: "podium", params: ({data, maxRank: podiumRank_, minRank: Math.min(4, podiumRank_)})},
|
||||
]];
|
||||
});
|
||||
}
|
||||
|
||||
const print = (action) => {
|
||||
const pagesPromise = [];
|
||||
|
||||
@ -424,6 +434,9 @@ function PrintModal({menuActions}) {
|
||||
if (allCat && menuActions.printAllCategorie)
|
||||
pagesPromise.push(menuActions.printAllCategorie(categorieEmpty, welcomeData?.name + " - " + t('toutesLesCatégories')))
|
||||
|
||||
if (podium)
|
||||
pagesPromise.push(podiumPromise(podiumRank));
|
||||
|
||||
toast.promise(
|
||||
toDataURL("/Logo-FFSAF-2023.png").then(logo => {
|
||||
return Promise.allSettled(pagesPromise).then(results => {
|
||||
@ -497,6 +510,17 @@ function PrintModal({menuActions}) {
|
||||
<label className="form-check-label" htmlFor="checkPrint6">{t('feuilleVierge')}</label>
|
||||
</div>}
|
||||
|
||||
<div className="form-check">
|
||||
<input className="form-check-input" type="checkbox" checked={podium} id="checkPrint7"
|
||||
onChange={e => setPodium(e.target.checked)}/>
|
||||
<label className="form-check-label" htmlFor="checkPrint7">Podium</label>
|
||||
</div>
|
||||
{podium &&
|
||||
<div style={{marginLeft: "1em"}}>
|
||||
<label htmlFor="range3" className="form-label">{t('jusquauRang')} {podiumRank} </label>
|
||||
<input type="range" className="form-range" min="1" max="20" step="1" id="range3" value={podiumRank}
|
||||
onChange={e => setPodiumRank(Number(e.target.value))}/>
|
||||
</div>}
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="submit" className="btn btn-primary" data-bs-dismiss="modal" onClick={() => print("show")}>{t('afficher')}</button>
|
||||
|
||||
@ -1,6 +1,17 @@
|
||||
import {jsPDF} from 'jspdf'
|
||||
import {useCardsStatic} from "../hooks/useCard.jsx";
|
||||
import {CatList, getCatName, getShieldSize, getShieldTypeName, getSwordSize, getSwordTypeName, timePrint, virtualScore, win_end} from "./Tools.js";
|
||||
import {
|
||||
CatList,
|
||||
getCatName,
|
||||
getShieldSize,
|
||||
getShieldTypeName,
|
||||
getSwordSize,
|
||||
getSwordTypeName,
|
||||
sortCategories,
|
||||
timePrint,
|
||||
virtualScore,
|
||||
win_end
|
||||
} from "./Tools.js";
|
||||
import {getMandatoryProtectionsList} from "../components/ProtectionSelector.jsx";
|
||||
import {scoreToString2} from "./CompetitionTools.js";
|
||||
import {TreeNode} from "./TreeUtils.js";
|
||||
@ -31,6 +42,9 @@ export function makePDF(action, pagesList, name, c_name, getComb, t, logo) {
|
||||
case "categorie":
|
||||
generateCategoriePDF(context);
|
||||
break;
|
||||
case "podium":
|
||||
generatePodium(context);
|
||||
break;
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -356,3 +370,74 @@ function generateCategoriePDF({pdf_doc, cat, matches, groups, getComb, cards_v,
|
||||
buildTree(pdf_doc, cat.trees, cat.raw_trees, marches2, cat, categorieEmpty ? [] : cards_v, getComb, t, categorieEmpty, nbComb)
|
||||
}
|
||||
}
|
||||
|
||||
function generatePodium({pdf_doc, data, t, logo, c_name, minRank = 4, maxRank = 4}) {
|
||||
makeHeader(pdf_doc, c_name, "Podium", logo)
|
||||
|
||||
const data2 = data.sort((a, b) => {
|
||||
let tmp = sortCategories(a.categorie, b.categorie);
|
||||
if (tmp !== 0)
|
||||
return tmp;
|
||||
return a.poule_name.localeCompare(b.poule_name)
|
||||
})
|
||||
|
||||
let finalY2 = pdf_doc.lastAutoTable.finalY;
|
||||
let finalY3 = pdf_doc.lastAutoTable.finalY;
|
||||
for (let i = 0; i < data2.length; i++) {
|
||||
const p = data2[i];
|
||||
let pageNumber = pdf_doc.internal.getNumberOfPages()
|
||||
|
||||
const body = Array.from({length: minRank}, () => []);
|
||||
for (const c of p.podium) {
|
||||
if (c.rank > maxRank)
|
||||
continue;
|
||||
|
||||
if (body[c.rank - 1])
|
||||
body[c.rank - 1].push(c.name)
|
||||
else
|
||||
body[c.rank - 1] = [c.name]
|
||||
}
|
||||
for (let j = 0; j < body.length; j++) {
|
||||
if (body[j].length === 0)
|
||||
body[j].push(" ")
|
||||
body[j] = [
|
||||
{content: j + 1, styles: {halign: "center"}},
|
||||
{content: body[j].join(", "), styles: {halign: "center"}}
|
||||
]
|
||||
}
|
||||
|
||||
autoTable(pdf_doc, {
|
||||
startY: finalY3 + 7,
|
||||
margin: i % 2 ? {left: pdf_doc.internal.pageSize.getWidth() / 2 + 7} : {right: pdf_doc.internal.pageSize.getWidth() / 2 + 7},
|
||||
styles: {fontSize: 10, cellPadding: 3},
|
||||
columnStyles: {
|
||||
0: {cellWidth: 35},
|
||||
1: {cellWidth: "auto"},
|
||||
},
|
||||
pageBreak: "avoid",
|
||||
showHead: 'firstPage',
|
||||
head: [[
|
||||
{content: p.poule_name, colSpan: 2, styles: {halign: "center"}},
|
||||
], [
|
||||
{content: t('place', {ns: "result"}), styles: {halign: "center"}},
|
||||
{content: t('combattants', {ns: 'result'}), styles: {halign: "center"}},
|
||||
]],
|
||||
body: body,
|
||||
foot: [[
|
||||
{content: t('source') + " : " + p.source, colSpan: 2, styles: {halign: "left", fontSize: 8}},
|
||||
]],
|
||||
rowPageBreak: 'auto',
|
||||
theme: 'grid',
|
||||
})
|
||||
|
||||
if (i % 2 === 0) {
|
||||
finalY2 = pdf_doc.lastAutoTable.finalY;
|
||||
if (pageNumber !== pdf_doc.internal.getNumberOfPages())
|
||||
finalY3 = 33
|
||||
} else {
|
||||
pdf_doc.lastAutoTable.finalY = Math.max(finalY2, pdf_doc.lastAutoTable.finalY);
|
||||
finalY3 = pdf_doc.lastAutoTable.finalY;
|
||||
}
|
||||
}
|
||||
pdf_doc.lastAutoTable.finalY = Math.max(finalY2, pdf_doc.lastAutoTable.finalY);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user