split api/impl; v2.x

This commit is contained in:
2023-10-04 16:55:58 -04:00
parent 360165c64e
commit dfca5f639d
67 changed files with 37 additions and 7073 deletions

16
pom.xml
View File

@@ -2,8 +2,8 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.poststats.golf</groupId>
<artifactId>golf-api</artifactId>
<version>1.0-SNAPSHOT</version>
<artifactId>golf-rs-api</artifactId>
<version>2.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
@@ -40,10 +40,20 @@
<version>5.9.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.20.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.2</version>
</plugin>
<plugin>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-maven-plugin-jakarta</artifactId>

View File

@@ -1,73 +0,0 @@
package com.poststats.golf.api.impl;
import org.slf4j.Logger;
import com.brianlong.util.FlexMap;
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.service.FacilityService;
import com.poststats.transformer.impl.DaoConverter;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Response.Status;
/**
* @author brian.long@poststats.com
*/
@RequestScoped
public class CourseApi implements com.poststats.golf.api.CourseApi {
@Inject
private Logger logger;
@Inject
private CourseService courseService;
@Inject
private CourseNineService courseNineService;
@Inject
private FacilityService facilityService;
@Inject
private DaoConverter converter;
@Override
public Course get(int courseId) {
FlexMap row = this.courseService.get(courseId);
if (row == null)
throw new WebApplicationException("Course not found", Status.NOT_FOUND);
this.logger.trace("found: {}", courseId);
this.facilityService.inject("facilityID", row, "facility");
return this.converter.convertValue(row, Course.class);
}
@Override
public CourseNine getNine(int courseId, String name) {
FlexMap row = this.courseNineService.getNine(courseId, name);
if (row == null)
throw new WebApplicationException("Course nine not found", Status.NOT_FOUND);
this.logger.trace("found: {}", courseId);
this.courseService.injectDeep("courseID", row, "course");
return this.converter.convertValue(row, CourseNine.class);
}
@Override
public CourseNine getNine(int courseId, long courseNineId) {
FlexMap row = this.courseNineService.getNine(courseNineId);
if (row == null)
throw new WebApplicationException("Course nine not found", Status.NOT_FOUND);
if (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

@@ -1,80 +0,0 @@
package com.poststats.golf.api.impl;
import java.util.List;
import org.slf4j.Logger;
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 jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Response.Status;
/**
* @author brian.long@poststats.com
*/
@RequestScoped
public class CoursesApi implements com.poststats.golf.api.CoursesApi {
@Inject
private Logger logger;
@Inject
private CourseService courseService;
@Inject
private DaoConverter converter;
@PostConstruct
public void init() {
this.logger.debug("CoursesApi init");
}
@Override
public PagedCollection<List<Course>> searchByName(String name, 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());
}
@Override
public PagedCollection<List<Course>> searchByJurisdiction(String country, String state, 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);
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());
}
@Override
public PagedCollection<List<Course>> searchByJurisdiction(double latitude, double longitude, Integer radiusInMiles,
Pagination paging) {
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);
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());
}
}

View File

@@ -1,122 +0,0 @@
package com.poststats.golf.api.impl;
import java.io.IOException;
import java.math.BigInteger;
import java.sql.SQLException;
import java.util.Collections;
import java.util.Map;
import org.slf4j.Logger;
import com.brianlong.cache.CacheRetrievalException;
import com.brianlong.util.FlexMap;
import com.poststats.golf.api.model.Event;
import com.poststats.golf.job.EventAgendaJob;
import com.poststats.golf.service.EventDocumentService;
import com.poststats.golf.service.EventPersonService;
import com.poststats.golf.service.EventService;
import com.poststats.golf.service.SeriesService;
import com.poststats.transformer.impl.DaoConverter;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.mail.MessagingException;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Response.Status;
/**
* @author brian.long@poststats.com
*/
@RequestScoped
public class EventApi implements com.poststats.golf.api.EventApi {
@Inject
private Logger logger;
@Inject
private EventService eventService;
@Inject
private EventDocumentService eventDocumentService;
@Inject
private EventPersonService eventPersonService;
@Inject
private EventAgendaJob eventAgendaJob;
@Inject
private SeriesService seriesService;
@Inject
private DaoConverter converter;
@PostConstruct
public void init() {
this.logger.debug("EventApi init");
}
@Override
public Event get(long eventId) {
FlexMap row = this.eventService.get(eventId);
if (row == null)
throw new WebApplicationException("Event not found", Status.NOT_FOUND);
return this.converter.convertValue(row, Event.class);
}
@Override
public Event getDetail(long eventId) {
FlexMap row = this.eventService.get(eventId);
if (row == null)
throw new WebApplicationException("Event not found", Status.NOT_FOUND);
this.seriesService.inject("seriesID", row, "series");
return this.converter.convertValue(row, Event.class);
}
@Override
public void sendDocument(long eventId, long documentId) {
FlexMap document = this.eventDocumentService.get(eventId, documentId);
if (document == null)
throw new WebApplicationException("Document not found", Status.NOT_FOUND);
switch (document.getString("type")) {
case "agenda":
try {
this.eventAgendaJob.send(document);
} catch (CacheRetrievalException | SQLException | MessagingException | IOException e) {
throw new WebApplicationException(e);
}
break;
default:
throw new WebApplicationException("Document is not an agenda", Status.UNSUPPORTED_MEDIA_TYPE);
}
}
@Override
public void sendTestDocument(long eventId, long documentId, long personId) {
FlexMap document = this.eventDocumentService.get(eventId, documentId);
if (document == null)
throw new WebApplicationException("Document not found", Status.NOT_FOUND);
FlexMap eperson = this.eventPersonService.get(eventId, personId);
if (eperson == null)
throw new WebApplicationException("Person not found", Status.NOT_FOUND);
Map<Long, BigInteger> recipientIds = Collections.singletonMap(personId, eperson.getBigInteger("epersonID"));
switch (document.getString("type")) {
case "agenda":
try {
this.eventAgendaJob.send(document, recipientIds);
} catch (CacheRetrievalException | SQLException | MessagingException | IOException e) {
throw new WebApplicationException(e);
}
break;
default:
throw new WebApplicationException("Document is not an agenda", Status.UNSUPPORTED_MEDIA_TYPE);
}
}
}

View File

@@ -1,104 +0,0 @@
package com.poststats.golf.api.impl;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import com.brianlong.sql.DataSet;
import com.poststats.golf.service.EventFinanceService;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.StreamingOutput;
@RequestScoped
public class EventFinanceApi implements com.poststats.golf.api.EventFinanceApi {
@Inject
private EventFinanceService eventFinanceService;
@Override
public List<Map<String, Object>> getBalanceByPersonsAsJson(long eventId, Float minBalance, Float maxBalance) {
Map<Long, DataSet> personsBalances = this.eventFinanceService.getPersonsBalances(eventId, minBalance,
maxBalance);
List<Map<String, Object>> personsBalancesJson = new ArrayList<>(personsBalances.size());
for (DataSet personBalance : personsBalances.values()) {
Map<String, Object> personBalanceJson = new HashMap<>(5);
personBalanceJson.put("personId", personBalance.getLong("personID"));
personBalanceJson.put("expense", personBalance.getFloat("expense"));
personBalanceJson.put("paid", personBalance.getFloat("paid"));
personBalanceJson.put("balance", personBalance.getFloat("balance"));
personsBalancesJson.add(personBalanceJson);
}
return personsBalancesJson;
}
@Override
public StreamingOutput getBalanceByPersonsAsCsv(long eventId) {
Map<Long, DataSet> personsBalances = this.eventFinanceService.getPersonsBalances(eventId);
return new StreamingOutput() {
@Override
public void write(OutputStream output) throws IOException, WebApplicationException {
PrintStream pstream = new PrintStream(output);
CSVPrinter personsBalancesCsvPrinter = new CSVPrinter(pstream, CSVFormat.DEFAULT);
try {
personsBalancesCsvPrinter.printRecord("personID", "lname", "fname", "suffix", "expense", "paid",
"balance");
for (DataSet personBalance : personsBalances.values()) {
personsBalancesCsvPrinter.printRecord(personBalance.getLong("personID"),
personBalance.getString("lname"), personBalance.getString("fname"),
personBalance.getString("suffix"), personBalance.getFloat("expense"),
personBalance.getFloat("paid"), personBalance.getFloat("balance"));
}
personsBalancesCsvPrinter.flush();
} finally {
personsBalancesCsvPrinter.close();
}
}
};
}
@Override
public Map<String, Object> getBalanceByPersonsAsJson(long eventId, long personId) {
DataSet personBalance = this.eventFinanceService.getPersonBalance(eventId, personId);
Map<String, Object> personBalanceJson = new HashMap<>(5);
personBalanceJson.put("personId", personBalance.getLong("personID"));
personBalanceJson.put("expense", personBalance.getFloat("expense"));
personBalanceJson.put("paid", personBalance.getFloat("paid"));
personBalanceJson.put("balance", personBalance.getFloat("balance"));
return personBalanceJson;
}
@Override
public List<Map<String, Object>> getSeriesBalanceByPersonsAsJson(long eventId) {
Map<Long, DataSet> personsBalances = this.eventFinanceService.getSeriesPersonsPreviousBalances(eventId);
List<Map<String, Object>> personsBalancesJson = new ArrayList<>(personsBalances.size());
for (DataSet personBalance : personsBalances.values()) {
Map<String, Object> personBalanceJson = new HashMap<>(5);
personBalanceJson.put("personId", personBalance.getLong("personID"));
personBalanceJson.put("expense", personBalance.getFloat("expense"));
personBalanceJson.put("paid", personBalance.getFloat("paid"));
personBalanceJson.put("balance", personBalance.getFloat("balance"));
personsBalancesJson.add(personBalanceJson);
}
return personsBalancesJson;
}
}

View File

@@ -1,116 +0,0 @@
package com.poststats.golf.api.impl;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import com.brianlong.util.FlexMap;
import com.poststats.golf.api.model.EventPerson;
import com.poststats.golf.service.EventPersonService;
import com.poststats.golf.service.EventService;
import com.poststats.golf.service.PersonService;
import com.poststats.transformer.impl.DaoConverter;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.StreamingOutput;
@RequestScoped
public class EventPersonApi implements com.poststats.golf.api.EventPersonApi {
@Inject
private EventService eventService;
@Inject
private EventPersonService eventPersonService;
@Inject
private PersonService golferService;
@Inject
private DaoConverter converter;
@Override
public List<? extends EventPerson> get(long eventId) {
return this.converter.convertValue(this.eventPersonService.getPeople(eventId), EventPerson.class);
}
@Override
public List<? extends EventPerson> getDetail(long eventId) {
List<? extends FlexMap> persons = this.eventPersonService.getPeople(eventId);
this.golferService.injectDeep("personID", persons, "golfer");
return this.converter.convertValue(persons, EventPerson.class);
}
@Override
public StreamingOutput getAsCsv(long eventId) {
List<? extends FlexMap> persons = this.eventPersonService.getPeople(eventId);
return this.toCsv(persons);
}
@Override
public List<? extends EventPerson> getParticipants(long eventId) {
List<? extends FlexMap> participants = this.eventPersonService.getParticipants(eventId);
this.golferService.injectDeep("personID", participants, "golfer");
return this.converter.convertValue(participants, EventPerson.class);
}
@Override
public StreamingOutput getParticipantsAsCsv(long eventId) {
List<? extends FlexMap> persons = this.eventPersonService.getParticipants(eventId);
return this.toCsv(persons);
}
@Override
public Set<Long> getSeriesParticipants(long eventId) {
int seriesId = this.eventService.getSeriesId(eventId);
Set<Long> eventIds = this.eventService.getIdsBySeriesId(seriesId);
Set<Long> personIds = new HashSet<>();
for (long otherEventId : eventIds) {
List<? extends FlexMap> tmpPersons = this.eventPersonService.getParticipants(otherEventId);
for (FlexMap person : tmpPersons)
personIds.add(person.getLong(com.poststats.sql.Constants.PERSON_ID));
}
return personIds;
}
private StreamingOutput toCsv(List<? extends FlexMap> persons) {
this.golferService.injectDeep("personID", persons, "golfer");
return new StreamingOutput() {
@Override
public void write(OutputStream output) throws IOException {
PrintStream pstream = new PrintStream(output);
CSVPrinter personsCsvPrinter = new CSVPrinter(pstream, CSVFormat.DEFAULT);
try {
personsCsvPrinter.printRecord("ID", "Prefix", "Last Name", "First Name", "Suffix", "Email Address",
"Mobile Phone", "Address Lines", "City", "State", "Country", "Postal Code");
for (FlexMap eperson : persons) {
FlexMap golfer = (FlexMap) eperson.get("golfer");
FlexMap person = (FlexMap) golfer.get("person");
personsCsvPrinter.printRecord(person.getLong("personID"), person.getString("prefix"),
person.getString("lname"), person.getString("fname"), person.getString("suffix"),
person.getString("email"), person.getString("cellphone"), person.getString("addrlines"),
person.getString("addrcity"), person.getString("addrstate"),
person.getString("addrcountry"), person.getString("addrzip"));
}
personsCsvPrinter.flush();
} finally {
personsCsvPrinter.close();
}
}
};
}
}

View File

@@ -1,291 +0,0 @@
package com.poststats.golf.api.impl;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import com.brianlong.util.FlexMap;
import com.poststats.formatter.PersonFormatter;
import com.poststats.formatter.PersonsFormatter;
import com.poststats.golf.api.model.EventRound;
import com.poststats.golf.api.model.EventRoundPairing;
import com.poststats.golf.api.model.EventRoundPairingOrder;
import com.poststats.golf.formatter.GolfCourseFormatter;
import com.poststats.golf.service.CourseService;
import com.poststats.golf.service.EventRoundPairingService;
import com.poststats.golf.service.EventRoundService;
import com.poststats.golf.service.PersonService;
import com.poststats.transformer.impl.DaoConverter;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Response.Status;
import jakarta.ws.rs.core.StreamingOutput;
/**
* @author brian.long@poststats.com
*/
@RequestScoped
public class EventRoundApi implements com.poststats.golf.api.EventRoundApi {
@Inject
private Logger logger;
@Inject
private PersonService golferService;
@Inject
private EventRoundService roundService;
@Inject
private EventRoundPairingService pairingService;
@Inject
private CourseService courseService;
@Inject
private DaoConverter converter;
@PostConstruct
public void init() {
this.logger.debug("EventRoundApi init");
}
@Override
public List<EventRound> getNext(long eventId) {
List<? extends FlexMap> rows = this.roundService.getUpcoming(eventId);
if (rows == null || rows.isEmpty())
throw new WebApplicationException("No event round was found", Status.NOT_FOUND);
this.courseService.injectDeep("courseID", rows, "course");
return this.converter.convertValue(rows, EventRound.class);
}
@Override
public EventRound getOne(long eventId, long eroundId) {
FlexMap row = this.roundService.get(eroundId);
if (row == null)
throw new WebApplicationException("Event round not found", Status.NOT_FOUND);
if (eventId != row.getLong("eventID")) {
this.logger.warn("The event round {} was requested without the appropriate event ID {}", eroundId, eventId);
throw new WebApplicationException("Event round not found", Status.NOT_FOUND);
}
this.courseService.injectDeep("courseID", row, "course");
return this.converter.convertValue(row, EventRound.class);
}
@Override
public List<EventRound> getAll(long eventId) {
Map<Long, ? extends FlexMap> rows = this.roundService.getByEventId(eventId);
if (rows.isEmpty())
throw new WebApplicationException("No event rounds found", Status.NOT_FOUND);
return this.converter.convertValue(rows.values(), EventRound.class);
}
@Override
public List<EventRoundPairing> getPairings(long eventId, 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);
}
@Override
public StreamingOutput getPairingsAsCsv(long eventId, long eroundId, List<EventRoundPairingOrder> orderBys,
boolean lastNameFirst) {
this.logger.debug("getPairingsAsCsv({}, {})", eroundId, lastNameFirst);
List<? extends FlexMap> rows = this.pairingService.getParticipantsByRoundId(eroundId);
if (rows == null || rows.isEmpty())
throw new WebApplicationException("No pairings found", Status.NOT_FOUND);
this.logger.debug("Found {} pairings in round: {}", rows.size(), eroundId);
this.golferService.injectDeep("personID", rows, "golfer");
PersonFormatter personFormatter = new PersonFormatter();
personFormatter.withNameReversal(lastNameFirst);
GolfCourseFormatter courseFormatter = new GolfCourseFormatter();
for (FlexMap row : rows) {
FlexMap golfer = row.get("golfer", FlexMap.class);
FlexMap person = golfer.get("person", FlexMap.class);
row.put("name", personFormatter.format(person));
FlexMap pairing = row.get("pairing", FlexMap.class);
this.courseService.inject("courseID", pairing, "course");
FlexMap course = pairing.get("course", FlexMap.class);
if (course != null)
pairing.put("courseName", courseFormatter.format(course));
}
this.sort(rows, orderBys);
return this.toCsv(rows, lastNameFirst);
}
@Override
public EventRoundPairing getPairing(long eventId, long eroundId, 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);
}
private void sort(List<? extends FlexMap> persons, List<EventRoundPairingOrder> orderBys) {
final List<EventRoundPairingOrder> orders = new LinkedList<>((orderBys == null || orderBys.isEmpty())
? Arrays.asList(EventRoundPairingOrder.Pairing, EventRoundPairingOrder.FormattedName)
: orderBys);
this.logger.debug("Sorting by: {}", orders);
ListIterator<EventRoundPairingOrder> i = orders.listIterator();
while (i.hasNext()) {
EventRoundPairingOrder order = i.next();
if (order == EventRoundPairingOrder.Pairing) {
i.remove();
i.add(EventRoundPairingOrder.TeeTime);
i.add(EventRoundPairingOrder.Course);
i.add(EventRoundPairingOrder.CourseNine);
i.add(EventRoundPairingOrder.HoleNumber);
}
}
Collections.sort(persons, new Comparator<FlexMap>() {
@Override
public int compare(FlexMap person1, FlexMap person2) {
if (person1 == null && person2 == null) {
return 0;
} else if (person1 == null) {
return -1;
} else if (person2 == null) {
return 1;
} else {
for (EventRoundPairingOrder order : orders) {
int compare = 0;
FlexMap pairing1 = person1.get("pairing", FlexMap.class);
FlexMap pairing2 = person2.get("pairing", FlexMap.class);
switch (order) {
case FormattedName:
compare = person1.getString("name").compareTo(person2.getString("name"));
break;
case LastName:
compare = person1.get("golfer", FlexMap.class).get("person", FlexMap.class)
.getString("lname").compareTo(person2.get("golfer", FlexMap.class)
.get("person", FlexMap.class).getString("lname"));
break;
case FirstName:
compare = person1.get("golfer", FlexMap.class).get("person", FlexMap.class)
.getString("fname").compareTo(person2.get("golfer", FlexMap.class)
.get("person", FlexMap.class).getString("fname"));
break;
case Course:
if (!pairing1.isNotEmpty("course") && !pairing2.isNotEmpty("course")) {
} else if (!pairing1.isNotEmpty("course")) {
return -1;
} else if (!pairing2.isNotEmpty("course")) {
return 1;
} else {
compare = StringUtils.compare(
pairing1.get("course", FlexMap.class).getString("nine"),
pairing2.get("course", FlexMap.class).getString("nine"));
}
break;
case CourseNine:
if (!pairing1.isNotEmpty("nine") && !pairing2.isNotEmpty("nine")) {
} else if (!pairing1.isNotEmpty("nine")) {
return -1;
} else if (!pairing2.isNotEmpty("nine")) {
return 1;
} else {
compare = StringUtils.compare(pairing1.get("nine", FlexMap.class).getString("nine"),
pairing2.get("nine", FlexMap.class).getString("nine"));
}
break;
case HoleNumber:
compare = compareTo(pairing1.getByte("number"), pairing2.getByte("number"));
break;
case TeeTime:
compare = compareTo(pairing1.getTime("teetime"), pairing2.getTime("teetime"));
break;
default:
throw new IllegalArgumentException();
}
if (compare != 0)
return compare;
}
return 0;
}
}
});
}
private StreamingOutput toCsv(List<? extends FlexMap> persons, boolean lastNameFirst) {
return new StreamingOutput() {
@Override
public void write(OutputStream output) throws IOException {
PersonsFormatter<FlexMap> personsFormatter = new PersonsFormatter<>();
personsFormatter.withNameReversal(lastNameFirst);
PrintStream pstream = new PrintStream(output);
CSVPrinter personsCsvPrinter = new CSVPrinter(pstream, CSVFormat.DEFAULT);
try {
personsCsvPrinter.printRecord("Name", "Tee Time", "Course", "Course Nine", "Hole Number");
for (FlexMap eperson : persons) {
FlexMap pairing = eperson.get("pairing", FlexMap.class);
FlexMap nine = pairing.get("nine", FlexMap.class);
personsCsvPrinter.printRecord(eperson.getString("name"), pairing.getTime("teetime"),
pairing.getString("courseName"), nine == null ? null : nine.getString("nine"),
pairing.getByte("number"));
}
personsCsvPrinter.flush();
} finally {
personsCsvPrinter.close();
}
}
};
}
private <T extends Comparable<T>> int compareTo(T c1, T c2) {
if (c1 == null && c2 == null) {
return 0;
} else if (c1 == null) {
return -1;
} else if (c2 == null) {
return 1;
} else {
return c1.compareTo(c2);
}
}
}

View File

@@ -1,49 +0,0 @@
package com.poststats.golf.api.impl;
import org.slf4j.Logger;
import com.brianlong.util.FlexMap;
import com.poststats.golf.api.model.Golfer;
import com.poststats.golf.service.PersonService;
import com.poststats.transformer.impl.DaoConverter;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Response.Status;
/**
* @author brian.long@poststats.com
*/
@RequestScoped
public class GolferApi extends com.poststats.golf.api.GolferApi {
@Inject
private Logger logger;
@Inject
private PersonService personService;
@Inject
private com.poststats.service.PersonService poststatsPersonService;
@Inject
private DaoConverter converter;
@PostConstruct
public void init() {
this.logger.debug("GolferApi init: {}", this.getPersonId());
}
@Override
public Golfer get() {
FlexMap row = this.personService.get(this.getPersonId());
if (row == null)
throw new WebApplicationException("Event not found", Status.NOT_FOUND);
this.poststatsPersonService.inject("personID", row, "person");
return this.converter.convertValue(row, Golfer.class);
}
}

View File

@@ -1,66 +0,0 @@
package com.poststats.golf.api.impl;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import com.brianlong.util.FlexMap;
import com.poststats.golf.api.model.Event;
import com.poststats.golf.api.model.Series;
import com.poststats.golf.service.EventService;
import com.poststats.golf.service.SeriesService;
import com.poststats.transformer.impl.DaoConverter;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Response.Status;
/**
* @author brian.long@poststats.com
*/
@RequestScoped
public class SeriesApi extends com.poststats.golf.api.SeriesApi {
@Inject
private Logger logger;
@Inject
private SeriesService seriesService;
@Inject
private EventService eventService;
@Inject
private DaoConverter converter;
@PostConstruct
public void init() {
this.logger.debug("SeriesApi init: {}", this.getSeriesId());
}
@Override
public Series get() {
FlexMap row = this.seriesService.get(this.getSeriesId());
if (row == null)
throw new WebApplicationException("Series not found", Status.NOT_FOUND);
return this.converter.convertValue(row, Series.class);
}
@Override
public List<Event> getEvents(Boolean reverse) {
Map<Long, ? extends FlexMap> rows = this.eventService.getBySeriesId(this.getSeriesId());
if (rows.isEmpty())
throw new WebApplicationException("Series or events not found", Status.NOT_FOUND);
List<Event> events = this.converter.convertValue(rows.values(), Event.class);
if (Boolean.TRUE.equals(reverse))
Collections.reverse(events);
return events;
}
}

View File

@@ -1,292 +0,0 @@
package com.poststats.golf.job;
import java.io.IOException;
import java.math.BigInteger;
import java.sql.SQLException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.io.IOUtils;
import org.apache.http.NameValuePair;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.slf4j.Logger;
import com.brianlong.cache.CacheRetrievalException;
import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.util.DateTimeFormatter;
import com.brianlong.util.FlexMap;
import com.poststats.golf.formatter.EventFormatter;
import com.poststats.golf.provider.GolfProvider;
import com.poststats.golf.service.EventPersonService;
import com.poststats.golf.service.EventService;
import com.poststats.golf.sql.EventAutolist;
import com.poststats.provider.NonTransactionalProvider;
import com.poststats.provider.Statement;
import com.poststats.provider.StatementProvider;
import com.poststats.service.PersonService;
import com.poststats.service.file.EnvironmentConfiguration;
import com.poststats.util.CompositeTexter;
import com.poststats.util.Contact;
import com.poststats.util.Emailer;
import jakarta.annotation.PostConstruct;
import jakarta.ejb.Schedule;
import jakarta.ejb.Singleton;
import jakarta.inject.Inject;
import jakarta.mail.MessagingException;
@Singleton
public class EventAgendaJob {
private final DateTimeFormatter weekdayFormatter = new DateTimeFormatter("EEEE");
@Inject
private Logger logger;
@Inject
private EnvironmentConfiguration envConfig;
@Inject
private PersonService personService;
@Inject
private EventService eventService;
@Inject
private EventPersonService eventPersonService;
private String baseGolfUrl;
private CompositeTexter texter;
@PostConstruct
public void init() {
this.baseGolfUrl = this.envConfig.getString("golf.url")
+ "/c";
this.texter = new CompositeTexter(this.envConfig);
}
@Schedule(hour = "21", timezone = "EDT")
protected void nightly() throws CacheRetrievalException, SQLException, MessagingException, IOException {
this.logger.trace("run()");
// if executed after 6p, then agendas should be for following day
LocalDateTime now = LocalDateTime.now(); // (2016, 6, 7, 22, 0);
LocalDate date = now.plusHours(6).toLocalDate();
this.logger.info("Sending agendas for {}", date);
List<DataSet> agendas = this.getAgendas(date);
if (agendas.isEmpty()) {
this.logger.debug("No agenda items to send: {}", date);
return;
}
for (DataSet agenda : agendas)
this.send(agenda);
}
private List<DataSet> getAgendas(LocalDate date) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectAgendas.buildPreparedStatement();
try {
fps.setDate(1, date);
return fps.executeQuery().getAllRows();
} finally {
fps.close();
}
}
public void send(FlexMap agenda) throws CacheRetrievalException, SQLException, MessagingException, IOException {
Long eventID = agenda.getLong("eventID");
Map<Long, BigInteger> recipientIDs = EventAutolist.getInstance().getPersonIDMap("signed", eventID.longValue());
this.send(agenda, recipientIDs);
}
public void send(FlexMap agenda, Map<Long, BigInteger> recipientIDs)
throws CacheRetrievalException, SQLException, MessagingException, IOException {
boolean doEmailLink = Boolean.TRUE.equals(agenda.getBoolean("sendEmailWithLink"));
boolean doEmail = Boolean.TRUE.equals(agenda.getBoolean("sendEmailWithContent"));
boolean doTextLink = Boolean.TRUE.equals(agenda.getBoolean("sendTextWithLink"));
boolean doText = Boolean.TRUE.equals(agenda.getBoolean("sendTextWithContent"));
if (!doTextLink && !doText && !doEmailLink && !doEmail)
return;
Long eventId = agenda.getLong("eventID");
Long documentId = agenda.getLong("documentID");
LocalDate day = agenda.getDate("day");
this.logger.debug("Sending agenda with document ID: {}", documentId);
FlexMap event = this.eventService.get(eventId);
String subject = new EventFormatter().format(event)
+ " Agenda for "
+ this.weekdayFormatter.format(day);
Set<Long> textedPersonIDs = Collections.emptySet();
if (doTextLink) {
textedPersonIDs = this.textLink(eventId, documentId, recipientIDs, subject);
} else if (doText) {
textedPersonIDs = this.text(eventId, documentId, recipientIDs, subject);
}
if (doEmailLink) {
this.emailLink(eventId, documentId, recipientIDs, textedPersonIDs, subject);
} else if (doEmail) {
this.email(eventId, documentId, recipientIDs, textedPersonIDs, subject);
}
}
private Set<Long> textLink(long eventID, long documentID, Map<Long, BigInteger> recipientIDs, String subject)
throws SQLException, MessagingException {
this.logger.debug("Sending agenda links by text with document ID: {}", documentID);
List<Contact> recipients = this.personService.getContacts(recipientIDs.keySet(), false, true);
String baseUrl = baseGolfUrl
+ "?n=documentAgenda&eventID="
+ eventID
+ "&documentID="
+ documentID
+ "&epersonID=";
Set<Long> textedPersonIDs = new HashSet<Long>(recipientIDs.size());
for (Contact recipient : recipients) {
long personId = recipient.getPersonId();
this.logger.debug("Sending agenda to: {}", personId);
String message = baseUrl + recipientIDs.get(personId);
this.texter.send(Arrays.asList(recipient), subject, message);
textedPersonIDs.add(personId);
}
return textedPersonIDs;
}
private Set<Long> text(long eventID, long documentID, Map<Long, BigInteger> recipientIDs, String subject)
throws SQLException, MessagingException, IOException {
this.logger.debug("Sending agenda links by text with document ID: {}", documentID);
List<Contact> recipients = this.personService.getContacts(recipientIDs.keySet(), false, true);
NameValuePair[] params = new NameValuePair[] {
new BasicNameValuePair("n", "documentAgendaMarkdown"),
new BasicNameValuePair("eventID", String.valueOf(eventID)),
new BasicNameValuePair("documentID", String.valueOf(documentID))
};
Set<Long> textedPersonIDs = new HashSet<Long>(recipientIDs.size());
for (Contact recipient : recipients) {
long personId = recipient.getPersonId();
this.logger.debug("Sending agenda to: {}", personId);
HttpUriRequest request = RequestBuilder.get(this.baseGolfUrl).addParameters(params)
.addParameter("epersonID", recipientIDs.get(personId).toString()).build();
CloseableHttpClient hclient = HttpClientBuilder.create().build();
CloseableHttpResponse response = hclient.execute(request);
try {
if (response.getStatusLine().getStatusCode() / 100 == 2) {
String markdown = IOUtils.toString(response.getEntity().getContent(), "utf-8");
this.texter.send(Arrays.asList(recipient), subject, markdown.trim());
textedPersonIDs.add(personId);
} else {
this.logger.warn("The URL could not be loaded properly: {}", request.getURI());
}
} finally {
response.close();
}
}
return textedPersonIDs;
}
private void emailLink(long eventID, long documentID, Map<Long, BigInteger> recipientIDs,
Set<Long> excludePersonIDs, String subject) throws SQLException, MessagingException {
this.logger.debug("Sending agenda links by email with document ID: {}", documentID);
List<Contact> recipients = this.personService.getContacts(recipientIDs.keySet(), true, false);
String baseUrl = this.baseGolfUrl
+ "?n=documentAgenda&eventID="
+ eventID
+ "&documentID="
+ documentID
+ "&epersonID=";
for (Contact recipient : recipients) {
long personId = recipient.getPersonId();
if (excludePersonIDs.contains(personId))
continue;
if (logger.isDebugEnabled())
logger.debug("Sending agenda to: {}", personId);
String message = "<html><body><p><a href=\""
+ baseUrl
+ "\">"
+ baseUrl
+ recipientIDs.get(personId)
+ "</a></p></body></html>";
Emailer.getInstance(this.envConfig).send(Arrays.asList(recipient), subject, message);
}
}
private void email(long eventID, long documentID, Map<Long, BigInteger> recipientIDs, Set<Long> excludePersonIDs,
String subject) throws SQLException, MessagingException, IOException {
this.logger.debug("Sending agenda contents with document ID: {}", documentID);
List<Contact> recipients = this.personService.getContacts(recipientIDs.keySet(), true, false);
NameValuePair[] params = new NameValuePair[] {
new BasicNameValuePair("n", "documentAgendaMinimal"),
new BasicNameValuePair("eventID", String.valueOf(eventID)),
new BasicNameValuePair("documentID", String.valueOf(documentID))
};
for (Contact recipient : recipients) {
long personId = recipient.getPersonId();
this.logger.debug("Sending agenda to: {}", personId);
HttpUriRequest request = RequestBuilder.get(this.baseGolfUrl).addParameters(params)
.addParameter("epersonID", recipientIDs.get(personId).toString()).build();
CloseableHttpClient hclient = HttpClientBuilder.create().build();
CloseableHttpResponse response = hclient.execute(request);
try {
if (response.getStatusLine().getStatusCode() / 100 == 2) {
String html = IOUtils.toString(response.getEntity().getContent(), "utf-8");
Emailer.getInstance(this.envConfig).send(Arrays.asList(recipient), subject, html);
} else {
this.logger.warn("The URL could not be loaded properly: {}", request.getURI());
}
} finally {
response.close();
}
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT ED.* "
+ "FROM ~g~.EventDocument ED "
+ "WHERE ED.day=? "
+ " AND (ED.sendEmailWithLink IS TRUE OR ED.sendEmailWithContent IS TRUE OR ED.sendTextWithLink IS TRUE)"
)
private StatementProvider sqlSelectAgendas;
}

View File

@@ -1,19 +0,0 @@
package com.poststats.golf.provider;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jakarta.inject.Qualifier;
@Qualifier
@Target({
ElementType.TYPE, ElementType.PARAMETER, ElementType.FIELD
})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GolfProvider {
}

View File

@@ -1,41 +0,0 @@
package com.poststats.golf.provider.impl;
import java.sql.Connection;
import com.poststats.golf.provider.GolfProvider;
import com.poststats.provider.ConnectionProvider;
import com.poststats.provider.DataSourceProvider;
import com.poststats.provider.NonTransactionalProvider;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
@ApplicationScoped
@NonTransactionalProvider
@GolfProvider
public class DefaultConnectionProvider implements ConnectionProvider {
@Inject
@GolfProvider
private DataSourceProvider dsp;
private Connection dbcon;
@PostConstruct
public void acquire() {
this.dbcon = this.dsp.get().acquire(true);
}
@PreDestroy
public void release() {
this.dsp.get().release(this.dbcon);
}
@Override
public Connection get() {
return this.dbcon;
}
}

View File

@@ -1,18 +0,0 @@
package com.poststats.golf.provider.impl;
import com.brianlong.sql.DataSource;
import com.poststats.golf.provider.GolfProvider;
import com.poststats.golf.sql.GolfDataSource;
import com.poststats.provider.impl.AbstractDataSourceProvider;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
@GolfProvider
public class DefaultDataSourceProvider extends AbstractDataSourceProvider {
public DataSource createDataSource() {
return GolfDataSource.getInstance();
}
}

View File

@@ -1,32 +0,0 @@
package com.poststats.golf.provider.impl;
import com.poststats.golf.provider.GolfProvider;
import com.poststats.golf.sql.GolfSQL;
import com.poststats.provider.ConnectionProvider;
import com.poststats.provider.NonTransactionalProvider;
import com.poststats.provider.impl.AbstractStatementProvider;
import jakarta.enterprise.context.Dependent;
import jakarta.inject.Inject;
@Dependent
@NonTransactionalProvider
@GolfProvider
public class DefaultStatementProvider extends AbstractStatementProvider {
@Inject
@NonTransactionalProvider
@GolfProvider
private ConnectionProvider cp;
@Override
protected ConnectionProvider getConnectionProvider() {
return this.cp;
}
@Override
protected String transformSchema(String sql) {
return GolfSQL.changeSchema(sql);
}
}

View File

@@ -1,42 +0,0 @@
package com.poststats.golf.provider.impl;
import java.io.Serializable;
import java.sql.Connection;
import com.poststats.golf.provider.GolfProvider;
import com.poststats.provider.ConnectionProvider;
import com.poststats.provider.DataSourceProvider;
import com.poststats.provider.TransactionalProvider;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.inject.Inject;
import jakarta.transaction.TransactionScoped;
@TransactionScoped
@TransactionalProvider
@GolfProvider
public class TxConnectionProvider implements ConnectionProvider, Serializable {
@Inject
@GolfProvider
private DataSourceProvider dsp;
private Connection dbcon;
@PostConstruct
public void acquire() {
this.dbcon = this.dsp.get().acquireTX(true);
}
@PreDestroy
public void release() {
this.dsp.get().releaseTX(this.dbcon);
}
@Override
public Connection get() {
return this.dbcon;
}
}

View File

@@ -1,32 +0,0 @@
package com.poststats.golf.provider.impl;
import com.poststats.golf.provider.GolfProvider;
import com.poststats.golf.sql.GolfSQL;
import com.poststats.provider.ConnectionProvider;
import com.poststats.provider.TransactionalProvider;
import com.poststats.provider.impl.AbstractStatementProvider;
import jakarta.enterprise.context.Dependent;
import jakarta.inject.Inject;
@Dependent
@TransactionalProvider
@GolfProvider
public class TxStatementProvider extends AbstractStatementProvider {
@Inject
@TransactionalProvider
@GolfProvider
private ConnectionProvider cp;
@Override
protected ConnectionProvider getConnectionProvider() {
return this.cp;
}
@Override
protected String transformSchema(String sql) {
return GolfSQL.changeSchema(sql);
}
}

View File

@@ -1,61 +0,0 @@
package com.poststats.golf.security;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class AuthenticatedPerson extends com.poststats.security.AuthenticatedPerson {
private final Set<Long> buddyIds;
private final Set<String> acSids;
private final Map<Long, Set<String>> eventAcSids;
public AuthenticatedPerson(com.poststats.security.AuthenticatedPerson person, Set<Long> buddyIds,
Set<String> acSids, Map<Long, Set<String>> eventAcSids) {
super(person);
this.buddyIds = buddyIds;
this.acSids = acSids;
this.eventAcSids = eventAcSids;
}
public Set<Long> getBuddyIds() {
return Collections.unmodifiableSet(this.buddyIds);
}
public Set<String> getAccessControls() {
return Collections.unmodifiableSet(this.acSids);
}
public Set<String> getEventAccessControls(long eventId) {
Set<String> roles = this.eventAcSids.get(eventId);
return roles == null ? null : Collections.unmodifiableSet(roles);
}
public Set<String> getAllAccessControls() {
Set<String> roles = new HashSet<>();
roles.addAll(this.acSids);
for (Entry<Long, Set<String>> eroles : this.eventAcSids.entrySet())
for (String role : eroles.getValue())
roles.add(eroles.getKey()
+ ":"
+ role);
return roles == null ? null : Collections.unmodifiableSet(roles);
}
@Override
public boolean hasAccessControl(String ac) {
return this.acSids.contains(ac);
}
public boolean hasBuddy(long buddyId) {
return this.buddyIds.contains(buddyId);
}
public boolean hasAccessControl(String ac, long eventId) {
Set<String> sids = this.eventAcSids.get(eventId);
return sids != null && sids.contains(ac);
}
}

View File

@@ -1,50 +0,0 @@
package com.poststats.golf.security;
import java.security.Principal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.core.SecurityContext;
public class AuthenticatedSecurityContext implements SecurityContext {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final SecurityContext securityContext;
private final AuthenticatedPerson authPerson;
public AuthenticatedSecurityContext(SecurityContext securityContext, AuthenticatedPerson authPerson) {
if (securityContext == null)
throw new IllegalArgumentException();
this.securityContext = securityContext;
this.authPerson = authPerson;
}
@Override
public Principal getUserPrincipal() {
return this.authPerson;
}
@Override
public boolean isUserInRole(String role) {
this.logger.trace("isUserInRole({}, {})", this.getUserPrincipal(), role);
if (this.authPerson.hasAccessControl(role)) {
this.logger.debug("user '{}' is in role '{}'", this.authPerson, role);
return true;
}
return this.securityContext.isUserInRole(role);
}
@Override
public String getAuthenticationScheme() {
return this.securityContext.getAuthenticationScheme();
}
@Override
public boolean isSecure() {
return this.securityContext.isSecure();
}
}

View File

@@ -1,62 +0,0 @@
package com.poststats.golf.security;
import java.security.Principal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.poststats.golf.api.Constants;
import jakarta.ws.rs.core.SecurityContext;
public class EventSecurityContext implements SecurityContext {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final SecurityContext securityContext;
private final long eventId;
public EventSecurityContext(SecurityContext securityContext, long eventId) {
if (securityContext == null)
throw new IllegalArgumentException();
this.securityContext = securityContext;
this.eventId = eventId;
}
@Override
public Principal getUserPrincipal() {
return this.securityContext.getUserPrincipal();
}
@Override
public boolean isUserInRole(String role) {
this.logger.trace("isUserInRole({}, {}, {})", this.getUserPrincipal(), role, this.eventId);
if (role.startsWith(Constants.EVENT_ROLE_PREFIX)) {
if (this.getUserPrincipal() instanceof AuthenticatedPerson) {
AuthenticatedPerson authPerson = (AuthenticatedPerson) this.getUserPrincipal();
String eventRole = role.substring(Constants.EVENT_ROLE_PREFIX.length());
this.logger.trace("checking if user '{}' has role '{}' in event {}", this.getUserPrincipal(), eventRole,
this.eventId);
if (authPerson.hasAccessControl(eventRole, this.eventId)) {
this.logger.debug("user '{}' has role '{}' in event {}", this.getUserPrincipal(), eventRole,
this.eventId);
return true;
}
}
}
return this.securityContext.isUserInRole(role);
}
@Override
public String getAuthenticationScheme() {
return this.securityContext.getAuthenticationScheme();
}
@Override
public boolean isSecure() {
return this.securityContext.isSecure();
}
}

View File

@@ -1,58 +0,0 @@
package com.poststats.golf.security;
import java.security.Principal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.poststats.golf.api.Constants;
import jakarta.ws.rs.core.SecurityContext;
public class PersonSecurityContext implements SecurityContext {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final SecurityContext securityContext;
private final long personId;
public PersonSecurityContext(SecurityContext securityContext, long personId) {
if (securityContext == null)
throw new IllegalArgumentException();
this.securityContext = securityContext;
this.personId = personId;
}
@Override
public Principal getUserPrincipal() {
return this.securityContext.getUserPrincipal();
}
@Override
public boolean isUserInRole(String role) {
this.logger.trace("isUserInRole({}, {}, {})", this.getUserPrincipal(), role, this.personId);
if (role.equals(Constants.BUDDY_ROLE)) {
if (this.getUserPrincipal() instanceof AuthenticatedPerson) {
AuthenticatedPerson authPerson = (AuthenticatedPerson) this.getUserPrincipal();
this.logger.trace("checking if user '{}' is buddy of {}", this.getUserPrincipal(), this.personId);
if (authPerson.hasBuddy(this.personId)) {
this.logger.debug("user '{}' is buddy of {}", this.getUserPrincipal(), this.personId);
return true;
}
}
}
return this.securityContext.isUserInRole(role);
}
@Override
public String getAuthenticationScheme() {
return this.securityContext.getAuthenticationScheme();
}
@Override
public boolean isSecure() {
return this.securityContext.isSecure();
}
}

View File

@@ -1,13 +0,0 @@
package com.poststats.golf.service;
import java.util.List;
import com.brianlong.util.FlexMap;
public interface CourseHoleService {
List<? extends FlexMap> getHolesByNineTee(long nineteeId);
List<? extends FlexMap> getHolesByEighteenTee(long eighteenteeId);
}

View File

@@ -1,11 +0,0 @@
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

@@ -1,33 +0,0 @@
package com.poststats.golf.service;
public interface CourseRatingService<R> {
/**
* This method computes a golf course eighteen/tee combination's difficulty
* rating as of today, inclusive.
*
* In some cases, there is no computation, but a simple fetch from an external
* resource. In other cases the ratings will need to be computed. In other
* cases, a rating cannot be determined.
*
* @param etratingId A unique identifier for a golf course eighteen/tee
* combination (gender) rating.
* @return A rating for the course; `null` if one cannot be determined.
*/
R computeEighteenTeeRatingIndex(long etratingId);
/**
* This method computes a golf course nine/tee combination's difficulty rating
* as of today, inclusive.
*
* In some cases, there is no computation, but a simple fetch from an external
* resource. In other cases the ratings will need to be computed. In other
* cases, a rating cannot be determined.
*
* @param ntratingId A unique identifier for a golf course nine/tee combination
* (gender) rating.
* @return A rating for the course; `null` if one cannot be determined.
*/
R computeNineTeeRatingIndex(long ntratingId);
}

View File

@@ -1,55 +0,0 @@
package com.poststats.golf.service;
import java.util.Collection;
import java.util.Map;
import com.brianlong.util.FlexMap;
import com.brianlong.util.SubList;
import com.poststats.service.CacheableService;
public interface CourseService extends CacheableService<Integer> {
/**
* 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

@@ -1,17 +0,0 @@
package com.poststats.golf.service;
import com.brianlong.util.FlexMap;
import com.poststats.service.CacheableService;
public interface EventDocumentService extends CacheableService<Long> {
/**
* This method retrieves meta-data about the specified event document.
*
* @param eventId A unique identifier for the event.
* @param eventId A unique identifier for the document.
* @return A map of meta-data specific to the event document.
*/
FlexMap get(long eventId, long documentId);
}

View File

@@ -1,82 +0,0 @@
package com.poststats.golf.service;
import java.util.Map;
import com.brianlong.sql.DataSet;
/**
* This service provides financial metadata about the event, series, associated
* people, or a combination of those.
*
* The balance metadata includes money `paid`, `expenses`, and `balance`. The
* balance numerically consistent with the perspective of the person, not the
* event organizers. That means it is positive when the person paid more than
* they should owe and negative if they owe money.
*
* @author brian
*/
public interface EventFinanceService {
/**
* Computes and retrieves the person balances for those associated with the
* specified event.
*
* @param eventId The unique identifier of an event.
* @return A map of `personId` to balance metadata.
*/
Map<Long, DataSet> getPersonsBalances(long eventId);
/**
* Computes and retrieves the person balances for those associated with the
* specified event.
*
* @param eventId The unique identifier of an event.
* @param minBalance The minimum balance for returned balances; `0.01` for those
* that have paid more than they owe; `null` for no
* constraint.
* @param maxBalance The maximum balance for returned balances; `-0.01` for
* those that owe; `null` for no constraint.
* @return A map of `personId` to balance metadata.
*/
Map<Long, DataSet> getPersonsBalances(long eventId, Float minBalance, Float maxBalance);
/**
* Computes and retrieves the specified person's balance for the specified
* event.
*
* @param eventId The unique identifier of an event.
* @param personId The unique identifier of a person.
* @return Balance metadata.
*/
DataSet getPersonBalance(long eventId, long personId);
/**
* Computes and retrieves all associated persons' cumulative balance for all
* events in the series before the specified event.
*
* @param eventId The unique identifier of an event.
* @return A map of `personId` to balance metadata.
*/
Map<Long, DataSet> getSeriesPersonsPreviousBalances(long eventId);
/**
* Computes and retrieves the specified person's balances for all events in the
* specified series.
*
* @param seriesId The unique identifier of a series.
* @param personId The unique identifier of a person.
* @return A map of `eventId` to balance metadata.
*/
Map<Long, DataSet> getSeriesPersonBalances(int seriesId, long personId);
/**
* Computes and retrieves the specified person's balances for all events in the
* series before the specified event.
*
* @param eventId The unique identifier of an event.
* @param personId The unique identifier of a person.
* @return A map of `eventId` to balance metadata.
*/
Map<Long, DataSet> getSeriesPersonPreviousBalances(long eventId, long personId);
}

View File

@@ -1,51 +0,0 @@
package com.poststats.golf.service;
import java.math.BigInteger;
import java.util.List;
import java.util.Set;
import com.brianlong.util.FlexMap;
import com.poststats.service.CacheableService;
import com.poststats.util.Contact;
public interface EventPersonService extends CacheableService<BigInteger> {
FlexMap get(BigInteger epersonId);
FlexMap get(long eventId, long personId);
List<? extends FlexMap> getPeople(long eventId);
List<? extends FlexMap> getParticipants(long eventId);
Set<Long> getSeriesEventFellowParticipantIds(int seriesId, long personId);
/**
* This method retrieves contacts appropriate to the specified parameters.
*
* If both `allowEmail` and `allowText` are specified, only `allowText` will be
* used for users with email and text capability.
*
* @param eventId The unique identifier of an event.
* @param listId The unique identifier of a contact list.
* @param allowEmail `true` to allow email contact with each person.
* @param allowText `true` to allow text contact with each person.
* @return A map of meta-data specific to the person.
*/
List<Contact> getContactsByListId(long eventId, long listId, boolean allowEmail, boolean allowText);
/**
* This method retrieves contacts appropriate to the specified parameters.
*
* If both `allowEmail` and `allowText` are specified, only `allowText` will be
* used for users with email and text capability.
*
* @param eventId The unique identifier of an event.
* @param autolist The unique identifier of a contact list.
* @param allowEmail `true` to allow email contact with each person.
* @param allowText `true` to allow text contact with each person.
* @return A map of meta-data specific to the person.
*/
List<Contact> getContactsByAutolist(long eventId, String autolist, boolean allowEmail, boolean allowText);
}

View File

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

View File

@@ -1,39 +0,0 @@
package com.poststats.golf.service;
import java.util.List;
import java.util.Map;
import com.brianlong.util.FlexMap;
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 next event round in the specified
* event.
*
* It will only return more than one event round if the next date has more than
* one round. It will return today's round(s) until 6p EST.
*
* @param eventId A unique identifier for the event.
* @return A list of meta-data about each event round.
*/
List<? extends FlexMap> getUpcoming(long eventId);
/**
* 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

@@ -1,34 +0,0 @@
package com.poststats.golf.service;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import com.brianlong.util.FlexMap;
import com.poststats.service.CacheableService;
public interface EventService extends CacheableService<Long> {
/**
* This method retrieves meta-data about the specified event.
*
* @param eventId A unique identifier for the event.
* @return A map of meta-data specific to the event.
*/
FlexMap get(long eventId);
/**
* This method retrieves meta-data about the specified event.
*
* @param eventIds Unique identifiers for the event.
* @return A map of unique identifiers to meta-data specific to each event.
*/
Map<Long, ? extends FlexMap> get(Collection<Long> eventIds);
Set<Long> getIdsBySeriesId(int seriesId);
Map<Long, ? extends FlexMap> getBySeriesId(int seriesId);
Integer getSeriesId(long eventId);
}

View File

@@ -1,35 +0,0 @@
package com.poststats.golf.service;
import java.time.LocalDate;
public interface HandicapIndexService<I> {
/**
* This method computes a golfer's handicap index as of today, inclusive.
*
* This will consider the use of all individually played non-event and event
* rounds. Each implementation may exclude more rounds, like unsigned or unrated
* courses.
*
* @param personId A unique identifier for the golfer.
* @return A handicap for the golfer.
*/
default I computeGolferIndex(long personId) {
return this.computeGolferIndex(personId, LocalDate.now().plusDays(1L));
}
/**
* This method computes a golfer's handicap index as of the specified date,
* inclusive.
*
* This will consider the use of all individually played non-event and event
* rounds. Each implementation may exclude more rounds, like unsigned or unrated
* courses.
*
* @param personId A unique identifier for the golfer.
* @param beforeDay A date to exclude all rounds on or after.
* @return A handicap for the golfer.
*/
I computeGolferIndex(long personId, LocalDate beforeDay);
}

View File

@@ -1,37 +0,0 @@
package com.poststats.golf.service;
import java.math.BigInteger;
public interface HandicapScoreService<S> {
/**
* This method computes a non-event round's score.
*
* The score depends on the handicap system. For instance, the simple
* stroke (non-handicap) system, it will be strokes-minus-par. For the
* world handicap system (WHS), it will be your adjusted strokes-to-par,
* applying the course rating, your handicap, hole handicaps, and course
* condition adjustment. For point handicap systems, it will be your
* points, including the course adjustment.
*
* @param roundId A unique identifier for the non-event round.
* @return A score for the round.
*/
S computeRoundScore(BigInteger roundId);
/**
* This method computes a person's event round score.
*
* The score depends on the handicap system. For instance, the simple
* stroke (non-handicap) system, it will be strokes-minus-par. For the
* world handicap system (WHS), it will be your adjusted strokes-to-par,
* applying the course rating, your handicap, hole handicaps, and course
* condition adjustment. For point handicap systems, it will be your
* points, including the course adjustment.
*
* @param proundId A unique identifier for the event/person round.
* @return A score for the round.
*/
S computeEventRoundScore(BigInteger proundId);
}

View File

@@ -1,92 +0,0 @@
package com.poststats.golf.service;
import java.math.BigInteger;
import java.time.LocalDate;
import java.util.List;
import com.brianlong.util.FlexMap;
public interface PersonRoundService {
public enum Selection { ScoreToPar, StrokeHandicapIndex }
public enum Filter { AttestedOnly, CourseRatedOnly }
/**
* This method retrieves meta-data about the specified non-event round.
*
* @param roundId A unique identifier for the non-event round.
* @param selection Include `ScoreToPar` or `StrokeHandicapIndex` values in the
* results.
* @param filter An array of filters.
* @return A non-event round and its meta-data.
*/
FlexMap fetchNonEvent(BigInteger roundId, Selection selection, Filter... filters);
/**
* This method retrieves meta-data about the specified golfer's event round.
*
* @param proundId A unique identifier for a golfer's event round.
* @param selection Include `ScoreToPar` or `StrokeHandicapIndex` values in the
* results.
* @param filter An array of filters.
* @return A golfer's event round and its meta-data.
*/
FlexMap fetchEvent(BigInteger proundId, Selection selection, Filter... filters);
/**
* This method retrieves scores for all holes played in the specified non-event round.
*
* @param roundId A unique identifier for the non-event round.
* @param selection Include `ScoreToPar` or `StrokeHandicapIndex` values in the
* results.
* @return A list of holes and associated scores.
*/
List<? extends FlexMap> fetchNonEventHoles(BigInteger roundId, Selection selection);
/**
* This method retrieves scores for all holes played in the specified golfer's event round.
*
* @param proundId A unique identifier for the golfer's event round.
* @param selection Include `ScoreToPar` or `StrokeHandicapIndex` values in the
* results.
* @return A list of holes and associated scores.
*/
List<? extends FlexMap> fetchEventHoles(BigInteger proundId, Selection selection);
/**
* This method retrieves recent round meta-data about the specified golfer.
*
* The rounds are retrieved in reverse chronological order. Both Non-event and
* event rounds are included.
*
* @param personId A unique identifier for the golfer.
* @param roundCount A maximum number of rounds to return.
* @param selection Include `ScoreToPar` or `StrokeHandicapIndex` values in the
* results.
* @param filter An array of filters.
* @return A list of recent rounds played by the golfer.
*/
default List<? extends FlexMap> findRecent(long personId, short roundCount, Selection selection,
Filter... filters) {
return this.findBefore(personId, LocalDate.now().plusDays(1L), roundCount, selection, filters);
}
/**
* This method retrieves recent round meta-data about the specified golfer.
*
* The rounds are retrieved in reverse chronological order. Both Non-event and
* event rounds are included.
*
* @param personId A unique identifier for the golfer.
* @param beforeDay A date to start excluding rounds on or after.
* @param roundCount A maximum number of rounds to return.
* @param selection Include `ScoreToPar` or `StrokeHandicapIndex` values in the
* results.
* @param filter An array of filters.
* @return A list of recent rounds played by the golfer.
*/
List<? extends FlexMap> findBefore(long personId, LocalDate beforeDay, short roundCount, Selection selection,
Filter... filters);
}

View File

@@ -1,53 +0,0 @@
package com.poststats.golf.service;
import java.util.Collection;
import java.util.Map;
import com.brianlong.util.FlexMap;
import com.poststats.golf.security.AuthenticatedPerson;
import com.poststats.service.CacheableService;
public interface PersonService extends CacheableService<Long> {
/**
* This method builds a `UserPrincipal` object about the golfer.
*
* Unlike the non-golf `PersonService`, this will include golfer and event
* roles.
*
* @param person A non-golf `UserPrincipal` object.
* @return A golfer `UserPrincipal` object.
*/
AuthenticatedPerson getUserPrincipal(com.poststats.security.AuthenticatedPerson person);
/**
* This method retrieves meta-data about the specified golfer.
*
* If you want meta-data information about only the person, use the non-golf
* `PersonService`.
*
* This retrieves everything from the non-golf `PersonService` and supplements
* it with simple golfer meta-data, like stroke handicaps. It does not include
* related data like point handicap or round counts.
*
* @param personId A unique identifier for the golfer.
* @return A map of meta-data specific to the golfer.
*/
FlexMap get(long personId);
/**
* This method retrieves meta-data about the specified golfers.
*
* If you want meta-data information about only the person, use the non-golf
* `PersonService`.
*
* This retrieves everything from the non-golf `PersonService` and supplements
* it with simple golfer meta-data, like stroke handicaps. It does not include
* related data like point handicap or round counts.
*
* @param personIds Unique identifiers for the golfers.
* @return A map of unique identifiers to meta-data specific to each golfer.
*/
Map<Long, ? extends FlexMap> get(Collection<Long> personIds);
}

View File

@@ -1,13 +0,0 @@
package com.poststats.golf.service;
import java.util.List;
import com.brianlong.sql.DataSet;
public interface SeriesPersonService {
List<DataSet> getSeriesPeople(int seriesId);
List<DataSet> getSeriesParticipants(int seriesId);
}

View File

@@ -1,35 +0,0 @@
package com.poststats.golf.service;
import java.util.Collection;
import java.util.Map;
import com.brianlong.util.FlexMap;
import com.poststats.service.CacheableService;
public interface SeriesService extends CacheableService<Integer> {
/**
* This method retrieves meta-data about the specified series.
*
* @param seriesId A unique identifier for the series.
* @return A map of meta-data specific to the series.
*/
FlexMap get(int seriesId);
/**
* This method retrieves meta-data about the specified series.
*
* @param seriesIds Unique identifiers for the series.
* @return A map of unique identifiers to meta-data specific to each series.
*/
Map<Integer, ? extends FlexMap> get(Collection<Integer> seriesIds);
/**
* This method retrieves series meta-data about the specified event.
*
* @param eventId A unique identifier for the event.
* @return A map of meta-data specific to the series.
*/
FlexMap getByEventId(long eventId);
}

View File

@@ -1,45 +0,0 @@
package com.poststats.golf.service.compute;
import java.time.LocalDate;
import java.util.List;
import com.brianlong.util.FlexMap;
import com.poststats.golf.service.CourseRatingService;
import com.poststats.golf.service.HandicapIndexService;
import com.poststats.golf.service.HandicapScoreService;
import com.poststats.golf.service.PersonRoundService;
import com.poststats.golf.service.PersonRoundService.Filter;
import com.poststats.golf.service.PersonRoundService.Selection;
import jakarta.inject.Inject;
public abstract class AbstractHandicappingService<I, R, S> implements HandicapIndexService<I>, CourseRatingService<R>, HandicapScoreService<S> {
@Inject
protected PersonRoundService personRoundService;
protected short getMinimumRounds() {
return 1;
}
protected abstract short getMaximumRounds();
protected abstract Selection getRoundSelection();
protected Filter[] getRoundFilter() {
return new Filter[0];
}
@Override
public I computeGolferIndex(long personId, LocalDate beforeDay) {
List<? extends FlexMap> rounds = this.personRoundService.findBefore(personId, beforeDay,
this.getMaximumRounds(), this.getRoundSelection(), this.getRoundFilter());
if (rounds.size() < this.getMinimumRounds())
throw new IllegalStateException("The person does not have enough rounds to compute a handicap index");
return this.compute(rounds);
}
public abstract I compute(List<? extends FlexMap> rounds);
}

View File

@@ -1,242 +0,0 @@
package com.poststats.golf.service.compute;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.brianlong.util.FlexMap;
import com.brianlong.util.MapUtil;
import com.poststats.golf.service.PersonRoundService.Selection;
import com.poststats.golf.service.model.PointHandicapIndex;
public abstract class AbstractPointHandicappingService extends AbstractHandicappingService<PointHandicapIndex, Byte, Short> {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
protected short getMaximumRounds() {
return 15;
}
protected short getBestRoundsToToss(short roundCount) {
return 0;
}
protected abstract short getWorstRoundsToToss(short roundCount);
protected abstract short getPointTarget();
protected abstract float getAccelerant();
protected float getHomeCourseZeroRate() {
return 0.25f;
}
protected float getHomeCourseMultiplier() {
return 4f;
}
@Override
protected Selection getRoundSelection() {
return Selection.ScoreToPar;
}
@Override
public PointHandicapIndex compute(List<? extends FlexMap> rounds) {
short target = this.getPointTarget();
short bestRoundsToToss = this.getBestRoundsToToss((short) rounds.size());
short worstRoundsToToss = this.getWorstRoundsToToss((short) rounds.size());
short roundsToCount = (short) (rounds.size() - bestRoundsToToss - worstRoundsToToss);
float forgiveness = 0.5f;
Map<Integer, Float> computedPoints = new HashMap<>();
PointHandicapIndex bestLowPhi = null;
PointHandicapIndex bestHighPhi = null;
Pair<Integer, Float> homeCourseAdj = this.computeHomeCourseAdjustment(rounds);
this.logger.debug("Computed home course adjustment: {}", homeCourseAdj);
short[] scoreToParCounts = this.computeScoreToParCounts(rounds);
byte[] mostCommonScoreToPar = this.determineMostCommonStoresToPar(scoreToParCounts, (byte) 2);
this.logger.debug("Determined most common score-to-par: {}", mostCommonScoreToPar);
PointHandicapIndex phi = this.generateIndexBy2MostCommon(mostCommonScoreToPar[0],
scoreToParCounts[(mostCommonScoreToPar[0] + 10) % 10], mostCommonScoreToPar[1],
scoreToParCounts[(mostCommonScoreToPar[1] + 10) % 10], target, this.getAccelerant());
this.logger.debug("Base point handicap index: {}", phi);
byte lastHighScoreToPar = 6;
byte lastLowScoreToPar = -4;
while (true) {
List<Integer> roundsPoints = new ArrayList<>(rounds.size());
for (FlexMap round : rounds) {
int points = round.getByte("bogey5") * phi.getQuintupleBogeyPoints()
+ round.getByte("bogey4") * phi.getQuadrupleBogeyPoints()
+ round.getByte("bogey3") * phi.getTripleBogeyPoints()
+ round.getByte("bogey2") * phi.getDoubleBogeyPoints()
+ round.getByte("bogey") * phi.getBogeyPoints() + round.getByte("par") * phi.getParPoints()
+ round.getByte("birdie") * phi.getBirdiePoints()
+ round.getByte("eagle") * phi.getEaglePoints()
+ round.getByte("alby") * phi.getAlbatrossPoints() + round.getByte("pointAdj");
if (round.getInteger("courseID").equals(homeCourseAdj.getKey())) {
// always negative, so we are making the course seem easier for the home course
// player
points += homeCourseAdj.getValue().intValue();
}
roundsPoints.add(points);
}
Collections.sort(roundsPoints);
float points = 0f;
for (Integer roundPoints : roundsPoints.subList(worstRoundsToToss, roundsPoints.size() - bestRoundsToToss))
points += roundPoints.floatValue();
points /= roundsToCount;
computedPoints.put(phi.getId(), points);
this.logger.debug("Computed {} points with index: {}", points, phi);
if (Math.abs(points - target) < forgiveness) {
System.out.println(points);
return phi;
}
forgiveness += 0.02f;
if (points > target) {
this.logger.debug("{} points are higher than the target {}; trying something lower", points, target);
if (lastHighScoreToPar < -2)
lastHighScoreToPar = 6;
lastHighScoreToPar--;
phi = phi.decrement(lastHighScoreToPar);
} else {
this.logger.debug("{} points are lower than the target {}; trying something higher", points, target);
if (lastLowScoreToPar > 4)
lastLowScoreToPar = -4;
lastLowScoreToPar++;
phi = phi.increment(lastLowScoreToPar);
}
// make sure we are not guaranteed to be outside the bounds of the best low/high
// if the best low/high with nothing possible in between, pick the one that is
// closest
}
}
@Override
public Short computeRoundScore(BigInteger roundId) {
// TODO Auto-generated method stub
return null;
}
@Override
public Short computeEventRoundScore(BigInteger proundId) {
// TODO Auto-generated method stub
return null;
}
private PointHandicapIndex generateIndexBy2MostCommon(byte mostCommonScoreToParIndex,
short mostCommonScoreToParCount, byte nextMostCommonScoreToParIndex, short nextMostCommonScoreToParCount,
short pointTarget, float accelerant) {
boolean roundDown = nextMostCommonScoreToParIndex < mostCommonScoreToParCount;
float targetPerHole = pointTarget / 18f;
int[] pointsForScoreToPar = new int[10];
int targetScoreToParIndex = (mostCommonScoreToParIndex + 10) % 10;
double factor = Math.pow(Math.E, accelerant);
// floor the target, so we target low
byte lastPointsForScoreToPar = roundDown ? (byte) targetPerHole : (byte) Math.ceil(targetPerHole);
pointsForScoreToPar[targetScoreToParIndex] = lastPointsForScoreToPar;
// go forward: higher scores; lower points; eventually 0 points
for (byte scoreToPar = (byte) (mostCommonScoreToParIndex + 1); scoreToPar < 6; scoreToPar++) {
int scoreToParIndex = (scoreToPar + 10) % 10;
lastPointsForScoreToPar = (byte) Math.max(0,
Math.min(lastPointsForScoreToPar - 1, Math.ceil(lastPointsForScoreToPar / factor)));
pointsForScoreToPar[scoreToParIndex] = lastPointsForScoreToPar;
}
lastPointsForScoreToPar = (byte) pointsForScoreToPar[targetScoreToParIndex];
// go backwards; lower scores; higher points
for (byte scoreToPar = (byte) (mostCommonScoreToParIndex - 1); scoreToPar > -4; scoreToPar--) {
int scoreToParIndex = (scoreToPar + 10) % 10;
lastPointsForScoreToPar = (byte) Math.max(lastPointsForScoreToPar + 1,
Math.floor(lastPointsForScoreToPar * factor));
pointsForScoreToPar[scoreToParIndex] = lastPointsForScoreToPar;
}
return new PointHandicapIndex(pointsForScoreToPar);
}
protected Pair<Integer, Float> computeHomeCourseAdjustment(List<? extends FlexMap> rounds) {
Pair<Integer, Float> maxHomeCourseAdj = Pair.of(null, 0f);
float zeroRate = this.getHomeCourseMultiplier() * this.getHomeCourseZeroRate();
Map<Integer, Integer> courseIdCounts = MapUtil.countKeys(rounds, "courseID", Integer.class);
for (Entry<Integer, Integer> courseIdCount : courseIdCounts.entrySet()) {
float homeCourseAdj = courseIdCount.getValue() * -this.getHomeCourseMultiplier() / rounds.size() + zeroRate;
if (homeCourseAdj < maxHomeCourseAdj.getRight())
maxHomeCourseAdj = Pair.of(courseIdCount.getKey(), homeCourseAdj);
if (homeCourseAdj <= -1f)
// over 50%; we are done
break;
}
return maxHomeCourseAdj;
}
protected short[] computeScoreToParCounts(List<? extends FlexMap> rounds) {
short[] scoreToParTotalHoles = new short[10];
for (FlexMap round : rounds) {
scoreToParTotalHoles[5] += round.getByte("bogey5");
scoreToParTotalHoles[4] += round.getByte("bogey4");
scoreToParTotalHoles[3] += round.getByte("bogey3");
scoreToParTotalHoles[2] += round.getByte("bogey2");
scoreToParTotalHoles[1] += round.getByte("bogey");
scoreToParTotalHoles[0] += round.getByte("par");
scoreToParTotalHoles[9] += round.getByte("birdie");
scoreToParTotalHoles[8] += round.getByte("eagle");
scoreToParTotalHoles[7] += round.getByte("alby");
}
return scoreToParTotalHoles;
}
protected byte[] determineMostCommonStoresToPar(short[] scoreToParCounts, byte xNumberOfMostCommonIndex) {
byte[] maxScoreToPar = new byte[xNumberOfMostCommonIndex];
for (byte i = 0; i < xNumberOfMostCommonIndex; i++)
maxScoreToPar[i] = 6; // unused and always 0
for (byte scoreToPar = 0; scoreToPar < 10; scoreToPar++) {
for (int i = 0; i < xNumberOfMostCommonIndex; i++) {
if (scoreToParCounts[maxScoreToPar[i]] < scoreToParCounts[scoreToPar]) {
for (int i2 = xNumberOfMostCommonIndex - 1; i2 > 0; i2--) {
maxScoreToPar[i2] = maxScoreToPar[i2 - 1];
break;
}
maxScoreToPar[i] = scoreToPar;
break;
}
}
}
for (int i = 0; i < maxScoreToPar.length; i++)
maxScoreToPar[i] = maxScoreToPar[i] < 0 ? (byte) (maxScoreToPar[i] - 10) : maxScoreToPar[i];
return maxScoreToPar;
}
}

View File

@@ -1,415 +0,0 @@
package com.poststats.golf.service.compute;
import java.math.BigInteger;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.util.FlexMap;
import com.poststats.golf.provider.GolfProvider;
import com.poststats.golf.service.PersonRoundService.Selection;
import com.poststats.golf.service.PersonService;
import com.poststats.golf.service.model.StrokeCourseRating;
import com.poststats.provider.NonTransactionalProvider;
import com.poststats.provider.Statement;
import com.poststats.provider.StatementProvider;
import com.poststats.provider.TransactionalProvider;
import com.poststats.service.ServiceException;
import jakarta.inject.Inject;
public abstract class AbstractStrokeHandicappingService
extends AbstractHandicappingService<Float, StrokeCourseRating, Float> {
@Inject
private Logger logger;
@Inject
private PersonService personService;
private final Comparator<FlexMap> roundComparator;
public AbstractStrokeHandicappingService() {
this.roundComparator = new Comparator<FlexMap>() {
@Override
public int compare(FlexMap round1, FlexMap round2) {
Float sh1 = round1.getFloat(getStrokeHandicapColumn());
Float sh2 = round2.getFloat(getStrokeHandicapColumn());
return sh1.compareTo(sh2);
}
};
}
protected abstract String getStrokeHandicapColumn();
protected abstract String getHoleHandicapColumn();
@Override
protected Selection getRoundSelection() {
return Selection.StrokeHandicapIndex;
}
protected short getRoundsToCount(short rounds) {
switch (rounds) {
case 0:
throw new IllegalArgumentException();
case 1:
case 2:
case 3:
case 4:
case 5:
return 1;
case 6:
case 7:
case 8:
return 2;
case 9:
case 10:
case 11:
return 3;
case 12:
case 13:
case 14:
return 4;
case 15:
case 16:
return 5;
case 17:
case 18:
return 6;
case 19:
return 7;
case 20:
return 8;
default:
throw new IllegalArgumentException();
}
}
@Override
public Float compute(List<? extends FlexMap> rounds) {
Collections.sort(rounds, this.roundComparator);
short roundsToCount = this.getRoundsToCount((short) rounds.size());
return this.compute(rounds.subList(0, roundsToCount), (short) rounds.size());
}
public Float compute(List<? extends FlexMap> rounds, short roundsConsidered) {
float sh = 0f;
for (FlexMap round : rounds)
sh += round.getFloat(this.getStrokeHandicapColumn());
sh /= rounds.size();
float adjustment = this.getHandicapAdjustment(roundsConsidered);
return sh + adjustment;
}
protected float getHandicapAdjustment(short rounds) {
switch (rounds) {
case 0:
throw new IllegalArgumentException();
case 1:
return -3f;
case 2:
case 3:
return -2f;
case 4:
case 6:
return -1f;
default:
return 0f;
}
}
@Override
public StrokeCourseRating computeNineTeeRatingIndex(long ntratingId) {
FlexMap ntrating = this.getNineTeeRating(ntratingId);
if (ntrating == null)
return null;
LocalDateTime syncd = ntrating.getDateTime("syncd");
if (LocalDateTime.now().getYear() == syncd.getYear() && LocalDateTime.now().minusMonths(3).isBefore(syncd))
// assume it hasn't changed if sync'd within the last 3 months and still the same calendar year
return new StrokeCourseRating(ntrating.getShort("slopeRating"), ntrating.getFloat("courseRating"));
CourseMetadata courseMeta = this.fetchNineTeeRating(ntratingId);
if (courseMeta == null)
return null;
this.updateNineTeeRating(ntratingId, courseMeta);
throw new UnsupportedOperationException();
// FIXME return new StrokeCourseRating(courseMeta.getSlopeRating(), courseMeta.getCourseRating());
}
@Override
public StrokeCourseRating computeEighteenTeeRatingIndex(long etratingId) {
FlexMap etrating = this.getNineTeeRating(etratingId);
if (etrating == null)
return null;
LocalDateTime syncd = etrating.getDateTime("syncd");
if (LocalDateTime.now().getYear() == syncd.getYear() && LocalDateTime.now().minusMonths(3).isBefore(syncd))
// assume it hasn't changed if sync'd within the last 3 months and still the same calendar year
return new StrokeCourseRating(etrating.getShort("slopeRating"), etrating.getFloat("courseRating"));
CourseMetadata courseMeta = this.fetchEighteenTeeRating(etratingId);
if (courseMeta == null)
return null;
this.updateEighteenTeeRating(etratingId, courseMeta);
throw new UnsupportedOperationException();
// FIXME return new StrokeCourseRating(courseMeta.getSlopeRating(), courseMeta.getCourseRating());
}
public Float computeEighteenTeeRatingHandicap(long etratingId, long personId) {
FlexMap person = this.personService.get(personId);
Float strokeHandicap = person.getFloat(this.getStrokeHandicapColumn());
if (strokeHandicap == null)
return null;
CourseMetadata courseMeta = this.fetchEighteenTeeRating(etratingId);
float handicap = strokeHandicap.floatValue();
if (courseMeta.getSlopeRating() != null)
handicap = handicap * courseMeta.getSlopeRating() / 113f;
if (courseMeta.getCourseRating() != null)
handicap += courseMeta.getCourseRating() - courseMeta.getPar();
return handicap;
}
public Float computeNineTeeRatingHandicap(long ntratingId, long personId) {
FlexMap person = this.personService.get(personId);
Float strokeHandicap = person.getFloat(this.getStrokeHandicapColumn());
if (strokeHandicap == null)
return null;
CourseMetadata courseMeta = this.fetchNineTeeRating(ntratingId);
float handicap = strokeHandicap.floatValue() / 2f;
if (courseMeta.getSlopeRating() != null)
handicap = handicap * courseMeta.getSlopeRating() / 113f;
if (courseMeta.getCourseRating() != null)
handicap += courseMeta.getCourseRating() - courseMeta.getPar();
return handicap;
}
@Override
public Float computeRoundScore(BigInteger roundId) {
FlexMap round = this.personRoundService.fetchNonEvent(roundId, this.getRoundSelection(), this.getRoundFilter());
if (round == null) {
this.logger.debug("The round does not exist or does not meeting the requirements to be handicap scored: {}", roundId);
return null;
}
List<? extends FlexMap> holes = this.personRoundService.fetchNonEventHoles(roundId, this.getRoundSelection());
if (holes.size() != 18) {
this.logger.debug("The round does not have 18 scored holes; cannot compute handicap score: {}", roundId);
return null;
}
return this.computeRoundScore(round, holes, roundId, "round");
}
@Override
public Float computeEventRoundScore(BigInteger proundId) {
FlexMap pround = this.personRoundService.fetchEvent(proundId, this.getRoundSelection(), this.getRoundFilter());
if (pround == null) {
// the round either doesn't exist or it is not eligible for handicap computation
return null;
}
int totalHolesNeeded = pround.isNotEmpty("ntratingID") ? 9 : 18;
List<? extends FlexMap> holes = this.personRoundService.fetchEventHoles(proundId, this.getRoundSelection());
if (holes.size() != totalHolesNeeded) {
this.logger.debug("The pround does not have {} scored holes; cannot compute handicap score: {}", totalHolesNeeded, proundId);
return null;
}
return this.computeRoundScore(pround, holes, proundId, "pround");
}
private Float computeRoundScore(FlexMap round, List<? extends FlexMap> holes, Object roundId, String msgName) {
// prepare for equitable stroke control
Pair<Byte, Byte> esc = this.computeEquitableStrokeControl(round.getLong("personID"));
int escStrokes = 0;
for (FlexMap hole : holes) {
Byte strokeIndex = hole.getByte(this.getHoleHandicapColumn());
if (strokeIndex == null) {
this.logger.debug("The course for the {} does not have the appropriate hole handicaps to compute the handicap with equitable stroke control: {}", msgName, roundId);
return null;
}
int scoreToPar = hole.getByte("strokes") - hole.getByte("par");
if (strokeIndex.byteValue() <= esc.getRight().byteValue()) {
scoreToPar = Math.min(scoreToPar, esc.getLeft() + 1);
} else {
scoreToPar = Math.min(scoreToPar, esc.getLeft());
}
escStrokes += scoreToPar + hole.getByte("par");
}
float adjStrokes = (float) escStrokes;
if (round.getFloat("courseRating") != null) {
adjStrokes -= round.getFloat("courseRating").floatValue();
} else {
adjStrokes -= round.getByte("par").floatValue();
}
if (round.getShort("slopeRating") != null)
adjStrokes = adjStrokes * 113 / round.getShort("slopeRating").floatValue();
return adjStrokes;
}
private Pair<Byte, Byte> computeEquitableStrokeControl(long personId) {
byte netDoubleBogey = 5;
byte netDoubleBogeyRemainder = 0;
FlexMap person = this.personService.get(personId);
Float strokeHandicap = person.getFloat(this.getStrokeHandicapColumn());
if (strokeHandicap != null) {
byte baseHandicap = (byte) Math.round(strokeHandicap.floatValue());
netDoubleBogey = (byte) (baseHandicap / 18 + 2);
netDoubleBogeyRemainder = (byte) (baseHandicap % 18);
}
return Pair.of(netDoubleBogey, netDoubleBogeyRemainder);
}
private CourseMetadata fetchNineTeeRating(long ntratingId) {
// TODO call WHS service for the information
return null;
}
private CourseMetadata fetchEighteenTeeRating(long etratingId) {
// TODO call WHS service for the information
return null;
}
private FlexMap getNineTeeRating(long ntratingId) {
try {
FlexPreparedStatement fps = this.sqlSelectCourseNineTeeRating.buildPreparedStatement();
try {
fps.setIntegerU(1, ntratingId);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT CNTR.*, CNT.par, CNT.yards "
+ "FROM ~g~.CourseNineTeeRating CNTR "
+ " INNER JOIN ~g~.CourseNineTee CNT ON (CNTR.nineteeID=CNT.nineteeID) "
+ "WHERE CNTR.ntratingID=? "
)
private StatementProvider sqlSelectCourseNineTeeRating;
private FlexMap getEighteenTeeRating(long etratingId) {
try {
FlexPreparedStatement fps = this.sqlSelectCourseEighteenTeeRating.buildPreparedStatement();
try {
fps.setIntegerU(1, etratingId);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT CETR.*, CET.par, CET.yards "
+ "FROM ~g~.CourseEighteenTeeRating CETR "
+ " INNER JOIN ~g~.CourseEighteenTee CET ON (CETR.eighteenteeID=CET.eighteenteeID) "
+ "WHERE CETR.etratingID=? "
)
private StatementProvider sqlSelectCourseEighteenTeeRating;
private void updateNineTeeRating(long ntratingId, CourseMetadata courseMeta) {
try {
FlexPreparedStatement fps = this.sqlUpdateCourseNineTeeRating.buildPreparedStatement();
try {
//fps.setTinyintU(1, courseMeta.getSlopeRating());
//fps.setTinyintU(2, courseMeta.getCourseRating());
fps.setIntegerU(3, ntratingId);
fps.executeUpdate();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@TransactionalProvider
@GolfProvider
@Statement(
sql = "UPDATE ~g~.CourseNineTeeRating "
+ "SET slopeRating=?, courseRating=? "
+ "WHERE ntratingID=? "
)
private StatementProvider sqlUpdateCourseNineTeeRating;
private void updateEighteenTeeRating(long etratingId, CourseMetadata courseMeta) {
try {
FlexPreparedStatement fps = this.sqlUpdateCourseEighteenTeeRating.buildPreparedStatement();
try {
//fps.setTinyintU(1, courseMeta.getSlopeRating());
//fps.setTinyintU(2, courseMeta.getCourseRating());
fps.setIntegerU(3, etratingId);
fps.executeUpdate();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@TransactionalProvider
@GolfProvider
@Statement(
sql = "UPDATE ~g~.CourseEighteenTeeRating "
+ "SET slopeRating=?, courseRating=? "
+ "WHERE etratingID=? "
)
private StatementProvider sqlUpdateCourseEighteenTeeRating;
private interface CourseMetadata {
// TODO replace with WHS model
Short getSlopeRating();
Float getCourseRating();
byte getPar();
}
}

View File

@@ -1,117 +0,0 @@
package com.poststats.golf.service.compute;
import com.brianlong.util.FlexMap;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class LegacyPostStatsPointHandicapIndexService extends AbstractPointHandicappingService {
@Override
protected float getAccelerant() {
return 0.6f;
}
@Override
protected short getPointTarget() {
return 60;
}
@Override
protected short getWorstRoundsToToss(short roundCount) {
switch (roundCount) {
case 0:
case 1:
case 2:
case 3:
return 0;
case 4:
case 5:
case 6:
case 7:
return 1;
case 8:
case 9:
case 10:
case 11:
return 2;
case 12:
case 13:
case 14:
return 3;
case 15:
return 4;
default:
throw new IllegalArgumentException();
}
}
@Override
protected short getBestRoundsToToss(short roundCount) {
return (short) (roundCount < 13 ? 0 : 1);
}
@Override
public Byte computeEighteenTeeRatingIndex(long etratingId) {
FlexMap etrating = this.courseRatingService.getEighteenTeeRating(etratingId);
return this.computeEighteenTeeRatingIndex(etrating);
}
protected Byte computeEighteenTeeRatingIndex(FlexMap etrating) {
Short slopeRating = etrating.getShort("slopeRating");
Float courseRating = etrating.getFloat("courseRating");
char gender = etrating.getString("gender").charAt(0);
short yards = etrating.getShort("yards");
byte par = etrating.getByte("par");
return this.computeRatingIndex(slopeRating, courseRating, gender, yards, par);
}
@Override
public Byte computeNineTeeRatingIndex(long ntratingId) {
FlexMap ntrating = this.courseRatingService.getNineTeeRating(ntratingId);
return this.computeNineTeeRatingIndex(ntrating);
}
protected Byte computeNineTeeRatingIndex(FlexMap ntrating) {
Short slopeRating = ntrating.getShort("slopeRating");
Float courseRating = ntrating.getFloat("courseRating");
char gender = ntrating.getString("gender").charAt(0);
short yards = ntrating.getShort("yards");
byte par = ntrating.getByte("par");
return this.computeRatingIndex(slopeRating, courseRating, gender, yards, par);
}
protected Byte computeRatingIndex(Short slopeRating, Float courseRating, char gender, short yards, byte par) {
// A par X hole assumes (X-2) non-putts and 2 putts to complete par
// So an 18 hole course assumes 36 putts and (X-36) non-putts to complete par
int nonPuttPar = par - 36;
// we normalize those (X-2) full strokes, so par 60 courses get a normalization
// ratio of 36/24 or 1.5
// that means we need to 1.5x any yards/par/ratings to make it look like a
// standard par 72 course
double normalizedRatioNonPuttPar = 36.0 / nonPuttPar;
// we normalize the yards to look like a standard par 72 course
double normalizedYards = yards * normalizedRatioNonPuttPar;
// we are linearly applying the yards, where:
// 6000 yd par 72 has 0 points
// 6350 yd par 72 has 6 points
// 6500 yd par 70 has 9 points
// 7000 yd par 71 has 18 points
// 3000 yd par 54 has 0 points
// 2000 yd par 54 has -34 points
int genderYards = gender == 'M' ? 6000 : 5000;
double adjYards = normalizedYards - genderYards;
byte yardBonus = (byte) (adjYards * 3.0 / 175.0);
if (slopeRating == null)
return (byte) (yardBonus * 2);
// a 130 slope course has 10 points
// a 155 slope (max) course has 21 points
// a 55 slope (min) course has -21 points
byte slopeBonus = (byte) ((slopeRating - 105) * 3 / 7);
return (byte) (yardBonus + slopeBonus);
}
}

View File

@@ -1,136 +0,0 @@
package com.poststats.golf.service.compute;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.brianlong.util.FlexMap;
import com.poststats.golf.service.CourseHoleService;
import com.poststats.golf.service.CourseRatingService;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
@ApplicationScoped
public class PostStatsPointHandicapIndexService extends AbstractPointHandicappingService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Inject
private CourseRatingService courseRatingService;
@Inject
private CourseHoleService courseHoleService;
@Override
protected float getAccelerant() {
return 0.5f;
}
@Override
protected short getPointTarget() {
return 100;
}
@Override
protected short getWorstRoundsToToss(short roundCount) {
switch (roundCount) {
case 0:
case 1:
case 2:
return 0;
case 3:
case 4:
case 5:
case 6:
return 1;
case 7:
case 8:
case 9:
case 10:
return 2;
case 11:
case 12:
case 13:
case 14:
return 3;
case 15:
return 4;
default:
throw new IllegalArgumentException();
}
}
@Override
protected short getBestRoundsToToss(short roundCount) {
return (short) (roundCount < 10 ? 0 : 1);
}
@Override
public Byte computeEighteenTeeRatingIndex(long etratingId) {
FlexMap etrating = this.courseRatingService.getEighteenTeeRating(etratingId);
List<? extends FlexMap> holes = this.courseHoleService.getHolesByEighteenTee(etrating.getLong("eighteenteeID"));
return this.computeRatingIndex(etrating, holes);
}
@Override
public Byte computeNineTeeRatingIndex(long ntratingId) {
FlexMap ntrating = this.courseRatingService.getNineTeeRating(ntratingId);
List<? extends FlexMap> holes = this.courseHoleService.getHolesByEighteenTee(ntrating.getLong("nineteeID"));
return this.computeRatingIndex(ntrating, holes);
}
protected Byte computeRatingIndex(FlexMap netrating, List<? extends FlexMap> holes) {
// M normalized to 150 yd (150 + 3 * 8) par 3s
// F normalized to 125 yd (125 + 3 * 8) par 3s
char gender = netrating.getString("gender").charAt(0);
float divisorBase = gender == 'M' ? 165f : 140f;
// capture running total for points
float points = 0;
for (FlexMap hole : holes) {
short yards = hole.getShort("yards");
byte par = hole.getByte("par");
// par assumes 2 putts per hole
int nonPuttPar = par - 2;
float nonPuttYardsPerStroke = 1f * yards / nonPuttPar;
// make longer par 4s/5s correlate with shorter par 3s, as it is harder to
// "score" (birdies/eagles) on par 3s than par 5s
float exponentDivisor = divisorBase - (par * 5);
// 80 yd par 3 will be -3.89 pts
// 100 yd par 3 will be -3.16 pts
// 125 yd par 3 (272/4 or 446/5) will be -2.1 pts
// 150 yd par 3 (326/4 or 536/5) will be -0.85 pts
// 200 yd par 3 (435/4 or 714/5) will be +2.38 pts
// 225 yd par 3 (489/4 or 804/5) will be +4.45 pts
points += Math.pow(Math.E, nonPuttYardsPerStroke / exponentDivisor) * 3f - 9f;
}
if (netrating.isNotEmpty("etratingID")) {
this.logger.debug("computed course rating: et #{} => {}", netrating.get("etratingID"), points);
} else {
this.logger.debug("computed course rating: nt #{} => {}", netrating.get("ntratingID"), points);
}
Float courseRating = netrating.getFloat("courseRating");
if (courseRating == null)
return (byte) points;
byte par = netrating.getByte("par");
float difficulty = courseRating - par;
// 69 rating on par 72 gets -9 pts
// 72 rating on par 72 gets +0 pts
// 73 rating on par 72 gets +3 pts
// 75 rating on par 72 gets +9 pts
double ratingAdj = 3 * difficulty;
return (byte) (points + ratingAdj);
}
}

View File

@@ -1,23 +0,0 @@
package com.poststats.golf.service.compute;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class PostStatsStrokeHandicappingService extends AbstractStrokeHandicappingService {
@Override
protected short getMaximumRounds() {
return 20;
}
@Override
protected String getStrokeHandicapColumn() {
return "strokeHandicapIndex";
}
@Override
protected String getHoleHandicapColumn() {
return "handicap";
}
}

View File

@@ -1,37 +0,0 @@
package com.poststats.golf.service.compute;
import com.poststats.golf.service.PersonRoundService.Filter;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class WhsStrokeHandicapIndexService extends AbstractStrokeHandicappingService {
@Override
protected short getMinimumRounds() {
return 3;
}
@Override
protected short getMaximumRounds() {
return 20;
}
@Override
protected String getStrokeHandicapColumn() {
return "whsStrokeHandicapIndex";
}
@Override
protected String getHoleHandicapColumn() {
return "whsHandicap";
}
@Override
protected Filter[] getRoundFilter() {
return new Filter[] {
Filter.AttestedOnly, Filter.CourseRatedOnly
};
}
}

View File

@@ -1,277 +0,0 @@
package com.poststats.golf.service.db;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexManyToOneDef;
import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.sql.ResultSubSetFeature;
import com.brianlong.util.FlexMap;
import com.brianlong.util.SubList;
import com.poststats.golf.provider.GolfProvider;
import com.poststats.golf.service.CourseHoleService;
import com.poststats.golf.service.CourseNineService;
import com.poststats.golf.service.CourseService;
import com.poststats.provider.NonTransactionalProvider;
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;
@ApplicationScoped
public class CourseServiceDAO extends CacheableServiceDAO<Integer>
implements CourseService, CourseNineService, CourseHoleService {
private final int defaultCacheExpirationInSeconds = 600;
private final FlexManyToOneDef facilityManyToOneDef = new FlexManyToOneDef("facilityID", "facility");
@Inject
private FacilityService facilityService;
@Override
public FlexMap get(int courseId) {
return this.get(Integer.valueOf(courseId));
}
@Override
public FlexMap injectDeep(String idKey, FlexMap parentMap, String mapKey) {
FlexMap map = this.inject(idKey, parentMap, mapKey);
return this.facilityService.inject("facilityID", map, "facility");
}
@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.facilityService.inject("facilityID", maps, "facility");
}
@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(this.facilityManyToOneDef);
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
feature = ResultSubSetFeature.class,
sql = "SELECT C.courseID, C.course, C.access, CP.prefix, "
+ " F.*, FP.prefix, FS.suffix, FS.suffixAbbrev "
+ "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 C.course LIKE ? OR F.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(this.facilityManyToOneDef);
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
feature = ResultSubSetFeature.class,
sql = "SELECT C.courseID, C.course, C.access, CP.prefix, "
+ " F.*, FP.prefix, FS.suffix, FS.suffixAbbrev "
+ "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.addrcountry=? AND F.addrstate=? "
+ " AND C.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(this.facilityManyToOneDef);
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
feature = ResultSubSetFeature.class,
sql = "SELECT C.courseID, C.course, C.access, CP.prefix, "
+ " F.*, FP.prefix, FS.suffix, FS.suffixAbbrev "
+ "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 "
)
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
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT CP.*, C.* "
+ "FROM ~g~.Course C "
+ " LEFT JOIN ~g~.CoursePrefix CP ON (C.prefixID=CP.prefixID) "
+ "WHERE C.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
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT CP.*, C.* "
+ "FROM ~g~.Course C "
+ " LEFT JOIN ~g~.CoursePrefix CP ON (C.prefixID=CP.prefixID) "
+ "WHERE C.courseID IN (??) "
)
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;
@Override
public List<? extends FlexMap> getHolesByEighteenTee(long eighteenteeId) {
// TODO Auto-generated method stub
return null;
}
@Override
public List<? extends FlexMap> getHolesByNineTee(long nineteeId) {
// TODO Auto-generated method stub
return null;
}
}

View File

@@ -1,79 +0,0 @@
package com.poststats.golf.service.db;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Map;
import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.util.FlexMap;
import com.poststats.golf.provider.GolfProvider;
import com.poststats.golf.service.EventDocumentService;
import com.poststats.provider.NonTransactionalProvider;
import com.poststats.provider.Statement;
import com.poststats.provider.StatementProvider;
import com.poststats.service.db.CacheableServiceDAO;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
@ApplicationScoped
public class EventDocumentServiceDAO extends CacheableServiceDAO<Long> implements EventDocumentService {
private final long defaultCacheExpirationInSeconds = 3600;
@Override
public FlexMap get(long eventId, long documentId) {
FlexMap document = this.get(documentId);
if (document != null && eventId != document.getLong("eventID"))
return null;
return document;
}
@Override
protected long getCacheExpirationInSeconds() {
return this.defaultCacheExpirationInSeconds;
}
@Override
protected DataSet fetchOne(Long documentId) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectEventDocument.buildPreparedStatement();
try {
fps.setIntegerU(1, documentId);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT ED.* "
+ "FROM ~g~.EventDocument ED "
+ "WHERE ED.documentId=? "
)
private StatementProvider sqlSelectEventDocument;
@Override
protected Map<Long, DataSet> fetchBulk(Collection<Long> documentIds) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectEvents.buildPreparedStatement(documentIds);
try {
return fps.executeQuery().getAllRows("documentID", Long.class);
} finally {
fps.close();
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT ED.* "
+ "FROM ~g~.EventDocument ED "
+ "WHERE ED.documentId IN (??) "
)
private StatementProvider sqlSelectEvents;
}

View File

@@ -1,300 +0,0 @@
package com.poststats.golf.service.db;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.Map;
import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.util.FlexMap;
import com.poststats.golf.provider.GolfProvider;
import com.poststats.golf.service.EventFinanceService;
import com.poststats.golf.service.EventService;
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;
@ApplicationScoped
public class EventFinanceServiceDAO implements EventFinanceService {
@Inject
private EventService eventService;
@Override
public Map<Long, DataSet> getPersonsBalances(long eventId) {
return this.getPersonsBalances(eventId, null, null);
}
@Override
public Map<Long, DataSet> getPersonsBalances(long eventId, Float minBalance, Float maxBalance) {
try {
FlexPreparedStatement fps = this.sqlSelectBalances.buildPreparedStatement();
try {
for (int i = 1; i <= 5; i++)
fps.setIntegerU(i, eventId);
fps.setFloat(6, minBalance == null ? -1e20f : minBalance);
fps.setFloat(7, maxBalance == null ? 1e20f : maxBalance);
return fps.executeQuery().getAllRows("personID", Long.class);
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Override
public DataSet getPersonBalance(long eventId, long personId) {
try {
FlexPreparedStatement fps = this.sqlSelectPersonBalances.buildPreparedStatement();
try {
for (int i = 1; i <= 10; i += 2) {
fps.setIntegerU(i, eventId);
fps.setIntegerU(i + 1, personId);
}
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Override
public Map<Long, DataSet> getSeriesPersonsPreviousBalances(long eventId) {
FlexMap event = this.eventService.get(eventId);
int seriesId = event.getInteger("seriesID");
LocalDate liveline = event.getDate("liveline");
try {
FlexPreparedStatement fps = this.sqlSelectSeriesBalances.buildPreparedStatement();
try {
for (int i = 1; i <= 15; i += 3) {
fps.setSmallintU(i, seriesId);
fps.setIntegerU(i + 1, eventId);
fps.setDate(i + 2, liveline);
}
return fps.executeQuery().getAllRows("personID", Long.class);
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Override
public Map<Long, DataSet> getSeriesPersonBalances(int seriesId, long personId) {
return this.getSeriesPersonBalances(seriesId, null, null, personId);
}
@Override
public Map<Long, DataSet> getSeriesPersonPreviousBalances(long eventId, long personId) {
FlexMap event = this.eventService.get(eventId);
int seriesId = event.getInteger("seriesID");
LocalDate liveline = event.getDate("liveline");
return this.getSeriesPersonBalances(seriesId, eventId, liveline, personId);
}
private Map<Long, DataSet> getSeriesPersonBalances(int seriesId, Long eventId, LocalDate liveline, long personId) {
if (eventId == null)
eventId = -1L;
if (liveline == null)
liveline = LocalDate.now().plusYears(10L);
try {
FlexPreparedStatement fps = this.sqlSelectSeriesPersonEventBalances.buildPreparedStatement();
try {
for (int i = 1; i <= 20; i += 4) {
fps.setSmallintU(i, seriesId);
fps.setIntegerU(i + 1, eventId);
fps.setDate(i + 2, liveline);
fps.setIntegerU(i + 3, personId);
}
return fps.executeQuery().getAllRows("personID", Long.class);
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT TT.personID, TT.epersonID, SUM(TT.expenses) expenses, SUM(TT.paid) paid, (SUM(TT.paid)-SUM(TT.expenses)) balance "
+ "FROM ("
+ " SELECT ALL EP.personID, EP.epersonID, IF(EB.projectedValue IS NULL, 0, EB.projectedValue) expenses, 0 paid "
+ " FROM ~g~.EventPerson EP "
+ " LEFT JOIN ~g~.EventBudget EB ON (EP.eventID=EB.eventID) "
+ " WHERE EP.eventID=? "
+ " UNION ALL "
+ " SELECT ALL EP.personID, EP.epersonID, EO.amount expenses, 0 paid "
+ " FROM ~g~.EventPerson EP "
+ " INNER JOIN ~g~.EventPersonOption EPO ON (EP.epersonID=EPO.epersonID) "
+ " INNER JOIN ~g~.EventOption EO ON (EPO.optionID=EO.optionID) "
+ " WHERE EP.eventID=? AND EPO.answer='Y' AND EO.amount IS NOT NULL AND EO.waive IS FALSE "
+ " UNION ALL "
+ " SELECT ALL EP.personID, EP.epersonID, EO.amount expenses, 0 paid "
+ " FROM ~g~.EventPerson EP "
+ " INNER JOIN ~g~.EventPersonOption EPO ON (EP.epersonID=EPO.epersonID) "
+ " INNER JOIN ~g~.EventOption EO ON (EPO.optionID=EO.optionID) "
+ " WHERE EP.eventID=? AND EPO.itemID IS NOT NULL "
+ " AND (EO.multiple IS FALSE OR EO.funded='option' OR EO.static IS NOT NULL) "
+ " AND EO.amount IS NOT NULL AND EO.waive IS FALSE "
+ " UNION ALL "
+ " SELECT ALL EP.personID, EP.epersonID, EOI.amount expenses, 0 paid "
+ " FROM ~g~.EventPerson EP "
+ " INNER JOIN ~g~.EventPersonOption EPO ON (EP.epersonID=EPO.epersonID) "
+ " INNER JOIN ~g~.EventOption EO ON (EPO.optionID=EO.optionID) "
+ " INNER JOIN ~g~.EventOptionItem EOI ON (EPO.itemID=EOI.itemID) "
+ " WHERE EP.eventID=? AND EO.waive IS FALSE AND EO.funded='selection' AND EOI.amount IS NOT NULL "
+ " UNION ALL "
+ " SELECT ALL EPP.personID, EP.epersonID, 0 expenses, EPP.amount paid "
+ " FROM ~g~.EventPersonPayment EPP "
+ " LEFT JOIN ~g~.EventPerson EP ON (EPP.eventID=EP.eventID AND EPP.personID=EP.personID) "
+ " WHERE EPP.eventID=?) TT "
+ "GROUP BY TT.personID "
+ "HAVING (SUM(TT.paid)-SUM(TT.expenses))>=? AND (SUM(TT.paid)-SUM(TT.expenses))<=? "
)
private StatementProvider sqlSelectBalances;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT SUM(TT.expenses) expenses, SUM(TT.paid) paid, (SUM(TT.paid)-SUM(TT.expenses)) balance "
+ "FROM ("
+ " SELECT ALL EP.personID, EP.epersonID, IF(EB.projectedValue IS NULL, 0, EB.projectedValue) expenses, 0 paid "
+ " FROM ~g~.EventPerson EP "
+ " LEFT JOIN ~g~.EventBudget EB ON (EP.eventID=EB.eventID) "
+ " WHERE EP.eventID=? AND EP.personID=? "
+ " UNION ALL "
+ " SELECT ALL EP.personID, EP.epersonID, EO.amount expenses, 0 paid "
+ " FROM ~g~.EventPerson EP "
+ " INNER JOIN ~g~.EventPersonOption EPO ON (EP.epersonID=EPO.epersonID) "
+ " INNER JOIN ~g~.EventOption EO ON (EPO.optionID=EO.optionID) "
+ " WHERE EP.eventID=? AND EP.personID=? AND EPO.answer='Y' AND EO.amount IS NOT NULL AND EO.waive IS FALSE "
+ " UNION ALL "
+ " SELECT ALL EP.personID, EP.epersonID, EO.amount expenses, 0 paid "
+ " FROM ~g~.EventPerson EP "
+ " INNER JOIN ~g~.EventPersonOption EPO ON (EP.epersonID=EPO.epersonID) "
+ " INNER JOIN ~g~.EventOption EO ON (EPO.optionID=EO.optionID) "
+ " WHERE EP.eventID=? AND EP.personID=? AND EPO.itemID IS NOT NULL "
+ " AND (EO.multiple IS FALSE OR EO.funded='option' OR EO.static IS NOT NULL) "
+ " AND EO.amount IS NOT NULL AND EO.waive IS FALSE "
+ " UNION ALL "
+ " SELECT ALL EP.personID, EP.epersonID, EOI.amount expenses, 0 paid "
+ " FROM ~g~.EventPerson EP "
+ " INNER JOIN ~g~.EventPersonOption EPO ON (EP.epersonID=EPO.epersonID) "
+ " INNER JOIN ~g~.EventOption EO ON (EPO.optionID=EO.optionID) "
+ " INNER JOIN ~g~.EventOptionItem EOI ON (EPO.itemID=EOI.itemID) "
+ " WHERE EP.eventID=? AND EP.personID=? AND EO.waive IS FALSE AND EO.funded='selection' AND EOI.amount IS NOT NULL "
+ " UNION ALL "
+ " SELECT ALL EPP.personID, EP.epersonID, 0 expenses, EPP.amount paid "
+ " FROM ~g~.EventPersonPayment EPP "
+ " LEFT JOIN ~g~.EventPerson EP ON (EPP.eventID=EP.eventID AND EPP.personID=EP.personID) "
+ " WHERE EPP.eventID=? AND EPP.personID=?) TT "
)
private StatementProvider sqlSelectPersonBalances;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT TT.personID, TT.epersonID, SUM(TT.expenses) expenses, SUM(TT.paid) paid, (SUM(TT.paid)-SUM(TT.expenses)) balance "
+ "FROM ("
+ " SELECT ALL EP.personID, EP.epersonID, IF(EB.projectedValue IS NULL, 0, EB.projectedValue) expenses, 0 paid "
+ " FROM ~g~.Event E "
+ " INNER JOIN ~g~.EventPerson EP ON (E.eventID=EP.eventID) "
+ " INNER JOIN ~g~.EventBudget EB ON (EP.eventID=EB.eventID) "
+ " WHERE E.seriesID=? AND E.eventID<>? AND E.liveline<? "
+ " UNION ALL "
+ " SELECT ALL EP.personID, EP.epersonID, EO.amount expenses, 0 paid "
+ " FROM ~g~.Event E "
+ " INNER JOIN ~g~.EventPerson EP ON (E.eventID=EP.eventID) "
+ " INNER JOIN ~g~.EventPersonOption EPO ON (EP.epersonID=EPO.epersonID) "
+ " INNER JOIN ~g~.EventOption EO ON (EPO.optionID=EO.optionID) "
+ " WHERE E.seriesID=? AND E.eventID<>? AND E.liveline<? AND EPO.answer='Y' AND EO.amount IS NOT NULL AND EO.waive IS FALSE "
+ " UNION ALL "
+ " SELECT ALL EP.personID, EP.epersonID, EO.amount expenses, 0 paid "
+ " FROM ~g~.Event E "
+ " INNER JOIN ~g~.EventPerson EP ON (E.eventID=EP.eventID) "
+ " INNER JOIN ~g~.EventPersonOption EPO ON (EP.epersonID=EPO.epersonID) "
+ " INNER JOIN ~g~.EventOption EO ON (EPO.optionID=EO.optionID) "
+ " WHERE E.seriesID=? AND E.eventID<>? AND E.liveline<? AND EPO.itemID IS NOT NULL "
+ " AND (EO.multiple IS FALSE OR EO.funded='option' OR EO.static IS NOT NULL) "
+ " AND EO.amount IS NOT NULL AND EO.waive IS FALSE "
+ " UNION ALL "
+ " SELECT ALL EP.personID, EP.epersonID, EOI.amount expenses, 0 paid "
+ " FROM ~g~.Event E "
+ " INNER JOIN ~g~.EventPerson EP ON (E.eventID=EP.eventID) "
+ " INNER JOIN ~g~.EventPersonOption EPO ON (EP.epersonID=EPO.epersonID) "
+ " INNER JOIN ~g~.EventOption EO ON (EPO.optionID=EO.optionID) "
+ " INNER JOIN ~g~.EventOptionItem EOI ON (EPO.itemID=EOI.itemID) "
+ " WHERE E.seriesID=? AND E.eventID<>? AND E.liveline<? AND EO.waive IS FALSE AND EO.funded='selection' AND EOI.amount IS NOT NULL "
+ " UNION ALL "
+ " SELECT ALL EPP.personID, EP.epersonID, 0 expenses, EPP.amount paid "
+ " FROM ~g~.Event E "
+ " INNER JOIN ~g~.EventPersonPayment EPP ON (E.eventID=EPP.eventID) "
+ " LEFT JOIN ~g~.EventPerson EP ON (EPP.eventID=EP.eventID AND EPP.personID=EP.personID) "
+ " WHERE E.seriesID=? AND E.eventID<>? AND E.liveline<?) TT "
+ "GROUP BY TT.personID"
)
private StatementProvider sqlSelectSeriesBalances;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT TT.eventID, TT.epersonID, SUM(TT.expenses) expenses, SUM(TT.paid) paid, (SUM(TT.paid)-SUM(TT.expenses)) balance "
+ "FROM ("
+ " SELECT ALL E.eventID, EP.epersonID, IF(EB.projectedValue IS NULL, 0, EB.projectedValue) expenses, 0 paid "
+ " FROM ~g~.Event E "
+ " INNER JOIN ~g~.EventPerson EP ON (E.eventID=EP.eventID) "
+ " INNER JOIN ~g~.EventBudget EB ON (EP.eventID=EB.eventID) "
+ " WHERE E.seriesID=? AND E.eventID<>? AND E.liveline<? AND EP.personID=? "
+ " UNION ALL "
+ " SELECT ALL E.eventID, EP.epersonID, EO.amount expenses, 0 paid "
+ " FROM ~g~.Event E "
+ " INNER JOIN ~g~.EventPerson EP ON (E.eventID=EP.eventID) "
+ " INNER JOIN ~g~.EventPersonOption EPO ON (EP.epersonID=EPO.epersonID) "
+ " INNER JOIN ~g~.EventOption EO ON (EPO.optionID=EO.optionID) "
+ " WHERE E.seriesID=? AND E.eventID<>? AND E.liveline<? AND EP.personID=? "
+ " AND EPO.answer='Y' AND EO.amount IS NOT NULL AND EO.waive IS FALSE "
+ " UNION ALL "
+ " SELECT ALL E.eventID, EP.epersonID, EO.amount expenses, 0 paid "
+ " FROM ~g~.Event E "
+ " INNER JOIN ~g~.EventPerson EP ON (E.eventID=EP.eventID) "
+ " INNER JOIN ~g~.EventPersonOption EPO ON (EP.epersonID=EPO.epersonID) "
+ " INNER JOIN ~g~.EventOption EO ON (EPO.optionID=EO.optionID) "
+ " WHERE E.seriesID=? AND E.eventID<>? AND E.liveline<? AND EP.personID=? AND EPO.itemID IS NOT NULL "
+ " AND (EO.multiple IS FALSE OR EO.funded='option' OR EO.static IS NOT NULL) "
+ " AND EO.amount IS NOT NULL AND EO.waive IS FALSE "
+ " UNION ALL "
+ " SELECT ALL E.eventID, EP.epersonID, EOI.amount expenses, 0 paid "
+ " FROM ~g~.Event E "
+ " INNER JOIN ~g~.EventPerson EP ON (E.eventID=EP.eventID) "
+ " INNER JOIN ~g~.EventPersonOption EPO ON (EP.epersonID=EPO.epersonID) "
+ " INNER JOIN ~g~.EventOption EO ON (EPO.optionID=EO.optionID) "
+ " INNER JOIN ~g~.EventOptionItem EOI ON (EPO.itemID=EOI.itemID) "
+ " WHERE E.seriesID=? AND E.eventID<>? AND E.liveline<? AND EP.personID=? "
+ " AND EO.waive IS FALSE AND EO.funded='selection' AND EOI.amount IS NOT NULL "
+ " UNION ALL "
+ " SELECT ALL E.eventID, EP.epersonID, 0 expenses, EPP.amount paid "
+ " FROM ~g~.Event E "
+ " INNER JOIN ~g~.EventPersonPayment EPP ON (E.eventID=EPP.eventID) "
+ " LEFT JOIN ~g~.EventPerson EP ON (EPP.eventID=EP.eventID AND EPP.personID=EP.personID) "
+ " WHERE E.seriesID=? AND E.eventID<>? AND E.liveline<? AND EPP.personID=?) TT "
+ "GROUP BY TT.eventID"
)
private StatementProvider sqlSelectSeriesPersonEventBalances;
}

View File

@@ -1,404 +0,0 @@
package com.poststats.golf.service.db;
import java.math.BigInteger;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.util.FlexMap;
import com.poststats.golf.provider.GolfProvider;
import com.poststats.golf.service.EventFinanceService;
import com.poststats.golf.service.EventPersonService;
import com.poststats.provider.NonTransactionalProvider;
import com.poststats.provider.Statement;
import com.poststats.provider.StatementProvider;
import com.poststats.service.ServiceException;
import com.poststats.service.db.CacheableServiceDAO;
import com.poststats.util.Contact;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.mail.MessagingException;
@ApplicationScoped
public class EventPersonServiceDAO extends CacheableServiceDAO<BigInteger> implements EventPersonService {
@Inject
private EventFinanceService eventFinanceService;
@Override
public FlexMap get(long eventId, long personId) {
try {
FlexPreparedStatement fps = this.sqlSelectEventPersonByEventIdPersonId.buildPreparedStatement();
try {
fps.setIntegerU(1, eventId);
fps.setIntegerU(2, personId);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT * FROM ~g~.EventPerson WHERE eventID=? AND personID=?")
private StatementProvider sqlSelectEventPersonByEventIdPersonId;
@Override
public List<? extends FlexMap> getPeople(long eventId) {
try {
return this.query(this.sqlSelectPersons, eventId, 2);
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT EP.* FROM ~g~.EventPerson EP WHERE EP.eventID=? "
+ "UNION "
+ "SELECT DISTINCT NULL, personID "
+ "FROM ~g~.EventPersonAccessControl EPAC "
+ " LEFT JOIN ~g~.EventPerson EP ON (EPAC.eventID=EP.eventID AND EPAC.personID=EP.personID) "
+ "WHERE EPAC.eventID=? AND EP.personID IS NULL "
)
private StatementProvider sqlSelectPersons;
@Override
public List<? extends FlexMap> getParticipants(long eventId) {
try {
return this.query(this.sqlSelectParticipantIds, eventId, 1);
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT * FROM ~g~.EventPerson WHERE eventID=? AND pending IS FALSE")
private StatementProvider sqlSelectParticipantIds;
@Override
public Set<Long> getSeriesEventFellowParticipantIds(int seriesId, long personId) {
try {
FlexPreparedStatement fps = this.sqlSelectFellowParticipants.buildPreparedStatement();
try {
fps.setSmallintU(1, seriesId);
fps.setIntegerU(2, personId);
return fps.executeQuery().getFirstColumn(Long.class, new HashSet<>());
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT DISTINCT EP2.personID "
+ "FROM ~g~.EventPerson EP1 "
+ " INNER JOIN Event E ON (EP1.eventID=E.eventID) "
+ " INNER JOIN EventPerson EP2 ON (E.eventID=EP2.eventID) "
+ "WHERE EP1.personID=? AND EP1.pending IS FALSE AND E.seriesID=? AND EP2.pending IS FALSE "
)
private StatementProvider sqlSelectFellowParticipants;
private List<? extends FlexMap> query(StatementProvider provider, long eventId, int parameterCount)
throws SQLException {
FlexPreparedStatement fps = provider.buildPreparedStatement();
try {
for (int i = 1; i <= parameterCount; i++)
fps.setIntegerU(i, eventId);
return fps.executeQuery().getAllRows();
} finally {
fps.close();
}
}
@Override
public List<Contact> getContactsByListId(long eventId, long listId, boolean allowEmail, boolean allowText) {
try {
List<? extends FlexMap> persons = this.getByContactListIds(eventId, listId);
List<Contact> contacts = new ArrayList<>(persons.size());
for (FlexMap person : persons)
contacts.add(new Contact(person, allowEmail, allowText));
return contacts;
} catch (MessagingException me) {
throw new ServiceException(me);
}
}
@Override
public List<Contact> getContactsByAutolist(long eventId, String autolist, boolean allowEmail, boolean allowText) {
try {
List<? extends FlexMap> persons = this.getByAutolist(eventId, autolist);
List<Contact> contacts = new ArrayList<>(persons.size());
for (FlexMap person : persons)
contacts.add(new Contact(person, allowEmail, allowText));
return contacts;
} catch (MessagingException me) {
throw new ServiceException(me);
}
}
public List<? extends FlexMap> getByContactListIds(long eventId, long listId) {
try {
FlexPreparedStatement fps = this.sqlSelectByListId.buildPreparedStatement();
try {
fps.setIntegerU(1, eventId);
fps.setIntegerU(2, listId);
return fps.executeQuery().getAllRows();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT P.* "
+ "FROM ~g~.EventContactList ECL "
+ " INNER JOIN ~p~.ContactListPerson CLP ON (ECL.listID=CLP.listID) "
+ " INNER JOIN ~p~.Person P ON (CLP.personID=P.personID) "
+ "WHERE ECL.eventID=? AND ECL.listID=? "
)
private StatementProvider sqlSelectByListId;
public List<? extends FlexMap> getByAutolist(long eventId, String autolist) {
switch (autolist) {
case "balance":
Map<Long, DataSet> balances = this.eventFinanceService.getPersonsBalances(eventId, null, -0.01f);
return new ArrayList<>(balances.values());
default:
}
StatementProvider statementProvider = this.getStatementProviderByAutolist(autolist);
try {
FlexPreparedStatement fps = this.getStatementByAutolist(statementProvider, eventId, autolist);
try {
return fps.executeQuery().getAllRows();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
private StatementProvider getStatementProviderByAutolist(String autolist) {
switch (autolist) {
case "all":
return this.sqlSelectEventAllIds;
case "signed":
return this.sqlSelectEventParticipantIds;
case "unsigned":
return this.sqlSelectEventRumormillIds;
case "rookies":
return this.sqlSelectEventRookieIds;
case "rookies-refs":
return this.sqlSelectEventRookieReferralIds;
case "options":
return this.sqlSelectEventParticipantIdsWithUnansweredOptions;
default:
}
if (autolist.startsWith("previous")) {
if (autolist.length() > "previous".length()) {
return this.sqlSelectRecentSeriesParticipantIds;
} else {
return this.sqlSelectSeriesParticipantIds;
}
} else {
throw new IllegalArgumentException();
}
}
private FlexPreparedStatement getStatementByAutolist(StatementProvider statementProvider, long eventId,
String autolist) throws SQLException {
int weeks = -1;
if (autolist.startsWith("previous") && autolist.length() > "previous".length())
weeks = Integer.parseInt(autolist.substring("previous".length() + 1));
FlexPreparedStatement fps = statementProvider.buildPreparedStatement();
try {
int c = 1;
fps.setIntegerU(c++, eventId);
if (weeks >= 0) {
fps.setIntegerU(c++, eventId);
fps.setSmallintU(c++, weeks);
fps.setSmallintU(c++, weeks);
}
if (autolist.equals("all") || autolist.equals("rookies-refs") || autolist.startsWith("previous")) {
fps.setIntegerU(c++, eventId);
}
return fps;
} catch (SQLException se) {
fps.close();
throw se;
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT personID, epersonID FROM ~g~.EventPerson WHERE eventID=?")
private StatementProvider sqlSelectEventParticipantIds;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT personID, NULL epersonID FROM ~g~.EventPersonContract WHERE eventID=?")
private StatementProvider sqlSelectEventRumormillIds;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT personID, epersonID FROM ~g~.EventPerson WHERE eventID=? "
+ "UNION "
+ "SELECT personID, NULL epersonID FROM ~g~.EventPersonContract WHERE eventID=? "
)
private StatementProvider sqlSelectEventAllIds;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT DISTINCT EP.personID, EP.epersonID "
+ "FROM ~g~.EventPerson EP "
+ " LEFT JOIN ~g~.EventPerson EP2 ON (EP.eventID<>EP2.eventID AND EP.personID=EP2.personID) "
+ "WHERE EP.eventID=? AND EP2.epersonID IS NULL"
)
private StatementProvider sqlSelectEventRookieIds;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT DISTINCT EP.personID "
+ "FROM ~g~.EventPerson EP "
+ " LEFT JOIN ~g~.EventPerson EP2 ON (EP.eventID<>EP2.eventID AND EP.personID=EP2.personID) "
+ "WHERE EP.eventID=? AND EP2.epersonID IS NULL "
+ "UNION "
+ "SELECT DISTINCT P.referralPersonID "
+ "FROM ~g~.EventPerson EP "
+ " LEFT JOIN ~g~.EventPerson EP2 ON (EP.eventID<>EP2.eventID AND EP.personID=EP2.personID) "
+ " INNER JOIN ~p~.Person P ON (EP.personID=P.personID) "
+ "WHERE EP.eventID=? AND EP2.epersonID IS NULL AND P.referralPersonID IS NOT NULL"
)
private StatementProvider sqlSelectEventRookieReferralIds;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT EP.personID, EP.epersonID "
+ "FROM ~g~.EventPerson EP "
+ " INNER JOIN ?TT? TT1 ON (EP.personID=TT1.personID) "
+ "WHERE EP.eventID=? "
+ "GROUP BY EP.personID HAVING SUM(TT1.balance) < 0 "
)
private StatementProvider sqlSelectEventParticipantIdsWithBalance;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT DISTINCT EP.personID, EP.epersonID "
+ "FROM ~g~.EventOption EO "
+ " INNER JOIN ~g~.EventPerson EP ON (EO.eventID=EP.eventID) "
+ " LEFT JOIN ~g~.EventPersonOption EPO ON (EO.optionID=EPO.optionID AND EP.epersonID=EPO.epersonID) "
+ "WHERE EO.eventID=? "
+ " AND (EO.liveline IS NULL OR EO.liveline<=CURRENT_DATE) "
+ " AND (EO.deadline IS NULL OR EO.deadline>=CURRENT_DATE) "
+ " AND EPO.optionID IS NULL "
)
private StatementProvider sqlSelectEventParticipantIdsWithUnansweredOptions;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT DISTINCT EP.personID "
+ "FROM ~g~.Event E "
+ " INNER JOIN ~g~.Event E2 ON (E.seriesID=E2.seriesID) "
+ " INNER JOIN ~g~.EventPerson EP ON (E2.eventID=EP.eventID) "
+ "WHERE E.eventID=? AND E2.eventID<>? "
+ "UNION DISTINCT "
+ "SELECT personID FROM ~g~.EventPersonContract WHERE eventID=? "
)
private StatementProvider sqlSelectSeriesParticipantIds;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT DISTINCT EP.personID "
+ "FROM ~g~.Event E "
+ " INNER JOIN ~g~.Event E2 ON (E.seriesID=E2.seriesID) "
+ " INNER JOIN ~g~.EventPerson EP ON (E2.eventID=EP.eventID) "
+ "WHERE E.eventID=? AND E2.eventID<>? "
+ " AND ((E2.deadline IS NOT NULL AND DATE_ADD(E2.deadline, INTERVAL ? WEEK)>=CURRENT_DATE) "
+ " OR (E2.liveline IS NOT NULL AND DATE_ADD(E2.liveline, INTERVAL ? WEEK)>=CURRENT_DATE)) "
+ "UNION DISTINCT "
+ "SELECT personID FROM ~g~.EventPersonContract WHERE eventID=? "
)
private StatementProvider sqlSelectRecentSeriesParticipantIds;
@Override
protected DataSet fetchOne(BigInteger epersonId) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectEventPersonByIds.buildPreparedStatement();
try {
fps.setBigintU(1, epersonId);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT * FROM ~g~.EventPerson WHERE epersonID=?")
private StatementProvider sqlSelectEventPersonById;
@Override
protected Map<BigInteger, DataSet> fetchBulk(Collection<BigInteger> epersonIds) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectEventPersonByIds.buildPreparedStatement(epersonIds);
try {
return fps.executeQuery().getAllRows("epersonID", BigInteger.class);
} finally {
fps.close();
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT * FROM ~g~.EventPerson WHERE epersonID IN (??)")
private StatementProvider sqlSelectEventPersonByIds;
}

View File

@@ -1,108 +0,0 @@
package com.poststats.golf.service.db;
import java.math.BigInteger;
import java.sql.SQLException;
import java.util.List;
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;
@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;
@Override
public List<DataSet> getParticipantsByRoundId(long eroundId) {
try {
FlexPreparedStatement fps = this.sqlSelectParticipantsByRoundId.buildPreparedStatement();
try {
fps.setIntegerU(1, eroundId);
return fps.executeQuery().getAllRows(new FlexManyToOneDef("pairingID", "pairing"),
new FlexManyToOneDef("nineID", "pairing", "nine"));
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT EPR.epersonID, EP.personID, "
+ " ERP.pairingID, ERP.teetime, ERP.eroundID, ERP.courseID, ERP.number, "
+ " CN.nineID, CN.nine, CN.courseID "
+ "FROM ~g~.EventRoundPairing ERP "
+ " INNER JOIN ~g~.EventPersonRound EPR ON (ERP.pairingID=EPR.pairingID) "
+ " INNER JOIN ~g~.EventPerson EP ON (EPR.epersonID=EP.epersonID) "
+ " LEFT JOIN ~g~.CourseNine CN ON (ERP.nineID=CN.nineID) "
+ "WHERE ERP.eroundID=? "
)
private StatementProvider sqlSelectParticipantsByRoundId;
}

View File

@@ -1,138 +0,0 @@
package com.poststats.golf.service.db;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.util.FlexMap;
import com.poststats.golf.provider.GolfProvider;
import com.poststats.golf.service.EventRoundService;
import com.poststats.provider.NonTransactionalProvider;
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;
@ApplicationScoped
public class EventRoundServiceDAO extends CacheableServiceDAO<Long> implements EventRoundService {
private final long defaultCacheExpirationInSeconds = 3600;
@Override
public FlexMap get(long eroundId) {
return this.get(Long.valueOf(eroundId));
}
@Override
public List<? extends FlexMap> getUpcoming(long eventId) {
LocalDateTime now = LocalDateTime.now();
if (now.getHour() > 17)
now.plusDays(1);
try {
FlexPreparedStatement fps = this.sqlSelectUpcoming.buildPreparedStatement();
try {
fps.setIntegerU(1, eventId);
fps.setDate(2, now.toLocalDate());
return fps.executeQuery().getAllRows();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT TF.*, ER.* "
+ "FROM ~g~.EventRound ER "
+ " LEFT JOIN ~g~.TeeFormat TF ON (ER.teeFormatID=TF.teeFormatID) "
+ "WHERE ER.eventID=? AND ER.date>=? AND MIN(ER.date)=ER.date "
)
private StatementProvider sqlSelectUpcoming;
@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
@NonTransactionalProvider
@GolfProvider
@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
@NonTransactionalProvider
@GolfProvider
@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
@NonTransactionalProvider
@GolfProvider
@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

@@ -1,148 +0,0 @@
package com.poststats.golf.service.db;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.util.FlexMap;
import com.poststats.golf.provider.GolfProvider;
import com.poststats.golf.service.EventService;
import com.poststats.provider.NonTransactionalProvider;
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;
@ApplicationScoped
public class EventServiceDAO extends CacheableServiceDAO<Long> implements EventService {
private final long defaultCacheExpirationInSeconds = 3600;
@Override
public FlexMap get(long eventId) {
return this.get(Long.valueOf(eventId));
}
@Override
public Set<Long> getIdsBySeriesId(int seriesId) {
try {
FlexPreparedStatement fps = this.sqlSelectIdsBySeriesId.buildPreparedStatement();
try {
fps.setSmallintU(1, seriesId);
return fps.executeQuery().getFirstColumn(Long.class, new HashSet<>());
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT eventId FROM ~g~.Event WHERE seriesID=? ")
private StatementProvider sqlSelectIdsBySeriesId;
@Override
public Map<Long, DataSet> getBySeriesId(int seriesId) {
try {
FlexPreparedStatement fps = this.sqlSelectBySeriesId.buildPreparedStatement();
try {
fps.setSmallintU(1, seriesId);
return fps.executeQuery().getAllRows("eventID", Long.class);
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT * FROM ~g~.Event WHERE seriesID=? ORDER BY liveline DESC ")
private StatementProvider sqlSelectBySeriesId;
@Override
public Integer getSeriesId(long eventId) {
FlexMap event = this.getIfHit(eventId);
if (event != null)
return event.getInteger("seriesID");
try {
FlexPreparedStatement fps = this.sqlSelectSeriesId.buildPreparedStatement();
try {
fps.setSmallintU(1, eventId);
return fps.executeQuery().getOne(Integer.class);
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT seriesID FROM ~g~.Event WHERE eventID=? ")
private StatementProvider sqlSelectSeriesId;
@Override
protected long getCacheExpirationInSeconds() {
return this.defaultCacheExpirationInSeconds;
}
@Override
protected DataSet fetchOne(Long eventId) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectEvent.buildPreparedStatement();
try {
fps.setIntegerU(1, eventId);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT EF.*, E.* "
+ "FROM ~g~.Event E "
+ " INNER JOIN ~g~.EventFeature EF ON (E.eventID=EF.eventID) "
+ "WHERE E.eventID=? "
)
private StatementProvider sqlSelectEvent;
@Override
protected Map<Long, DataSet> fetchBulk(Collection<Long> eventIds) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectEvents.buildPreparedStatement(eventIds);
try {
return fps.executeQuery().getAllRows("eventID", Long.class);
} finally {
fps.close();
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT EF.*, E.* "
+ "FROM ~g~.Event E "
+ " INNER JOIN ~g~.EventFeature EF ON (E.eventID=EF.eventID) "
+ "WHERE E.eventID IN (??) "
)
private StatementProvider sqlSelectEvents;
}

View File

@@ -1,740 +0,0 @@
package com.poststats.golf.service.db;
import java.math.BigInteger;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.util.FlexMap;
import com.poststats.golf.provider.GolfProvider;
import com.poststats.golf.service.PersonRoundService;
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;
@ApplicationScoped
public class PersonRoundServiceDAO implements PersonRoundService {
@Override
public FlexMap fetchNonEvent(BigInteger roundId, Selection selection, Filter... filters) {
StatementProvider stmt = this.getNonEventRoundStatementProvider(selection, filters);
try {
FlexPreparedStatement fps = stmt.buildPreparedStatement();
try {
for (int c = 1; c <= fps.getPreparedStatement().getParameterMetaData().getParameterCount(); c++)
fps.setBigintU(c, roundId);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Override
public FlexMap fetchEvent(BigInteger proundId, Selection selection, Filter... filters) {
StatementProvider stmt = this.getEventRoundStatementProvider(selection, filters);
try {
FlexPreparedStatement fps = stmt.buildPreparedStatement();
try {
for (int c = 1; c <= fps.getPreparedStatement().getParameterMetaData().getParameterCount(); c++)
fps.setBigintU(c, proundId);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Override
public List<? extends FlexMap> fetchNonEventHoles(BigInteger roundId, Selection selection) {
StatementProvider stmt = this.getNonEventRoundHolesStatementProvider(selection);
try {
FlexPreparedStatement fps = stmt.buildPreparedStatement();
try {
for (int c = 1; c <= fps.getPreparedStatement().getParameterMetaData().getParameterCount(); c++)
fps.setBigintU(c, roundId);
return fps.executeQuery().getAllRows();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Override
public List<? extends FlexMap> fetchEventHoles(BigInteger proundId, Selection selection) {
StatementProvider stmt = this.getEventRoundHolesStatementProvider(selection);
try {
FlexPreparedStatement fps = stmt.buildPreparedStatement();
try {
for (int c = 1; c <= fps.getPreparedStatement().getParameterMetaData().getParameterCount(); c++)
fps.setBigintU(c, proundId);
return fps.executeQuery().getAllRows();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Override
public List<? extends FlexMap> findBefore(long personId, LocalDate beforeDay, short roundCount, Selection selection,
Filter... filters) {
StatementProvider stmt = this.getRoundsStatementProvider(selection, filters);
try {
FlexPreparedStatement fps = stmt.buildPreparedStatement();
try {
fps.setIntegerU(1, personId); // for non-event rounds
fps.setDate(2, beforeDay);
fps.setIntegerU(3, personId); // for 18-hole event rounds
fps.setDate(4, beforeDay);
fps.setIntegerU(5, personId); // for 9-hole event rounds
fps.setDate(6, beforeDay);
fps.setSmallint(7, roundCount); // limit
return fps.executeQuery().getAllRows();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
private StatementProvider getNonEventRoundStatementProvider(Selection selection, Filter... filters) {
Set<Filter> filterSet = new HashSet<>();
for (Filter filter : filters)
filterSet.add(filter);
switch (selection) {
case StrokeHandicapIndex:
if (filterSet.contains(Filter.AttestedOnly)) {
if (filterSet.contains(Filter.CourseRatedOnly)) {
return this.sqlSelectSignedRatedNonEventRoundWithStrokeHandicap;
} else {
return this.sqlSelectSignedNonEventRoundWithStrokeHandicap;
}
} else {
if (filterSet.contains(Filter.CourseRatedOnly)) {
return this.sqlSelectRatedNonEventRoundWithStrokeHandicap;
} else {
return this.sqlSelectNonEventRoundWithStrokeHandicap;
}
}
case ScoreToPar:
if (filterSet.contains(Filter.AttestedOnly)) {
if (filterSet.contains(Filter.CourseRatedOnly)) {
return this.sqlSelectSignedRatedNonEventRoundWithScoreToPar;
} else {
return this.sqlSelectSignedNonEventRoundWithScoreToPar;
}
} else {
if (filterSet.contains(Filter.CourseRatedOnly)) {
return this.sqlSelectRatedNonEventRoundWithScoreToPar;
} else {
return this.sqlSelectNonEventRoundWithScoreToPar;
}
}
default:
throw new IllegalArgumentException();
}
}
private StatementProvider getEventRoundStatementProvider(Selection selection, Filter... filters) {
Set<Filter> filterSet = new HashSet<>();
for (Filter filter : filters)
filterSet.add(filter);
switch (selection) {
case StrokeHandicapIndex:
if (filterSet.contains(Filter.AttestedOnly)) {
if (filterSet.contains(Filter.CourseRatedOnly)) {
return this.sqlSelectSignedRatedEventRoundWithStrokeHandicap;
} else {
return this.sqlSelectSignedEventRoundWithStrokeHandicap;
}
} else {
if (filterSet.contains(Filter.CourseRatedOnly)) {
return this.sqlSelectRatedEventRoundWithStrokeHandicap;
} else {
return this.sqlSelectEventRoundWithStrokeHandicap;
}
}
case ScoreToPar:
if (filterSet.contains(Filter.AttestedOnly)) {
if (filterSet.contains(Filter.CourseRatedOnly)) {
return this.sqlSelectSignedRatedEventRoundWithScoreToPar;
} else {
return this.sqlSelectSignedEventRoundWithScoreToPar;
}
} else {
if (filterSet.contains(Filter.CourseRatedOnly)) {
return this.sqlSelectRatedEventRoundWithScoreToPar;
} else {
return this.sqlSelectEventRoundWithScoreToPar;
}
}
default:
throw new IllegalArgumentException();
}
}
private StatementProvider getNonEventRoundHolesStatementProvider(Selection selection) {
switch (selection) {
case StrokeHandicapIndex:
return this.sqlSelectNonEventRoundHolesWithStrokeHandicap;
case ScoreToPar:
return this.sqlSelectNonEventRoundHolesWithScoreToPar;
default:
throw new IllegalArgumentException();
}
}
private StatementProvider getEventRoundHolesStatementProvider(Selection selection) {
switch (selection) {
case StrokeHandicapIndex:
return this.sqlSelectEventRoundHolesWithStrokeHandicap;
case ScoreToPar:
return this.sqlSelectEventRoundHolesWithScoreToPar;
default:
throw new IllegalArgumentException();
}
}
private StatementProvider getRoundsStatementProvider(Selection selection, Filter... filters) {
Set<Filter> filterSet = new HashSet<>();
for (Filter filter : filters)
filterSet.add(filter);
switch (selection) {
case StrokeHandicapIndex:
if (filterSet.contains(Filter.AttestedOnly)) {
if (filterSet.contains(Filter.CourseRatedOnly)) {
return this.sqlSelectSignedRatedRoundsWithStrokeHandicap;
} else {
return this.sqlSelectSignedRoundsWithStrokeHandicap;
}
} else {
if (filterSet.contains(Filter.CourseRatedOnly)) {
return this.sqlSelectRatedRoundsWithStrokeHandicap;
} else {
return this.sqlSelectRoundsWithStrokeHandicap;
}
}
case ScoreToPar:
if (filterSet.contains(Filter.AttestedOnly)) {
if (filterSet.contains(Filter.CourseRatedOnly)) {
return this.sqlSelectSignedRatedRoundsWithScoreToPar;
} else {
return this.sqlSelectSignedRoundsWithScoreToPar;
}
} else {
if (filterSet.contains(Filter.CourseRatedOnly)) {
return this.sqlSelectRatedRoundsWithScoreToPar;
} else {
return this.sqlSelectRoundsWithScoreToPar;
}
}
default:
throw new IllegalArgumentException();
}
}
private final static String nonEventRoundSqlSelectClause = "SELECT R.roundID, NULL proundID, NULL linkProundID, R.personID, R.etratingID, R.courseID, R.teedate, R.teetime, R.strokes, ";
private final static String nonEventRoundSqlFromClause = "FROM ~g~.Round R "
+ " INNER JOIN ~p~.Person P ON (R.personID=P.personID) ";
private final static String nonEventRoundSqlWhereClause = "WHERE R.roundID=? ";
private final static String nonEventRoundsSqlWhereClause = "WHERE R.personID=? AND R.complete IS TRUE AND R.teedate<? "
+ " AND (P.healthSetback IS NULL OR R.teedate>P.healthSetback) ";
private final static String event18RoundSqlSelectClause = "SELECT NULL roundID, EPR.proundID, NULL linkProundID, EP.personID, EPR.etratingID, EPR.courseID, ER.date, ERP.teetime, EPR.strokes, ";
private final static String event18RoundSqlFromClause = "FROM ~g~.EventPersonRound EPR "
+ " INNER JOIN ~g~.EventRound ER ON (EPR.eroundID=ER.eroundID) "
+ " INNER JOIN ~g~.EventPerson EP ON (EPR.epersonID=EP.epersonID) "
+ " INNER JOIN ~p~.Person P ON (EP.personID=P.personID) "
+ " LEFT JOIN ~g~.EventRoundPairing ERP ON (EPR.pairingID=ERP.pairingID) ";
private final static String event18RoundSqlWhereClause = "WHERE EPR.proundID=? ";
private final static String event18RoundsSqlWhereClause = "WHERE EP.personID=? AND EPR.complete IS TRUE AND EPR.etratingID IS NOT NULL AND ER.date<? "
+ " AND (P.healthSetback IS NULL OR ER.date>P.healthSetback) ";
private final static String event9RoundSqlSelectClause = "SELECT NULL roundID, EPR.proundID, EPR.linkProundID, EP.personID, CETR.etratingID, EPR.courseID, ER.date, ERP.teetime, "
+ " (EPR.strokes+EPR2.strokes) strokes, ";
private final static String event9RoundSqlFromClause = "FROM ~g~.EventPersonRound EPR "
+ " INNER JOIN ~g~.EventPersonRound EPR2 ON (EPR.linkProundID=EPR2.proundID AND EPR.proundID>EPR2.proundID) "
+ " INNER JOIN ~g~.CourseNineTeeRating CNTR1 ON (EPR.ntratingID=CNTR1.ntratingID) "
+ " INNER JOIN ~g~.CourseNineTeeRating CNTR2 ON (EPR2.ntratingID=CNTR2.ntratingID) "
+ " INNER JOIN ~g~.CourseEighteenTee CET ON ((CNTR1.nineteeID=CET.nineteeID1 AND CNTR2.nineteeID=CET.nineteeID2) "
+ " OR (CNTR2.nineteeID=CET.nineteeID1 AND CNTR1.nineteeID=CET.nineteeID2)) "
+ " INNER JOIN ~g~.CourseEighteenTeeRating CETR ON (CET.eighteenteeID=CETR.eighteenteeID "
+ " AND CNTR1.gender=CETR.gender "
+ " AND CETR.liveline<=ER.date AND (CETR.deadline IS NULL OR ER.date<=CETR.deadline)) "
+ " INNER JOIN ~g~.EventRound ER ON (EPR.eroundID=ER.eroundID) "
+ " INNER JOIN ~g~.EventPerson EP ON (EPR.epersonID=EP.epersonID) "
+ " INNER JOIN ~p~.Person P ON (EP.personID=P.personID) "
+ " LEFT JOIN ~g~.EventRoundPairing ERP ON (EPR.pairingID=ERP.pairingID) ";
private final static String event9RoundSqlWhereClause = "WHERE EPR.proundID=? ";
private final static String event9RoundsSqlWhereClause = "WHERE EP.personID=? AND EPR.complete IS TRUE AND ER.date<? "
+ " AND (P.healthSetback IS NULL OR ER.date>P.healthSetback) ";
private final static String nonEventRoundSqlSelectStrokeHandicap = nonEventRoundSqlSelectClause
+ " R.strokeHandicapIndex, R.whsStrokeHandicapIndex "
+ nonEventRoundSqlFromClause
+ nonEventRoundSqlWhereClause;
private final static String nonEventRoundsSqlSelectStrokeHandicap = nonEventRoundSqlSelectClause
+ " R.strokeHandicapIndex, R.whsStrokeHandicapIndex "
+ nonEventRoundSqlFromClause
+ nonEventRoundsSqlWhereClause;
private final static String event18RoundSqlSelectStrokeHandicap = event18RoundSqlSelectClause
+ " EPR.strokeHandicapIndex, EPR.whsStrokeHandicapIndex "
+ event18RoundSqlFromClause
+ event18RoundSqlWhereClause;
private final static String event18RoundsSqlSelectStrokeHandicap = event18RoundSqlSelectClause
+ " EPR.strokeHandicapIndex, EPR.whsStrokeHandicapIndex "
+ event18RoundSqlFromClause
+ event18RoundsSqlWhereClause;
private final static String event9RoundSqlSelectStrokeHandicap = event9RoundSqlSelectClause
+ " (EPR.strokeHandicapIndex+EPR2.strokeHandicapIndex) strokeHandicapIndex, "
+ " (EPR.whsStrokeHandicapIndex+EPR2.whsStrokeHandicapIndex) whsStrokeHandicapIndex "
+ event9RoundSqlFromClause
+ event9RoundSqlWhereClause;
private final static String event9RoundsSqlSelectStrokeHandicap = event9RoundSqlSelectClause
+ " (EPR.strokeHandicapIndex+EPR2.strokeHandicapIndex) strokeHandicapIndex, "
+ " (EPR.whsStrokeHandicapIndex+EPR2.whsStrokeHandicapIndex) whsStrokeHandicapIndex "
+ event9RoundSqlFromClause
+ event9RoundsSqlWhereClause;
private final static String nonEventRoundSqlSelectPointsClause =
" SUM(CASE WHEN (RX.strokes - CNTH.par) = 5 THEN 1 ELSE 0 END) bogey5, "
+ " SUM(CASE WHEN (RX.strokes - CNTH.par) = 4 THEN 1 ELSE 0 END) bogey4, "
+ " SUM(CASE WHEN (RX.strokes - CNTH.par) = 3 THEN 1 ELSE 0 END) bogey3, "
+ " SUM(CASE WHEN (RX.strokes - CNTH.par) = 2 THEN 1 ELSE 0 END) bogey2, "
+ " SUM(CASE WHEN (RX.strokes - CNTH.par) = 1 THEN 1 ELSE 0 END) bogey, "
+ " SUM(CASE WHEN RX.strokes = CNTH.par THEN 1 ELSE 0 END) par, "
+ " SUM(CASE WHEN (CNTH.par - RX.strokes) = 1 THEN 1 ELSE 0 END) birdie, "
+ " SUM(CASE WHEN (CNTH.par - RX.strokes) = 2 THEN 1 ELSE 0 END) eagle, "
+ " SUM(CASE WHEN (CNTH.par - RX.strokes) = 3 THEN 1 ELSE 0 END) alby, ";
private final static String nonEventRoundSqlFromPointsClause =
" INNER JOIN ~g~.RoundScore RX ON (R.roundID=RX.roundID) "
+ " INNER JOIN ~g~.CourseNineTeeHole CNTH ON (RX.holeID=CNTH.holeID) "
+ " INNER JOIN ~g~.CourseEighteenTeeRating CETR ON (R.etratingID=CETR.etratingID) ";
private final static String nonEventRoundSqlSelectScoreToPar = nonEventRoundSqlSelectClause
+ nonEventRoundSqlSelectPointsClause
+ " CETR.pointAdj "
+ nonEventRoundSqlFromClause
+ nonEventRoundSqlFromPointsClause
+ nonEventRoundSqlWhereClause
+ "GROUP BY R.roundID ";
private final static String nonEventRoundsSqlSelectScoreToPar = nonEventRoundSqlSelectClause
+ nonEventRoundSqlSelectPointsClause
+ " CETR.pointAdj "
+ nonEventRoundSqlFromClause
+ nonEventRoundSqlFromPointsClause
+ nonEventRoundsSqlWhereClause
+ "GROUP BY R.roundID ";
private final static String eventRoundSqlSelectPointsClause =
"SUM(CASE WHEN (EPRS.strokes - CNTH.par) = 5 THEN 1 ELSE 0 END) bogey5, "
+ " SUM(CASE WHEN (EPRS.strokes - CNTH.par) = 4 THEN 1 ELSE 0 END) bogey4, "
+ " SUM(CASE WHEN (EPRS.strokes - CNTH.par) = 3 THEN 1 ELSE 0 END) bogey3, "
+ " SUM(CASE WHEN (EPRS.strokes - CNTH.par) = 2 THEN 1 ELSE 0 END) bogey2, "
+ " SUM(CASE WHEN (EPRS.strokes - CNTH.par) = 1 THEN 1 ELSE 0 END) bogey, "
+ " SUM(CASE WHEN EPRS.strokes = CNTH.par THEN 1 ELSE 0 END) par, "
+ " SUM(CASE WHEN (CNTH.par - EPRS.strokes) = 1 THEN 1 ELSE 0 END) birdie, "
+ " SUM(CASE WHEN (CNTH.par - EPRS.strokes) = 2 THEN 1 ELSE 0 END) eagle, "
+ " SUM(CASE WHEN (CNTH.par - EPRS.strokes) = 3 THEN 1 ELSE 0 END) alby, ";
private final static String event18RoundSqlFromPointsClause =
" INNER JOIN ~g~.EventPersonRoundScore EPRS ON (EPR.proundID=EPRS.proundID) "
+ " INNER JOIN ~g~.CourseNineTeeHole CNTH ON (EPRS.holeID=CNTH.holeID) "
+ " INNER JOIN ~g~.CourseEighteenTeeRating CETR ON (EPR.etratingID=CETR.etratingID) ";
private final static String event9RoundSqlFromPointsClause =
" INNER JOIN ~g~.EventPersonRoundScore EPRS ON (EPR.proundID=EPRS.proundID OR EPR.linkProundID=EPRS.proundID) "
+ " INNER JOIN ~g~.CourseNineTeeHole CNTH ON (EPRS.holeID=CNTH.holeID) "
+ " INNER JOIN ~g~.CourseNineTeeRating CNTR ON (EPR.ntratingID=CNTR.ntratingID) ";
private final static String event18RoundSqlSelectScoreToPar = event18RoundSqlSelectClause
+ eventRoundSqlSelectPointsClause
+ " CETR.pointAdj "
+ event18RoundSqlFromClause
+ event18RoundSqlFromPointsClause
+ event18RoundSqlWhereClause
+ "GROUP BY EPR.proundID ";
private final static String event18RoundsSqlSelectScoreToPar = event18RoundSqlSelectClause
+ eventRoundSqlSelectPointsClause
+ " CETR.pointAdj "
+ event18RoundSqlFromClause
+ event18RoundSqlFromPointsClause
+ event18RoundsSqlWhereClause
+ "GROUP BY EPR.proundID ";
private final static String event9RoundSqlSelectScoreToPar = event9RoundSqlSelectClause
+ eventRoundSqlSelectPointsClause
+ " CNTR.pointAdj "
+ event9RoundSqlFromClause
+ event9RoundSqlFromPointsClause
+ event9RoundSqlWhereClause
+ "GROUP BY EPR.proundID ";
private final static String event9RoundsSqlSelectScoreToPar = event9RoundSqlSelectClause
+ eventRoundSqlSelectPointsClause
+ " CNTR.pointAdj "
+ event9RoundSqlFromClause
+ event9RoundSqlFromPointsClause
+ event9RoundsSqlWhereClause
+ "GROUP BY EPR.proundID ";
private final static String nonEventRoundHoleSqlSelectClause = "SELECT RX.holeID, RX.strokes, CNTH.par, ";
private final static String nonEventRoundHoleSqlFromClause =
"FROM ~g~.RoundScore RX "
+ " INNER JOIN ~g~.CourseNineTeeHole CNTH ON (RX.holeID=CNTH.holeID) ";
private final static String nonEventRoundHolesSqlSelectStrokeHandicap =
nonEventRoundHoleSqlSelectClause
+ " CNTH.handicap, CNTH.whsHandicap "
+ nonEventRoundHoleSqlFromClause
+ "WHERE RX.roundID=? ";
private final static String nonEventRoundHolesSqlSelectScoreToPar =
nonEventRoundHoleSqlSelectClause
+ nonEventRoundHoleSqlFromClause
+ "WHERE RX.roundID=? ";
private final static String eventRoundHoleSqlSelectClause = "SELECT EPRS.holeID, EPRS.strokes, CNTH.par, ";
private final static String eventRoundHoleSqlFromClause =
"FROM ~g~.EventPersonRoundScore EPRS "
+ " INNER JOIN ~g~.CourseNineTeeHole CNTH ON (EPRS.holeID=CNTH.holeID) "
+ " INNER JOIN ~g~.EventPersonRound EPR ON (EPRS.proundID=EPR.proundID) ";
private final static String eventRoundHolesSqlSelectStrokeHandicap =
eventRoundHoleSqlSelectClause
+ " CNTH.handicap, CNTH.whsHandicap "
+ eventRoundHoleSqlFromClause
+ "WHERE EPRS.proundID=? ";
private final static String eventRoundHolesSqlSelectScoreToPar =
eventRoundHoleSqlSelectClause
+ eventRoundHoleSqlFromClause
+ "WHERE EPRS.roundID=? ";
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = nonEventRoundSqlSelectStrokeHandicap)
private StatementProvider sqlSelectNonEventRoundWithStrokeHandicap;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = event18RoundsSqlSelectStrokeHandicap
+ "UNION "
+ event9RoundsSqlSelectStrokeHandicap
)
private StatementProvider sqlSelectEventRoundWithStrokeHandicap;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = nonEventRoundsSqlSelectStrokeHandicap
+ "UNION "
+ event18RoundsSqlSelectStrokeHandicap
+ "UNION "
+ event9RoundsSqlSelectStrokeHandicap
+ "ORDER BY teedate DESC, teetime DESC "
+ "LIMIT ?"
)
private StatementProvider sqlSelectRoundsWithStrokeHandicap;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = nonEventRoundsSqlSelectStrokeHandicap
+ " AND R.whsStrokeHandicapIndex IS NOT NULL "
)
private StatementProvider sqlSelectRatedNonEventRoundWithStrokeHandicap;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = event18RoundsSqlSelectStrokeHandicap
+ " AND EPR.whsStrokeHandicapIndex IS NOT NULL "
+ "UNION "
+ event9RoundsSqlSelectStrokeHandicap
+ " AND EPR.whsStrokeHandicapIndex IS NOT NULL "
)
private StatementProvider sqlSelectRatedEventRoundWithStrokeHandicap;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = nonEventRoundsSqlSelectStrokeHandicap
+ " AND R.whsStrokeHandicapIndex IS NOT NULL "
+ "UNION "
+ event18RoundsSqlSelectStrokeHandicap
+ " AND EPR.whsStrokeHandicapIndex IS NOT NULL "
+ "UNION "
+ event9RoundsSqlSelectStrokeHandicap
+ " AND EPR.whsStrokeHandicapIndex IS NOT NULL "
+ "ORDER BY teedate DESC, teetime DESC "
+ "LIMIT ?"
)
private StatementProvider sqlSelectRatedRoundsWithStrokeHandicap;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = nonEventRoundSqlSelectStrokeHandicap
+ " AND EXISTS (SELECT RS.signerID FROM ~g~.RoundSigning RS WHERE RS.roundID=R.roundID) "
)
private StatementProvider sqlSelectSignedNonEventRoundWithStrokeHandicap;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = event18RoundSqlSelectStrokeHandicap
+ "UNION "
+ event9RoundSqlSelectStrokeHandicap
)
private StatementProvider sqlSelectSignedEventRoundWithStrokeHandicap;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = nonEventRoundsSqlSelectStrokeHandicap
+ " AND EXISTS (SELECT RS.signerID FROM ~g~.RoundSigning RS WHERE RS.roundID=R.roundID) "
+ "UNION "
+ event18RoundsSqlSelectStrokeHandicap
+ "UNION "
+ event9RoundsSqlSelectStrokeHandicap
+ "ORDER BY teedate DESC, teetime DESC "
+ "LIMIT ?"
)
private StatementProvider sqlSelectSignedRoundsWithStrokeHandicap;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = nonEventRoundSqlSelectStrokeHandicap
+ " AND R.whsStrokeHandicapIndex IS NOT NULL "
+ " AND EXISTS (SELECT RS.signerID FROM ~g~.RoundSigning RS WHERE RS.roundID=R.roundID) "
)
private StatementProvider sqlSelectSignedRatedNonEventRoundWithStrokeHandicap;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = event18RoundSqlSelectStrokeHandicap
+ " AND EPR.whsStrokeHandicapIndex IS NOT NULL "
+ "UNION "
+ event9RoundSqlSelectStrokeHandicap
+ " AND EPR.whsStrokeHandicapIndex IS NOT NULL "
)
private StatementProvider sqlSelectSignedRatedEventRoundWithStrokeHandicap;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = nonEventRoundsSqlSelectStrokeHandicap
+ " AND R.whsStrokeHandicapIndex IS NOT NULL "
+ " AND EXISTS (SELECT RS.signerID FROM ~g~.RoundSigning RS WHERE RS.roundID=R.roundID) "
+ "UNION "
+ event18RoundsSqlSelectStrokeHandicap
+ " AND EPR.whsStrokeHandicapIndex IS NOT NULL "
+ "UNION "
+ event9RoundsSqlSelectStrokeHandicap
+ " AND EPR.whsStrokeHandicapIndex IS NOT NULL "
+ "ORDER BY teedate DESC, teetime DESC "
+ "LIMIT ?"
)
private StatementProvider sqlSelectSignedRatedRoundsWithStrokeHandicap;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = nonEventRoundSqlSelectScoreToPar)
private StatementProvider sqlSelectNonEventRoundWithScoreToPar;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = event18RoundSqlSelectScoreToPar
+ "UNION "
+ event9RoundSqlSelectScoreToPar
)
private StatementProvider sqlSelectEventRoundWithScoreToPar;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = nonEventRoundsSqlSelectScoreToPar
+ "UNION "
+ event18RoundsSqlSelectScoreToPar
+ "UNION "
+ event9RoundsSqlSelectScoreToPar
+ "ORDER BY teedate DESC, teetime DESC "
+ "LIMIT ?"
)
private StatementProvider sqlSelectRoundsWithScoreToPar;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = nonEventRoundSqlSelectScoreToPar
+ " AND R.whsStrokeHandicapIndex IS NOT NULL "
)
private StatementProvider sqlSelectRatedNonEventRoundWithScoreToPar;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = event18RoundSqlSelectScoreToPar
+ " AND EPR.whsStrokeHandicapIndex IS NOT NULL "
+ "UNION "
+ event9RoundSqlSelectScoreToPar
+ " AND EPR.whsStrokeHandicapIndex IS NOT NULL "
)
private StatementProvider sqlSelectRatedEventRoundWithScoreToPar;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = nonEventRoundsSqlSelectScoreToPar
+ " AND R.whsStrokeHandicapIndex IS NOT NULL "
+ "UNION "
+ event18RoundsSqlSelectScoreToPar
+ " AND EPR.whsStrokeHandicapIndex IS NOT NULL "
+ "UNION "
+ event9RoundsSqlSelectScoreToPar
+ " AND EPR.whsStrokeHandicapIndex IS NOT NULL "
+ "ORDER BY teedate DESC, teetime DESC "
+ "LIMIT ?"
)
private StatementProvider sqlSelectRatedRoundsWithScoreToPar;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = nonEventRoundSqlSelectScoreToPar
+ " AND EXISTS (SELECT RS.signerID FROM ~g~.RoundSigning RS WHERE RS.roundID=R.roundID) "
)
private StatementProvider sqlSelectSignedNonEventRoundWithScoreToPar;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = event18RoundSqlSelectScoreToPar
+ "UNION "
+ event9RoundSqlSelectScoreToPar
)
private StatementProvider sqlSelectSignedEventRoundWithScoreToPar;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = nonEventRoundsSqlSelectScoreToPar
+ " AND EXISTS (SELECT RS.signerID FROM ~g~.RoundSigning RS WHERE RS.roundID=R.roundID) "
+ "UNION "
+ event18RoundsSqlSelectScoreToPar
+ "UNION "
+ event9RoundsSqlSelectScoreToPar
+ "ORDER BY teedate DESC, teetime DESC "
+ "LIMIT ?"
)
private StatementProvider sqlSelectSignedRoundsWithScoreToPar;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = nonEventRoundSqlSelectScoreToPar
+ " AND R.whsStrokeHandicapIndex IS NOT NULL "
+ " AND EXISTS (SELECT RS.signerID FROM ~g~.RoundSigning RS WHERE RS.roundID=R.roundID) "
)
private StatementProvider sqlSelectSignedRatedNonEventRoundWithScoreToPar;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = event18RoundSqlSelectScoreToPar
+ " AND EPR.whsStrokeHandicapIndex IS NOT NULL "
+ "UNION "
+ event9RoundSqlSelectScoreToPar
+ " AND EPR.whsStrokeHandicapIndex IS NOT NULL "
)
private StatementProvider sqlSelectSignedRatedEventRoundWithScoreToPar;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = nonEventRoundsSqlSelectScoreToPar
+ " AND R.whsStrokeHandicapIndex IS NOT NULL "
+ " AND EXISTS (SELECT RS.signerID FROM ~g~.RoundSigning RS WHERE RS.roundID=R.roundID) "
+ "UNION "
+ event18RoundsSqlSelectScoreToPar
+ " AND EPR.whsStrokeHandicapIndex IS NOT NULL "
+ "UNION "
+ event9RoundsSqlSelectScoreToPar
+ " AND EPR.whsStrokeHandicapIndex IS NOT NULL "
+ "ORDER BY teedate DESC, teetime DESC "
+ "LIMIT ?"
)
private StatementProvider sqlSelectSignedRatedRoundsWithScoreToPar;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = nonEventRoundHolesSqlSelectStrokeHandicap)
private StatementProvider sqlSelectNonEventRoundHolesWithStrokeHandicap;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = eventRoundHolesSqlSelectStrokeHandicap)
private StatementProvider sqlSelectEventRoundHolesWithStrokeHandicap;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = nonEventRoundHolesSqlSelectScoreToPar)
private StatementProvider sqlSelectNonEventRoundHolesWithScoreToPar;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = eventRoundHolesSqlSelectScoreToPar)
private StatementProvider sqlSelectEventRoundHolesWithScoreToPar;
}

View File

@@ -1,209 +0,0 @@
package com.poststats.golf.service.db;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.brianlong.cache.CacheException;
import com.brianlong.cache.CacheableFetcher;
import com.brianlong.cache.ClusterAwareMemoryCacher;
import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.sql.FlexStatement;
import com.brianlong.util.FlexMap;
import com.poststats.golf.provider.GolfProvider;
import com.poststats.golf.security.AuthenticatedPerson;
import com.poststats.golf.service.PersonService;
import com.poststats.golf.sql.GolfDataSource;
import com.poststats.provider.NonTransactionalProvider;
import com.poststats.provider.Statement;
import com.poststats.provider.StatementProvider;
import com.poststats.service.ServiceException;
import com.poststats.service.db.CacheableServiceDAO;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
@ApplicationScoped
public class PersonServiceDAO extends CacheableServiceDAO<Long> implements PersonService {
@Inject
private com.poststats.service.PersonService poststatsPersonService;
private final int roleCacheExpirationInSeconds = 300;
private final int infoCacheExpirationInSeconds = 120;
private ClusterAwareMemoryCacher<Long, AuthenticatedPerson> principalCacher;
@PostConstruct
private void initCache() {
this.principalCacher = new ClusterAwareMemoryCacher<Long, AuthenticatedPerson>(
this.roleCacheExpirationInSeconds * 1000L, this.clusterService.getHazelcastInstance());
}
@Override
protected long getCacheExpirationInSeconds() {
return this.infoCacheExpirationInSeconds;
}
@Override
public AuthenticatedPerson getUserPrincipal(com.poststats.security.AuthenticatedPerson person) {
try {
return this.principalCacher.get(person.getId(), new PrincipalCacheableFetcher() {
@Override
public com.poststats.security.AuthenticatedPerson getPerson() {
return person;
}
});
} catch (CacheException ce) {
throw new ServiceException(ce);
}
}
@Override
public FlexMap get(long 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> {
public abstract com.poststats.security.AuthenticatedPerson getPerson();
@Override
public AuthenticatedPerson get(Long personId) throws SQLException {
Connection dbcon = GolfDataSource.getInstance().acquire(true);
try {
return new AuthenticatedPerson(this.getPerson(), queryBuddies(personId), queryAccessControls(personId),
queryEventAccessControls(personId));
} finally {
GolfDataSource.getInstance().release(dbcon);
}
}
@Override
public Map<Long, AuthenticatedPerson> get(Collection<Long> personIds) throws SQLException, IOException {
throw new UnsupportedOperationException();
}
private Set<Long> queryBuddies(long personId) throws SQLException {
FlexPreparedStatement fps = sqlSelectBuddyIds.buildPreparedStatement();
try {
fps.setIntegerU(1, personId);
Set<Long> set = new HashSet<>();
fps.executeQuery().getFirstColumn(Long.class, set);
return set;
} finally {
fps.close();
}
}
private Set<String> queryAccessControls(long personId) throws SQLException {
FlexPreparedStatement fps = sqlSelectAcs.buildPreparedStatement();
try {
fps.setIntegerU(1, personId);
Set<String> set = new HashSet<>();
fps.executeQuery().getFirstColumn(String.class, set);
return set;
} finally {
fps.close();
}
}
private Map<Long, Set<String>> queryEventAccessControls(long personId) throws SQLException {
FlexPreparedStatement fps = sqlSelectEventAcs.buildPreparedStatement();
try {
fps.setIntegerU(1, personId);
return fps.executeQuery().getFirstTwoColumnsOneToMany(Long.class, String.class);
} finally {
fps.close();
}
}
};
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT PS1.personID "
+ "FROM ~g~.PersonSpectator PS1 "
+ " INNER JOIN ~g~.PersonSpectator PS2 ON (PS1.personID=PS2.watchedPersonID AND PS1.watchedPersonID=PS2.personID) "
+ "WHERE PS1.watchedPersonID=?"
)
private StatementProvider sqlSelectBuddyIds;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT AC.acSID "
+ "FROM ~g~.PersonAccessControl PAC "
+ " INNER JOIN ~p~.AccessControl AC ON (PAC.acID=AC.acID) "
+ "WHERE PAC.personID=?"
)
private StatementProvider sqlSelectAcs;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(
sql = "SELECT EPAC.eventID, AC.acSID "
+ "FROM ~g~.EventPersonAccessControl EPAC "
+ " INNER JOIN ~p~.AccessControl AC ON (EPAC.acID=AC.acID) "
+ "WHERE EPAC.personID=?"
)
private StatementProvider sqlSelectEventAcs;
@Override
protected DataSet fetchOne(Long personId) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectPerson.buildPreparedStatement();
try {
fps.setIntegerU(1, personId);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT GP.* FROM ~g~.Person GP WHERE GP.personID=?")
private StatementProvider sqlSelectPerson;
@Override
protected Map<Long, DataSet> fetchBulk(Collection<Long> personIds) throws SQLException {
FlexStatement fs = this.sqlSelectPersons.buildStatement(personIds);
try {
return fs.executeQuery().getAllRows("personID", Long.class);
} finally {
fs.close();
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT GP.* FROM ~g~.Person GP WHERE GP.personID IN (??)")
private StatementProvider sqlSelectPersons;
}

View File

@@ -1,90 +0,0 @@
package com.poststats.golf.service.db;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Map;
import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.util.FlexMap;
import com.poststats.golf.provider.GolfProvider;
import com.poststats.golf.service.SeriesService;
import com.poststats.provider.NonTransactionalProvider;
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;
@ApplicationScoped
public class SeriesServiceDAO extends CacheableServiceDAO<Integer> implements SeriesService {
private final long defaultCacheExpirationInSeconds = 3600;
@Override
public FlexMap get(int seriesId) {
return this.get(Integer.valueOf(seriesId));
}
@Override
public DataSet getByEventId(long eventId) {
try {
FlexPreparedStatement fps = this.sqlSelectByEventId.buildPreparedStatement();
try {
fps.setIntegerU(1, eventId);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT * FROM ~g~.Series WHERE eventID=? ")
private StatementProvider sqlSelectByEventId;
@Override
protected long getCacheExpirationInSeconds() {
return this.defaultCacheExpirationInSeconds;
}
@Override
protected DataSet fetchOne(Integer seriesId) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectSeries.buildPreparedStatement();
try {
fps.setSmallintU(1, seriesId);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT S.* FROM ~g~.Series S WHERE seriesID=? ")
private StatementProvider sqlSelectSeries;
@Override
protected Map<Integer, DataSet> fetchBulk(Collection<Integer> seriesIds) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectSerieses.buildPreparedStatement(seriesIds);
try {
return fps.executeQuery().getAllRows("seriesID", Integer.class);
} finally {
fps.close();
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT S.* FROM ~g~.Series S WHERE seriesID IN (??) ")
private StatementProvider sqlSelectSerieses;
}

View File

@@ -1,404 +0,0 @@
package com.poststats.golf.service.model;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PointHandicapIndex implements Comparable<PointHandicapIndex> {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final int pointIndexId;
public PointHandicapIndex(int pointIndexId) {
this.pointIndexId = pointIndexId;
}
public PointHandicapIndex(byte alby, byte eagle, byte birdie, byte par, byte bogey, byte bogey2, byte bogey3,
byte bogey4, byte bogey5) {
int pointIndexId = 0;
if (bogey5 > 1)
throw new IllegalArgumentException();
pointIndexId <<= this.getBits((byte) 5);
pointIndexId += bogey5;
if (bogey4 > 3 || bogey4 > 0 && bogey5 >= bogey4)
throw new IllegalArgumentException();
pointIndexId <<= this.getBits((byte) 4);
pointIndexId += bogey4;
if (bogey3 > 3 || bogey3 > 0 && bogey4 >= bogey3)
throw new IllegalArgumentException();
pointIndexId <<= this.getBits((byte) 3);
pointIndexId += bogey3;
if (bogey2 > 7 || bogey2 > 0 && bogey3 >= bogey2)
throw new IllegalArgumentException();
pointIndexId <<= this.getBits((byte) 2);
pointIndexId += bogey2;
if (bogey > 7 || bogey > 0 && bogey2 >= bogey)
throw new IllegalArgumentException();
pointIndexId <<= this.getBits((byte) 1);
pointIndexId += bogey;
if (par > 15 || par > 0 && bogey >= par)
throw new IllegalArgumentException();
pointIndexId <<= this.getBits((byte) 0);
pointIndexId += par;
if (birdie > 15 || birdie > 0 && par >= birdie)
throw new IllegalArgumentException();
pointIndexId <<= this.getBits((byte) -1);
pointIndexId += birdie;
if (eagle > 31 || eagle > 0 && birdie >= eagle)
throw new IllegalArgumentException();
pointIndexId <<= this.getBits((byte) -2);
pointIndexId += eagle;
if (alby > 63 || alby > 0 && eagle >= alby)
throw new IllegalArgumentException();
pointIndexId <<= this.getBits((byte) -3);
pointIndexId += alby;
this.pointIndexId = pointIndexId;
}
public PointHandicapIndex(int alby, int eagle, int birdie, int par, int bogey, int bogey2, int bogey3, int bogey4,
int bogey5) {
int pointIndexId = 0;
if (bogey5 > 1)
throw new IllegalArgumentException();
pointIndexId <<= this.getBits((byte) 5);
pointIndexId += bogey5;
if (bogey4 > 3 || bogey4 > 0 && bogey5 >= bogey4)
throw new IllegalArgumentException();
pointIndexId <<= this.getBits((byte) 4);
pointIndexId += bogey4;
if (bogey3 > 3 || bogey3 > 0 && bogey4 >= bogey3)
throw new IllegalArgumentException();
pointIndexId <<= this.getBits((byte) 3);
pointIndexId += bogey3;
if (bogey2 > 7 || bogey2 > 0 && bogey3 >= bogey2)
throw new IllegalArgumentException();
pointIndexId <<= this.getBits((byte) 2);
pointIndexId += bogey2;
if (bogey > 7 || bogey > 0 && bogey2 >= bogey)
throw new IllegalArgumentException();
pointIndexId <<= this.getBits((byte) 1);
pointIndexId += bogey;
if (par > 15 || par > 0 && bogey >= par)
throw new IllegalArgumentException();
pointIndexId <<= this.getBits((byte) 0);
pointIndexId += par;
if (birdie > 15 || birdie > 0 && par >= birdie)
throw new IllegalArgumentException();
pointIndexId <<= this.getBits((byte) -1);
pointIndexId += birdie;
if (eagle > 31 || eagle > 0 && birdie >= eagle)
throw new IllegalArgumentException();
pointIndexId <<= this.getBits((byte) -2);
pointIndexId += eagle;
if (alby > 63 || alby > 0 && eagle >= alby)
throw new IllegalArgumentException();
pointIndexId <<= this.getBits((byte) -3);
pointIndexId += alby;
this.pointIndexId = pointIndexId;
}
public PointHandicapIndex(int[] scoresToPar) {
this(scoresToPar[7], scoresToPar[8], scoresToPar[9], scoresToPar[0], scoresToPar[1], scoresToPar[2],
scoresToPar[3], scoresToPar[4], scoresToPar[5]);
}
public int getId() {
return this.pointIndexId;
}
public byte getPointsWithScoreToPar(byte scoreToPar) {
int shift = this.getShift(scoreToPar);
int bits = this.getBits(scoreToPar);
int mask = ((1 << bits) - 1) << (shift - bits);
return (byte) ((this.pointIndexId & mask) >> (shift - bits));
}
public byte getQuintupleBogeyPoints() {
return this.getPointsWithScoreToPar((byte) 5);
}
public byte getQuadrupleBogeyPoints() {
return this.getPointsWithScoreToPar((byte) 4);
}
public byte getTripleBogeyPoints() {
return this.getPointsWithScoreToPar((byte) 3);
}
public byte getDoubleBogeyPoints() {
return this.getPointsWithScoreToPar((byte) 2);
}
public byte getBogeyPoints() {
return this.getPointsWithScoreToPar((byte) 1);
}
public byte getParPoints() {
return this.getPointsWithScoreToPar((byte) 0);
}
public byte getBirdiePoints() {
return this.getPointsWithScoreToPar((byte) -1);
}
public byte getEaglePoints() {
return this.getPointsWithScoreToPar((byte) -2);
}
public byte getAlbatrossPoints() {
return this.getPointsWithScoreToPar((byte) -3);
}
private int getShift(byte scoreToPar) {
if (scoreToPar < -2) {
return 6; // 0-63
} else switch (scoreToPar) {
case -2:
return 11; // 0-31
case -1:
return 15; // 0-15
case 0:
return 19; // 0-15
case 1:
return 22; // 0-7
case 2:
return 25; // 0-7
case 3:
return 27; // 0-3
case 4:
return 29; // 0-3
case 5:
return 30; // 0-1
default:
return 31; // 0
}
}
private int getBits(byte scoreToPar) {
if (scoreToPar < -2) {
return 6; // 0-63
} else switch (scoreToPar) {
case -2:
return 5; // 0-31
case -1:
return 4; // 0-15
case 0:
return 4; // 0-15
case 1:
return 3; // 0-7
case 2:
return 3; // 0-7
case 3:
return 2; // 0-3
case 4:
return 2; // 0-3
case 5:
return 1; // 0-1
default:
return 0; // 0
}
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof PointHandicapIndex))
return false;
return this.getId() == ((PointHandicapIndex) obj).getId();
}
@Override
public int hashCode() {
return this.getId();
}
@Override
public String toString() {
return new StringBuilder().append(this.getQuintupleBogeyPoints()).append('/')
.append(this.getQuadrupleBogeyPoints()).append('/').append(this.getTripleBogeyPoints()).append('/')
.append(this.getDoubleBogeyPoints()).append('/').append(this.getBogeyPoints()).append('/')
.append(this.getParPoints()).append('/').append(this.getBirdiePoints()).append('/')
.append(this.getEaglePoints()).append('/').append(this.getAlbatrossPoints()).toString();
}
@Override
public int compareTo(PointHandicapIndex o) {
float compare = this.diff(o);
if (Float.isNaN(compare)) {
return 0;
} else if (compare == 0f) {
return 0;
} else if (compare < 0f) {
return (int) Math.floor(compare);
} else {
return (int) Math.ceil(compare);
}
}
private float diff(PointHandicapIndex o) {
if (this.getId() == o.getId())
return 0f;
boolean pos = false;
boolean neg = false;
int compare = Byte.compare(this.getAlbatrossPoints(), o.getAlbatrossPoints());
if (compare < 0)
neg = true;
else if (compare > 0)
pos = true;
compare = Byte.compare(this.getEaglePoints(), o.getEaglePoints());
if (compare < 0)
neg = true;
else if (compare > 0)
pos = true;
if (neg && pos)
return Float.NaN;
compare = Byte.compare(this.getBirdiePoints(), o.getBirdiePoints());
if (compare < 0)
neg = true;
else if (compare > 0)
pos = true;
if (neg && pos)
return Float.NaN;
compare = Byte.compare(this.getParPoints(), o.getParPoints());
if (compare < 0)
neg = true;
else if (compare > 0)
pos = true;
if (neg && pos)
return Float.NaN;
compare = Byte.compare(this.getBogeyPoints(), o.getBogeyPoints());
if (compare < 0)
neg = true;
else if (compare > 0)
pos = true;
if (neg && pos)
return Float.NaN;
compare = Byte.compare(this.getDoubleBogeyPoints(), o.getDoubleBogeyPoints());
if (compare < 0)
neg = true;
else if (compare > 0)
pos = true;
if (neg && pos)
return Float.NaN;
compare = Byte.compare(this.getTripleBogeyPoints(), o.getTripleBogeyPoints());
if (compare < 0)
neg = true;
else if (compare > 0)
pos = true;
if (neg && pos)
return Float.NaN;
compare = Byte.compare(this.getQuadrupleBogeyPoints(), o.getQuadrupleBogeyPoints());
if (compare < 0)
neg = true;
else if (compare > 0)
pos = true;
if (neg && pos)
return Float.NaN;
compare = Byte.compare(this.getQuintupleBogeyPoints(), o.getQuintupleBogeyPoints());
if (compare < 0)
neg = true;
else if (compare > 0)
pos = true;
if (neg && pos) {
return Float.NaN;
} else if (neg) {
return -1;
} else if (pos) {
return 1;
} else {
this.logger.warn("This should never happen");
return 0;
}
}
public PointHandicapIndex decrement(byte scoreToPar) {
return this.minus(scoreToPar, (byte) 1);
}
public PointHandicapIndex increment(byte scoreToPar) {
return this.plus(scoreToPar, (byte) 1);
}
public PointHandicapIndex minus(byte scoreToPar, byte points) {
return this.plus(scoreToPar, (byte) -points);
}
public PointHandicapIndex plus(byte scoreToPar, byte points) {
int shift = this.getShift(scoreToPar);
int bits = this.getBits(scoreToPar);
int mask = ((1 << bits) - 1) << (shift - bits);
int oldPointIndexMasked = this.pointIndexId & mask;
byte oldPoints = (byte) (oldPointIndexMasked >> (shift - bits));
if (points < 0) {
if (oldPoints == 0)
return this;
} else {
if (oldPoints + points >= this.getPointsWithScoreToPar((byte) (scoreToPar - 1)))
return this;
}
if (oldPoints + points >= Math.pow(2, bits))
throw new IllegalArgumentException();
int pointIndexMasked = (oldPoints + points) << (shift - bits);
return new PointHandicapIndex(this.pointIndexId - oldPointIndexMasked + pointIndexMasked);
}
public PointHandicapIndex avg(PointHandicapIndex phi, int roundingBias) {
return new PointHandicapIndex(
this.computeAvg(this.getAlbatrossPoints(), phi.getAlbatrossPoints(), roundingBias),
this.computeAvg(this.getEaglePoints(), phi.getEaglePoints(), roundingBias),
this.computeAvg(this.getBirdiePoints(), phi.getBirdiePoints(), roundingBias),
this.computeAvg(this.getParPoints(), phi.getParPoints(), roundingBias),
this.computeAvg(this.getBogeyPoints(), phi.getBogeyPoints(), roundingBias),
this.computeAvg(this.getDoubleBogeyPoints(), phi.getDoubleBogeyPoints(), roundingBias),
this.computeAvg(this.getTripleBogeyPoints(), phi.getTripleBogeyPoints(), roundingBias),
this.computeAvg(this.getQuadrupleBogeyPoints(), phi.getQuadrupleBogeyPoints(), roundingBias),
this.computeAvg(this.getQuintupleBogeyPoints(), phi.getQuintupleBogeyPoints(), roundingBias));
}
private byte computeAvg(byte points1, byte points2, int roundingBias) {
if (points1 == points2)
return points1;
float avg = (points1 + points2) / 2f;
if (roundingBias == 0) {
return (byte) Math.round(avg);
} else if (roundingBias < 0) {
return (byte) Math.floor(avg);
} else {
return (byte) Math.ceil(avg);
}
}
}

View File

@@ -1,21 +0,0 @@
package com.poststats.golf.service.model;
public class StrokeCourseRating {
private final short slopeRating;
private final float courseRating;
public StrokeCourseRating(short slopeRating, float courseRating) {
this.slopeRating = slopeRating;
this.courseRating = courseRating;
}
public short getSlopeRating() {
return this.slopeRating;
}
public float getCourseRating() {
return this.courseRating;
}
}

View File

@@ -1,380 +0,0 @@
package com.poststats.golf.service.telegram;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.apache.http.client.HttpResponseException;
import org.slf4j.Logger;
import org.telegram.telegrambots.meta.api.methods.groupadministration.CreateChatInviteLink;
import org.telegram.telegrambots.meta.api.methods.groupadministration.GetChat;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.Chat;
import org.telegram.telegrambots.meta.api.objects.ChatInviteLink;
import org.telegram.telegrambots.meta.api.objects.ChatMemberUpdated;
import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.api.objects.chatmember.ChatMemberAdministrator;
import org.telegram.telegrambots.meta.api.objects.chatmember.ChatMemberBanned;
import org.telegram.telegrambots.meta.api.objects.chatmember.ChatMemberLeft;
import org.telegram.telegrambots.meta.api.objects.chatmember.ChatMemberMember;
import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexPreparedStatement;
import com.poststats.golf.provider.GolfProvider;
import com.poststats.golf.service.PersonService;
import com.poststats.provider.NonTransactionalProvider;
import com.poststats.provider.PostStatsProvider;
import com.poststats.provider.Statement;
import com.poststats.provider.StatementProvider;
import com.poststats.provider.TransactionalProvider;
import com.poststats.service.ConfigurationAccessor;
import com.poststats.service.ServiceException;
import com.poststats.service.TelegramChannelManager;
import com.poststats.service.TelegramChannelSubscriber;
import com.poststats.service.TelegramPushService;
import com.poststats.service.TelegramUpdateSubscriber.TelegramMessageReplier;
import com.poststats.service.model.TelegramCommand;
import com.poststats.transformer.JacksonObjectMapper;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.transaction.Transactional.TxType;
@ApplicationScoped
public class TelegramEventService implements TelegramChannelSubscriber, TelegramChannelManager<Long> {
@Inject
private Logger logger;
@Inject
private PersonService personService;
@Inject
private JacksonObjectMapper om;
@Inject
private ConfigurationAccessor config;
@Inject
private TelegramPushService telegramService;
private final List<String> helpLines = Arrays.asList("`/event_sub {shared_secret}`", "`/event_unsub`");
@Override
public Collection<String> getCommandLines() {
return this.helpLines;
}
@Override
public void memberStatusReceived(ChatMemberUpdated member, TelegramMessageReplier replier) {
if (Boolean.TRUE.equals(member.getNewChatMember().getUser().getIsBot())) {
if (!"poststats_bot".equals(member.getNewChatMember().getUser().getUserName())) {
this.logger.debug("Ignoring unregulated bot: {}", member.getNewChatMember().getUser().getUserName());
} else {
switch (member.getNewChatMember().getStatus()) {
case ChatMemberBanned.STATUS:
case ChatMemberLeft.STATUS:
this.unsetInDatabase(member.getChat().getId());
break;
}
}
} else {
switch (member.getNewChatMember().getStatus()) {
case ChatMemberBanned.STATUS:
case ChatMemberLeft.STATUS:
this.setInChannelInDatabase(member.getNewChatMember().getUser().getId(), member.getChat().getId(),
false);
break;
case ChatMemberMember.STATUS:
case ChatMemberAdministrator.STATUS:
this.setInChannelInDatabase(member.getNewChatMember().getUser().getId(), member.getChat().getId(),
true);
break;
}
}
}
@Override
public void messageReceived(Message message, TelegramMessageReplier replier) {
// ignore
}
@Override
@Transactional(TxType.REQUIRED)
public boolean commandReceived(TelegramCommand command, TelegramMessageReplier replier) {
switch (command.getCommand()) {
case "event_sub":
if (command.getText() != null) {
if (this.setInDatabase(command.getUserId(), command.getChannelId(), command.getText())) {
replier.reply("You have successfully subscribed this channel to your PostStats Event!");
} else {
replier.reply("The supplied shared secret or Event authority is not valid.");
}
return true;
}
break;
case "event_unsub":
if (this.unsetInDatabase(command.getUserId())) {
replier.reply(
"You have successfully unsubscribed this channel from PostStats Event communications.");
} else {
replier.reply(
"This channel was not and continue to not be subscribed to PostStats Event communications.");
}
return true;
case "start":
default:
}
return false;
}
@Override
public boolean addUserToChannel(Long eventId, long personId) {
try {
Long channelId = this.findEventChannelId(eventId);
if (channelId == null) {
this.logger.warn("The event is not linked to a Telegram channel");
return false;
}
Long userId = this.findPersonUserId(personId);
if (userId == null) {
this.logger.debug("An channel invite cannot be created for users without a linked Telegram account");
return false;
}
Chat chat = this.telegramService.callEndpoint(GetChat.builder().chatId(channelId).build());
if (chat == null) {
this.logger.warn("The channel could not be found");
return false;
}
ChatInviteLink inviteLink = this.telegramService
.callEndpoint(CreateChatInviteLink.builder().chatId(channelId).build());
if (inviteLink == null)
throw new ServiceException("An invite could not be created");
SendMessage sendMessage = SendMessage.builder().allowSendingWithoutReply(true).chatId(userId)
.text("You have been invited to join the "
+ chat.getTitle()
+ " channel: "
+ inviteLink.getInviteLink())
.build();
this.telegramService.callEndpoint(sendMessage);
return true;
} catch (HttpResponseException hre) {
throw new ServiceException(hre);
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Override
public boolean removeUserFromChannel(Long eventId, long personId) {
try {
Long channelId = this.findEventChannelId(eventId);
if (channelId == null)
return false;
Long userId = this.findPersonUserId(personId);
if (userId == null) {
this.logger.debug("An channel invite cannot be created for users without a linked Telegram account");
return false;
}
throw new UnsupportedOperationException();
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Override
public boolean reconcileChannelMembers(Long poststatsId) {
throw new UnsupportedOperationException();
}
@Override
public boolean reconcileChannels(long personId) {
throw new UnsupportedOperationException();
}
private boolean setInDatabase(long telegramUserId, long channelId, String sharedSecret) {
try {
DataSet row = this.querySharedSecretByCode(sharedSecret);
if (row == null) {
this.logger.debug("A shared secret was provided but not found: {}", sharedSecret);
return false;
} else if (row.isNotEmpty("eventID")) {
long eventId = row.getLong("eventID");
if (!this.checkAuthority(telegramUserId, eventId))
return false;
this.updateEvent(eventId, channelId);
this.deleteSharedSecret(row.getLong("sharedSecretId"));
} else {
this.logger.warn("A shared secret was shared by {} for something other than an 'event'",
telegramUserId);
return false;
}
return true;
} catch (SQLException se) {
throw new ServiceException(se);
}
}
private DataSet querySharedSecretByCode(String sharedSecret) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectSharedSecret.buildPreparedStatement();
try {
fps.setVarchar(1, sharedSecret);
return fps.executeQuery().getNextRow();
} finally {
fps.close();
}
}
@Inject
@NonTransactionalProvider
@PostStatsProvider
@Statement(sql = "SELECT * FROM ~p~.SharedSecret WHERE sharedSecret=? AND expiration>NOW()")
private StatementProvider sqlSelectSharedSecret;
private void deleteSharedSecret(long sharedSecretId) throws SQLException {
FlexPreparedStatement fps = this.sqlDeleteSharedSecret.buildPreparedStatement();
try {
fps.setIntegerU(1, sharedSecretId);
fps.executeUpdate();
} finally {
fps.close();
}
}
@Inject
@TransactionalProvider
@PostStatsProvider
@Statement(sql = "DELETE FROM ~p~.SharedSecret WHERE sharedSecretId=?")
private StatementProvider sqlDeleteSharedSecret;
private boolean checkAuthority(long telegramUserId, long eventId) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectAuthority.buildPreparedStatement();
try {
fps.setIntegerU(1, eventId);
fps.setBigint(2, telegramUserId);
return fps.executeQuery().getOne(Integer.class) > 0;
} finally {
fps.close();
}
}
@Inject
@TransactionalProvider
@PostStatsProvider
@Statement(
sql = "SELECT COUNT(*) "
+ "FROM ~g~.EventPersonAccessControl EPAC "
+ " INNER JOIN ~p~.Person P ON (EPAC.personID=P.personID) "
+ " INNER JOIN ~p~.AccessControl AC ON (EPAC.acID=AC.acID) "
+ "WHERE EPAC.eventID=? AND P.telegramUserId=? AND AC.acSID IN ('admin', 'communicate')"
)
private StatementProvider sqlSelectAuthority;
private Long findEventChannelId(long eventId) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectEventChannel.buildPreparedStatement();
try {
fps.setIntegerU(1, eventId);
return fps.executeQuery().getOne(Long.class);
} finally {
fps.close();
}
}
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT telegramChannelId FROM ~g~.Event WHERE eventId=?")
private StatementProvider sqlSelectEventChannel;
private Long findPersonUserId(long personId) throws SQLException {
FlexPreparedStatement fps = this.sqlSelectEventChannel.buildPreparedStatement();
try {
fps.setIntegerU(1, personId);
return fps.executeQuery().getOne(Long.class);
} finally {
fps.close();
}
}
@Inject
@NonTransactionalProvider
@PostStatsProvider
@Statement(sql = "SELECT telegramUserId FROM ~p~.Person WHERE personId=?")
private StatementProvider sqlSelectPersonUser;
private void updateEvent(long eventId, long telegramChannelId) throws SQLException {
FlexPreparedStatement fps = this.sqlUpdateEvent.buildPreparedStatement();
try {
fps.setBigint(1, telegramChannelId);
fps.setIntegerU(2, eventId);
fps.executeUpdate();
} finally {
fps.close();
}
}
@Inject
@TransactionalProvider
@GolfProvider
@Statement(sql = "UPDATE ~g~.Event SET telegramChannelId=? WHERE eventID=?")
private StatementProvider sqlUpdateEvent;
private boolean unsetInDatabase(long telegramChannelId) {
try {
FlexPreparedStatement fps = this.sqlUpdateEventUnset.buildPreparedStatement();
try {
fps.setBigint(1, telegramChannelId);
return fps.executeUpdate() > 0;
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@TransactionalProvider
@GolfProvider
@Statement(sql = "UPDATE ~g~.Event SET telegramChannelId=NULL WHERE telegramChannelId=?")
private StatementProvider sqlUpdateEventUnset;
private boolean setInChannelInDatabase(long telegramUserId, long channelId, boolean in) {
try {
FlexPreparedStatement fps = this.sqlUpdateEventPersonSet.buildPreparedStatement();
try {
fps.setBoolean(1, in);
fps.setBigint(2, channelId);
fps.setBigint(3, telegramUserId);
return fps.executeUpdate() > 0;
} finally {
fps.close();
}
} catch (SQLException se) {
throw new ServiceException(se);
}
}
@Inject
@TransactionalProvider
@GolfProvider
@Statement(
sql = "UPDATE ~g~.EventPerson "
+ "SET inTelegramChannel=? "
+ "WHERE eventID IN (SELECT eventID FROM ~g~.Event WHERE telegramChannelId=?) "
+ " AND personID IN (SELECT personID FROM ~p~.Person WHERE telegramUserId=?) "
)
private StatementProvider sqlUpdateEventPersonSet;
}

View File

@@ -1,51 +0,0 @@
package com.poststats.golf.servlet;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.poststats.golf.security.AuthenticatedSecurityContext;
import com.poststats.golf.service.PersonService;
import com.poststats.security.AuthenticatedPerson;
import jakarta.annotation.Priority;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.core.SecurityContext;
import jakarta.ws.rs.ext.Provider;
@ApplicationScoped
@Provider
@Priority(Priorities.AUTHENTICATION + 5)
public class AuthenticationFilter implements ContainerRequestFilter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Inject
private PersonService personService;
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
SecurityContext scontext = requestContext.getSecurityContext();
if (scontext == null || scontext.getUserPrincipal() == null)
return;
AuthenticatedPerson authPerson = (AuthenticatedPerson) scontext.getUserPrincipal();
this.logger.debug("Gathering roles for golf: {}", authPerson);
com.poststats.golf.security.AuthenticatedPerson gauthPerson = this.personService.getUserPrincipal(authPerson);
if (this.logger.isTraceEnabled())
this.logger.trace("Authorized roles: {} => {}", gauthPerson.getId(), gauthPerson.getAllAccessControls());
scontext = new AuthenticatedSecurityContext(scontext, gauthPerson);
requestContext.setSecurityContext(scontext);
}
}

View File

@@ -1,63 +0,0 @@
package com.poststats.golf.servlet;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.brianlong.util.StringUtil;
import com.poststats.golf.api.Constants;
import com.poststats.golf.security.AuthenticatedPerson;
import com.poststats.golf.security.EventSecurityContext;
import jakarta.annotation.Priority;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.core.SecurityContext;
import jakarta.ws.rs.ext.Provider;
@ApplicationScoped
@Provider
@Priority(Priorities.AUTHORIZATION - 5)
public class EventFilter implements ContainerRequestFilter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
String eventIdStr = requestContext.getUriInfo().getPathParameters().getFirst(Constants.EVENT_ID);
eventIdStr = StringUtil.getInstance().trim(eventIdStr);
if (eventIdStr == null) {
eventIdStr = requestContext.getUriInfo().getQueryParameters().getFirst(Constants.EVENT_ID);
eventIdStr = StringUtil.getInstance().trim(eventIdStr);
}
if (eventIdStr == null)
return;
this.logger.debug("Entering event context: {}", eventIdStr);
long eventId = Long.valueOf(eventIdStr);
requestContext.setProperty(Constants.EVENT_ID, eventId);
SecurityContext scontext = requestContext.getSecurityContext();
if (scontext.getUserPrincipal() == null)
return;
this.logger.debug("Narrowing authorization for event: {} => {}", scontext.getUserPrincipal(), eventId);
EventSecurityContext epscontext = new EventSecurityContext(scontext, eventId);
if (this.logger.isTraceEnabled()) {
AuthenticatedPerson person = (AuthenticatedPerson) epscontext.getUserPrincipal();
this.logger.trace("Authorized event roles: {} => {}", person.getId(),
person.getEventAccessControls(eventId));
}
requestContext.setSecurityContext(epscontext);
}
}

View File

@@ -1,47 +0,0 @@
package com.poststats.golf.servlet;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.poststats.golf.api.Constants;
import com.poststats.golf.security.PersonSecurityContext;
import com.poststats.golf.service.PersonService;
import jakarta.annotation.Priority;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.core.SecurityContext;
import jakarta.ws.rs.ext.Provider;
@ApplicationScoped
@Provider
@Priority(Priorities.AUTHORIZATION - 8)
public class PersonFilter implements ContainerRequestFilter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Inject
private PersonService personService;
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
Long personId = (Long) requestContext.getProperty(Constants.PERSON_ID);
if (personId == null)
return;
SecurityContext scontext = requestContext.getSecurityContext();
if (scontext == null || scontext.getUserPrincipal() == null)
return;
this.logger.debug("Entering golfer context: {}", personId);
scontext = new PersonSecurityContext(scontext, personId);
requestContext.setSecurityContext(scontext);
}
}

View File

@@ -1,44 +0,0 @@
package com.poststats.golf.servlet;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.brianlong.util.StringUtil;
import com.poststats.golf.api.Constants;
import jakarta.annotation.Priority;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.ext.Provider;
@ApplicationScoped
@Provider
@Priority(Priorities.HEADER_DECORATOR + 10)
public class SeriesFilter implements ContainerRequestFilter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
String seriesIdStr = requestContext.getUriInfo().getPathParameters().getFirst(Constants.EVENT_SERIES_ID);
seriesIdStr = StringUtil.getInstance().trim(seriesIdStr);
if (seriesIdStr == null) {
seriesIdStr = requestContext.getUriInfo().getQueryParameters().getFirst(Constants.EVENT_SERIES_ID);
seriesIdStr = StringUtil.getInstance().trim(seriesIdStr);
}
if (seriesIdStr == null)
return;
this.logger.debug("Entering series context: {}", seriesIdStr);
int seriesId = Integer.valueOf(seriesIdStr);
requestContext.setProperty(Constants.EVENT_SERIES_ID, seriesId);
}
}

View File

@@ -1,5 +0,0 @@
<beans version="4.0"
xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/beans_4_0.xsd">
</beans>

View File

@@ -1,31 +0,0 @@
package com.poststats.golf.service.compute;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class ComputeLegacyPointCourseRatingUnitTest {
private static LegacyPostStatsPointHandicapIndexService service;
@BeforeAll
public static void stage() {
service = new LegacyPostStatsPointHandicapIndexService();
}
@Test
public void cypressLandingGold() {
Assertions.assertEquals(-4f, service.computeRatingIndex((short) 117, 66.8f, 'M', (short) 5473, (byte) 72), 1f);
}
@Test
public void cypressLandingWhite() {
Assertions.assertEquals(12f, service.computeRatingIndex((short) 127, 69.7f, 'M', (short) 6124, (byte) 72), 1f);
}
@Test
public void cypressLandingBlue() {
Assertions.assertEquals(19f, service.computeRatingIndex((short) 131, 71.1f, 'M', (short) 6442, (byte) 72), 1f);
}
}

View File

@@ -1,124 +0,0 @@
package com.poststats.golf.service.compute;
import java.util.Arrays;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import com.brianlong.util.FlexMap;
public class ComputePointCourseRatingUnitTest {
private static PostStatsPointHandicapIndexService service;
@BeforeAll
public static void stage() {
service = new PostStatsPointHandicapIndexService();
}
@Test
public void cypressLandingGold() {
Assertions.assertEquals(-25f, service.computeRatingIndex(this.mockEighteenTeeRating('M', (byte) 72, 66.8f),
Arrays.asList(this.mockHole(301, 4), this.mockHole(295, 4), this.mockHole(136, 3),
this.mockHole(297, 4), this.mockHole(460, 5), this.mockHole(349, 4), this.mockHole(140, 3),
this.mockHole(492, 5), this.mockHole(323, 4), this.mockHole(314, 4), this.mockHole(118, 3),
this.mockHole(461, 5), this.mockHole(301, 4), this.mockHole(351, 4), this.mockHole(113, 3),
this.mockHole(279, 4), this.mockHole(450, 5), this.mockHole(293, 4))),
1f);
}
@Test
public void cypressLandingWhite() {
Assertions.assertEquals(5f, service.computeRatingIndex(this.mockEighteenTeeRating('M', (byte) 72, 69.7f),
Arrays.asList(this.mockHole(345, 4), this.mockHole(316, 4), this.mockHole(136, 3),
this.mockHole(371, 4), this.mockHole(503, 5), this.mockHole(404, 4), this.mockHole(184, 3),
this.mockHole(501, 5), this.mockHole(348, 4), this.mockHole(341, 4), this.mockHole(166, 3),
this.mockHole(532, 5), this.mockHole(318, 4), this.mockHole(385, 4), this.mockHole(129, 3),
this.mockHole(303, 4), this.mockHole(489, 5), this.mockHole(353, 4))),
1f);
}
@Test
public void cypressLandingBlue() {
Assertions.assertEquals(22f, service.computeRatingIndex(this.mockEighteenTeeRating('M', (byte) 72, 71.1f),
Arrays.asList(this.mockHole(373, 4), this.mockHole(316, 4), this.mockHole(166, 3),
this.mockHole(389, 4), this.mockHole(520, 5), this.mockHole(412, 4), this.mockHole(203, 3),
this.mockHole(520, 5), this.mockHole(366, 4), this.mockHole(374, 4), this.mockHole(185, 3),
this.mockHole(554, 5), this.mockHole(319, 4), this.mockHole(406, 4), this.mockHole(144, 3),
this.mockHole(318, 4), this.mockHole(489, 5), this.mockHole(388, 4))),
1f);
}
@Test
public void heronGlenBlue() {
Assertions.assertEquals(26f, service.computeRatingIndex(this.mockEighteenTeeRating('M', (byte) 72, 71.7f),
Arrays.asList(this.mockHole(395, 4), this.mockHole(581, 5), this.mockHole(424, 4),
this.mockHole(361, 4), this.mockHole(179, 3), this.mockHole(371, 4), this.mockHole(146, 3),
this.mockHole(512, 5), this.mockHole(378, 4), this.mockHole(545, 5), this.mockHole(308, 4),
this.mockHole(155, 3), this.mockHole(514, 5), this.mockHole(378, 4), this.mockHole(204, 3),
this.mockHole(473, 5), this.mockHole(158, 3), this.mockHole(449, 4))),
1f);
}
@Test
public void metamoreFieldsBlue() {
Assertions.assertEquals(17f, service.computeRatingIndex(this.mockEighteenTeeRating('M', (byte) 71, 70f),
Arrays.asList(this.mockHole(388, 4), this.mockHole(121, 3), this.mockHole(523, 5),
this.mockHole(326, 4), this.mockHole(170, 3), this.mockHole(526, 5), this.mockHole(338, 4),
this.mockHole(181, 3), this.mockHole(427, 4), this.mockHole(392, 4), this.mockHole(152, 3),
this.mockHole(485, 5), this.mockHole(344, 4), this.mockHole(164, 3), this.mockHole(385, 4),
this.mockHole(504, 5), this.mockHole(382, 4), this.mockHole(401, 4))),
1f);
}
@Test
public void pinonHillsBlue() {
Assertions.assertEquals(35f, service.computeRatingIndex(this.mockEighteenTeeRating('M', (byte) 72, 71.7f),
Arrays.asList(this.mockHole(387, 4), this.mockHole(398, 4), this.mockHole(412, 4),
this.mockHole(178, 3), this.mockHole(331, 4), this.mockHole(198, 3), this.mockHole(395, 4),
this.mockHole(537, 5), this.mockHole(572, 5), this.mockHole(404, 4), this.mockHole(397, 4),
this.mockHole(200, 3), this.mockHole(505, 5), this.mockHole(321, 4), this.mockHole(140, 3),
this.mockHole(405, 4), this.mockHole(520, 5), this.mockHole(446, 4))),
1f);
}
@Test
public void worthingtonHillsBlue() {
Assertions.assertEquals(48f, service.computeRatingIndex(this.mockEighteenTeeRating('M', (byte) 71, 72.7f),
Arrays.asList(this.mockHole(419, 4), this.mockHole(388, 4), this.mockHole(187, 3),
this.mockHole(520, 5), this.mockHole(420, 4), this.mockHole(560, 5), this.mockHole(200, 3),
this.mockHole(351, 4), this.mockHole(430, 4), this.mockHole(441, 4), this.mockHole(345, 4),
this.mockHole(192, 3), this.mockHole(365, 4), this.mockHole(398, 4), this.mockHole(172, 3),
this.mockHole(448, 4), this.mockHole(364, 4), this.mockHole(521, 5))),
1f);
}
@Test
public void darbyCreekBlue() {
Assertions.assertEquals(34f, service.computeRatingIndex(this.mockEighteenTeeRating('M', (byte) 72, 72.2f),
Arrays.asList(this.mockHole(350, 4), this.mockHole(374, 4), this.mockHole(169, 3),
this.mockHole(529, 5), this.mockHole(339, 4), this.mockHole(486, 5), this.mockHole(144, 3),
this.mockHole(418, 4), this.mockHole(438, 4), this.mockHole(309, 4), this.mockHole(168, 3),
this.mockHole(411, 4), this.mockHole(435, 4), this.mockHole(492, 5), this.mockHole(419, 4),
this.mockHole(188, 3), this.mockHole(540, 5), this.mockHole(452, 4))),
1f);
}
private FlexMap mockEighteenTeeRating(char gender, int par, float courseRating) {
FlexMap map = new FlexMap();
map.put("etratingID", 0L);
map.put("gender", String.valueOf(gender));
map.put("par", (byte) par);
map.put("courseRating", courseRating);
return map;
}
private FlexMap mockHole(int yards, int par) {
FlexMap map = new FlexMap();
map.put("yards", (short) yards);
map.put("par", (byte) par);
return map;
}
}

View File

@@ -1,59 +0,0 @@
package com.poststats.golf.service.compute;
import java.util.Arrays;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import com.brianlong.util.FlexMap;
import com.poststats.golf.service.model.PointHandicapIndex;
public class ComputePointHandicapIndexUnitTest {
private static PostStatsPointHandicapIndexService service;
@BeforeAll
public static void stage() {
service = new PostStatsPointHandicapIndexService();
}
@Test
public void brian() {
PointHandicapIndex phi = service.compute(Arrays.asList(this.mockRound(0, 22, 0, 0, 1, 5, 5, 6, 1, 0, 0),
this.mockRound(0, 22, 0, 1, 2, 5, 6, 4, 0, 0, 0), this.mockRound(0, 22, 0, 0, 1, 3, 7, 7, 0, 0, 0),
this.mockRound(0, 22, 0, 2, 2, 4, 8, 1, 1, 0, 0), this.mockRound(1, 26, 0, 0, 1, 5, 5, 6, 1, 0, 0),
this.mockRound(2, 17, 0, 1, 0, 4, 7, 6, 0, 0, 0), this.mockRound(2, 17, 0, 1, 2, 4, 5, 6, 0, 0, 0),
this.mockRound(2, 17, 0, 2, 0, 2, 7, 6, 1, 0, 0), this.mockRound(2, 17, 1, 0, 0, 3, 4, 8, 2, 0, 0),
this.mockRound(3, 35, 0, 3, 2, 2, 6, 4, 1, 0, 0), this.mockRound(3, 35, 0, 0, 1, 5, 4, 8, 0, 0, 0),
this.mockRound(3, 35, 1, 1, 3, 3, 3, 6, 1, 0, 0), this.mockRound(3, 35, 0, 2, 2, 5, 5, 4, 0, 0, 0)));
System.out.println(phi);
}
@Test
public void ridgeway() {
PointHandicapIndex phi = service.compute(Arrays.asList(this.mockRound(0, 22, 0, 0, 0, 0, 7, 9, 2, 0, 0),
this.mockRound(0, 22, 0, 0, 0, 4, 5, 7, 2, 0, 0), this.mockRound(0, 22, 0, 0, 0, 0, 5, 11, 2, 0, 0),
this.mockRound(1, 48, 0, 0, 0, 2, 5, 9, 2, 0, 0), this.mockRound(2, 34, 0, 0, 0, 1, 3, 8, 6, 0, 0),
this.mockRound(1, 48, 0, 0, 0, 1, 4, 11, 2, 0, 0), this.mockRound(2, 34, 0, 0, 0, 1, 2, 12, 3, 0, 0),
this.mockRound(2, 34, 0, 0, 0, 2, 4, 8, 4, 0, 0), this.mockRound(3, 17, 0, 0, 0, 0, 4, 10, 4, 0, 0),
this.mockRound(3, 17, 0, 0, 0, 0, 3, 11, 4, 0, 0), this.mockRound(3, 17, 0, 0, 0, 0, 5, 10, 3, 0, 0)));
System.out.println(phi);
}
private FlexMap mockRound(int courseId, int pointAdj, int... scoreToParCounts) {
FlexMap map = new FlexMap();
map.put("courseID", courseId);
map.put("pointAdj", (byte) pointAdj);
map.put("bogey5", (byte) scoreToParCounts[0]);
map.put("bogey4", (byte) scoreToParCounts[1]);
map.put("bogey3", (byte) scoreToParCounts[2]);
map.put("bogey2", (byte) scoreToParCounts[3]);
map.put("bogey", (byte) scoreToParCounts[4]);
map.put("par", (byte) scoreToParCounts[5]);
map.put("birdie", (byte) scoreToParCounts[6]);
map.put("eagle", (byte) scoreToParCounts[7]);
map.put("alby", (byte) scoreToParCounts[8]);
return map;
}
}

View File

@@ -1,90 +0,0 @@
package com.poststats.golf.service.model;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class PointHandicapIndexUnitTest {
@Test
public void zero() {
PointHandicapIndex phi = new PointHandicapIndex(0, 0, 0, 0, 0, 0, 0, 0, 0);
Assertions.assertEquals(0, phi.getId());
Assertions.assertEquals(0, phi.getPointsWithScoreToPar((byte) -10));
Assertions.assertEquals(0, phi.getPointsWithScoreToPar((byte) -3));
Assertions.assertEquals(0, phi.getPointsWithScoreToPar((byte) 5));
Assertions.assertEquals(0, phi.getPointsWithScoreToPar((byte) 10));
}
@Test
public void alby1() {
PointHandicapIndex phi = new PointHandicapIndex(1, 0, 0, 0, 0, 0, 0, 0, 0);
Assertions.assertEquals(1, phi.getPointsWithScoreToPar((byte) -3));
Assertions.assertEquals(0, phi.getPointsWithScoreToPar((byte) -2));
Assertions.assertEquals(0, phi.getPointsWithScoreToPar((byte) 5));
Assertions.assertEquals(0, phi.getPointsWithScoreToPar((byte) 10));
}
@Test
public void alby2eagle1() {
PointHandicapIndex phi = new PointHandicapIndex(2, 1, 0, 0, 0, 0, 0, 0, 0);
Assertions.assertEquals(2, phi.getPointsWithScoreToPar((byte) -3));
Assertions.assertEquals(1, phi.getPointsWithScoreToPar((byte) -2));
Assertions.assertEquals(0, phi.getPointsWithScoreToPar((byte) -1));
Assertions.assertEquals(0, phi.getPointsWithScoreToPar((byte) 5));
Assertions.assertEquals(0, phi.getPointsWithScoreToPar((byte) 10));
}
@Test
public void typicalScratch() {
PointHandicapIndex phi = new PointHandicapIndex(25, 14, 9, 2, 0, 0, 0, 0, 0);
Assertions.assertEquals(25, phi.getPointsWithScoreToPar((byte) -3));
Assertions.assertEquals(14, phi.getPointsWithScoreToPar((byte) -2));
Assertions.assertEquals(9, phi.getPointsWithScoreToPar((byte) -1));
Assertions.assertEquals(2, phi.getPointsWithScoreToPar((byte) 0));
Assertions.assertEquals(0, phi.getPointsWithScoreToPar((byte) 1));
Assertions.assertEquals(0, phi.getPointsWithScoreToPar((byte) 5));
Assertions.assertEquals(0, phi.getPointsWithScoreToPar((byte) 10));
}
@Test
public void typicalBogey() {
PointHandicapIndex phi = new PointHandicapIndex(25, 16, 7, 4, 2, 1, 0, 0, 0);
Assertions.assertEquals(25, phi.getPointsWithScoreToPar((byte) -3));
Assertions.assertEquals(16, phi.getPointsWithScoreToPar((byte) -2));
Assertions.assertEquals(7, phi.getPointsWithScoreToPar((byte) -1));
Assertions.assertEquals(4, phi.getPointsWithScoreToPar((byte) 0));
Assertions.assertEquals(2, phi.getPointsWithScoreToPar((byte) 1));
Assertions.assertEquals(1, phi.getPointsWithScoreToPar((byte) 2));
Assertions.assertEquals(0, phi.getPointsWithScoreToPar((byte) 4));
}
@Test
public void typicalBad() {
PointHandicapIndex phi = new PointHandicapIndex(40, 18, 10, 6, 4, 3, 1, 0, 0);
Assertions.assertEquals(40, phi.getPointsWithScoreToPar((byte) -3));
Assertions.assertEquals(18, phi.getPointsWithScoreToPar((byte) -2));
Assertions.assertEquals(10, phi.getPointsWithScoreToPar((byte) -1));
Assertions.assertEquals(6, phi.getPointsWithScoreToPar((byte) 0));
Assertions.assertEquals(4, phi.getPointsWithScoreToPar((byte) 1));
Assertions.assertEquals(3, phi.getPointsWithScoreToPar((byte) 2));
Assertions.assertEquals(1, phi.getPointsWithScoreToPar((byte) 3));
Assertions.assertEquals(0, phi.getPointsWithScoreToPar((byte) 4));
Assertions.assertEquals(0, phi.getPointsWithScoreToPar((byte) 5));
Assertions.assertEquals(0, phi.getPointsWithScoreToPar((byte) 10));
}
@Test
public void plusBogey() {
PointHandicapIndex basephi = new PointHandicapIndex(40, 18, 10, 6, 4, 3, 1, 0, 0);
PointHandicapIndex phi = basephi.plus((byte) 1, (byte) 1);
Assertions.assertEquals(5, phi.getPointsWithScoreToPar((byte) 1));
}
@Test
public void plus2Birdie() {
PointHandicapIndex basephi = new PointHandicapIndex(40, 18, 10, 6, 4, 3, 1, 0, 0);
PointHandicapIndex phi = basephi.plus((byte) -1, (byte) 2);
Assertions.assertEquals(12, phi.getPointsWithScoreToPar((byte) -1));
}
}

View File

@@ -1,19 +0,0 @@
rootLogger.level = debug
rootLogger.appenderRef.stdout.ref = api
appender.rolling.type = RollingFile
appender.rolling.name = api
appender.rolling.fileName = ${catalina.base}/logs/api.log
appender.rolling.filePattern = ${catalina.base}/logs/api-%d{MM-dd-yyyy}.log.gz
appender.rolling.layout.type = PatternLayout
appender.rolling.layout.pattern = %d %p %C{1.} [%t] %m%n
appender.rolling.policies.type = Policies
appender.rolling.policies.time.type = TimeBasedTriggeringPolicy
appender.rolling.strategy.type = DefaultRolloverStrategy
appender.rolling.strategy.max = 10
logger.brianlong.name = com.brianlong
logger.brianlong.level = debug
logger.poststats.name = com.poststats
logger.poststats.level = debug

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Configuration
xmlns="http://logging.apache.org/log4j/2.0/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://logging.apache.org/log4j/2.0/config https://raw.githubusercontent.com/apache/logging-log4j2/2.x/log4j-core/src/main/resources/Log4j-config.xsd">
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="%d %-5p %c{1} - %m%n" />
</Console>
<Async name="async">
<AppenderRef ref="console" />
</Async>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="async" />
</Root>
<Logger name="com.inteligr8" level="debug" />
<Logger name="com.poststats" level="trace" />
</Loggers>
</Configuration>