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