|
| 1 | +from sqlalchemy.orm import Session |
| 2 | + |
| 3 | +from app.gameConfig import ALL_LEVEL_TYPES, PHASES_WITH_LEVELS |
| 4 | +from app.model.Level import Level |
| 5 | +from app.model.LogEvents import ( |
| 6 | + AltTaskEvent, |
| 7 | + ChronoEvent, |
| 8 | + ClickEvent, |
| 9 | + ConfirmClickEvent, |
| 10 | + DrawEvent, |
| 11 | + GameOverEvent, |
| 12 | + GroupAssignmentEvent, |
| 13 | + IntroNavigationEvent, |
| 14 | + LanguageSelectionEvent, |
| 15 | + LogCreatedEvent, |
| 16 | + LogEvent, |
| 17 | + PopUpEvent, |
| 18 | + QualiEvent, |
| 19 | + ReconnectEvent, |
| 20 | + RedirectEvent, |
| 21 | + SelectDrawToolEvent, |
| 22 | + SimulateEvent, |
| 23 | + SkillAssessmentEvent, |
| 24 | + StartSessionEvent, |
| 25 | + SwitchClickEvent, |
| 26 | +) |
| 27 | + |
| 28 | +from app.model.Participant import Participant |
| 29 | +from app.statistics3.statisticsUtils import LogValidationError |
| 30 | +from app.statistics3.StatsParticipant import StatsParticipant |
| 31 | +from app.statistics3.StatsPhaseLevels import StatsPhaseLevels |
| 32 | + |
| 33 | + |
| 34 | +class LogEventValidator(): |
| 35 | + |
| 36 | + def handle_event(self, event: LogEvent, session: Session, statsParticipant: StatsParticipant, player: Participant): |
| 37 | + match event.eventType: |
| 38 | + case LogCreatedEvent.__name__: |
| 39 | + assert isinstance(event, LogCreatedEvent) |
| 40 | + self.event_log_created(statsParticipant, event) |
| 41 | + case LanguageSelectionEvent.__name__: |
| 42 | + pass |
| 43 | + case GroupAssignmentEvent.__name__: |
| 44 | + assert isinstance(event, GroupAssignmentEvent) |
| 45 | + self.event_group_assignment(statsParticipant, event) |
| 46 | + case RedirectEvent.__name__: |
| 47 | + pass |
| 48 | + case ReconnectEvent.__name__: |
| 49 | + pass |
| 50 | + case GameOverEvent.__name__: |
| 51 | + pass |
| 52 | + case ChronoEvent.__name__: |
| 53 | + assert isinstance(event, ChronoEvent) |
| 54 | + self.event_chrono(session, statsParticipant, player, event) |
| 55 | + case StartSessionEvent.__name__: |
| 56 | + pass |
| 57 | + case SkillAssessmentEvent.__name__: |
| 58 | + pass |
| 59 | + case QualiEvent.__name__: |
| 60 | + pass |
| 61 | + case ClickEvent.__name__: |
| 62 | + pass |
| 63 | + case SwitchClickEvent.__name__: |
| 64 | + assert isinstance(event, SwitchClickEvent) |
| 65 | + self.event_switch_click(session, statsParticipant, event) |
| 66 | + case ConfirmClickEvent.__name__: |
| 67 | + assert isinstance(event, ConfirmClickEvent) |
| 68 | + self.event_confirm_click(session, statsParticipant, event) |
| 69 | + case SimulateEvent.__name__: |
| 70 | + pass |
| 71 | + case IntroNavigationEvent.__name__: |
| 72 | + pass |
| 73 | + case SelectDrawToolEvent.__name__: |
| 74 | + pass |
| 75 | + case DrawEvent.__name__: |
| 76 | + pass |
| 77 | + case PopUpEvent.__name__: |
| 78 | + pass |
| 79 | + case AltTaskEvent.__name__: |
| 80 | + pass |
| 81 | + case _: |
| 82 | + raise LogValidationError('Unexpected Log Type') |
| 83 | + |
| 84 | + |
| 85 | + def event_log_created(self, |
| 86 | + participant: StatsParticipant, |
| 87 | + event: LogCreatedEvent |
| 88 | + ): |
| 89 | + participant.pseudonym = event.plain_pseudonym |
| 90 | + |
| 91 | + if event.plain_pseudonym != event.pseudonym: |
| 92 | + raise LogValidationError(f'Pseudonym mismatch in LogCreatedEvent: "{event.plain_pseudonym} != {event.pseudonym}"') |
| 93 | + |
| 94 | + |
| 95 | + def event_group_assignment(self, |
| 96 | + statsParticipant: StatsParticipant, |
| 97 | + event: GroupAssignmentEvent |
| 98 | + ): |
| 99 | + |
| 100 | + if event.group in statsParticipant.groups: |
| 101 | + raise LogValidationError(f'Group {event.group} was assigned twice', event) |
| 102 | + |
| 103 | + statsParticipant.groups.append(event.group) |
| 104 | + |
| 105 | + |
| 106 | + def event_chrono(self, session: Session, participant: StatsParticipant, player: Participant, event: ChronoEvent): |
| 107 | + if event.timeClient is None: |
| 108 | + raise LogValidationError('The chrono event did not contain the client time') |
| 109 | + |
| 110 | + # Phase Operations |
| 111 | + if 'phase' == event.timerType: |
| 112 | + # Phase Load |
| 113 | + if 'load' == event.operation: |
| 114 | + self.load_phase(event, participant, player) |
| 115 | + |
| 116 | + # Phase Start |
| 117 | + elif 'start' == event.operation: |
| 118 | + self.start_phase(event, participant) |
| 119 | + |
| 120 | + else: |
| 121 | + raise LogValidationError(f'Unknown operation "{event.operation}"') |
| 122 | + |
| 123 | + # Level Operations |
| 124 | + elif event.timerType in ALL_LEVEL_TYPES: |
| 125 | + # Check that the Level/Info Slide was created in a Phase which supports them |
| 126 | + if not isinstance(participant.activePhase, StatsPhaseLevels): |
| 127 | + raise LogValidationError(f'Slide type {event.timerType} should not exist in phase {participant.activePhase}', event) |
| 128 | + |
| 129 | + # Load a Slide |
| 130 | + if 'load' == event.operation: |
| 131 | + self.load_slide(event, participant) |
| 132 | + |
| 133 | + # Start a slide |
| 134 | + elif 'start' == event.operation: |
| 135 | + self.start_slide(event, participant) |
| 136 | + |
| 137 | + else: |
| 138 | + raise LogValidationError(f'Unknown operation "{event.operation}"') |
| 139 | + |
| 140 | + |
| 141 | + def event_switch_click(self, session: Session, statsParticipant: StatsParticipant, event: SwitchClickEvent): |
| 142 | + statsParticipant.activePhase |
| 143 | + |
| 144 | + |
| 145 | + def event_confirm_click(self, session: Session, statsParticipant: StatsParticipant, event: ConfirmClickEvent): |
| 146 | + statsParticipant.activePhase |
| 147 | + |
| 148 | + |
| 149 | + def load_phase(self, event: ChronoEvent, statsParticipant: StatsParticipant, player: Participant): |
| 150 | + assert event.timeClient is not None |
| 151 | + |
| 152 | + phaseType = event.timerName |
| 153 | + statsParticipant.load_phase(phaseType, event.timeClient) |
| 154 | + |
| 155 | + # The phase from the game state |
| 156 | + assert statsParticipant.phaseIdx is not None |
| 157 | + gamestate_phase = player.phases[statsParticipant.phaseIdx] |
| 158 | + |
| 159 | + # Check that the phase type matches what was shown during the game |
| 160 | + if gamestate_phase.name != phaseType: |
| 161 | + raise LogValidationError(f'{phaseType} does not match the gamestate {gamestate_phase.name}') |
| 162 | + |
| 163 | + # Assert that a phase with levels really has levels |
| 164 | + assert phaseType in PHASES_WITH_LEVELS and len(gamestate_phase.levels) > 0, ( |
| 165 | + f'Phase {phaseType} is expected to have no levels, but gameState has {len(gamestate_phase.levels)}' |
| 166 | + ) |
| 167 | + |
| 168 | + # Assert that a phase without levels really has no levels |
| 169 | + assert phaseType not in PHASES_WITH_LEVELS and len(gamestate_phase.levels) < 1, ( |
| 170 | + f'Phase {phaseType} is expected to have levels, but gameState has 0' |
| 171 | + ) |
| 172 | + |
| 173 | + |
| 174 | + def start_phase(self, event: ChronoEvent, statsParticipant: StatsParticipant): |
| 175 | + assert event.timeClient is not None |
| 176 | + |
| 177 | + statsParticipant.activePhase.start(event.timeClient) |
| 178 | + |
| 179 | + |
| 180 | + def load_slide(self, event: ChronoEvent, statsParticipant: StatsParticipant): |
| 181 | + assert event.timeClient is not None |
| 182 | + |
| 183 | + activePhase = statsParticipant.activePhase |
| 184 | + if not isinstance(activePhase, StatsPhaseLevels): |
| 185 | + raise LogValidationError('') |
| 186 | + |
| 187 | + activePhase.load_level( |
| 188 | + type_level=event.timerType, |
| 189 | + log_name=Level.uniformName(event.timerName), |
| 190 | + time_load=event.timeClient |
| 191 | + ) |
| 192 | + |
| 193 | + |
| 194 | + def start_slide(self, event: ChronoEvent, statsParticipant: StatsParticipant): |
| 195 | + assert event.timeClient is not None |
| 196 | + |
0 commit comments