/* * Copyright (C) 2016 jcgarner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.pikatimer.results.reports; import com.pikatimer.event.Event; import com.pikatimer.participant.CustomAttribute; import com.pikatimer.participant.ParticipantDAO; import com.pikatimer.race.Race; import com.pikatimer.race.RaceDAO; import com.pikatimer.results.ProcessedResult; import com.pikatimer.results.RaceReport; import com.pikatimer.results.RaceReportType; import com.pikatimer.util.AlphanumericComparator; import com.pikatimer.util.DurationFormatter; import com.pikatimer.util.Pace; import java.time.Duration; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import javafx.beans.property.BooleanProperty; import javafx.beans.property.IntegerProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleIntegerProperty; import org.apache.commons.lang3.StringUtils; /** * * @author jcgarner */ public class AgeGroup implements RaceReportType { Race race; // Defaults Boolean showDQ = true; Boolean inProgress = false; Boolean showSplits = false; Boolean showSegments = false; Boolean showSegmentPace = false; Boolean showDNF = true; Boolean showPace = true; Boolean showGun = true; IntegerProperty fullNameLength = new SimpleIntegerProperty(10); Boolean showCustomAttributes = false; List<CustomAttribute> customAttributesList = new ArrayList(); Map<Integer,Integer> customAttributeSizeMap = new HashMap(); Map<String,Boolean> supportedOptions = new HashMap(); BooleanProperty showCountry = new SimpleBooleanProperty(true); BooleanProperty showState = new SimpleBooleanProperty(true); Boolean penaltiesOrBonuses = false; public AgeGroup(){ supportedOptions.put("showDQ", true); supportedOptions.put("inProgress", false); supportedOptions.put("showSplits", false); supportedOptions.put("showSegments", true); supportedOptions.put("showSegmentPace", false); supportedOptions.put("showDNF", false); supportedOptions.put("showPace", true); supportedOptions.put("showGun", true); supportedOptions.put("hideCustomHeaders", false); supportedOptions.put("showCustomAttributes", false); } @Override public void init(Race r) { race = r; } @Override public Boolean optionSupport(String feature) { if (supportedOptions.containsKey(feature)) return true; return false; } @Override public String process(List<ProcessedResult> prList, RaceReport rr) { System.out.println("AgeGroup.process() Called... "); String report = new String(); race = rr.getRace(); Event event = Event.getInstance(); // fun with singletons... supportedOptions.keySet().forEach(k -> supportedOptions.put(k, rr.getBooleanAttribute(k))); showDQ = supportedOptions.get("showDQ"); inProgress = supportedOptions.get("inProgress"); showSplits = supportedOptions.get("showSplits"); showSegments = supportedOptions.get("showSegments"); showSegmentPace = supportedOptions.get("showSegmentPace"); showDNF = supportedOptions.get("showDNF"); showPace = supportedOptions.get("showPace"); showGun = supportedOptions.get("showGun"); showCustomAttributes = supportedOptions.get("showCustomAttributes"); Boolean customHeaders = race.getBooleanAttribute("useCustomHeaders"); if (customHeaders && supportedOptions.get("hideCustomHeaders")) customHeaders = false; Boolean showCO = false; Boolean showST = false; for (ProcessedResult x : prList){ if (! x.getParticipant().getCountry().isEmpty()) showCO=true; if (! x.getParticipant().getState().isEmpty()) showST=true; } // Stupid lambda workarounds.... showCountry.setValue(showCO); showState.setValue(showST); // what is the longest name? prList.forEach (pr ->{ if(pr.getParticipant().fullNameProperty().length().getValue() > fullNameLength.getValue()) fullNameLength.setValue(pr.getParticipant().fullNameProperty().length().getValue()); }); fullNameLength.setValue(fullNameLength.getValue() + 1); if (customHeaders && race.getStringAttribute("textHeader") != null && !race.getStringAttribute("textHeader").isEmpty()) { report += race.getStringAttribute("textHeader"); report += System.lineSeparator(); } if (RaceDAO.getInstance().listRaces().size() > 1) report += StringUtils.center(event.getEventName(),80) + System.lineSeparator(); report += StringUtils.center(race.getRaceName(),80) + System.lineSeparator(); report += StringUtils.center(event.getLocalEventDate().format(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)),80) + System.lineSeparator(); report += System.lineSeparator(); if(inProgress) { report += StringUtils.center("*******In Progress*******",80) + System.lineSeparator(); report += System.lineSeparator(); } if (customHeaders && race.getStringAttribute("textMessage") != null && !race.getStringAttribute("textMessage").isEmpty()) { report += race.getStringAttribute("textMessage"); report += System.lineSeparator(); } // Split the list into a Map<String,List<ProcessedResult>> agResultsMap = prList.stream() .collect(Collectors.groupingBy(pr -> {return pr.getSex() + pr.getAGCode();})); if (showCustomAttributes) customAttributesList= ParticipantDAO.getInstance().getCustomAttributes().stream().filter(a -> { if (rr.getBooleanAttribute(a.getUUID()) != null ) return rr.getBooleanAttribute(a.getUUID()); return false; }).collect(Collectors.toList()); if (showCustomAttributes && customAttributesList.isEmpty()) showCustomAttributes = false; // how long are these things? if (showCustomAttributes) { customAttributesList.forEach((a) -> { int l = a.getName().length() + 1; customAttributeSizeMap.put(a.getID(),l); for (ProcessedResult p: prList){ if (l <= p.getParticipant().getCustomAttribute(a.getID()).getValueSafe().length()){ l = p.getParticipant().getCustomAttribute(a.getID()).getValueSafe().length() + 1; customAttributeSizeMap.put(a.getID(),l); } } }); } List<String> agCatList = new ArrayList(agResultsMap.keySet()); agCatList.sort(new AlphanumericComparator()); StringBuilder chars = new StringBuilder(); agCatList.forEach(ag -> { String agDesc = race.getAgeGroups().ageToLongAGString(agResultsMap.get(ag).get(0).getAge()); String agSex = agResultsMap.get(ag).get(0).getSex().replace("F", "Female ").replace("M","Male "); String longAG = agSex + agDesc; chars.append(StringUtils.center(StringUtils.center(longAG,30, "*"),80)).append(System.lineSeparator()); chars.append(System.lineSeparator()); chars.append(printAG(agResultsMap.get(ag))); chars.append(System.lineSeparator()); chars.append(System.lineSeparator()); }); report += chars.toString(); if (customHeaders && race.getStringAttribute("textFooter") != null && !race.getStringAttribute("textFooter").isEmpty()) { report += race.getStringAttribute("textFooter"); report += System.lineSeparator(); } return report; } public String printAG(List<ProcessedResult> prList){ String dispFormat = race.getStringAttribute("TimeDisplayFormat"); String roundMode = race.getStringAttribute("TimeRoundingMode"); Pace pace = Pace.valueOf(race.getStringAttribute("PaceDisplayFormat")); //System.out.println("Age Group Processing: Display Format: " + dispFormat + " Rounding " + roundMode); Integer dispFormatLength; // add a space if (dispFormat.contains("[HH:]")) dispFormatLength = dispFormat.length()-1; // get rid of the two brackets and add a space else dispFormatLength = dispFormat.length()+1; Duration cutoffTime = Duration.ofNanos(race.getRaceCutoff()); String cutoffTimeString = DurationFormatter.durationToString(cutoffTime, dispFormat, roundMode); penaltiesOrBonuses = prList.stream().anyMatch(s -> (s.getBonus() || s.getPenalty())); final StringBuilder chars = new StringBuilder(); chars.append(printHeader()); prList.forEach(pr -> { // if they are a DNF or DQ swap out the placement stats Boolean hideTime = false; Boolean dnf = pr.getParticipant().getDNF(); Boolean dq = pr.getParticipant().getDQ(); if (dq) hideTime = true; if (pr.getChipFinish() == null) dnf = true; if (!showDNF && !inProgress && dnf) return; if (!showDQ && dq) return; if (dq) chars.append(StringUtils.center("***DQ****",14)); else if (inProgress && pr.getChipFinish() == null) { chars.append(StringUtils.center("**Started**",14)); //hideTime = true; } else if (!showDNF && pr.getChipFinish() == null){ return; } else if (! dnf && ! dq) { if (cutoffTime.isZero() || cutoffTime.compareTo(pr.getChipFinish()) >= 0 || cutoffTimeString.equals(DurationFormatter.durationToString(pr.getChipFinish(), dispFormat, roundMode))) { chars.append(StringUtils.leftPad(pr.getOverall().toString(),4)); chars.append(StringUtils.leftPad(pr.getSexPlace().toString(),5)); chars.append(StringUtils.leftPad(pr.getAGPlace().toString(),5)); } else { if (!showDNF && !inProgress) return; chars.append(StringUtils.center("***OCO***",14)); } } else { chars.append(StringUtils.center("***DNF***",14)); } chars.append(StringUtils.leftPad(pr.getParticipant().getBib(),5)); chars.append(StringUtils.leftPad(pr.getAge().toString(),4)); chars.append(StringUtils.center(pr.getSex(),5)); chars.append(StringUtils.rightPad(pr.getAGCode(),6)); chars.append(StringUtils.rightPad(pr.getParticipant().fullNameProperty().getValueSafe(),fullNameLength.get())); chars.append(StringUtils.rightPad(pr.getParticipant().getCity(),18)); if (showState.get()) chars.append(StringUtils.center(pr.getParticipant().getState(),4)); if (showCountry.get()) chars.append(StringUtils.rightPad(pr.getParticipant().getCountry(),4)); if (dq) { chars.append(" Reason: ").append(pr.getParticipant().getNote()); chars.append(System.lineSeparator()); return; } if (showCustomAttributes) { for( CustomAttribute a: customAttributesList){ chars.append(StringUtils.rightPad(pr.getParticipant().getCustomAttribute(a.getID()).getValueSafe(),customAttributeSizeMap.get(a.getID()))); } } // Insert split stuff here if (showSplits && ! hideTime) { // do stuff for (int i = 2; i < race.splitsProperty().size(); i++) { if (!race.splitsProperty().get(i-1).getIgnoreTime()) chars.append(StringUtils.leftPad(DurationFormatter.durationToString(pr.getSplit(i), dispFormat, roundMode), dispFormatLength)); } } if (showSegments) { Integer dispLen = dispFormatLength; race.raceSegmentsProperty().forEach(seg -> { if (seg.getHidden()) return; chars.append(StringUtils.leftPad(DurationFormatter.durationToString(pr.getSegmentTime(seg.getID()), dispFormat, roundMode),dispLen)); if (showSegmentPace) { if (seg.getUseCustomPace()) { if(!Pace.NONE.equals(seg.getCustomPace())) { if (pr.getSegmentTime(seg.getID()) != null ) chars.append(StringUtils.leftPad(seg.getCustomPace().getPace(seg.getSegmentDistance(), race.getRaceDistanceUnits(), pr.getSegmentTime(seg.getID())),seg.getCustomPace().getFieldWidth()+1)); else chars.append(StringUtils.leftPad("",seg.getCustomPace().getFieldWidth()+1)); } } else { if (pr.getSegmentTime(seg.getID()) != null ) chars.append(StringUtils.leftPad(pace.getPace(seg.getSegmentDistance(), race.getRaceDistanceUnits(), pr.getSegmentTime(seg.getID())),pace.getFieldWidth()+1)); else chars.append(StringUtils.leftPad("",pace.getFieldWidth()+1)); } } }); } if (dnf) { chars.append(System.lineSeparator()); return; } if (penaltiesOrBonuses){ if (pr.getBonus() || pr.getPenalty()) { if (pr.getBonus()) chars.append(StringUtils.leftPad("-"+DurationFormatter.durationToString(pr.getBonusTime(), dispFormat, roundMode),dispFormatLength)); else chars.append(StringUtils.leftPad("+"+DurationFormatter.durationToString(pr.getPenaltyTime(), dispFormat, roundMode),dispFormatLength)); } else chars.append(StringUtils.leftPad("---",dispFormatLength)); } // chip time if (! hideTime) chars.append(StringUtils.leftPad(DurationFormatter.durationToString(pr.getChipFinish(), dispFormat, roundMode), dispFormatLength)); if (showGun && ! hideTime) chars.append(StringUtils.leftPad(DurationFormatter.durationToString(pr.getGunFinish(), dispFormat, roundMode), dispFormatLength)); if (showPace && ! hideTime) chars.append(StringUtils.leftPad(pace.getPace(race.getRaceDistance().floatValue(), race.getRaceDistanceUnits(), pr.getChipFinish()),pace.getFieldWidth()+1)); // System.out.println("Results: " + r.getRaceName() + ": " // + r.getParticipant().fullNameProperty().getValueSafe() // + "(" + pr.getSex() + pr.getAGCode() + "): " // + DurationFormatter.durationToString(pr.getChipFinish()) // + " O:" + pr.getOverall() + " S:" + pr.getSexPlace() // + " AG:" + pr.getAGPlace() // ); chars.append(System.lineSeparator()); }); return chars.toString(); } private String printHeader(){ String dispFormat = race.getStringAttribute("TimeDisplayFormat"); Integer dispFormatLength = dispFormat.length()+1; // add a space if (dispFormat.contains("[HH:]")) dispFormatLength = dispFormat.length()-1; // get rid of the two brackets and add a space Pace pace = Pace.valueOf(race.getStringAttribute("PaceDisplayFormat")); String report = new String(); // print the headder report += " OA#"; // 4R chars report += " SEX#"; // 5R chars report += " AG#"; // 5R chars report += " BIB"; // 5R chars for the bib # report += " AGE"; // 4R for the age report += " SEX"; // 4R for the sex report += " AG "; //6L for the AG Group report += StringUtils.rightPad(" Name",fullNameLength.get()); // based on the longest name report += " City "; // 18L for the city if (showState.get() )report += " ST "; // 4C for the state code if (showCountry.get() )report += " CO "; // 4C for the state code if (showCustomAttributes) { for( CustomAttribute a: customAttributesList){ report += StringUtils.rightPad(a.getName(),customAttributeSizeMap.get(a.getID())); } } // Insert split stuff here if (showSplits) { // do stuff // 9 chars per split for (int i = 2; i < race.splitsProperty().size(); i++) { if (!race.splitsProperty().get(i-1).getIgnoreTime()) report += StringUtils.leftPad(race.splitsProperty().get(i-1).getSplitName(),dispFormatLength); } } if (showSegments) { final StringBuilder chars = new StringBuilder(); Integer dispLeg = dispFormatLength; race.raceSegmentsProperty().forEach(seg -> { if(seg.getHidden() ) return; chars.append(StringUtils.leftPad(seg.getSegmentName(),dispLeg)); if (showSegmentPace) { if (seg.getUseCustomPace() ) { if (! (seg.getUseCustomPace() && Pace.NONE.equals(seg.getCustomPace()))) chars.append(StringUtils.leftPad("Pace",seg.getCustomPace().getFieldWidth()+1)); } else chars.append(StringUtils.leftPad("Pace",pace.getFieldWidth()+1)); } // pace.getFieldWidth()+1 }); report += chars.toString(); } if (penaltiesOrBonuses) report += StringUtils.leftPad("Adj", dispFormatLength); // Chip time report += StringUtils.leftPad("Finish", dispFormatLength); // gun time if (showGun) report += StringUtils.leftPad("Gun", dispFormatLength); // pace if (showPace) report += StringUtils.leftPad("Pace",pace.getFieldWidth()+1); report += System.lineSeparator(); return report; } }