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