001// -------------------------------------------------------------------------------- 002// Copyright 2002-2026 Echo Three, LLC 003// 004// Licensed under the Apache License, Version 2.0 (the "License"); 005// you may not use this file except in compliance with the License. 006// You may obtain a copy of the License at 007// 008// http://www.apache.org/licenses/LICENSE-2.0 009// 010// Unless required by applicable law or agreed to in writing, software 011// distributed under the License is distributed on an "AS IS" BASIS, 012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013// See the License for the specific language governing permissions and 014// limitations under the License. 015// -------------------------------------------------------------------------------- 016 017package com.echothree.model.control.training.server.logic; 018 019import com.echothree.model.control.training.common.exception.UnknownPartyTrainingClassSessionAnswerException; 020import com.echothree.model.control.training.common.exception.UnknownPartyTrainingClassSessionPageException; 021import com.echothree.model.control.training.common.exception.UnknownPartyTrainingClassSessionQuestionException; 022import com.echothree.model.control.training.common.exception.UnknownPartyTrainingClassSessionSequenceException; 023import com.echothree.model.control.training.common.exception.UnknownPartyTrainingClassSessionStatusException; 024import com.echothree.model.control.training.server.control.TrainingControl; 025import com.echothree.model.data.training.server.entity.PartyTrainingClass; 026import com.echothree.model.data.training.server.entity.PartyTrainingClassSession; 027import com.echothree.model.data.training.server.entity.PartyTrainingClassSessionAnswer; 028import com.echothree.model.data.training.server.entity.PartyTrainingClassSessionPage; 029import com.echothree.model.data.training.server.entity.PartyTrainingClassSessionQuestion; 030import com.echothree.model.data.training.server.entity.PartyTrainingClassSessionSection; 031import com.echothree.model.data.training.server.entity.PartyTrainingClassSessionStatus; 032import com.echothree.model.data.training.server.entity.TrainingClassPage; 033import com.echothree.model.data.training.server.entity.TrainingClassQuestion; 034import com.echothree.util.common.message.ExecutionErrors; 035import com.echothree.util.common.persistence.BasePK; 036import com.echothree.util.server.control.BaseLogic; 037import com.echothree.util.server.message.ExecutionErrorAccumulator; 038import com.echothree.util.server.persistence.EntityPermission; 039import com.echothree.util.server.persistence.RandomUtils; 040import com.echothree.util.server.persistence.Session; 041import java.io.Serializable; 042import java.util.ArrayList; 043import java.util.Collections; 044import java.util.Comparator; 045import java.util.List; 046import javax.enterprise.context.ApplicationScoped; 047import javax.inject.Inject; 048 049@ApplicationScoped 050public class PartyTrainingClassSessionLogic 051 extends BaseLogic { 052 053 @Inject 054 TrainingControl trainingControl; 055 056 @Inject 057 PartyTrainingClassLogic partyTrainingClassLogic; 058 059 @Inject 060 RandomUtils randomUtils; 061 062 protected PartyTrainingClassSessionLogic() { 063 super(); 064 } 065 066 private PartyTrainingClassSession getPartyTrainingClassSession(final ExecutionErrorAccumulator eea, final PartyTrainingClass partyTrainingClass, 067 final Integer partyTrainingClassSessionSequence, final EntityPermission entityPermission) { 068 var partyTrainingClassSession = trainingControl.getPartyTrainingClassSessionBySequence(partyTrainingClass, 069 partyTrainingClassSessionSequence, entityPermission); 070 071 if(partyTrainingClass == null) { 072 handleExecutionError(UnknownPartyTrainingClassSessionSequenceException.class, eea, ExecutionErrors.UnknownPartyTrainingClassSessionSequence.name(), 073 partyTrainingClass.getLastDetail().getPartyTrainingClassName(), partyTrainingClassSessionSequence.toString()); 074 } 075 076 return partyTrainingClassSession; 077 } 078 079 public PartyTrainingClassSession getPartyTrainingClassSession(final ExecutionErrorAccumulator eea, final PartyTrainingClass partyTrainingClass, 080 final Integer partyTrainingClassSessionSequence) { 081 return getPartyTrainingClassSession(eea, partyTrainingClass, partyTrainingClassSessionSequence, EntityPermission.READ_ONLY); 082 } 083 084 public PartyTrainingClassSession getPartyTrainingClassSessionForUpdate(final ExecutionErrorAccumulator eea, final PartyTrainingClass partyTrainingClass, 085 final Integer partyTrainingClassSessionSequence) { 086 return getPartyTrainingClassSession(eea, partyTrainingClass, partyTrainingClassSessionSequence, EntityPermission.READ_WRITE); 087 } 088 089 private PartyTrainingClassSession getLatestPartyTrainingClassSession(final ExecutionErrorAccumulator eea, final PartyTrainingClass partyTrainingClass, 090 final EntityPermission entityPermission) { 091 var partyTrainingClassStatus = trainingControl.getPartyTrainingClassStatus(partyTrainingClass); 092 var partyTrainingClassSessionSequence = partyTrainingClassStatus.getPartyTrainingClassSessionSequence(); 093 var partyTrainingClassSession = partyTrainingClassStatus == null ? null 094 : trainingControl.getPartyTrainingClassSessionBySequence(partyTrainingClass, partyTrainingClassSessionSequence, entityPermission); 095 096 if(partyTrainingClass == null) { 097 handleExecutionError(UnknownPartyTrainingClassSessionSequenceException.class, eea, ExecutionErrors.UnknownPartyTrainingClassSessionSequence.name(), 098 partyTrainingClass.getLastDetail().getPartyTrainingClassName(), partyTrainingClassSessionSequence.toString()); 099 } 100 101 return partyTrainingClassSession; 102 } 103 104 public PartyTrainingClassSession getLatestPartyTrainingClassSession(final ExecutionErrorAccumulator eea, 105 final PartyTrainingClass partyTrainingClass) { 106 return getLatestPartyTrainingClassSession(eea, partyTrainingClass, EntityPermission.READ_ONLY); 107 } 108 109 public PartyTrainingClassSession getLatestPartyTrainingClassSessionForUpdate(final ExecutionErrorAccumulator eea, 110 final PartyTrainingClass partyTrainingClass) { 111 return getLatestPartyTrainingClassSession(eea, partyTrainingClass, EntityPermission.READ_WRITE); 112 } 113 114 private PartyTrainingClassSessionPage getPartyTrainingClassSessionPage(final ExecutionErrorAccumulator eea, 115 final PartyTrainingClassSession partyTrainingClassSession, final Integer partyTrainingClassSessionPageSequence, 116 final EntityPermission entityPermission) { 117 var partyTrainingClassSessionPage = trainingControl.getPartyTrainingClassSessionPage(partyTrainingClassSession, 118 partyTrainingClassSessionPageSequence, entityPermission); 119 120 if(partyTrainingClassSessionPage == null) { 121 var partyTrainingClassSessionDetail = partyTrainingClassSession.getLastDetail(); 122 var partyTrainingClassDetail = partyTrainingClassSessionDetail.getPartyTrainingClass().getLastDetail(); 123 124 handleExecutionError(UnknownPartyTrainingClassSessionPageException.class, eea, ExecutionErrors.UnknownPartyTrainingClassSessionPage.name(), 125 partyTrainingClassDetail.getPartyTrainingClassName(), partyTrainingClassSessionDetail.getPartyTrainingClassSessionSequence().toString(), 126 partyTrainingClassSessionPageSequence.toString()); 127 } 128 129 return partyTrainingClassSessionPage; 130 } 131 132 public PartyTrainingClassSessionPage getPartyTrainingClassSessionPage(final ExecutionErrorAccumulator eea, 133 final PartyTrainingClassSession partyTrainingClassSession, final Integer partyTrainingClassSessionPageSequence) { 134 return getPartyTrainingClassSessionPage(eea, partyTrainingClassSession, partyTrainingClassSessionPageSequence, EntityPermission.READ_ONLY); 135 } 136 137 public PartyTrainingClassSessionPage getPartyTrainingClassSessionPageForUpdate(final ExecutionErrorAccumulator eea, 138 final PartyTrainingClassSession partyTrainingClassSession, final Integer partyTrainingClassSessionPageSequence) { 139 return getPartyTrainingClassSessionPage(eea, partyTrainingClassSession, partyTrainingClassSessionPageSequence, EntityPermission.READ_WRITE); 140 } 141 142 private PartyTrainingClassSessionQuestion getPartyTrainingClassSessionQuestion(final ExecutionErrorAccumulator eea, 143 final PartyTrainingClassSession partyTrainingClassSession, final TrainingClassQuestion trainingClassQuestion, 144 final EntityPermission entityPermission) { 145 var partyTrainingClassSessionQuestion = trainingControl.getPartyTrainingClassSessionQuestion(partyTrainingClassSession, 146 trainingClassQuestion, entityPermission); 147 148 if(partyTrainingClassSessionQuestion == null) { 149 var partyTrainingClassSessionDetail = partyTrainingClassSession.getLastDetail(); 150 var partyTrainingClassDetail = partyTrainingClassSessionDetail.getPartyTrainingClass().getLastDetail(); 151 var trainingClassQuestionDetail = trainingClassQuestion.getLastDetail(); 152 var trainingClassSectionDetail = trainingClassQuestionDetail.getTrainingClassSection().getLastDetail(); 153 var trainingClassDetail = trainingClassSectionDetail.getTrainingClass().getLastDetail(); 154 155 handleExecutionError(UnknownPartyTrainingClassSessionQuestionException.class, eea, ExecutionErrors.UnknownPartyTrainingClassSessionQuestion.name(), 156 partyTrainingClassDetail.getPartyTrainingClassName(), partyTrainingClassSessionDetail.getPartyTrainingClassSessionSequence().toString(), 157 trainingClassDetail.getTrainingClassName(), trainingClassSectionDetail.getTrainingClassSectionName(), 158 trainingClassQuestionDetail.getTrainingClassQuestionName()); 159 } 160 161 return partyTrainingClassSessionQuestion; 162 } 163 164 public PartyTrainingClassSessionQuestion getPartyTrainingClassSessionQuestion(final ExecutionErrorAccumulator eea, 165 final PartyTrainingClassSession partyTrainingClassSession, final TrainingClassQuestion trainingClassQuestion) { 166 return getPartyTrainingClassSessionQuestion(eea, partyTrainingClassSession, trainingClassQuestion, EntityPermission.READ_ONLY); 167 } 168 169 public PartyTrainingClassSessionQuestion getPartyTrainingClassSessionQuestionForUpdate(final ExecutionErrorAccumulator eea, 170 final PartyTrainingClassSession partyTrainingClassSession, final TrainingClassQuestion trainingClassQuestion) { 171 return getPartyTrainingClassSessionQuestion(eea, partyTrainingClassSession, trainingClassQuestion, EntityPermission.READ_WRITE); 172 } 173 174 private PartyTrainingClassSessionAnswer getPartyTrainingClassSessionAnswer(final ExecutionErrorAccumulator eea, 175 final PartyTrainingClassSessionQuestion partyTrainingClassSessionQuestion, final Integer partyTrainingClassSessionAnswerSequence, 176 final EntityPermission entityPermission) { 177 var partyTrainingClassSessionAnswer = trainingControl.getPartyTrainingClassSessionAnswer(partyTrainingClassSessionQuestion, 178 partyTrainingClassSessionAnswerSequence, entityPermission); 179 180 if(partyTrainingClassSessionAnswer == null) { 181 var partyTrainingClassSessionQuestionDetail = partyTrainingClassSessionQuestion.getLastDetail(); 182 var partyTrainingClassSessionDetail = partyTrainingClassSessionQuestionDetail.getPartyTrainingClassSession().getLastDetail(); 183 var partyTrainingClassDetail = partyTrainingClassSessionDetail.getPartyTrainingClass().getLastDetail(); 184 var trainingClassQuestionDetail = partyTrainingClassSessionQuestionDetail.getTrainingClassQuestion().getLastDetail(); 185 var trainingClassSectionDetail = trainingClassQuestionDetail.getTrainingClassSection().getLastDetail(); 186 var trainingClassDetail = trainingClassSectionDetail.getTrainingClass().getLastDetail(); 187 188 handleExecutionError(UnknownPartyTrainingClassSessionAnswerException.class, eea, ExecutionErrors.UnknownPartyTrainingClassSessionAnswer.name(), 189 partyTrainingClassDetail.getPartyTrainingClassName(), partyTrainingClassSessionDetail.getPartyTrainingClassSessionSequence().toString(), 190 trainingClassDetail.getTrainingClassName(), trainingClassSectionDetail.getTrainingClassSectionName(), 191 trainingClassQuestionDetail.getTrainingClassQuestionName(), partyTrainingClassSessionAnswerSequence.toString()); 192 } 193 194 return partyTrainingClassSessionAnswer; 195 } 196 197 public PartyTrainingClassSessionAnswer getPartyTrainingClassSessionAnswer(final ExecutionErrorAccumulator eea, 198 final PartyTrainingClassSessionQuestion partyTrainingClassSessionQuestion, final Integer partyTrainingClassSessionAnswerSequence) { 199 return getPartyTrainingClassSessionAnswer(eea, partyTrainingClassSessionQuestion, partyTrainingClassSessionAnswerSequence, EntityPermission.READ_ONLY); 200 } 201 202 public PartyTrainingClassSessionAnswer getPartyTrainingClassSessionAnswerForUpdate(final ExecutionErrorAccumulator eea, 203 final PartyTrainingClassSessionQuestion partyTrainingClassSessionQuestion, final Integer partyTrainingClassSessionAnswerSequence) { 204 return getPartyTrainingClassSessionAnswer(eea, partyTrainingClassSessionQuestion, partyTrainingClassSessionAnswerSequence, EntityPermission.READ_WRITE); 205 } 206 207 public PartyTrainingClassSessionStatus getLatestPartyTrainingClassSessionStatusForUpdate(final ExecutionErrorAccumulator eea, final String partyTrainingClassName) { 208 PartyTrainingClassSessionStatus partyTrainingClassSessionStatus = null; 209 var partyTrainingClass = partyTrainingClassLogic.getPartyTrainingClassByName(eea, partyTrainingClassName); 210 211 if(!hasExecutionErrors(eea)) { 212 var partyTrainingClassSession = getLatestPartyTrainingClassSession(eea, partyTrainingClass); 213 214 if(!hasExecutionErrors(eea)) { 215 partyTrainingClassSessionStatus = trainingControl.getPartyTrainingClassSessionStatusForUpdate(partyTrainingClassSession); 216 217 if(partyTrainingClassSessionStatus == null) { 218 handleExecutionError(UnknownPartyTrainingClassSessionStatusException.class, eea, ExecutionErrors.UnknownPartyTrainingClassSessionStatus.name(), 219 partyTrainingClass.getLastDetail().getPartyTrainingClassName(), 220 partyTrainingClassSession.getLastDetail().getPartyTrainingClassSessionSequence().toString()); 221 } 222 } 223 } 224 225 return partyTrainingClassSessionStatus; 226 } 227 228 public PartyTrainingClassSessionPage createPartyTrainingClassSessionPage(final Session session, final PartyTrainingClassSession partyTrainingClassSession, 229 final TrainingClassPage trainingClassPage, final BasePK createdBy) { 230 var partyTrainingClassSessionPage = trainingControl.createPartyTrainingClassSessionPage(partyTrainingClassSession, 231 trainingClassPage, session.getStartTime(), null, createdBy); 232 233 return partyTrainingClassSessionPage; 234 } 235 236 public PartyTrainingClassSessionQuestion createPartyTrainingClassSessionQuestion(final PartyTrainingClassSession partyTrainingClassSession, 237 final TrainingClassQuestion trainingClassQuestion, final Integer sortOrder, final BasePK createdBy) { 238 return trainingControl.createPartyTrainingClassSessionQuestion(partyTrainingClassSession, trainingClassQuestion, sortOrder, createdBy); 239 } 240 241 static class SortBySortOrder implements Comparator<TrainingClassQuestion>, Serializable { 242 243 @Override 244 public int compare(TrainingClassQuestion o1, TrainingClassQuestion o2) { 245 var s1 = o1.getLastDetail().getSortOrder(); 246 var s2 = o2.getLastDetail().getSortOrder(); 247 248 return s1.compareTo(s2); 249 } 250 251 } 252 253 public void setupPartyTrainingClassSessionQuestions(final PartyTrainingClassSession partyTrainingClassSession, final BasePK createdBy) { 254 var trainingClass = partyTrainingClassSession.getLastDetail().getPartyTrainingClass().getLastDetail().getTrainingClass(); 255 var trainingClassSections = trainingControl.getTrainingClassSections(trainingClass); 256 var overallQuestionCount = trainingClass.getLastDetail().getOverallQuestionCount(); 257 List<TrainingClassQuestion> randomOverallQuestions = new ArrayList<>(); 258 List<TrainingClassQuestion> finalTrainingClassQuestions = new ArrayList<>(); 259 var random = randomUtils.getRandom(); 260 261 var overallQuestionTotal = 0; 262 for(var trainingClassSection : trainingClassSections) { 263 var questionCount = trainingClassSection.getLastDetail().getQuestionCount(); 264 var trainingClassQuestions = trainingControl.getTrainingClassQuestions(trainingClassSection); 265 List<TrainingClassQuestion> randomSectionQuestions = new ArrayList<>(); 266 267 // Add in all required questions... 268 var questionTotal = 0; 269 for(var trainingClassQuestion : trainingClassQuestions) { 270 if(trainingClassQuestion.getLastDetail().getPassingRequired()) { 271 finalTrainingClassQuestions.add(trainingClassQuestion); 272 questionTotal++; 273 overallQuestionTotal++; 274 } else { 275 // If it isn't required, add it to the pool of questions used to fill in random ones. 276 randomSectionQuestions.add(trainingClassQuestion); 277 } 278 } 279 280 // If there's a required minimum Question count, try to fill in the rest randomly. 281 if(questionCount != null) { 282 var remainingQuestions = questionCount - questionTotal; 283 284 // If there are random questions, add in a few from the section randomly. 285 while(remainingQuestions > 0) { 286 if(randomSectionQuestions.isEmpty()) { 287 break; 288 } else { 289 // pick a Question and add to finalTrainingClassQuestions 290 var randomQuestion = random.nextInt(randomSectionQuestions.size()); 291 finalTrainingClassQuestions.add(randomSectionQuestions.get(randomQuestion)); 292 overallQuestionTotal++; 293 294 // removed picked one from randomSectionQuestions 295 randomSectionQuestions.remove(randomQuestion); 296 297 remainingQuestions--; 298 } 299 } 300 } 301 302 // Add any remaining Questions to the overall random Question Set. 303 randomOverallQuestions.addAll(randomSectionQuestions); 304 } 305 306 if(overallQuestionCount != null) { 307 var remainingQuestions = overallQuestionCount - overallQuestionTotal; 308 309 while(remainingQuestions > 0) { 310 if(randomOverallQuestions.isEmpty()) { 311 break; 312 } else { 313 // pick one and add to finalTrainingClassQuestions 314 var randomQuestion = random.nextInt(randomOverallQuestions.size()); 315 finalTrainingClassQuestions.add(randomOverallQuestions.get(randomQuestion)); 316 317 // removed picked one from randomOverallQuestions 318 randomOverallQuestions.remove(randomQuestion); 319 320 remainingQuestions--; 321 } 322 } 323 } 324 325 Collections.sort(finalTrainingClassQuestions, new SortBySortOrder()); 326 327 var sortOrder = 0; 328 for(var finalTrainingClassQuestion : finalTrainingClassQuestions) { 329 createPartyTrainingClassSessionQuestion(partyTrainingClassSession, finalTrainingClassQuestion, sortOrder++, createdBy); 330 } 331 } 332 333 public PartyTrainingClassSession createPartyTrainingClassSession(final PartyTrainingClass partyTrainingClass, final BasePK createdBy) { 334 var partyTrainingClassSession = trainingControl.createPartyTrainingClassSession(partyTrainingClass, createdBy); 335 var partyTrainingClassStatus = trainingControl.getPartyTrainingClassStatusForUpdate(partyTrainingClass); 336 337 partyTrainingClassStatus.setLastPartyTrainingClassSession(partyTrainingClassSession); 338 339 setupPartyTrainingClassSessionQuestions(partyTrainingClassSession, createdBy); 340 341 return partyTrainingClassSession; 342 } 343 344 public void deletePartyTrainingClassSession(final PartyTrainingClassSession partyTrainingClassSession, final BasePK deletedBy) { 345 var partyTrainingClass = partyTrainingClassSession.getLastDetail().getPartyTrainingClass(); 346 347 trainingControl.deletePartyTrainingClassSession(partyTrainingClassSession, deletedBy); 348 349 // Create a new PartyTrainingClassSession if the PartyTrainingClass is not yet complete (where "complete" means that the 350 // completedTime is null, and the LastPartyTrainingClassSession is equals to the one we're deleting in the PartyTrainingClassStatus. 351 if(partyTrainingClass.getLastDetail().getCompletedTime() == null) { 352 var partyTrainingClassStatus = trainingControl.getPartyTrainingClassStatusForUpdate(partyTrainingClass); 353 354 if(partyTrainingClassSession.equals(partyTrainingClassStatus.getLastPartyTrainingClassSession())) { 355 createPartyTrainingClassSession(partyTrainingClass, deletedBy); 356 } 357 } 358 } 359 360 public void updatePartyTrainingClassSessionStatus(final Session session, final PartyTrainingClassSessionStatus partyTrainingClassSessionStatus, 361 final PartyTrainingClassSessionSection lastPartyTrainingClassSessionSection, final PartyTrainingClassSessionPage lastPartyTrainingClassSessionPage, 362 final PartyTrainingClassSessionQuestion lastPartyTrainingClassSessionQuestion) { 363 partyTrainingClassSessionStatus.setLastPartyTrainingClassSessionSection(lastPartyTrainingClassSessionSection); 364 partyTrainingClassSessionStatus.setLastPartyTrainingClassSessionPage(lastPartyTrainingClassSessionPage); 365 partyTrainingClassSessionStatus.setLastPartyTrainingClassSessionQuestion(lastPartyTrainingClassSessionQuestion); 366 } 367 368}