refactored model; added course/round APIs

This commit is contained in:
2023-02-10 15:02:56 -05:00
parent ed80f1bd26
commit 9a43692415
32 changed files with 1622 additions and 159 deletions

View File

@@ -0,0 +1,74 @@
package com.poststats.golf.api;
import com.brianlong.util.FlexMap;
import com.poststats.golf.api.model.Course;
import com.poststats.golf.service.CourseService;
import com.poststats.transformer.impl.DaoConverter;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.Response.Status;
import jakarta.ws.rs.core.SecurityContext;
import org.slf4j.Logger;
/**
* @author brian.long@poststats.com
*/
@RequestScoped
@Path("/golf/course/{courseId}")
@Tag(name = "Course API")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "You are not authorized"),
@ApiResponse(responseCode = "403", description = "You are not identified or authenticated")
})
public class CourseApi {
@Parameter(name = "courseId", description = "A unique identifier for a golf course")
@PathParam("courseId")
private int courseId;
@Inject
private Logger logger;
@Inject
private CourseService courseService;
@Inject
private DaoConverter converter;
@PostConstruct
public void init() {
this.logger.debug("CourseApi init: {}", this.courseId);
}
@GET
@Produces(Constants.V1_JSON)
@Operation(
summary = "Retrieves limited meta-data about a course.",
description = "Retreives name, location, and other direct meta-data about the specified course."
)
@ApiResponses({
@ApiResponse(responseCode = "404", description = "A golf course with the specified ID could not be found")
})
public Course get(@Context SecurityContext scontext) {
FlexMap row = this.courseService.get(this.courseId);
if (row == null)
throw new WebApplicationException("Course not found", Status.NOT_FOUND);
this.logger.trace("found: {}", this.courseId);
return this.converter.convertValue(row, Course.class);
}
}

View File

@@ -0,0 +1,176 @@
package com.poststats.golf.api;
import com.brianlong.util.FlexMap;
import com.brianlong.util.SubList;
import com.poststats.api.model.PagedCollection;
import com.poststats.api.model.Pagination;
import com.poststats.golf.api.model.Course;
import com.poststats.golf.service.CourseService;
import com.poststats.transformer.impl.DaoConverter;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.BeanParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Response.Status;
import java.util.List;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
/**
* @author brian.long@poststats.com
*/
@RequestScoped
@Path("/golf/courses")
@Tag(name = "Course API")
public class CoursesApi {
@Inject
private Logger logger;
@Inject
private CourseService courseService;
@Inject
private DaoConverter converter;
@PostConstruct
public void init() {
this.logger.debug("CoursesApi init");
}
@GET
@Path("/byName")
@Produces(Constants.V1_JSON)
@Operation(
summary = "Searches for golf courses by their name.",
description = "Searches for golf courses by their name, excluding prefix (e.g. The) and suffix (e.g. Golf Club), returning limited meta-data about each matched golf course."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "404", description = "No matching golf courses were found"),
@ApiResponse(responseCode = "411", description = "The page number must be at least 1"),
@ApiResponse(responseCode = "413", description = "The page size is too large")
})
@Parameters({
@Parameter(
name = "name",
required = true,
description = "A fragment or whole golf course name; the prefix and suffix are not included"
), @Parameter(name = "page", description = "A page number, starting at 1", example = "1"),
@Parameter(name = "perPage", description = "A page size", example = "10")
})
@Tag(name = "Search API")
public PagedCollection<List<Course>> searchByName(@QueryParam("name") String name, @BeanParam Pagination paging) {
SubList<? extends FlexMap> rows = this.courseService.findByName(name, paging.getPage(), paging.getPerPage());
if (rows.isEmpty())
throw new WebApplicationException("No matching courses found", Status.NOT_FOUND);
this.logger.trace("found: {} [{}-{}] of {}", rows.size(), rows.getStartIndex() + 1, rows.getEndIndex(),
rows.getTotal());
return this.converter.convertValue(rows, Course.class).withPage(paging.getPage())
.withPerPage(paging.getPerPage());
}
@GET
@Path("/byLocation/{country}/{state}")
@Produces(Constants.V1_JSON)
@Operation(
summary = "Searches for golf courses by their location.",
description = "Searches for golf courses by their major jurisdiction (e.g. US States), returning limited meta-data about each matched golf course."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(
responseCode = "404",
description = "The country, state, or matching golf courses were not found"
), @ApiResponse(responseCode = "411", description = "The page number must be at least 1"),
@ApiResponse(responseCode = "413", description = "The page size is too large")
})
@Parameters({
@Parameter(name = "country", required = true, description = "A country code", example = "US"),
@Parameter(name = "state", description = "A State or high-level jurisdiction", example = "FL"),
@Parameter(name = "page", description = "A page number, starting at 1", example = "1"),
@Parameter(name = "perPage", description = "A page size", example = "10")
})
@Tag(name = "Search API")
public PagedCollection<List<Course>> searchByJurisdiction(@PathParam("country") String country,
@PathParam("state") String state, @BeanParam Pagination paging) {
SubList<? extends FlexMap> rows = this.courseService.findByJurisdiction(country, state, paging.getPage(),
paging.getPerPage());
if (rows.isEmpty())
throw new WebApplicationException("No matching golf courses found", Status.NOT_FOUND);
return this.converter.convertValue(rows, Course.class).withPage(paging.getPage())
.withPerPage(paging.getPerPage());
}
@GET
@Path("/byGeo")
@Produces(Constants.V1_JSON)
@Operation(
summary = "Searches for golf courses by their location.",
description = "Searches for golf courses by their major jurisdiction (e.g. US States), returning limited meta-data about each matched golf course."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(
responseCode = "400",
description = "A latitude/longitude/radius was outside its natural limits"
), @ApiResponse(responseCode = "404", description = "No golf courses were found"),
@ApiResponse(responseCode = "411", description = "The page number must be at least 1"),
@ApiResponse(responseCode = "413", description = "The page size is too large")
})
@Parameters({
@Parameter(
name = "latitude",
required = true,
description = "A latitude in decimal degrees",
example = "41.2390"
),
@Parameter(
name = "longitude",
required = true,
description = "A longitude in decimal degrees",
example = "-81.34891"
), @Parameter(name = "radius", description = "A search radius in miles", example = "10"),
@Parameter(name = "page", description = "A page number, starting at 1", example = "1"),
@Parameter(name = "perPage", description = "A page size", example = "20")
})
@Tag(name = "Search API")
public PagedCollection<List<Course>> searchByJurisdiction(@QueryParam("latitude") double latitude,
@QueryParam("longitude") double longitude, @QueryParam("radius") Integer radiusInMiles,
@BeanParam Pagination paging) {
if (latitude < -90.0 || latitude > 90.0)
throw new WebApplicationException("A latitude of -90 to 90 is expected", HttpStatus.SC_BAD_REQUEST);
if (longitude < -180.0 || longitude > 180.0)
throw new WebApplicationException("A longitude of -180 to 180 is expected", HttpStatus.SC_BAD_REQUEST);
if (radiusInMiles == null)
radiusInMiles = 10;
if (radiusInMiles <= 0)
throw new WebApplicationException("A positive radius is expected", HttpStatus.SC_BAD_REQUEST);
if (radiusInMiles > 100)
throw new WebApplicationException("A radius under 100 miles is expected", HttpStatus.SC_BAD_REQUEST);
SubList<? extends FlexMap> rows = this.courseService.findByLocation(latitude, longitude, radiusInMiles,
paging.getPage(), paging.getPerPage());
if (rows.isEmpty())
throw new WebApplicationException("No matching facilities found", Status.NOT_FOUND);
return this.converter.convertValue(rows, Course.class).withPage(paging.getPage())
.withPerPage(paging.getPerPage());
}
}

View File

@@ -0,0 +1,112 @@
package com.poststats.golf.api;
import com.brianlong.util.FlexMap;
import com.poststats.api.Constants;
import com.poststats.golf.api.model.EventRound;
import com.poststats.golf.service.EventRoundService;
import com.poststats.transformer.impl.DaoConverter;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Response.Status;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author brian.long@poststats.com
*/
@RequestScoped
@Path("/golf/event/{eventId}")
@Tag(name = "Event Round API")
public class EventRoundApi {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@PathParam("eventId")
private long eventId;
@Inject
private EventRoundService roundService;
@Inject
private DaoConverter converter;
@PostConstruct
public void init() {
this.logger.debug("EventRoundApi init: {}", this.eventId);
}
@GET
@Path("/r{number}")
@Produces(Constants.V1_JSON)
@Operation(
summary = "Retrieves limited meta-data about an event.",
description = "Retreives name, location, dates, and other direct meta-data about the specified event."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
})
public EventRound getOne(@PathParam("number") short roundNumber) {
FlexMap row = this.roundService.get(this.eventId, roundNumber);
if (row == null)
throw new WebApplicationException("Event round not found", Status.NOT_FOUND);
return this.converter.convertValue(row, EventRound.class);
}
@GET
@Path("/round/{eroundId}")
@Produces(Constants.V1_JSON)
@Operation(
summary = "Retrieves limited meta-data about an event.",
description = "Retreives name, location, dates, and other direct meta-data about the specified event."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
})
public EventRound getOne(@PathParam("eroundId") long eroundId) {
FlexMap row = this.roundService.get(eroundId);
if (row == null)
throw new WebApplicationException("Event round not found", Status.NOT_FOUND);
if (this.eventId != row.getLong("eventID")) {
this.logger.warn("The event round {} was requested without the appropriate event ID {}", eroundId,
this.eventId);
throw new WebApplicationException("Event round not found", Status.NOT_FOUND);
}
return this.converter.convertValue(row, EventRound.class);
}
@GET
@Path("/rounds")
@Produces(Constants.V1_JSON)
@Operation(
summary = "Retrieves limited meta-data about an event.",
description = "Retreives name, location, dates, and other direct meta-data about the specified event."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
})
public List<EventRound> getAll() {
Map<Long, ? extends FlexMap> rows = this.roundService.getByEventId(this.eventId);
if (rows.isEmpty())
throw new WebApplicationException("No event rounds found", Status.NOT_FOUND);
return this.converter.convertValue(rows.values(), EventRound.class);
}
}

View File

@@ -0,0 +1,109 @@
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.Year;
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class BaseCourse<ConcreteT extends BaseCourse<ConcreteT>> extends BaseModel<ConcreteT> {
public enum Access {
@JsonProperty("private")
@MapEntry("private")
Private, @JsonProperty("military")
@MapEntry("military")
Military, @JsonProperty("semi-private")
@MapEntry("semi-private")
SemiPrivate, @JsonProperty("public")
@MapEntry("public")
Public,
}
@JsonProperty
@MapEntry("course")
private String name;
@JsonProperty
@MapEntry("coursePrefix")
private String namePrefix;
@JsonProperty
@MapEntry
private Access access;
@JsonProperty
@MapEntry
private Year opened;
@JsonProperty
@MapEntry
private String architect;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNamePrefix() {
return namePrefix;
}
public void setNamePrefix(String namePrefix) {
this.namePrefix = namePrefix;
}
public Access getAccess() {
return access;
}
public void setAccess(Access access) {
this.access = access;
}
public Year getOpened() {
return opened;
}
public void setOpened(Year opened) {
this.opened = opened;
}
public String getArchitect() {
return architect;
}
public void setArchitect(String architect) {
this.architect = architect;
}
public ConcreteT withName(String name) {
this.name = name;
return this.withThis();
}
public ConcreteT withNamePrefix(String prefix) {
this.namePrefix = prefix;
return this.withThis();
}
public ConcreteT withAccess(Access access) {
this.access = access;
return this.withThis();
}
public ConcreteT withOpened(Year opened) {
this.opened = opened;
return this.withThis();
}
public ConcreteT withArchitect(String architect) {
this.architect = architect;
return this.withThis();
}
}

View File

@@ -0,0 +1,92 @@
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;
/**
* @author brian.long@poststats.com
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class BaseEvent<ConcreteT extends BaseEvent<ConcreteT>> extends BaseModel<ConcreteT> {
public enum EventType {
@JsonProperty("outing")
@MapEntry("outing")
Outing, @JsonProperty("tourney")
@MapEntry("tourney")
Tourney, @JsonProperty("trip")
@MapEntry("trip")
Trip, @JsonProperty("league")
@MapEntry("league")
League
}
@JsonProperty(required = true)
@MapEntry("event")
private String name;
@JsonProperty(required = true)
@MapEntry
private EventType type;
@JsonProperty
private boolean showPublic;
@JsonProperty
private String fuzzyLocation;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public EventType getType() {
return type;
}
public void setType(EventType type) {
this.type = type;
}
public boolean isShowPublic() {
return showPublic;
}
public void setShowPublic(boolean showPublic) {
this.showPublic = showPublic;
}
public String getFuzzyLocation() {
return fuzzyLocation;
}
public void setFuzzyLocation(String fuzzyLocation) {
this.fuzzyLocation = fuzzyLocation;
}
public ConcreteT withName(String name) {
this.name = name;
return this.withThis();
}
public ConcreteT withType(EventType type) {
this.type = type;
return this.withThis();
}
public ConcreteT withShowPublic(boolean showPublic) {
this.showPublic = showPublic;
return this.withThis();
}
public ConcreteT withFuzzyLocation(String fuzzyLocation) {
this.fuzzyLocation = fuzzyLocation;
return this.withThis();
}
}

View File

@@ -0,0 +1,95 @@
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.LocalDate;
/**
* @author brian.long@poststats.com
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class BaseEventRound<ConcreteT extends BaseEventRound<ConcreteT>> extends BaseModel<ConcreteT> {
public enum TeeFormat {
@JsonProperty("straight")
@MapEntry("straight")
Straight, @JsonProperty("dual")
@MapEntry("dual")
Dual, @JsonProperty("shotgun-9")
@MapEntry("shotgun-9")
Shotgun9Hole, @JsonProperty("shotgun-18")
@MapEntry("shotgun-18")
Shotgun18Hole
}
@JsonProperty
@MapEntry("round")
private Short number;
@JsonProperty
@MapEntry("title")
private String name;
@JsonProperty
@MapEntry
private LocalDate date;
@JsonProperty
@MapEntry("teeFormatSID")
private TeeFormat teeFormat;
public Short getNumber() {
return number;
}
public void setNumber(Short number) {
this.number = number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public LocalDate getDate() {
return date;
}
public void setDate(LocalDate date) {
this.date = date;
}
public TeeFormat getTeeFormat() {
return teeFormat;
}
public void setTeeFormat(TeeFormat teeFormat) {
this.teeFormat = teeFormat;
}
public ConcreteT withNumber(Short number) {
this.number = number;
return this.withThis();
}
public ConcreteT withName(String name) {
this.name = name;
return this.withThis();
}
public ConcreteT withDate(LocalDate date) {
this.date = date;
return this.withThis();
}
public ConcreteT withTeeFormat(TeeFormat teeFormat) {
this.teeFormat = teeFormat;
return this.withThis();
}
}

View File

@@ -0,0 +1,46 @@
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.Year;
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class BaseGolfer<ConcreteT extends BaseGolfer<ConcreteT>> extends BaseModel<ConcreteT> {
@JsonProperty
@MapEntry
private Year started;
@JsonProperty
@MapEntry("aStrokeHandicap")
private Float strokeHandicap;
public Year getStarted() {
return started;
}
public void setStarted(Year started) {
this.started = started;
}
public Float getStrokeHandicap() {
return strokeHandicap;
}
public void setStrokeHandicap(Float strokeHandicap) {
this.strokeHandicap = strokeHandicap;
}
public ConcreteT withStarted(Year started) {
this.started = started;
return this.withThis();
}
public ConcreteT withStrokeHandicap(Float strokeHandicap) {
this.strokeHandicap = strokeHandicap;
return this.withThis();
}
}

View File

@@ -0,0 +1,31 @@
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;
/**
* @author brian.long@poststats.com
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class BaseSeries<ConcreteT extends BaseSeries<ConcreteT>> extends BaseModel<ConcreteT> {
@JsonProperty(required = true)
@MapEntry("series")
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,63 @@
package com.poststats.golf.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.api.model.Facility;
import com.poststats.transformer.MapEntry;
@JsonIgnoreProperties(ignoreUnknown = true)
public class Course extends BaseCourse<Course> implements ReferenceableCourse {
@JsonProperty(required = true, access = com.fasterxml.jackson.annotation.JsonProperty.Access.READ_ONLY)
@MapEntry("courseID")
private int id;
@JsonProperty
@MapEntry
private Facility facility;
@JsonProperty
@MapEntry("facilityID")
private Integer facilityId;
@Override
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Facility getFacility() {
return facility;
}
public void setFacility(Facility facility) {
this.facility = facility;
}
public Integer getFacilityId() {
return facilityId;
}
public void setFacilityId(Integer facilityId) {
this.facilityId = facilityId;
}
public Course withId(int id) {
this.id = id;
return this;
}
public Course withFacility(Facility facility) {
this.facility = facility;
return this;
}
public Course withFacilityId(Integer facilityId) {
this.facilityId = facilityId;
return this;
}
}

View File

@@ -2,6 +2,7 @@ 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.golf.api.Constants;
import com.poststats.service.impl.DefaultFormattingService;
import com.poststats.transformer.GeocodeSource;
@@ -9,16 +10,15 @@ import com.poststats.transformer.GeocodeTarget;
import com.poststats.transformer.GeocodeTarget.GeocodeField;
import com.poststats.transformer.MapCondition;
import com.poststats.transformer.MapEntry;
import jakarta.annotation.Generated;
import java.time.LocalDate;
/**
* @author brian.long@poststats.com
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class Event extends TransientEvent {
public class Event extends BaseEvent<Event> implements ReferenceableEvent {
@JsonProperty(required = true)
@JsonProperty(required = true, access = Access.READ_ONLY)
@MapEntry("eventID")
private long id;
@@ -46,6 +46,10 @@ public class Event extends TransientEvent {
@MapEntry
private Series series;
@JsonProperty(access = Access.READ_ONLY)
@MapEntry("seriesID")
private Integer seriesId;
@JsonProperty
@MapEntry
private Boolean financesEnabled;
@@ -150,32 +154,27 @@ public class Event extends TransientEvent {
})
private LocalDate specialHolesLiveline;
@Generated("Eclipse")
@Override
public long getId() {
return id;
}
@Generated("Eclipse")
public void setId(long id) {
this.id = id;
}
@Generated("Eclipse")
public LocalDate getLiveline() {
return liveline;
}
@Generated("Eclipse")
public void setLiveline(LocalDate liveline) {
this.liveline = liveline;
}
@Generated("Eclipse")
public LocalDate getDeadline() {
return deadline;
}
@Generated("Eclipse")
public void setDeadline(LocalDate deadline) {
this.deadline = deadline;
}
@@ -202,6 +201,14 @@ public class Event extends TransientEvent {
this.series = series;
}
public Integer getSeriesId() {
return seriesId;
}
public void setSeriesId(Integer seriesId) {
this.seriesId = seriesId;
}
public Boolean getAwardsEnabled() {
return awardsEnabled;
}
@@ -362,19 +369,16 @@ public class Event extends TransientEvent {
this.teamsLiveline = teamsLiveline;
}
@Generated("Spark")
public Event withId(long id) {
this.id = id;
return this;
}
@Generated("Spark")
public Event withLiveline(LocalDate liveline) {
this.liveline = liveline;
return this;
}
@Generated("Spark")
public Event withDeadline(LocalDate deadline) {
this.deadline = deadline;
return this;
@@ -385,6 +389,11 @@ public class Event extends TransientEvent {
return this;
}
public Event withSeriesId(Integer seriesId) {
this.seriesId = seriesId;
return this;
}
public Event withDocumentsEnabled(Boolean documentsEnabled) {
this.documentsEnabled = documentsEnabled;
return this;

View File

@@ -0,0 +1,66 @@
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 EventRound extends BaseEventRound<EventRound> implements ReferenceableEventRound {
@JsonProperty(required = true, access = Access.READ_ONLY)
@MapEntry("eroundID")
private long id;
@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;
}
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 EventRound withId(long id) {
this.id = id;
return this;
}
public EventRound withCourse(Course course) {
this.course = course;
return this;
}
public EventRound withCourseId(Integer courseId) {
this.courseId = courseId;
return this;
}
}

View File

@@ -4,24 +4,16 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.poststats.api.model.Person;
import com.poststats.api.model.ReferenceablePerson;
import com.poststats.transformer.MapEntry;
import java.time.Year;
@JsonIgnoreProperties(ignoreUnknown = true)
public class Golfer {
public class Golfer extends BaseGolfer<Golfer> implements ReferenceablePerson {
@JsonProperty(required = true, access = Access.READ_ONLY)
@MapEntry("personID")
private long personId;
@JsonProperty
@MapEntry
private Year started;
@JsonProperty
@MapEntry("aStrokeHandicap")
private Float strokeHandicap;
@JsonProperty
@MapEntry("unofficialStrokeHandicap")
private Float poststatsStrokeHandicap;
@@ -34,30 +26,15 @@ public class Golfer {
@MapEntry
private Person person;
public long getPersonId() {
@Override
public long getId() {
return personId;
}
public void setPersonId(long personId) {
public void setId(long personId) {
this.personId = personId;
}
public Year getStarted() {
return started;
}
public void setStarted(Year started) {
this.started = started;
}
public Float getStrokeHandicap() {
return strokeHandicap;
}
public void setStrokeHandicap(Float strokeHandicap) {
this.strokeHandicap = strokeHandicap;
}
public Float getPoststatsStrokeHandicap() {
return poststatsStrokeHandicap;
}
@@ -87,16 +64,6 @@ public class Golfer {
return this;
}
public Golfer withStarted(Year started) {
this.started = started;
return this;
}
public Golfer withStrokeHandicap(Float strokeHandicap) {
this.strokeHandicap = strokeHandicap;
return this;
}
public Golfer withPoststatsStrokeHandicap(Float poststatsStrokeHandicap) {
this.poststatsStrokeHandicap = poststatsStrokeHandicap;
return this;

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,7 @@
package com.poststats.golf.api.model;
public interface ReferenceableSeries {
int 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 ReferencedCourse implements ReferenceableCourse {
@JsonProperty(required = true, access = Access.READ_ONLY)
@MapEntry("courseID")
private int id;
@Override
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public ReferencedCourse withId(int id) {
this.id = id;
return this;
}
}

View File

@@ -0,0 +1,32 @@
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 ReferencedEvent implements ReferenceableEvent {
@JsonProperty(required = true, access = Access.READ_ONLY)
@MapEntry("eventID")
private long id;
@Override
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public ReferencedEvent withId(long id) {
this.id = id;
return this;
}
}

View File

@@ -0,0 +1,32 @@
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 ReferencedEventRound implements ReferenceableEvent {
@JsonProperty(required = true, access = Access.READ_ONLY)
@MapEntry("eroundID")
private long id;
@Override
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public ReferencedEventRound withId(long id) {
this.id = id;
return this;
}
}

View File

@@ -0,0 +1,27 @@
package com.poststats.golf.api.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.poststats.transformer.MapEntry;
public class ReferencedSeries implements ReferenceableSeries {
@JsonProperty(required = true, access = Access.READ_ONLY)
@MapEntry("seriesID")
private int id;
@Override
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public ReferencedSeries withId(int id) {
this.id = id;
return this;
}
}

View File

@@ -4,30 +4,27 @@ 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 jakarta.annotation.Generated;
/**
* @author brian.long@poststats.com
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class Series extends TransientSeries {
public class Series extends BaseSeries<Series> implements ReferenceableSeries {
@JsonProperty(required = true, access = Access.READ_ONLY)
@MapEntry("seriesID")
private long id;
private int id;
@Generated("Eclipse")
public long getId() {
@Override
public int getId() {
return id;
}
@Generated("Eclipse")
public void setId(long id) {
public void setId(int id) {
this.id = id;
}
@Generated("Spark")
public Series withId(long id) {
public Series withId(int id) {
this.id = id;
return this;
}

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.ReferenceableFacility;
import com.poststats.transformer.MapEntry;
@JsonIgnoreProperties(ignoreUnknown = true)
public class TransientCourse extends BaseCourse<TransientCourse> {
@JsonProperty
@MapEntry
private ReferenceableFacility facility;
public ReferenceableFacility getFacility() {
return facility;
}
public void setFacility(ReferenceableFacility facility) {
this.facility = facility;
}
public TransientCourse withFacility(ReferenceableFacility facility) {
this.facility = facility;
return this;
}
}

View File

@@ -4,92 +4,27 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.transformer.MapEntry;
import jakarta.annotation.Generated;
/**
* @author brian.long@poststats.com
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class TransientEvent {
public class TransientEvent extends BaseEvent<TransientEvent> {
public enum EventType {
@JsonProperty("outing")
@MapEntry("outing")
Outing, @JsonProperty("tourney")
@MapEntry("tourney")
Tourney, @JsonProperty("trip")
@MapEntry("trip")
Trip, @JsonProperty("league")
@MapEntry("league")
League
}
@JsonProperty(required = true)
@MapEntry("event")
private String name;
@JsonProperty(required = true)
@JsonProperty
@MapEntry
private EventType type;
private ReferenceableSeries series;
@JsonProperty
private boolean showPublic;
@JsonProperty
private String fuzzyLocation;
@Generated("Eclipse")
public String getName() {
return name;
public ReferenceableSeries getSeries() {
return series;
}
@Generated("Eclipse")
public void setName(String name) {
this.name = name;
public void setSeries(ReferenceableSeries series) {
this.series = series;
}
public EventType getType() {
return type;
}
public void setType(EventType type) {
this.type = type;
}
public boolean isShowPublic() {
return showPublic;
}
public void setShowPublic(boolean showPublic) {
this.showPublic = showPublic;
}
public String getFuzzyLocation() {
return fuzzyLocation;
}
public void setFuzzyLocation(String fuzzyLocation) {
this.fuzzyLocation = fuzzyLocation;
}
public TransientEvent withName(String name) {
this.name = name;
return this;
}
public TransientEvent withType(EventType type) {
this.type = type;
return this;
}
public TransientEvent withShowPublic(boolean showPublic) {
this.showPublic = showPublic;
return this;
}
public TransientEvent withFuzzyLocation(String fuzzyLocation) {
this.fuzzyLocation = fuzzyLocation;
return this;
public TransientEvent withSeries(ReferenceableSeries series) {
this.series = series;
return this.withThis();
}
}

View File

@@ -0,0 +1,30 @@
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 TransientEventRound extends BaseEventRound<TransientEventRound> {
@JsonProperty
@MapEntry
private ReferenceableCourse course;
public ReferenceableCourse getCourse() {
return course;
}
public void setCourse(ReferenceableCourse course) {
this.course = course;
}
public TransientEventRound withCourse(ReferenceableCourse course) {
this.course = course;
return this;
}
}

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.ReferenceablePerson;
import com.poststats.transformer.MapEntry;
@JsonIgnoreProperties(ignoreUnknown = true)
public class TransientGolfer extends BaseGolfer<TransientGolfer> {
@JsonProperty
@MapEntry
private ReferenceablePerson person;
public ReferenceablePerson getPerson() {
return person;
}
public void setPerson(ReferenceablePerson person) {
this.person = person;
}
public TransientGolfer withPerson(ReferenceablePerson person) {
this.person = person;
return this;
}
}

View File

@@ -1,34 +1,11 @@
package com.poststats.golf.api.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.poststats.transformer.MapEntry;
import jakarta.annotation.Generated;
/**
* @author brian.long@poststats.com
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class TransientSeries {
@JsonProperty(required = true)
@MapEntry("series")
private String name;
@Generated("Eclipse")
public String getName() {
return name;
}
@Generated("Eclipse")
public void setName(String name) {
this.name = name;
}
@Generated("Spark")
public TransientSeries withName(String name) {
this.name = name;
return this;
}
public class TransientSeries extends BaseSeries<TransientSeries> {
}

View File

@@ -0,0 +1,53 @@
package com.poststats.golf.service;
import com.brianlong.util.FlexMap;
import com.brianlong.util.SubList;
import java.util.Collection;
import java.util.Map;
public interface CourseService {
/**
* This method retrieves meta-data about the specified course.
*
* This retrieves the facility meta-data as well.
*
* @param courseId A unique identifier for the course.
* @return A map of meta-data specific to the course.
*/
FlexMap get(int courseId);
/**
* This method retrieves meta-data about the specified courses.
*
* This retrieves the facility meta-data as well.
*
* @param courseIds Unique identifier for the coursees.
* @return A map of unique identifiers to meta-data specific to the coursees.
*/
Map<Integer, ? extends FlexMap> get(Collection<Integer> courseIds);
default SubList<? extends FlexMap> findByName(String name) {
return this.findByName(name, 1, 10);
}
SubList<? extends FlexMap> findByName(String name, int page, int perPage);
default SubList<? extends FlexMap> findByJurisdiction(String country, String state) {
return this.findByJurisdiction(country, state, 1, 50);
}
SubList<? extends FlexMap> findByJurisdiction(String country, String state, int page, int perPage);
default SubList<? extends FlexMap> findByLocation(double latitude, double longitude) {
return this.findByLocation(latitude, longitude, 10, 1, 25);
}
default SubList<? extends FlexMap> findByLocation(double latitude, double longitude, int radiusInMiles) {
return this.findByLocation(latitude, longitude, radiusInMiles, 1, 25);
}
SubList<? extends FlexMap> findByLocation(double latitude, double longitude, int radiusInMiles, int page,
int perPage);
}

View File

@@ -0,0 +1,34 @@
package com.poststats.golf.service;
import com.brianlong.util.FlexMap;
import java.util.Map;
public interface EventRoundService {
/**
* This method retrieves meta-data about the specified event round.
*
* @param eroundId A unique identifier for the event round.
* @return A map of meta-data specific to the event round.
*/
FlexMap get(long eroundId);
/**
* This method retrieves meta-data about the specified event round.
*
* @param eventId A unique identifier for the event.
* @param number A sequential number for the event round, starting with 1.
* @return A map of meta-data specific to the event round.
*/
FlexMap get(long eventId, short number);
/**
* This method retrieves meta-data about the specified event rounds.
*
* @param eventId A unique identifier for the event.
* @return A map of unique identifiers to meta-data specific to each event
* round.
*/
Map<Long, ? extends FlexMap> getByEventId(long eventId);
}

View File

@@ -0,0 +1,218 @@
package com.poststats.golf.service.db;
import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.sql.ResultSubSetFeature;
import com.brianlong.util.FlexMap;
import com.brianlong.util.SubList;
import com.poststats.golf.api.Constants;
import com.poststats.golf.service.CourseService;
import com.poststats.provider.Statement;
import com.poststats.provider.StatementProvider;
import com.poststats.service.FacilityService;
import com.poststats.service.ServiceException;
import com.poststats.service.db.CacheableServiceDAO;
import com.poststats.sql.PostStatsSQL;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@ApplicationScoped
public class CourseServiceDAO extends CacheableServiceDAO<Integer> implements CourseService {
private final int defaultCacheExpirationInSeconds = 600;
@Inject
private FacilityService facilityService;
@Override
public FlexMap get(int courseId) {
return this.get(Integer.valueOf(courseId));
}
@Override
public FlexMap get(Integer courseId) {
FlexMap row = super.get(Integer.valueOf(courseId));
if (row == null)
return null;
row.put("facility", this.facilityService.get(row.getInteger("facilityID")));
return row;
}
@Override
public Map<Integer, ? extends FlexMap> get(Collection<Integer> courseIds) {
Map<Integer, ? extends FlexMap> rows = super.get(courseIds);
// bulk fetch series from cache/db
Set<Integer> facilityIds = new HashSet<>();
for (Entry<Integer, ? extends FlexMap> row : rows.entrySet())
facilityIds.add(row.getValue().getInteger("facilityId"));
Map<Integer, ? extends FlexMap> facilities = this.facilityService.get(facilityIds);
for (Entry<Integer, ? extends FlexMap> row : rows.entrySet())
row.getValue().put("facility", facilities.get(row.getValue().getInteger("facilityId")));
return rows;
}
@Override
public SubList<? extends FlexMap> findByName(String name, int page, int perPage) {
try {
FlexPreparedStatement fps = this.sqlSelectByName.buildPreparedStatement();
try {
fps.setVarchar(1, "%" + name + "%");
fps.setVarchar(2, "%" + name + "%");
fps.setSmallintU(3, (page - 1) * perPage);
fps.setSmallintU(4, perPage);
return (SubList<? extends FlexMap>) fps.executeQuery().getAllRows();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
feature = ResultSubSetFeature.class,
sql = "SELECT FP.*, FS.*, F.*, CP.prefix coursePrefix, C.* "
+ "FROM ~g~.Course C "
+ " LEFT JOIN ~g~.CoursePrefix CP ON (C.prefixID=CP.prefixID) "
+ " INNER JOIN ~p~.Facility F ON (C.facilityID=F.facilityID) "
+ " LEFT JOIN ~p~.FacilityPrefix FP ON (F.prefixID=FP.prefixID) "
+ " LEFT JOIN ~p~.FacilitySuffix FS ON (F.suffixID=FS.suffixID) "
+ "WHERE course LIKE ? OR facility LIKE ?"
)
private StatementProvider sqlSelectByName;
@Override
public SubList<? extends FlexMap> findByJurisdiction(String country, String state, int page, int perPage) {
try {
FlexPreparedStatement fps = this.sqlSelectByJurisdiction.buildPreparedStatement();
try {
fps.setVarchar(1, country);
fps.setVarchar(2, state);
fps.setSmallintU(3, (page - 1) * perPage);
fps.setSmallintU(4, perPage);
return (SubList<? extends FlexMap>) fps.executeQuery().getAllRows();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
feature = ResultSubSetFeature.class,
sql = "SELECT FP.*, FS.*, F.*, CP.prefix coursePrefix, C.* "
+ "FROM ~g~.Course C "
+ " LEFT JOIN ~g~.CoursePrefix CP ON (C.prefixID=CP.prefixID) "
+ " INNER JOIN ~p~.Facility F ON (C.facilityID=F.facilityID) "
+ " LEFT JOIN ~p~.FacilityPrefix FP ON (F.prefixID=FP.prefixID) "
+ " LEFT JOIN ~p~.FacilitySuffix FS ON (F.suffixID=FS.suffixID) "
+ "WHERE addrcountry=? AND addrstate=? AND C.deadline IS NULL AND F.deadline IS NULL "
)
private StatementProvider sqlSelectByJurisdiction;
@Override
public SubList<? extends FlexMap> findByLocation(double latitude, double longitude, int radiusInMiles, int page,
int perPage) {
double degrees = PostStatsSQL.miles2degrees(radiusInMiles);
try {
FlexPreparedStatement fps = this.sqlSelectByGeolocation.buildPreparedStatement();
try {
fps.setDouble(1, latitude - degrees);
fps.setDouble(2, latitude + degrees);
fps.setDouble(3, longitude - degrees);
fps.setDouble(4, longitude + degrees);
fps.setSmallintU(5, (page - 1) * perPage);
fps.setSmallintU(6, perPage);
return (SubList<? extends FlexMap>) fps.executeQuery().getAllRows();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
feature = ResultSubSetFeature.class,
sql = "SELECT FP.*, FS.*, F.*, CP.prefix coursePrefix, C.* "
+ "FROM ~g~.Course C "
+ " LEFT JOIN ~g~.CoursePrefix CP ON (C.prefixID=CP.prefixID) "
+ " INNER JOIN ~p~.Facility F ON (C.facilityID=F.facilityID) "
+ " LEFT JOIN ~p~.FacilityPrefix FP ON (F.prefixID=FP.prefixID) "
+ " LEFT JOIN ~p~.FacilitySuffix FS ON (F.suffixID=FS.suffixID) "
+ "WHERE F.latitude>=? AND F.latitude<=? AND F.longitude>=? AND F.longitude<=? "
+ " AND C.deadline IS NULL AND F.deadline IS NULL "
)
private StatementProvider sqlSelectByGeolocation;
@Override
protected long getCacheExpirationInSeconds() {
return this.defaultCacheExpirationInSeconds;
}
@Override
protected DataSet fetchOne(Integer courseId) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectCourse.buildPreparedStatement();
try {
fps.setSmallintU(1, courseId);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
sql = "SELECT FP.*, FS.*, F.*, CP.prefix coursePrefix, C.* "
+ "FROM ~g~.Course C "
+ " LEFT JOIN ~g~.CoursePrefix CP ON (C.prefixID=CP.prefixID) "
+ " INNER JOIN ~p~.Facility F ON (C.facilityID=F.facilityID) "
+ " LEFT JOIN ~p~.FacilityPrefix FP ON (F.prefixID=FP.prefixID) "
+ " LEFT JOIN ~p~.FacilitySuffix FS ON (F.suffixID=FS.suffixID) "
+ "WHERE courseID=? "
)
private StatementProvider sqlSelectCourse;
@Override
protected Map<Integer, DataSet> fetchBulk(Collection<Integer> courseIds) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectCourses.buildPreparedStatement(courseIds);
try {
return fps.executeQuery().getAllRows("courseID", Integer.class);
} finally {
fps.close();
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_POSTSTATS)
@Statement(
sql = "SELECT FP.*, FS.*, F.*, CP.prefix coursePrefix, C.* "
+ "FROM ~g~.Course C "
+ " LEFT JOIN ~g~.CoursePrefix CP ON (C.prefixID=CP.prefixID) "
+ " INNER JOIN ~p~.Facility F ON (C.facilityID=F.facilityID) "
+ " LEFT JOIN ~p~.FacilityPrefix FP ON (F.prefixID=FP.prefixID) "
+ " LEFT JOIN ~p~.FacilitySuffix FS ON (F.suffixID=FS.suffixID) "
+ "WHERE courseID IN (??) "
)
private StatementProvider sqlSelectCourses;
}

View File

@@ -0,0 +1,140 @@
package com.poststats.golf.service.db;
import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.util.FlexMap;
import com.poststats.golf.api.Constants;
import com.poststats.golf.service.CourseService;
import com.poststats.golf.service.EventRoundService;
import com.poststats.provider.Statement;
import com.poststats.provider.StatementProvider;
import com.poststats.service.ServiceException;
import com.poststats.service.db.CacheableServiceDAO;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Map;
@ApplicationScoped
public class EventRoundServiceDAO extends CacheableServiceDAO<Long> implements EventRoundService {
private final long defaultCacheExpirationInSeconds = 3600;
@Inject
private CourseService courseService;
@Override
public FlexMap get(long eroundId) {
return this.get(Long.valueOf(eroundId));
}
@Override
public FlexMap get(Long eventId) {
FlexMap row = super.get(Long.valueOf(eventId));
if (row == null)
return null;
row.put("course", this.courseService.get(row.getInteger("courseID")));
return row;
}
@Override
public FlexMap get(long eventId, short number) {
try {
FlexPreparedStatement fps = this.sqlSelectByNumber.buildPreparedStatement();
try {
fps.setIntegerU(1, eventId);
fps.setTinyintU(2, number);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
sql = "SELECT TF.*, ER.* "
+ "FROM ~g~.EventRound ER "
+ " LEFT JOIN ~g~.TeeFormat TF ON (ER.teeFormatID=TF.teeFormatID) "
+ "WHERE ER.eventID=? AND ER.round=? "
)
private StatementProvider sqlSelectByNumber;
@Override
public Map<Long, DataSet> getByEventId(long eventId) {
try {
FlexPreparedStatement fps = this.sqlSelectByEventId.buildPreparedStatement();
try {
fps.setIntegerU(1, eventId);
return fps.executeQuery().getAllRows("eroundID", Long.class);
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
sql = "SELECT TF.*, ER.* "
+ "FROM ~g~.EventRound ER "
+ " LEFT JOIN ~g~.TeeFormat TF ON (ER.teeFormatID=TF.teeFormatID) "
+ "WHERE ER.eventID=? "
)
private StatementProvider sqlSelectByEventId;
@Override
protected long getCacheExpirationInSeconds() {
return this.defaultCacheExpirationInSeconds;
}
@Override
protected DataSet fetchOne(Long eroundId) throws SQLException {
FlexPreparedStatement fps = this.sqlSelect.buildPreparedStatement();
try {
fps.setIntegerU(1, eroundId);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
sql = "SELECT TF.*, ER.* "
+ "FROM ~g~.EventRound ER "
+ " LEFT JOIN ~g~.TeeFormat TF ON (ER.teeFormatID=TF.teeFormatID) "
+ "WHERE ER.eroundID=? "
)
private StatementProvider sqlSelect;
@Override
protected Map<Long, DataSet> fetchBulk(Collection<Long> eroundIds) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectManys.buildPreparedStatement(eroundIds);
try {
return fps.executeQuery().getAllRows("eroundID", Long.class);
} finally {
fps.close();
}
}
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(
sql = "SELECT TF.*, ER.* "
+ "FROM ~g~.EventRound ER "
+ " LEFT JOIN ~g~.TeeFormat TF ON (ER.teeFormatID=TF.teeFormatID) "
+ "WHERE ER.eroundID IN (??) "
)
private StatementProvider sqlSelectManys;
}

View File

@@ -5,6 +5,7 @@ import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.util.FlexMap;
import com.poststats.golf.api.Constants;
import com.poststats.golf.service.EventService;
import com.poststats.golf.service.SeriesService;
import com.poststats.provider.Statement;
import com.poststats.provider.StatementProvider;
import com.poststats.service.ServiceException;
@@ -16,6 +17,7 @@ import java.sql.SQLException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@ApplicationScoped
@@ -23,11 +25,39 @@ public class EventServiceDAO extends CacheableServiceDAO<Long> implements EventS
private final long defaultCacheExpirationInSeconds = 3600;
@Inject
private SeriesService seriesService;
@Override
public FlexMap get(long eventId) {
return this.get(Long.valueOf(eventId));
}
@Override
public FlexMap get(Long eventId) {
FlexMap row = super.get(Long.valueOf(eventId));
if (row == null)
return null;
row.put("series", this.seriesService.get(row.getInteger("seriesID")));
return row;
}
@Override
public Map<Long, ? extends FlexMap> get(Collection<Long> eventIds) {
Map<Long, ? extends FlexMap> rows = super.get(eventIds);
// bulk fetch series from cache/db
Set<Integer> seriesIds = new HashSet<>();
for (Entry<Long, ? extends FlexMap> row : rows.entrySet())
seriesIds.add(row.getValue().getInteger("seriesId"));
Map<Integer, ? extends FlexMap> serieses = this.seriesService.get(seriesIds);
for (Entry<Long, ? extends FlexMap> row : rows.entrySet())
row.getValue().put("series", serieses.get(row.getValue().getInteger("seriesID")));
return rows;
}
@Override
public Set<Long> getIdsBySeriesId(int seriesId) {
try {
@@ -65,7 +95,7 @@ public class EventServiceDAO extends CacheableServiceDAO<Long> implements EventS
@Inject
@Named(Constants.STATEMENT_PROVIDER_GOLF)
@Statement(sql = "SELECT * FROM ~g~.Event WHERE seriesID=? ORDER BY E.liveline DESC ")
@Statement(sql = "SELECT * FROM ~g~.Event WHERE seriesID=? ORDER BY liveline DESC ")
private StatementProvider sqlSelectBySeriesId;
@Override

View File

@@ -66,6 +66,11 @@ public class PersonServiceDAO extends CacheableServiceDAO<Long> implements Perso
@Override
public FlexMap get(long personId) {
return this.get(Long.valueOf(personId));
}
@Override
public FlexMap get(Long personId) {
FlexMap row = this.get(Long.valueOf(personId));
if (row == null)
return null;