various fixes; added nines/pairings

This commit is contained in:
2023-02-15 11:19:56 -05:00
parent 1fdb9fac53
commit 987e5c3547
25 changed files with 915 additions and 77 deletions

View File

@@ -2,6 +2,8 @@ package com.poststats.golf.api;
import com.brianlong.util.FlexMap; import com.brianlong.util.FlexMap;
import com.poststats.golf.api.model.Course; import com.poststats.golf.api.model.Course;
import com.poststats.golf.api.model.CourseNine;
import com.poststats.golf.service.CourseNineService;
import com.poststats.golf.service.CourseService; import com.poststats.golf.service.CourseService;
import com.poststats.service.FacilityService; import com.poststats.service.FacilityService;
import com.poststats.transformer.impl.DaoConverter; import com.poststats.transformer.impl.DaoConverter;
@@ -40,6 +42,9 @@ public class CourseApi {
@Inject @Inject
private CourseService courseService; private CourseService courseService;
@Inject
private CourseNineService courseNineService;
@Inject @Inject
private FacilityService facilityService; private FacilityService facilityService;
@@ -54,7 +59,7 @@ public class CourseApi {
@GET @GET
@Produces(Constants.V1_JSON) @Produces(Constants.V1_JSON)
@Operation( @Operation(
summary = "Retrieves limited meta-data about a course.", summary = "Retrieves meta-data about a course.",
description = "Retreives name, location, and other direct meta-data about the specified course." description = "Retreives name, location, and other direct meta-data about the specified course."
) )
public Course get() { public Course get() {
@@ -67,4 +72,39 @@ public class CourseApi {
return this.converter.convertValue(row, Course.class); return this.converter.convertValue(row, Course.class);
} }
@GET
@Path("/nine/byName/{name}")
@Produces(Constants.V1_JSON)
@Operation(
summary = "Retrieves meta-data about a course nine.",
description = "Retreives name, location, and other direct meta-data about the specified course."
)
public CourseNine getNine(@PathParam("name") String name) {
FlexMap row = this.courseNineService.getNine(this.courseId, name);
if (row == null)
throw new WebApplicationException("Course nine not found", Status.NOT_FOUND);
this.logger.trace("found: {}", this.courseId);
this.courseService.injectDeep("courseID", row, "course");
return this.converter.convertValue(row, CourseNine.class);
}
@GET
@Path("/nine/{nineId:[0-9]+}")
@Produces(Constants.V1_JSON)
@Operation(
summary = "Retrieves limited meta-data about a course nine.",
description = "Retreives name, location, and other direct meta-data about the specified course."
)
public CourseNine getNine(@PathParam("nineId") long courseNineId) {
FlexMap row = this.courseNineService.getNine(courseNineId);
if (row == null)
throw new WebApplicationException("Course nine not found", Status.NOT_FOUND);
if (this.courseId != row.getInteger("courseID"))
throw new WebApplicationException("Course nine not found", Status.NOT_FOUND);
this.logger.trace("found: {}", courseNineId);
return this.converter.convertValue(row, CourseNine.class);
}
} }

View File

@@ -2,9 +2,10 @@ package com.poststats.golf.api;
import com.brianlong.util.FlexMap; import com.brianlong.util.FlexMap;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.poststats.api.model.Person; import com.poststats.golf.api.model.EventPerson;
import com.poststats.golf.service.EventPersonService; import com.poststats.golf.service.EventPersonService;
import com.poststats.golf.service.EventService; import com.poststats.golf.service.EventService;
import com.poststats.golf.service.PersonService;
import com.poststats.transformer.impl.DaoConverter; import com.poststats.transformer.impl.DaoConverter;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -22,7 +23,6 @@ import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PrintStream; import java.io.PrintStream;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVFormat;
@@ -42,6 +42,9 @@ public class EventPersonApi {
@Inject @Inject
private EventPersonService eventPersonService; private EventPersonService eventPersonService;
@Inject
private PersonService golferService;
@Inject @Inject
private DaoConverter converter; private DaoConverter converter;
@@ -56,8 +59,8 @@ public class EventPersonApi {
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"), @ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found") @ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
}) })
public List<? extends Person> get() { public List<? extends EventPerson> get() {
return this.converter.convertValue(this.eventPersonService.getPeople(this.eventId, false), Person.class); return this.converter.convertValue(this.eventPersonService.getPeople(this.eventId), EventPerson.class);
} }
@GET @GET
@@ -71,8 +74,10 @@ public class EventPersonApi {
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"), @ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found") @ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
}) })
public List<? extends Person> getDetail() { public List<? extends EventPerson> getDetail() {
return this.converter.convertValue(this.eventPersonService.getPeople(this.eventId, true), Person.class); List<? extends FlexMap> persons = this.eventPersonService.getPeople(this.eventId);
this.golferService.injectDeep("personID", persons, "golfer");
return this.converter.convertValue(persons, EventPerson.class);
} }
@GET @GET
@@ -87,7 +92,7 @@ public class EventPersonApi {
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found") @ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
}) })
public StreamingOutput getAsCsv() { public StreamingOutput getAsCsv() {
List<FlexMap> persons = this.eventPersonService.getPeople(this.eventId, true); List<? extends FlexMap> persons = this.eventPersonService.getPeople(this.eventId);
return this.toCsv(persons); return this.toCsv(persons);
} }
@@ -99,8 +104,10 @@ public class EventPersonApi {
@ApiResponse(responseCode = "200", description = "Success"), @ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found") @ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
}) })
public List<? extends Person> getParticipants() { public List<? extends EventPerson> getParticipants() {
return this.converter.convertValue(this.eventPersonService.getParticipants(this.eventId, false), Person.class); List<? extends FlexMap> participants = this.eventPersonService.getParticipants(this.eventId);
this.golferService.injectDeep("personID", participants, "golfer");
return this.converter.convertValue(participants, EventPerson.class);
} }
@GET @GET
@@ -115,7 +122,7 @@ public class EventPersonApi {
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found") @ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
}) })
public StreamingOutput getParticipantsAsCsv() { public StreamingOutput getParticipantsAsCsv() {
List<FlexMap> persons = this.eventPersonService.getParticipants(this.eventId, true); List<? extends FlexMap> persons = this.eventPersonService.getParticipants(this.eventId);
return this.toCsv(persons); return this.toCsv(persons);
} }
@@ -130,23 +137,23 @@ public class EventPersonApi {
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"), @ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found") @ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
}) })
public List<? extends Person> getSeriesParticipants() throws JsonProcessingException, IOException { public Set<Long> getSeriesParticipants() throws JsonProcessingException, IOException {
int seriesId = this.eventService.getSeriesId(this.eventId); int seriesId = this.eventService.getSeriesId(this.eventId);
Set<Long> eventIds = this.eventService.getIdsBySeriesId(seriesId); Set<Long> eventIds = this.eventService.getIdsBySeriesId(seriesId);
Set<Long> personIds = new HashSet<>(); Set<Long> personIds = new HashSet<>();
List<FlexMap> persons = new LinkedList<>();
for (long eventId : eventIds) { for (long eventId : eventIds) {
List<FlexMap> tmpPersons = this.eventPersonService.getParticipants(eventId, false); List<? extends FlexMap> tmpPersons = this.eventPersonService.getParticipants(eventId);
for (FlexMap person : tmpPersons) for (FlexMap person : tmpPersons)
if (personIds.add(person.getLong(com.poststats.sql.Constants.PERSON_ID))) personIds.add(person.getLong(com.poststats.sql.Constants.PERSON_ID));
persons.add(person);
} }
return this.converter.convertValue(persons, Person.class); return personIds;
} }
private StreamingOutput toCsv(List<FlexMap> persons) { private StreamingOutput toCsv(List<? extends FlexMap> persons) {
this.golferService.injectDeep("personID", persons, "golfer");
return new StreamingOutput() { return new StreamingOutput() {
@Override @Override
public void write(OutputStream output) throws IOException { public void write(OutputStream output) throws IOException {
@@ -156,7 +163,10 @@ public class EventPersonApi {
personsCsvPrinter.printRecord("ID", "Prefix", "Last Name", "First Name", "Suffix", "Email Address", personsCsvPrinter.printRecord("ID", "Prefix", "Last Name", "First Name", "Suffix", "Email Address",
"Mobile Phone", "Address Lines", "City", "State", "Country", "Postal Code"); "Mobile Phone", "Address Lines", "City", "State", "Country", "Postal Code");
for (FlexMap person : persons) { for (FlexMap eperson : persons) {
FlexMap golfer = (FlexMap) eperson.get("golfer");
FlexMap person = (FlexMap) golfer.get("person");
personsCsvPrinter.printRecord(person.getLong("personID"), person.getString("prefix"), personsCsvPrinter.printRecord(person.getLong("personID"), person.getString("prefix"),
person.getString("lname"), person.getString("fname"), person.getString("suffix"), person.getString("lname"), person.getString("fname"), person.getString("suffix"),
person.getString("email"), person.getString("cellphone"), person.getString("addrlines"), person.getString("email"), person.getString("cellphone"), person.getString("addrlines"),

View File

@@ -3,7 +3,9 @@ package com.poststats.golf.api;
import com.brianlong.util.FlexMap; import com.brianlong.util.FlexMap;
import com.poststats.api.Constants; import com.poststats.api.Constants;
import com.poststats.golf.api.model.EventRound; import com.poststats.golf.api.model.EventRound;
import com.poststats.golf.api.model.EventRoundPairing;
import com.poststats.golf.service.CourseService; import com.poststats.golf.service.CourseService;
import com.poststats.golf.service.EventRoundPairingService;
import com.poststats.golf.service.EventRoundService; import com.poststats.golf.service.EventRoundService;
import com.poststats.transformer.impl.DaoConverter; import com.poststats.transformer.impl.DaoConverter;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
@@ -19,6 +21,7 @@ import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces; import jakarta.ws.rs.Produces;
import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.Response.Status;
import java.math.BigInteger;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -40,6 +43,9 @@ public class EventRoundApi {
@Inject @Inject
private EventRoundService roundService; private EventRoundService roundService;
@Inject
private EventRoundPairingService pairingService;
@Inject @Inject
private CourseService courseService; private CourseService courseService;
@@ -124,4 +130,50 @@ public class EventRoundApi {
return this.converter.convertValue(rows.values(), EventRound.class); return this.converter.convertValue(rows.values(), EventRound.class);
} }
@GET
@Path("/round/{eroundId:[0-9]+}/pairings")
@Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves limited meta-data about all pairings for a single event round.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(
responseCode = "404",
description = "An event with the specified ID or any event rounds could not be found"
)
})
public List<EventRoundPairing> getPairings(@PathParam("eroundId") long eroundId) {
List<? extends FlexMap> rows = this.pairingService.getByRoundId(eroundId);
if (rows == null || rows.isEmpty())
throw new WebApplicationException("No pairings found", Status.NOT_FOUND);
this.courseService.inject("courseID", rows, "course");
return this.converter.convertValue(rows, EventRoundPairing.class);
}
@GET
@Path("/round/{eroundId:[0-9]+}/pairing/{pairingID:[0-9]+}")
@Produces(Constants.V1_JSON)
@Operation(summary = "Retrieves limited meta-data about an event round pairing.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(
responseCode = "404",
description = "An event with the specified ID or upcoming event rounds could not be found"
)
})
public EventRoundPairing getPairing(@PathParam("eroundId") long eroundId,
@PathParam("pairingID") BigInteger pairingId) {
FlexMap row = this.pairingService.get(pairingId);
if (row == null)
throw new WebApplicationException("No pairing was found", Status.NOT_FOUND);
if (eroundId != row.getLong("eroundID")) {
this.logger.warn("The event round pairing {} was requested without the appropriate event round ID {}",
pairingId, eroundId);
throw new WebApplicationException("No pairing was found", Status.NOT_FOUND);
}
this.courseService.inject("courseID", row, "course");
return this.converter.convertValue(row, EventRoundPairing.class);
}
} }

View File

@@ -0,0 +1,28 @@
package com.poststats.golf.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.api.model.BaseModel;
import com.poststats.transformer.MapEntry;
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class BaseCourseNine<ConcreteT extends BaseCourseNine<ConcreteT>> extends BaseModel<ConcreteT> {
@JsonProperty
@MapEntry("nine")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ConcreteT withName(String name) {
this.name = name;
return this.withThis();
}
}

View File

@@ -0,0 +1,50 @@
package com.poststats.golf.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.api.model.BaseModel;
import com.poststats.transformer.MapEntry;
import java.time.LocalTime;
/**
* @author brian.long@poststats.com
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class BaseEventRoundPairing<ConcreteT extends BaseEventRoundPairing<ConcreteT>>
extends BaseModel<ConcreteT> {
@JsonProperty
@MapEntry
private LocalTime teetime;
@JsonProperty
@MapEntry("number")
private Short holeNumber;
public LocalTime getTeetime() {
return teetime;
}
public void setTeetime(LocalTime teetime) {
this.teetime = teetime;
}
public Short getHoleNumber() {
return holeNumber;
}
public void setHoleNumber(Short holeNumber) {
this.holeNumber = holeNumber;
}
public ConcreteT withTeetime(LocalTime teetime) {
this.teetime = teetime;
return this.withThis();
}
public ConcreteT withHoleNumber(Short holeNumber) {
this.holeNumber = holeNumber;
return this.withThis();
}
}

View File

@@ -2,16 +2,42 @@ package com.poststats.golf.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.api.Constants;
import com.poststats.api.model.Facility; import com.poststats.api.model.Facility;
import com.poststats.api.model.PeriodConstrainable;
import com.poststats.transformer.MapCondition;
import com.poststats.transformer.MapEntry; import com.poststats.transformer.MapEntry;
import java.time.LocalDate;
import java.time.OffsetDateTime;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class Course extends BaseCourse<Course> implements ReferenceableCourse { public class Course extends BaseCourse<Course> implements ReferenceableCourse, PeriodConstrainable<LocalDate> {
@JsonProperty(required = true, access = com.fasterxml.jackson.annotation.JsonProperty.Access.READ_ONLY) @JsonProperty(required = true, access = JsonProperty.Access.READ_ONLY)
@MapEntry("courseID") @MapEntry("courseID")
private int id; private int id;
@JsonProperty
@MapEntry("liveline")
@MapCondition(rolesAllowed = {
Constants.ADMIN_ROLE, "course"
})
private LocalDate liveline;
@JsonProperty
@MapEntry("deadline")
@MapCondition(rolesAllowed = {
Constants.ADMIN_ROLE, "course"
})
private LocalDate deadline;
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@MapEntry("created")
@MapCondition(rolesAllowed = {
Constants.ADMIN_ROLE, "course"
})
private OffsetDateTime created;
@JsonProperty @JsonProperty
@MapEntry @MapEntry
private Facility facility; private Facility facility;
@@ -29,6 +55,34 @@ public class Course extends BaseCourse<Course> implements ReferenceableCourse {
this.id = id; this.id = id;
} }
@Override
public LocalDate getLiveline() {
return liveline;
}
@Override
public void setLiveline(LocalDate liveline) {
this.liveline = liveline;
}
@Override
public LocalDate getDeadline() {
return deadline;
}
@Override
public void setDeadline(LocalDate deadline) {
this.deadline = deadline;
}
public OffsetDateTime getCreated() {
return created;
}
public void setCreated(OffsetDateTime created) {
this.created = created;
}
public Facility getFacility() { public Facility getFacility() {
return facility; return facility;
} }
@@ -50,6 +104,21 @@ public class Course extends BaseCourse<Course> implements ReferenceableCourse {
return this; return this;
} }
public Course withLiveline(LocalDate liveline) {
this.liveline = liveline;
return this;
}
public Course withDeadline(LocalDate deadline) {
this.deadline = deadline;
return this;
}
public Course withCreated(OffsetDateTime created) {
this.created = created;
return this;
}
public Course withFacility(Facility facility) { public Course withFacility(Facility facility) {
this.facility = facility; this.facility = facility;
return this; return this;

View File

@@ -0,0 +1,111 @@
package com.poststats.golf.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.api.Constants;
import com.poststats.api.model.PeriodConstrainable;
import com.poststats.transformer.MapCondition;
import com.poststats.transformer.MapEntry;
import java.time.LocalDate;
@JsonIgnoreProperties(ignoreUnknown = true)
public class CourseNine extends BaseCourseNine<CourseNine>
implements ReferenceableCourseNine, PeriodConstrainable<LocalDate> {
@JsonProperty(required = true, access = JsonProperty.Access.READ_ONLY)
@MapEntry("nineID")
private long id;
@JsonProperty
@MapEntry("liveline")
@MapCondition(rolesAllowed = {
Constants.ADMIN_ROLE, "course"
})
private LocalDate liveline;
@JsonProperty
@MapEntry("deadline")
@MapCondition(rolesAllowed = {
Constants.ADMIN_ROLE, "course"
})
private LocalDate deadline;
@JsonProperty
@MapEntry
private Course course;
@JsonProperty
@MapEntry("courseID")
private Integer courseId;
@Override
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
@Override
public LocalDate getLiveline() {
return liveline;
}
@Override
public void setLiveline(LocalDate liveline) {
this.liveline = liveline;
}
@Override
public LocalDate getDeadline() {
return deadline;
}
@Override
public void setDeadline(LocalDate deadline) {
this.deadline = deadline;
}
public Course getCourse() {
return course;
}
public void setCourse(Course course) {
this.course = course;
}
public Integer getCourseId() {
return courseId;
}
public void setCourseId(Integer courseId) {
this.courseId = courseId;
}
public CourseNine withId(int id) {
this.id = id;
return this;
}
public CourseNine withLiveline(LocalDate liveline) {
this.liveline = liveline;
return this;
}
public CourseNine withDeadline(LocalDate deadline) {
this.deadline = deadline;
return this;
}
public CourseNine withCourse(Course course) {
this.course = course;
return this;
}
public CourseNine withCourseId(Integer courseId) {
this.courseId = courseId;
return this;
}
}

View File

@@ -0,0 +1,67 @@
package com.poststats.golf.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.poststats.api.model.BaseModel;
import com.poststats.transformer.MapEntry;
import java.math.BigInteger;
/**
* @author brian.long@poststats.com
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class EventPerson extends BaseModel<EventPerson> {
@JsonProperty(required = true, access = Access.READ_ONLY)
@MapEntry("epersonID")
private BigInteger id;
@JsonProperty
@MapEntry
private Golfer golfer;
@JsonProperty(required = true, access = Access.READ_ONLY)
@MapEntry("personID")
private long personId;
public BigInteger getId() {
return id;
}
public void setId(BigInteger id) {
this.id = id;
}
public Golfer getGolfer() {
return golfer;
}
public void setGolfer(Golfer golfer) {
this.golfer = golfer;
}
public long getPersonId() {
return personId;
}
public void setPersonId(long personId) {
this.personId = personId;
}
public EventPerson withId(BigInteger id) {
this.id = id;
return this;
}
public EventPerson withGolfer(Golfer golfer) {
this.golfer = golfer;
return this;
}
public EventPerson withPersonId(long personId) {
this.personId = personId;
return this;
}
}

View File

@@ -0,0 +1,134 @@
package com.poststats.golf.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.poststats.transformer.MapEntry;
/**
* @author brian.long@poststats.com
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class EventRoundPairing extends BaseEventRoundPairing<EventRoundPairing> implements ReferenceableEventRound {
@JsonProperty(required = true, access = Access.READ_ONLY)
@MapEntry("pairingID")
private long id;
@JsonProperty
@MapEntry
private EventRound round;
@JsonProperty
@MapEntry("eroundID")
private Long eroundId;
@JsonProperty
@MapEntry
private Course course;
@JsonProperty
@MapEntry("courseID")
private Integer courseId;
@JsonProperty
@MapEntry("nine")
private CourseNine courseNine;
@JsonProperty
@MapEntry("nineID")
private Long courseNineId;
@Override
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public EventRound getRound() {
return round;
}
public void setRound(EventRound round) {
this.round = round;
}
public Long getEroundId() {
return eroundId;
}
public void setEroundId(Long eroundId) {
this.eroundId = eroundId;
}
public Course getCourse() {
return course;
}
public void setCourse(Course course) {
this.course = course;
}
public Integer getCourseId() {
return courseId;
}
public void setCourseId(Integer courseId) {
this.courseId = courseId;
}
public CourseNine getCourseNine() {
return courseNine;
}
public void setCourseNine(CourseNine courseNine) {
this.courseNine = courseNine;
}
public Long getCourseNineId() {
return courseNineId;
}
public void setCourseNineId(Long courseNineId) {
this.courseNineId = courseNineId;
}
public EventRoundPairing withId(long id) {
this.id = id;
return this;
}
public EventRoundPairing withRound(EventRound round) {
this.round = round;
return this;
}
public EventRoundPairing withRoundId(Long eroundId) {
this.eroundId = eroundId;
return this;
}
public EventRoundPairing withCourse(Course course) {
this.course = course;
return this;
}
public EventRoundPairing withCourseId(Integer courseId) {
this.courseId = courseId;
return this;
}
public EventRoundPairing withCourseNine(CourseNine courseNine) {
this.courseNine = courseNine;
return this;
}
public EventRoundPairing withCourseNineId(Long courseNineId) {
this.courseNineId = courseNineId;
return this.withThis();
}
}

View File

@@ -0,0 +1,7 @@
package com.poststats.golf.api.model;
public interface ReferenceableCourseNine {
long getId();
}

View File

@@ -0,0 +1,9 @@
package com.poststats.golf.api.model;
import java.math.BigInteger;
public interface ReferenceableEventRoundPairing {
BigInteger getId();
}

View File

@@ -0,0 +1,29 @@
package com.poststats.golf.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.poststats.transformer.MapEntry;
@JsonIgnoreProperties(ignoreUnknown = true)
public class ReferencedCourseNine implements ReferenceableCourseNine {
@JsonProperty(required = true, access = Access.READ_ONLY)
@MapEntry("nineID")
private long id;
@Override
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public ReferencedCourseNine withId(long id) {
this.id = id;
return this;
}
}

View File

@@ -9,7 +9,7 @@ import com.poststats.transformer.MapEntry;
* @author brian.long@poststats.com * @author brian.long@poststats.com
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class ReferencedEventRound implements ReferenceableEvent { public class ReferencedEventRound implements ReferenceableEventRound {
@JsonProperty(required = true, access = Access.READ_ONLY) @JsonProperty(required = true, access = Access.READ_ONLY)
@MapEntry("eroundID") @MapEntry("eroundID")

View File

@@ -0,0 +1,33 @@
package com.poststats.golf.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.poststats.transformer.MapEntry;
import java.math.BigInteger;
/**
* @author brian.long@poststats.com
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class ReferencedEventRoundPairing implements ReferenceableEventRoundPairing {
@JsonProperty(required = true, access = Access.READ_ONLY)
@MapEntry("pairingID")
private BigInteger id;
@Override
public BigInteger getId() {
return id;
}
public void setId(BigInteger id) {
this.id = id;
}
public ReferencedEventRoundPairing withId(BigInteger id) {
this.id = id;
return this;
}
}

View File

@@ -0,0 +1,27 @@
package com.poststats.golf.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.transformer.MapEntry;
@JsonIgnoreProperties(ignoreUnknown = true)
public class TransientCourseNine extends BaseCourseNine<TransientCourseNine> {
@JsonProperty
@MapEntry
private ReferenceableCourse course;
public ReferenceableCourse getCourse() {
return course;
}
public void setCourse(ReferenceableCourse course) {
this.course = course;
}
public TransientCourseNine withCourse(ReferenceableCourse course) {
this.course = course;
return this;
}
}

View File

@@ -0,0 +1,47 @@
package com.poststats.golf.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.transformer.MapEntry;
/**
* @author brian.long@poststats.com
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class TransientEventRoundPairing extends BaseEventRoundPairing<TransientEventRoundPairing> {
@JsonProperty
@MapEntry
private ReferenceableCourse course;
@JsonProperty
@MapEntry
private ReferenceableCourseNine courseNine;
public ReferenceableCourse getCourse() {
return course;
}
public void setCourse(ReferenceableCourse course) {
this.course = course;
}
public ReferenceableCourseNine getCourseNine() {
return courseNine;
}
public void setCourseNine(ReferenceableCourseNine courseNine) {
this.courseNine = courseNine;
}
public TransientEventRoundPairing withCourse(ReferenceableCourse course) {
this.course = course;
return this;
}
public TransientEventRoundPairing withCourseNine(ReferenceableCourseNine courseNine) {
this.courseNine = courseNine;
return this;
}
}

View File

@@ -31,10 +31,12 @@ public class EventSecurityContext implements SecurityContext {
if (role.startsWith(Constants.EVENT_ROLE_PREFIX)) { if (role.startsWith(Constants.EVENT_ROLE_PREFIX)) {
if (this.getUserPrincipal() instanceof AuthenticatedPerson) { if (this.getUserPrincipal() instanceof AuthenticatedPerson) {
AuthenticatedPerson authPerson = (AuthenticatedPerson) this.getUserPrincipal(); AuthenticatedPerson authPerson = (AuthenticatedPerson) this.getUserPrincipal();
this.logger.trace("checking if user '{}' has role '{}' in event {}", this.getUserPrincipal(), role, String eventRole = role.substring(Constants.EVENT_ROLE_PREFIX.length());
this.logger.trace("checking if user '{}' has role '{}' in event {}", this.getUserPrincipal(), eventRole,
this.eventId); this.eventId);
if (authPerson.hasAccessControl(Constants.EVENT_ROLE, this.eventId)) {
this.logger.debug("user '{}' has role '{}' in event {}", this.getUserPrincipal(), role, if (authPerson.hasAccessControl(eventRole, this.eventId)) {
this.logger.debug("user '{}' has role '{}' in event {}", this.getUserPrincipal(), eventRole,
this.eventId); this.eventId);
return true; return true;
} }

View File

@@ -0,0 +1,11 @@
package com.poststats.golf.service;
import com.brianlong.util.FlexMap;
public interface CourseNineService {
FlexMap getNine(long courseNineId);
FlexMap getNine(int courseId, String name);
}

View File

@@ -28,10 +28,6 @@ public interface CourseService extends CacheableService<Integer> {
*/ */
Map<Integer, ? extends FlexMap> get(Collection<Integer> courseIds); Map<Integer, ? extends FlexMap> get(Collection<Integer> courseIds);
FlexMap injectDeep(String idKey, FlexMap parentMap, String mapKey);
Collection<? extends FlexMap> injectDeep(String idKey, Collection<? extends FlexMap> parentMaps, String mapKey);
default SubList<? extends FlexMap> findByName(String name) { default SubList<? extends FlexMap> findByName(String name) {
return this.findByName(name, 1, 10); return this.findByName(name, 1, 10);
} }

View File

@@ -6,10 +6,10 @@ import java.util.Set;
public interface EventPersonService { public interface EventPersonService {
List<FlexMap> getPeople(long eventId, boolean fullDetails); List<? extends FlexMap> getPeople(long eventId);
List<FlexMap> getParticipants(long eventId, boolean fullDetails); List<? extends FlexMap> getParticipants(long eventId);
Set<Long> getSeriesEventIdsAsParticipant(int seriesId, long personId); Set<Long> getSeriesEventFellowParticipantIds(int seriesId, long personId);
} }

View File

@@ -0,0 +1,13 @@
package com.poststats.golf.service;
import com.brianlong.util.FlexMap;
import java.math.BigInteger;
import java.util.List;
public interface EventRoundPairingService {
FlexMap get(BigInteger pairingId);
List<? extends FlexMap> getByRoundId(long eroundId);
}

View File

@@ -7,6 +7,7 @@ import com.brianlong.sql.ResultSubSetFeature;
import com.brianlong.util.FlexMap; import com.brianlong.util.FlexMap;
import com.brianlong.util.SubList; import com.brianlong.util.SubList;
import com.poststats.golf.provider.GolfProvider; import com.poststats.golf.provider.GolfProvider;
import com.poststats.golf.service.CourseNineService;
import com.poststats.golf.service.CourseService; import com.poststats.golf.service.CourseService;
import com.poststats.provider.NonTransactionalProvider; import com.poststats.provider.NonTransactionalProvider;
import com.poststats.provider.Statement; import com.poststats.provider.Statement;
@@ -22,7 +23,7 @@ import java.util.Collection;
import java.util.Map; import java.util.Map;
@ApplicationScoped @ApplicationScoped
public class CourseServiceDAO extends CacheableServiceDAO<Integer> implements CourseService { public class CourseServiceDAO extends CacheableServiceDAO<Integer> implements CourseService, CourseNineService {
private final int defaultCacheExpirationInSeconds = 600; private final int defaultCacheExpirationInSeconds = 600;
private final FlexManyToOneDef facilityManyToOneDef = new FlexManyToOneDef("facilityID", "facility"); private final FlexManyToOneDef facilityManyToOneDef = new FlexManyToOneDef("facilityID", "facility");
@@ -205,4 +206,49 @@ public class CourseServiceDAO extends CacheableServiceDAO<Integer> implements Co
) )
private StatementProvider sqlSelectCourses; private StatementProvider sqlSelectCourses;
@Override
public FlexMap getNine(long nineId) {
try {
FlexPreparedStatement fps = this.sqlSelectCourseNine.buildPreparedStatement();
try {
fps.setIntegerU(1, nineId);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT * FROM ~g~.CourseNine WHERE nineID=? ")
private StatementProvider sqlSelectCourseNine;
@Override
public FlexMap getNine(int courseId, String name) {
try {
FlexPreparedStatement fps = this.sqlSelectCourseNineByName.buildPreparedStatement();
try {
fps.setSmallintU(1, courseId);
fps.setVarchar(2, name);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT * FROM ~g~.CourseNine WHERE courseID=? AND nine=? AND deadline IS NULL ")
private StatementProvider sqlSelectCourseNineByName;
} }

View File

@@ -1,40 +1,27 @@
package com.poststats.golf.service.db; package com.poststats.golf.service.db;
import com.brianlong.cache.CacheRetrievalException;
import com.brianlong.sql.FlexPreparedStatement; import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.util.FlexMap; import com.brianlong.util.FlexMap;
import com.poststats.golf.provider.GolfProvider; import com.poststats.golf.provider.GolfProvider;
import com.poststats.golf.service.EventPersonService; import com.poststats.golf.service.EventPersonService;
import com.poststats.golf.service.PersonService;
import com.poststats.provider.NonTransactionalProvider; import com.poststats.provider.NonTransactionalProvider;
import com.poststats.provider.Statement; import com.poststats.provider.Statement;
import com.poststats.provider.StatementProvider; import com.poststats.provider.StatementProvider;
import com.poststats.service.ServiceException; import com.poststats.service.ServiceException;
import com.poststats.sql.PostStatsDataSource;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import java.math.BigInteger;
import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
@ApplicationScoped @ApplicationScoped
public class EventPersonServiceDAO implements EventPersonService { public class EventPersonServiceDAO implements EventPersonService {
@Inject
private PersonService golferService;
@Override @Override
public List<FlexMap> getPeople(long eventId, boolean includeDetails) { public List<? extends FlexMap> getPeople(long eventId) {
try { try {
return this.query(this.sqlSelectPersonIds, eventId, 2, includeDetails); return this.query(this.sqlSelectPersons, eventId, 2);
} catch (CacheRetrievalException cre) {
throw new ServiceException(cre);
} catch (SQLException se) { } catch (SQLException se) {
throw new ServiceException(se); throw new ServiceException(se);
} }
@@ -44,37 +31,32 @@ public class EventPersonServiceDAO implements EventPersonService {
@NonTransactionalProvider @NonTransactionalProvider
@GolfProvider @GolfProvider
@Statement( @Statement(
sql = "SELECT epersonID, personID FROM ~g~.EventPerson WHERE eventID=? " sql = "SELECT EP.* FROM ~g~.EventPerson EP WHERE EP.eventID=? "
+ "UNION " + "UNION "
+ "SELECT DISTINCT NULL, personID " + "SELECT DISTINCT NULL, personID "
+ "FROM ~g~.EventPersonAccessControl EPAC " + "FROM ~g~.EventPersonAccessControl EPAC "
+ " LEFT JOIN ~g~.EventPerson EP ON (EPAC.eventID=EP.eventID AND EPAC.personID=EP.personID) " + " LEFT JOIN ~g~.EventPerson EP ON (EPAC.eventID=EP.eventID AND EPAC.personID=EP.personID) "
+ "WHERE eventID=? AND EP.personID IS NULL " + "WHERE EPAC.eventID=? AND EP.personID IS NULL "
) )
private StatementProvider sqlSelectPersonIds; private StatementProvider sqlSelectPersons;
@Override @Override
public List<FlexMap> getParticipants(long eventId, boolean includeDetails) { public List<? extends FlexMap> getParticipants(long eventId) {
Connection dbcon = PostStatsDataSource.getInstance().acquire(true);
try { try {
return this.query(this.sqlSelectParticipantIds, eventId, 1, includeDetails); return this.query(this.sqlSelectParticipantIds, eventId, 1);
} catch (CacheRetrievalException cre) {
throw new ServiceException(cre);
} catch (SQLException se) { } catch (SQLException se) {
throw new ServiceException(se); throw new ServiceException(se);
} finally {
PostStatsDataSource.getInstance().release(dbcon);
} }
} }
@Inject @Inject
@NonTransactionalProvider @NonTransactionalProvider
@GolfProvider @GolfProvider
@Statement(sql = "SELECT epersonID, personID FROM ~g~.EventPerson WHERE eventID=?") @Statement(sql = "SELECT * FROM ~g~.EventPerson WHERE eventID=? AND pending IS FALSE")
private StatementProvider sqlSelectParticipantIds; private StatementProvider sqlSelectParticipantIds;
@Override @Override
public Set<Long> getSeriesEventIdsAsParticipant(int seriesId, long personId) { public Set<Long> getSeriesEventFellowParticipantIds(int seriesId, long personId) {
try { try {
FlexPreparedStatement fps = this.sqlSelectFellowParticipants.buildPreparedStatement(); FlexPreparedStatement fps = this.sqlSelectFellowParticipants.buildPreparedStatement();
try { try {
@@ -97,33 +79,20 @@ public class EventPersonServiceDAO implements EventPersonService {
+ "FROM ~g~.EventPerson EP1 " + "FROM ~g~.EventPerson EP1 "
+ " INNER JOIN Event E ON (EP1.eventID=E.eventID) " + " INNER JOIN Event E ON (EP1.eventID=E.eventID) "
+ " INNER JOIN EventPerson EP2 ON (E.eventID=EP2.eventID) " + " INNER JOIN EventPerson EP2 ON (E.eventID=EP2.eventID) "
+ "WHERE EP1.personID=? AND E.seriesID=?" + "WHERE EP1.personID=? AND EP1.pending IS FALSE AND E.seriesID=? AND EP2.pending IS FALSE "
) )
private StatementProvider sqlSelectFellowParticipants; private StatementProvider sqlSelectFellowParticipants;
private List<FlexMap> query(StatementProvider provider, long eventId, int parameterCount, boolean includeDetails) private List<? extends FlexMap> query(StatementProvider provider, long eventId, int parameterCount)
throws CacheRetrievalException, SQLException { throws SQLException {
Map<BigInteger, Long> epersonIdMap;
FlexPreparedStatement fps = provider.buildPreparedStatement(); FlexPreparedStatement fps = provider.buildPreparedStatement();
try { try {
for (int i = 1; i <= parameterCount; i++) for (int i = 1; i <= parameterCount; i++)
fps.setIntegerU(i, eventId); fps.setIntegerU(i, eventId);
epersonIdMap = fps.executeQuery().getFirstTwoColumns(BigInteger.class, Long.class); return fps.executeQuery().getAllRows();
} finally { } finally {
fps.close(); fps.close();
} }
Map<Long, ? extends FlexMap> personMap = this.golferService.get(epersonIdMap.values());
List<FlexMap> rows = new ArrayList<>(personMap.size());
for (Entry<BigInteger, Long> eperson : epersonIdMap.entrySet()) {
FlexMap row = (FlexMap) personMap.get(eperson.getValue()).clone();
row.put("epersonID", eperson.getKey());
rows.add(row);
}
return rows;
} }
} }

View File

@@ -0,0 +1,75 @@
package com.poststats.golf.service.db;
import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexManyToOneDef;
import com.brianlong.sql.FlexPreparedStatement;
import com.poststats.golf.provider.GolfProvider;
import com.poststats.golf.service.EventRoundPairingService;
import com.poststats.provider.NonTransactionalProvider;
import com.poststats.provider.Statement;
import com.poststats.provider.StatementProvider;
import com.poststats.service.ServiceException;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.math.BigInteger;
import java.sql.SQLException;
import java.util.List;
@ApplicationScoped
public class EventRoundPairingServiceDAO implements EventRoundPairingService {
@Override
public DataSet get(BigInteger pairingId) {
try {
FlexPreparedStatement fps = this.sqlSelect.buildPreparedStatement();
try {
fps.setBigintU(1, pairingId);
return fps.executeQuery().getNextRow(new FlexManyToOneDef("nineID", "nine"));
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT ERP.pairingID, ERP.teetime, ERP.eroundID, ERP.courseID, ERP.number, "
+ " CN.nineID, CN.nine, CN.courseID "
+ "FROM ~g~.EventRoundPairing ERP "
+ " LEFT JOIN ~g~.CourseNine CN ON (ERP.nineID=CN.nineID) "
+ "WHERE ERP.pairingID=? "
)
private StatementProvider sqlSelect;
@Override
public List<DataSet> getByRoundId(long eroundId) {
try {
FlexPreparedStatement fps = this.sqlSelectByRoundId.buildPreparedStatement();
try {
fps.setIntegerU(1, eroundId);
return fps.executeQuery().getAllRows(new FlexManyToOneDef("nineID", "nine"));
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT ERP.pairingID, ERP.teetime, ERP.eroundID, ERP.courseID, ERP.number, "
+ " CN.nineID, CN.nine, CN.courseID "
+ "FROM ~g~.EventRoundPairing ERP "
+ " LEFT JOIN ~g~.CourseNine CN ON (ERP.nineID=CN.nineID) "
+ "WHERE ERP.eroundID=? "
)
private StatementProvider sqlSelectByRoundId;
}

View File

@@ -68,6 +68,19 @@ public class PersonServiceDAO extends CacheableServiceDAO<Long> implements Perso
return this.get(Long.valueOf(personId)); return this.get(Long.valueOf(personId));
} }
@Override
public FlexMap injectDeep(String idKey, FlexMap parentMap, String mapKey) {
FlexMap map = this.inject(idKey, parentMap, mapKey);
return this.poststatsPersonService.inject("personID", map, "person");
}
@Override
public Collection<? extends FlexMap> injectDeep(String idKey, Collection<? extends FlexMap> parentMaps,
String mapKey) {
Collection<? extends FlexMap> maps = this.inject(idKey, parentMaps, mapKey);
return this.poststatsPersonService.inject("personID", maps, "person");
}
private abstract class PrincipalCacheableFetcher implements CacheableFetcher<Long, AuthenticatedPerson> { private abstract class PrincipalCacheableFetcher implements CacheableFetcher<Long, AuthenticatedPerson> {
public abstract com.poststats.security.AuthenticatedPerson getPerson(); public abstract com.poststats.security.AuthenticatedPerson getPerson();