added pairing CSV; fixed agenda
This commit is contained in:
@@ -2,11 +2,16 @@ package com.poststats.golf.api;
|
||||
|
||||
import com.brianlong.util.FlexMap;
|
||||
import com.poststats.api.Constants;
|
||||
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 io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
@@ -19,11 +24,24 @@ import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.WebApplicationException;
|
||||
import jakarta.ws.rs.core.Response.Status;
|
||||
import jakarta.ws.rs.core.StreamingOutput;
|
||||
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 org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -40,6 +58,9 @@ public class EventRoundApi {
|
||||
@PathParam("eventId")
|
||||
private long eventId;
|
||||
|
||||
@Inject
|
||||
private PersonService golferService;
|
||||
|
||||
@Inject
|
||||
private EventRoundService roundService;
|
||||
|
||||
@@ -139,7 +160,7 @@ public class EventRoundApi {
|
||||
@ApiResponse(responseCode = "200", description = "Success"),
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "An event with the specified ID or any event rounds could not be found"
|
||||
description = "An event or its round with the specified ID could not be found"
|
||||
)
|
||||
})
|
||||
public List<EventRoundPairing> getPairings(@PathParam("eroundId")
|
||||
@@ -152,6 +173,51 @@ public class EventRoundApi {
|
||||
return this.converter.convertValue(rows, EventRoundPairing.class);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/round/{eroundId:[0-9]+}/pairings/csv")
|
||||
@Produces("text/csv")
|
||||
@Operation(summary = "Retrieves limited meta-data about all pairings for a single event round in the CSV format.")
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "Success"),
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "An event or its round with the specified ID could not be found"
|
||||
)
|
||||
})
|
||||
public StreamingOutput getPairingsAsCsv(@PathParam("eroundId")
|
||||
long eroundId, @QueryParam("orderBy")
|
||||
List<EventRoundPairingOrder> orderBys, @QueryParam("lastNameFirst")
|
||||
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);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/round/{eroundId:[0-9]+}/pairing/{pairingID:[0-9]+}")
|
||||
@Produces(Constants.V1_JSON)
|
||||
@@ -179,4 +245,135 @@ public class EventRoundApi {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,20 @@
|
||||
package com.poststats.golf.api.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public enum EventRoundPairingOrder {
|
||||
|
||||
@JsonProperty("name")
|
||||
FormattedName, @JsonProperty("lastName")
|
||||
LastName, @JsonProperty("firstName")
|
||||
FirstName, @JsonProperty("teetime")
|
||||
TeeTime, @JsonProperty("course")
|
||||
Course, @JsonProperty("nine")
|
||||
CourseNine, @JsonProperty("holeNumber")
|
||||
HoleNumber,
|
||||
|
||||
// an alias for teetime,course,nine,holeNumber
|
||||
@JsonProperty("pairing")
|
||||
Pairing
|
||||
|
||||
}
|
@@ -114,8 +114,9 @@ public class EventAgendaJob {
|
||||
throws CacheRetrievalException, SQLException, MessagingException, IOException {
|
||||
boolean doEmailLink = Boolean.TRUE.equals(agenda.getBoolean("sendEmailWithLink"));
|
||||
boolean doEmail = Boolean.TRUE.equals(agenda.getBoolean("sendEmailWithContent"));
|
||||
boolean doText = Boolean.TRUE.equals(agenda.getBoolean("sendTextWithLink"));
|
||||
if (!doText && !doEmailLink && !doEmail)
|
||||
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");
|
||||
@@ -130,18 +131,21 @@ public class EventAgendaJob {
|
||||
+ this.weekdayFormatter.format(day);
|
||||
|
||||
Set<Long> textedPersonIDs = Collections.emptySet();
|
||||
if (doText)
|
||||
|
||||
if (doTextLink) {
|
||||
textedPersonIDs = this.textLink(eventId, documentId, recipientIDs, subject);
|
||||
} else if (doText) {
|
||||
textedPersonIDs = this.text(eventId, documentId, recipientIDs, subject);
|
||||
}
|
||||
|
||||
if (doEmailLink)
|
||||
if (doEmailLink) {
|
||||
this.emailLink(eventId, documentId, recipientIDs, textedPersonIDs, subject);
|
||||
|
||||
if (doEmail) {
|
||||
} else if (doEmail) {
|
||||
this.email(eventId, documentId, recipientIDs, textedPersonIDs, subject);
|
||||
}
|
||||
}
|
||||
|
||||
private Set<Long> text(long eventID, long documentID, Map<Long, BigInteger> recipientIDs, String 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);
|
||||
|
||||
@@ -168,6 +172,45 @@ public class EventAgendaJob {
|
||||
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);
|
||||
|
@@ -10,4 +10,6 @@ public interface EventRoundPairingService {
|
||||
|
||||
List<? extends FlexMap> getByRoundId(long eroundId);
|
||||
|
||||
List<? extends FlexMap> getParticipantsByRoundId(long eroundId);
|
||||
|
||||
}
|
||||
|
@@ -72,4 +72,35 @@ public class EventRoundPairingServiceDAO implements EventRoundPairingService {
|
||||
)
|
||||
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;
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user