feat: add Result Privacy membre setting

This commit is contained in:
Thibaut Valentin 2026-01-01 19:43:25 +01:00
parent a730384d37
commit 29a1184994
11 changed files with 380 additions and 289 deletions

View File

@ -0,0 +1,9 @@
package fr.titionfire.ffsaf.data.model;
import fr.titionfire.ffsaf.utils.ResultPrivacy;
public interface CombModel {
Long getCombId();
String getName();
String getName(MembreModel model, ResultPrivacy privacy);
}

View File

@ -2,6 +2,7 @@ package fr.titionfire.ffsaf.data.model;
import fr.titionfire.ffsaf.utils.Categorie;
import fr.titionfire.ffsaf.utils.Genre;
import fr.titionfire.ffsaf.utils.ResultPrivacy;
import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
@ -17,7 +18,7 @@ import lombok.Setter;
@Entity
@Table(name = "competition_guest")
public class CompetitionGuestModel {
public class CompetitionGuestModel implements CombModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
@ -44,7 +45,18 @@ public class CompetitionGuestModel {
this.lname = s.substring(s.indexOf(" ") + 1);
}
@Override
public Long getCombId() {
return this.id * -1;
}
@Override
public String getName() {
return fname + " " + lname;
return this.fname + " " + this.lname;
}
@Override
public String getName(MembreModel model, ResultPrivacy privacy) {
return getName();
}
}

View File

@ -1,6 +1,7 @@
package fr.titionfire.ffsaf.data.model;
import fr.titionfire.ffsaf.utils.CompetitionSystem;
import fr.titionfire.ffsaf.utils.ResultPrivacy;
import fr.titionfire.ffsaf.utils.ScoreEmbeddable;
import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.persistence.*;
@ -9,6 +10,7 @@ import lombok.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
@Getter
@Setter
@ -65,19 +67,35 @@ public class MatchModel {
@JoinColumn(name = "match", referencedColumnName = "id")
List<CardboardModel> cardboard = new ArrayList<>();
public String getC1Name(MembreModel model, ResultPrivacy privacy) {
if (c1_id != null)
return c1_id.getName(model, privacy);
if (c1_guest != null)
return c1_guest.getName(model, privacy);
return "";
}
public String getC2Name(MembreModel model, ResultPrivacy privacy) {
if (c2_id != null)
return c2_id.getName(model, privacy);
if (c2_guest != null)
return c2_guest.getName(model, privacy);
return "";
}
public String getC1Name() {
if (c1_id != null)
return c1_id.fname + " " + c1_id.lname;
return c1_id.getName();
if (c1_guest != null)
return c1_guest.fname + " " + c1_guest.lname;
return c1_guest.getName();
return "";
}
public String getC2Name() {
if (c2_id != null)
return c2_id.fname + " " + c2_id.lname;
return c2_id.getName();
if (c2_guest != null)
return c2_guest.fname + " " + c2_guest.lname;
return c2_guest.getName();
return "";
}
@ -94,4 +112,24 @@ public class MatchModel {
}
return sum;
}
public boolean isC1(Object comb) {
if (comb instanceof Long id_) {
if (id_ >= 0)
return Objects.equals(this.c1_id != null ? this.c1_id.getId() : null, id_);
else
return Objects.equals(this.c1_guest != null ? this.c1_guest.getId() : null, -id_);
}
return Objects.equals(this.c1_id, comb) || Objects.equals(this.c1_guest, comb);
}
public boolean isC2(Object comb) {
if (comb instanceof Long id_) {
if (id_ >= 0)
return Objects.equals(this.c2_id != null ? this.c2_id.getId() : null, id_);
else
return Objects.equals(this.c2_guest != null ? this.c2_guest.getId() : null, -id_);
}
return Objects.equals(this.c2_id, comb) || Objects.equals(this.c2_guest, comb);
}
}

View File

@ -1,9 +1,6 @@
package fr.titionfire.ffsaf.data.model;
import fr.titionfire.ffsaf.utils.Categorie;
import fr.titionfire.ffsaf.utils.Genre;
import fr.titionfire.ffsaf.utils.GradeArbitrage;
import fr.titionfire.ffsaf.utils.RoleAsso;
import fr.titionfire.ffsaf.utils.*;
import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.persistence.*;
import lombok.*;
@ -11,6 +8,7 @@ import org.eclipse.microprofile.openapi.annotations.media.Schema;
import java.util.Date;
import java.util.List;
import java.util.Objects;
@Getter
@Setter
@ -21,23 +19,23 @@ import java.util.List;
@Entity
@Table(name = "membre")
public class MembreModel implements LoggableModel {
public class MembreModel implements LoggableModel, CombModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Access(AccessType.PROPERTY)
@Schema(description = "L'identifiant du membre.", example = "1")
@Schema(description = "L'identifiant du membre.", examples = "1")
Long id;
@Schema(description = "L'identifiant long du membre (userID).", example = "e81d1d35-d897-421e-8086-6c5e74d13c6e")
@Schema(description = "L'identifiant long du membre (userID).", examples = "e81d1d35-d897-421e-8086-6c5e74d13c6e")
String userId;
@Schema(description = "Le nom du membre.", example = "Dupont")
@Schema(description = "Le nom du membre.", examples = "Dupont")
String lname;
@Schema(description = "Le prénom du membre.", example = "Jean")
@Schema(description = "Le prénom du membre.", examples = "Jean")
String fname;
@Schema(description = "La catégorie du membre.", example = "SENIOR")
@Schema(description = "La catégorie du membre.", examples = "SENIOR")
Categorie categorie;
@ManyToOne(fetch = FetchType.EAGER)
@ -45,29 +43,30 @@ public class MembreModel implements LoggableModel {
@Schema(description = "Le club du membre.")
ClubModel club;
@Schema(description = "Le genre du membre.", example = "H")
@Schema(description = "Le genre du membre.", examples = "H")
Genre genre;
@Schema(description = "Le numéro de licence du membre.", example = "12345")
@Schema(description = "Le numéro de licence du membre.", examples = "12345")
Integer licence;
@Schema(description = "Le pays du membre.", example = "FR")
@Schema(description = "Le pays du membre.", examples = "FR")
String country;
@Schema(description = "La date de naissance du membre.")
Date birth_date;
@Schema(description = "L'adresse e-mail du membre.", example = "jean.dupont@example.com")
@Schema(description = "L'adresse e-mail du membre.", examples = "jean.dupont@examples.com")
String email;
@Schema(description = "Le rôle du membre dans l'association.", example = "MEMBRE")
@Schema(description = "Le rôle du membre dans l'association.", examples = "MEMBRE")
RoleAsso role;
@Schema(description = "Le grade d'arbitrage du membre.", example = "NA")
@Schema(description = "Le grade d'arbitrage du membre.", examples = "NA")
GradeArbitrage grade_arbitrage;
@Schema(hidden = true)
String url_photo;
@Schema(description = "La confidentialité de ces résultats", examples = "PUBLIC")
@Column(nullable = false, columnDefinition = "smallint default 0")
ResultPrivacy resultPrivacy = ResultPrivacy.PUBLIC;
@OneToMany(mappedBy = "membre", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@Schema(description = "Les licences du membre. (optionnel)")
@ -100,4 +99,25 @@ public class MembreModel implements LoggableModel {
", grade_arbitrage=" + grade_arbitrage +
'}';
}
@Override
public Long getCombId() {
return this.id;
}
@Override
public String getName() {
return this.fname + " " + this.lname;
}
@Override
public String getName(MembreModel model, ResultPrivacy privacy) {
if (model == null || !Objects.equals(this.getId(), model.getId())) {
if (model == null && this.getResultPrivacy() != ResultPrivacy.PUBLIC)
return "Anonyme";
if (this.getResultPrivacy().ordinal() > privacy.ordinal())
return "Anonyme";
}
return getName();
}
}

View File

@ -1,64 +0,0 @@
package fr.titionfire.ffsaf.domain.entity;
import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.utils.Categorie;
import fr.titionfire.ffsaf.utils.Genre;
import fr.titionfire.ffsaf.utils.GradeArbitrage;
import fr.titionfire.ffsaf.utils.RoleAsso;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import java.util.Date;
@Data
@Builder
@AllArgsConstructor
@RegisterForReflection
public class MembreEntity {
private long id;
private String lname = "";
private String fname = "";
private Categorie categorie;
private ClubEntity club;
private Genre genre;
private Integer licence;
private String country;
private Date birth_date;
private String email;
private RoleAsso role;
private GradeArbitrage grade_arbitrage;
private String url_photo;
public static MembreEntity fromModel(MembreModel model) {
if (model == null)
return null;
return new MembreEntityBuilder()
.id(model.getId())
.lname(model.getLname())
.fname(model.getFname())
.categorie(model.getCategorie())
.club(ClubEntity.fromModel(model.getClub()))
.genre(model.getGenre())
.licence(model.getLicence())
.country(model.getCountry())
.birth_date(model.getBirth_date())
.email(model.getEmail())
.role(model.getRole())
.grade_arbitrage(model.getGrade_arbitrage())
.url_photo(model.getUrl_photo())
.build();
}
public static String getFullName(MembreModel model) {
return model.getFname() + " " + model.getLname();
}
public String getFullName() {
return this.fname + " " + this.lname;
}
}

View File

@ -3,6 +3,7 @@ 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;
@ -16,9 +17,8 @@ import lombok.Builder;
import org.hibernate.reactive.mutiny.Mutiny;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream;
@WithSession
@ -48,6 +48,50 @@ public class ResultService {
private static final ResourceBundle BUNDLE = ResourceBundle.getBundle("lang.String");
private static final HashMap<Long, String> 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<Long, String> entry : combTempIds.entrySet()) {
if (entry.getValue().equals(value)) {
return entry.getKey();
}
}
return null;
}
}
private static final HashMap<String, Long> 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<String, Long> entry : clubTempIds.entrySet()) {
if (entry.getValue().equals(value)) {
return entry.getKey();
}
}
return null;
}
}
public Uni<List<Object[]>> getList(SecurityCtx securityCtx) {
return membreService.getByAccountId(securityCtx.getSubject())
.chain(m -> registerRepository.list("membre = ?1", m))
@ -70,11 +114,10 @@ public class ResultService {
}
public Uni<List<ResultCategoryData>> getCategory(String uuid, SecurityCtx securityCtx) {
AtomicReference<MembreModel> membreModel = new AtomicReference<>();
return hasAccess(uuid, securityCtx)
.chain(m -> categoryRepository.list("compet.uuid = ?1", uuid)
.chain(cats -> matchRepository.list(
"(c1_id = ?1 OR c2_id = ?1 OR True) AND category IN ?2", //TODO rm OR True
m.getMembre(), cats)))
.invoke(m -> membreModel.set(m.getMembre()))
.chain(m -> matchRepository.list("category.compet.uuid = ?1", uuid))
.map(matchModels -> {
HashMap<Long, List<MatchModel>> map = new HashMap<>();
for (MatchModel matchModel : matchModels) {
@ -88,12 +131,11 @@ public class ResultService {
.onItem()
.transformToMulti(Multi.createFrom()::iterable)
.onItem().call(list -> Mutiny.fetch(list.get(0).getCategory().getTree()))
.onItem().transform(this::getData)
.onItem().transform(list -> getData(list, membreModel.get()))
.collect().asList();
}
private ResultCategoryData getData(List<MatchModel> matchModels) {
private ResultCategoryData getData(List<MatchModel> matchModels, MembreModel membreModel) {
ResultCategoryData out = new ResultCategoryData();
CategoryModel categoryModel = matchModels.get(0).getCategory();
@ -102,12 +144,12 @@ public class ResultService {
out.setLiceName(categoryModel.getLiceName() == null ? new String[]{} : categoryModel.getLiceName().split(";"));
out.setGenTime(System.currentTimeMillis());
getArray2(matchModels, out);
getTree(categoryModel.getTree(), out);
getArray2(matchModels, membreModel, out);
getTree(categoryModel.getTree(), membreModel, out);
return out;
}
private void getArray2(List<MatchModel> matchModels_, ResultCategoryData out) {
private void getArray2(List<MatchModel> matchModels_, MembreModel membreModel, ResultCategoryData out) {
List<MatchModel> matchModels = matchModels_.stream().filter(o -> o.getCategory_ord() >= 0).toList();
HashMap<Character, List<MatchModel>> matchMap = new HashMap<>();
@ -121,31 +163,30 @@ public class ResultService {
matchMap.forEach((c, matchEntities) -> {
List<ResultCategoryData.PouleArrayData> matchs = matchEntities.stream()
.sorted(Comparator.comparing(MatchModel::getCategory_ord))
.map(ResultCategoryData.PouleArrayData::fromModel)
.map(o -> ResultCategoryData.PouleArrayData.fromModel(o, membreModel,
ResultPrivacy.REGISTERED_ONLY_NO_DETAILS))
.toList();
List<ResultCategoryData.RankArray> rankArray = matchEntities.stream()
.flatMap(m -> Stream.of(m.getC1Name(), m.getC2Name()))
.flatMap(m -> Stream.of(m.getC1_id(), m.getC2_id(), m.getC1_guest(), m.getC2_guest()))
.distinct()
.map(combName -> {
.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.getC1Name().equals(combName) || m.getC2Name()
.equals(combName)))
.filter(m -> m.isEnd() && (m.isC1(comb) || m.isC2(comb)))
.forEach(matchModel -> {
int win = matchModel.win();
if ((matchModel.getC1Name()
.equals(combName) && win > 0) || matchModel.getC2Name()
.equals(combName) && win < 0)
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.getC1Name().equals(combName)) {
if (matchModel.isC1(comb)) {
pointMake.addAndGet(score.getS1());
pointTake.addAndGet(score.getS2());
} else {
@ -156,7 +197,8 @@ public class ResultService {
});
float pointRate = (pointTake.get() == 0) ? pointMake.get() : (float) pointMake.get() / pointTake.get();
return new ResultCategoryData.RankArray(0, combName, w.get(),
return new ResultCategoryData.RankArray(0,
comb.getName(membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS), w.get(),
pointMake.get(), pointTake.get(), pointRate);
})
.sorted(Comparator
@ -180,29 +222,30 @@ public class ResultService {
});
}
private static void convertTree(TreeModel src, TreeNode<ResultCategoryData.TreeData> dst) {
dst.setData(ResultCategoryData.TreeData.from(src.getMatch()));
private static void convertTree(TreeModel src, TreeNode<ResultCategoryData.TreeData> 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());
convertTree(src.getLeft(), dst.getLeft(), membreModel, privacy);
}
if (src.getRight() != null) {
dst.setRight(new TreeNode<>());
convertTree(src.getRight(), dst.getRight());
convertTree(src.getRight(), dst.getRight(), membreModel, privacy);
}
}
public Uni<ResultCategoryData> getCategoryPublic(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(this::getData);
.map(list -> getData(list, null));
}
private void getTree(List<TreeModel> treeModels, ResultCategoryData out) {
private void getTree(List<TreeModel> treeModels, MembreModel membreModel, ResultCategoryData out) {
ArrayList<TreeNode<ResultCategoryData.TreeData>> trees = new ArrayList<>();
treeModels.stream().filter(t -> t.getLevel() != 0).forEach(treeModel -> {
TreeNode<ResultCategoryData.TreeData> root = new TreeNode<>();
convertTree(treeModel, root);
convertTree(treeModel, root, membreModel, ResultPrivacy.REGISTERED_ONLY_NO_DETAILS);
trees.add(root);
});
out.setTrees(trees);
@ -210,14 +253,14 @@ public class ResultService {
public Uni<CombsArrayData> getAllCombArray(String uuid, SecurityCtx securityCtx) {
return hasAccess(uuid, securityCtx)
.chain(__ -> getAllCombArray(uuid));
.chain(r -> getAllCombArray_(uuid, r.getMembre()));
}
public Uni<CombsArrayData> getAllCombArrayPublic(String uuid) {
return getAllCombArray(uuid);
return getAllCombArray_(uuid, null);
}
public Uni<CombsArrayData> getAllCombArray(String uuid) {
private Uni<CombsArrayData> 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)))
@ -228,57 +271,42 @@ public class ResultService {
CombsArrayData.CombsArrayDataBuilder builder = CombsArrayData.builder();
List<CombsArrayData.CombsData> combs = matchModels.stream()
.flatMap(m -> Stream.of(m.getC1Name(), m.getC2Name()))
.flatMap(m -> Stream.of(m.getC1_id(), m.getC2_id(), m.getC1_guest(), m.getC2_guest()))
.filter(Objects::nonNull)
.distinct()
.map(combName -> {
.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();
matchModels.stream()
.filter(m -> m.isEnd() && (m.getC1Name().equals(combName)
|| m.getC2Name().equals(combName)))
.forEach(matchModel -> {
int win = matchModel.win();
if ((combName.equals(matchModel.getC1Name()) && win > 0) ||
combName.equals(matchModel.getC2Name()) && win < 0) {
w.getAndIncrement();
} else {
l.getAndIncrement();
}
matchModel.getScores().stream()
.filter(s -> s.getS1() > -900 && s.getS2() > -900)
.forEach(score -> {
if (combName.equals(matchModel.getC1Name())) {
pointMake.addAndGet(score.getS1());
pointTake.addAndGet(score.getS2());
} else {
pointMake.addAndGet(score.getS2());
pointTake.addAndGet(score.getS1());
}
});
});
makeStat(matchModels, comb, w, l, pointMake, pointTake);
Categorie categorie = null;
ClubModel club = null;
String clubName = null;
Optional<RegisterModel> register = registers.stream()
.filter(r -> r.getName().equals(combName)).findFirst();
.filter(r -> Objects.equals(r.getMembre(), comb)).findFirst();
if (register.isPresent()) {
categorie = register.get().getCategorie();
club = register.get().getClub2();
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(combName);
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((club == null) ? BUNDLE.getString("no.licence") : club.getName());
builder2.club(clubName);
builder2.pointMake(pointMake.get());
builder2.pointTake(pointTake.get());
builder2.ratioPoint(
@ -298,62 +326,62 @@ public class ResultService {
});
}
public Uni<HashMap<String, String>> getCombList(String uuid) {
return registerRepository.list("competition.uuid = ?1", uuid)
public Uni<HashMap<String, String>> getCombList(String uuid, ResultPrivacy privacy) {
return registerRepository.list("competition.uuid = ?1 AND membre.resultPrivacy <= ?2", uuid, privacy)
.map(models -> {
HashMap<String, String> map = new HashMap<>();
models.forEach(registerEmbeddable -> {
map.put(Utils.getFullName(registerEmbeddable.getMembre()),
registerEmbeddable.getMembre().getFname() + "¤" + registerEmbeddable.getMembre()
.getLname());
});
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),
guestModel.getFname() + "¤" + guestModel.getLname()));
getCombTempId(guestModel.getId() * -1)));
return map;
})
);
}
public Uni<?> getCombArrayPublic(String uuid, String fname, String lname) {
public Uni<?> getCombArrayPublic(String uuid, String combTempId, ResultPrivacy privacy) {
CombArrayData.CombArrayDataBuilder builder = CombArrayData.builder();
AtomicBoolean guest = new AtomicBoolean(false);
AtomicLong id = new AtomicLong(0);
Long id = getCombTempId(combTempId);
if (id == null) {
return Uni.createFrom().failure(new DForbiddenException("Comb not found"));
}
return registerRepository.find("membre.fname = ?1 AND membre.lname = ?2 AND competition.uuid = ?3", fname,
lname, uuid).firstResult()
.chain(registerModel -> {
if (registerModel == null) {
return competitionGuestRepository.find("fname = ?1 AND lname = ?2 AND competition.uuid = ?3",
fname, lname, uuid).firstResult()
.chain(guestModel -> {
builder.name(Utils.getFullName(guestModel));
builder.club(guestModel.getClub());
guest.set(true);
id.set(guestModel.getId());
builder.cat((guestModel.getCategorie() == null) ? "---" : guestModel.getCategorie()
.getName(BUNDLE));
Uni<List<MatchModel>> 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");
return matchRepository.list(
"category.compet.uuid = ?1 AND (c1_guest = ?2 OR c2_guest = ?2)", uuid,
guestModel);
});
}
builder.name(Utils.getFullName(registerModel.getMembre()));
builder.club((registerModel.getClub2() == null) ? BUNDLE.getString(
"no.licence") : registerModel.getClub2().getName());
id.set(registerModel.getMembre().getId());
builder.cat((registerModel.getCategorie2() == null) ? "---" : registerModel.getCategorie2()
.getName(BUNDLE));
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());
})
.invoke(matchModels -> {
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<CategoryModel> pouleModels = matchModels.stream().map(MatchModel::getCategory).distinct()
.toList();
List<CombArrayData.MatchsData> matchs = new ArrayList<>();
@ -375,9 +403,7 @@ public class ResultService {
AtomicInteger pointMake = new AtomicInteger();
AtomicInteger pointTake = new AtomicInteger();
if ((!guest.get() && matchModel.getC1_id() != null && matchModel.getC1_id().getId() == id.get())
|| (guest.get() && matchModel.getC1_guest() != null && matchModel.getC1_guest()
.getId() == id.get())) {
if (matchModel.isC1(id)) {
builder2.adv(Utils.getFullName(matchModel.getC2_id(), matchModel.getC2_guest()));
if (matchModel.isEnd()) {
matchModel.getScores().stream()
@ -456,105 +482,147 @@ public class ResultService {
}
}
public Uni<HashMap<String, Long>> getClubList(String uuid) { // TODO add guest club
public Uni<HashMap<String, Long>> getClubList(String uuid) {
return registerRepository.list("competition.uuid = ?1", uuid)
.map(registers -> {
HashMap<String, Long> 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<ClubArrayData> getClubArray(String uuid, SecurityCtx securityCtx) {
return hasAccess(uuid, securityCtx).chain(cm_register -> getClubArray(uuid, cm_register.getClub2()));
return hasAccess(uuid, securityCtx).chain(cm_register ->
registerRepository.list("competition.uuid = ?1 AND membre.club = ?2", uuid, cm_register.getClub2())
.chain(registers -> matchRepository.list("category.compet.uuid = ?1", uuid)
.map(matchModels ->
getClubArray2(cm_register.getClub2().getName(),
registers.stream().map(o -> (CombModel) o.getMembre()).toList(),
matchModels, registers, cm_register.getMembre()))));
}
public Uni<ClubArrayData> getClubArrayPublic(String uuid, Long id) {
return clubRepository.findById(id).chain(clubModel -> getClubArray(uuid, clubModel));
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<>(), null)));
} 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, null))));
}
}
public Uni<ClubArrayData> getClubArray(String uuid, ClubModel clubModel) {
private ClubArrayData getClubArray2(String name, List<CombModel> combs, List<MatchModel> matchModels,
List<RegisterModel> registers, MembreModel membreModel) {
ClubArrayData.ClubArrayDataBuilder builder = ClubArrayData.builder();
builder.name(clubModel.getName());
builder.name(name);
builder.nb_insc(combs.size());
return registerRepository.list("competition.uuid = ?1 AND membre.club = ?2", uuid, clubModel)
.chain(registers -> matchRepository.list("category.compet.uuid = ?1", uuid)
.map(matchModels -> new Pair<>(registers, matchModels)))
.map(pair -> {
List<RegisterModel> registers = pair.getKey();
List<MatchModel> matchModels = pair.getValue();
AtomicInteger tt_win = new AtomicInteger(0);
AtomicInteger tt_match = new AtomicInteger(0);
builder.nb_insc(registers.size());
List<ClubArrayData.CombData> 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();
AtomicInteger tt_win = new AtomicInteger(0);
AtomicInteger tt_match = new AtomicInteger(0);
makeStat(matchModels, comb, w, l, pointMake, pointTake);
List<ClubArrayData.CombData> combData = registers.stream().map(register -> {
var builder2 = ClubArrayData.CombData.builder();
AtomicInteger w = new AtomicInteger(0);
AtomicInteger l = new AtomicInteger(0);
AtomicInteger pointMake = new AtomicInteger();
AtomicInteger pointTake = new AtomicInteger();
Categorie categorie = null;
matchModels.stream()
.filter(m -> m.isEnd() && (register.getMembre().equals(m.getC1_id())
|| register.getMembre().equals(m.getC2_id())))
.forEach(matchModel -> {
int win = matchModel.win();
if ((register.getMembre().equals(matchModel.getC1_id()) && win > 0) ||
register.getMembre().equals(matchModel.getC2_id()) && win < 0) {
w.getAndIncrement();
} else {
l.getAndIncrement();
}
Optional<RegisterModel> 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();
}
matchModel.getScores().stream()
.filter(s -> s.getS1() > -900 && s.getS2() > -900)
.forEach(score -> {
if (register.getMembre().equals(matchModel.getC1_id())) {
pointMake.addAndGet(score.getS1());
pointTake.addAndGet(score.getS2());
} else {
pointMake.addAndGet(score.getS2());
pointTake.addAndGet(score.getS1());
}
});
});
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());
Categorie categorie = register.getCategorie();
if (categorie == null)
categorie = register.getMembre().getCategorie();
tt_win.addAndGet(w.get());
tt_match.addAndGet(w.get() + l.get());
builder2.cat((categorie == null) ? "---" : categorie.getName(BUNDLE));
builder2.name(register.getName());
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());
return builder2.build();
})
.sorted(Comparator.comparing(ClubArrayData.CombData::name))
.toList();
tt_win.addAndGet(w.get());
tt_match.addAndGet(w.get() + l.get());
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 builder2.build();
})
.sorted(Comparator.comparing(ClubArrayData.CombData::name))
.toList();
return builder.build();
}
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);
private static void makeStat(List<MatchModel> 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();
}
return builder.build();
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());
}
});
});
}

View File

@ -2,6 +2,7 @@ package fr.titionfire.ffsaf.rest;
import fr.titionfire.ffsaf.domain.service.ResultService;
import fr.titionfire.ffsaf.domain.service.UpdateService;
import fr.titionfire.ffsaf.utils.ResultPrivacy;
import io.smallrye.mutiny.Uni;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
@ -46,7 +47,7 @@ public class ExternalResultEndpoints {
@Path("/comb/list")
@Produces(MediaType.APPLICATION_JSON)
public Uni<HashMap<String, String>> combList() {
return resultService.getCombList(id);
return resultService.getCombList(id, ResultPrivacy.PUBLIC);
}
@GET
@ -55,7 +56,7 @@ public class ExternalResultEndpoints {
public Uni<?> getArray(@QueryParam("comb") String comb) {
if (comb.equals("0"))
return Uni.createFrom().item("");
return resultService.getCombArrayPublic(id, comb.substring(0, comb.indexOf('¤')), comb.substring(comb.indexOf('¤') + 1));
return resultService.getCombArrayPublic(id, comb, ResultPrivacy.PUBLIC);
}
@GET

View File

@ -1,6 +1,8 @@
package fr.titionfire.ffsaf.rest.data;
import fr.titionfire.ffsaf.data.model.MatchModel;
import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.utils.ResultPrivacy;
import fr.titionfire.ffsaf.utils.ScoreEmbeddable;
import fr.titionfire.ffsaf.utils.TreeNode;
import io.quarkus.runtime.annotations.RegisterForReflection;
@ -38,15 +40,15 @@ public class ResultCategoryData {
@RegisterForReflection
public record PouleArrayData(String red, boolean red_w, List<Integer[]> score, boolean blue_w, String blue, boolean end) {
public static PouleArrayData fromModel(MatchModel matchModel) {
public static PouleArrayData fromModel(MatchModel matchModel, MembreModel membreModel, ResultPrivacy privacy) {
return new PouleArrayData(
matchModel.getC1Name(),
matchModel.getC1Name(membreModel, privacy),
matchModel.isEnd() && matchModel.win() > 0,
matchModel.isEnd() ?
matchModel.getScores().stream().map(s -> new Integer[]{s.getS1(), s.getS2()}).toList()
: new ArrayList<>(),
matchModel.isEnd() && matchModel.win() < 0,
matchModel.getC2Name(),
matchModel.getC2Name(membreModel, privacy),
matchModel.isEnd());
}
}
@ -54,8 +56,8 @@ public class ResultCategoryData {
@RegisterForReflection
public static record TreeData(long id, String c1FullName, String c2FullName, List<ScoreEmbeddable> scores,
boolean end) {
public static TreeData from(MatchModel match) {
return new TreeData(match.getId(), match.getC1Name(), match.getC2Name(), match.getScores(), match.isEnd());
public static TreeData from(MatchModel match, MembreModel membreModel, ResultPrivacy privacy) {
return new TreeData(match.getId(), match.getC1Name(membreModel, privacy), match.getC2Name(membreModel, privacy), match.getScores(), match.isEnd());
}
}
}

View File

@ -19,34 +19,32 @@ import java.util.Date;
@AllArgsConstructor
@RegisterForReflection
public class SimpleMembre {
@Schema(description = "L'identifiant du membre.", example = "1")
@Schema(description = "L'identifiant du membre.", examples = "1")
private long id;
@Schema(description = "L'identifiant long du membre (userID).", example = "e81d1d35-d897-421e-8086-6c5e74d13c6e")
@Schema(description = "L'identifiant long du membre (userID).", examples = "e81d1d35-d897-421e-8086-6c5e74d13c6e")
private String userId;
@Schema(description = "Le nom du membre.", example = "Dupont")
@Schema(description = "Le nom du membre.", examples = "Dupont")
private String lname = "";
@Schema(description = "Le prénom du membre.", example = "Jean")
@Schema(description = "Le prénom du membre.", examples = "Jean")
private String fname = "";
@Schema(description = "La catégorie du membre.", example = "SENIOR")
@Schema(description = "La catégorie du membre.", examples = "SENIOR")
private Categorie categorie;
@Schema(description = "Le club du membre.")
private SimpleClubModel club;
@Schema(description = "Le genre du membre.", example = "H")
@Schema(description = "Le genre du membre.", examples = "H")
private Genre genre;
@Schema(description = "Le numéro de licence du membre.", example = "12345")
@Schema(description = "Le numéro de licence du membre.", examples = "12345")
private Integer licence;
@Schema(description = "Le pays du membre.", example = "FR")
@Schema(description = "Le pays du membre.", examples = "FR")
private String country;
@Schema(description = "La date de naissance du membre.")
private Date birth_date;
@Schema(description = "L'adresse e-mail du membre.", example = "jean.dupont@example.com")
@Schema(description = "L'adresse e-mail du membre.", examples = "jean.dupont@examples.com")
private String email;
@Schema(description = "Le rôle du membre dans l'association.", example = "MEMBRE")
@Schema(description = "Le rôle du membre dans l'association.", examples = "MEMBRE")
private RoleAsso role;
@Schema(description = "Le grade d'arbitrage du membre.", example = "N/A")
@Schema(description = "Le grade d'arbitrage du membre.", examples = "N/A")
private GradeArbitrage grade_arbitrage;
@Schema(hidden = true)
private String url_photo;
public static SimpleMembre fromModel(MembreModel model) {
if (model == null)
@ -66,7 +64,6 @@ public class SimpleMembre {
.email(model.getEmail())
.role(model.getRole())
.grade_arbitrage(model.getGrade_arbitrage())
.url_photo(model.getUrl_photo())
.build();
}
}

View File

@ -0,0 +1,8 @@
package fr.titionfire.ffsaf.utils;
public enum ResultPrivacy {
PUBLIC,
REGISTERED_ONLY,
REGISTERED_ONLY_NO_DETAILS,
PRIVATE;
}

View File

@ -44,9 +44,9 @@ function AffiliationMenu() {
}
function CompMenu() {
const {is_authenticated, userinfo} = useAuth()
const {is_authenticated} = useAuth()
if (!is_authenticated || !userinfo?.roles?.includes("federation_admin"))
if (!is_authenticated)
return <></>
return <li className="nav-item dropdown">