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