session_saver.py
1 import logging 2 import threading 3 import time 4 5 from django.db import IntegrityError 6 7 from telemetry.models import Driver, Game, Session, SessionType 8 9 10 class SessionSaver: 11 def __init__(self, firehose, save=True): 12 self.firehose = firehose 13 self.sleep_time = 10 14 self.save = save 15 16 self._stop_event = threading.Event() 17 self.ready = False 18 19 def stop(self): 20 self._stop_event.set() 21 22 def stopped(self): 23 return self._stop_event.is_set() 24 25 def save_sessions_loop(self): 26 while True and not self.stopped(): 27 if self.save: 28 self.save_sessions() 29 else: 30 self.fetch_sessions() 31 self.ready = True 32 time.sleep(self.sleep_time) 33 34 def fetch_sessions(self): 35 session_ids = list(self.firehose.sessions.keys()) 36 for session_id in session_ids: 37 session = self.firehose.sessions.get(session_id) 38 if not session.record: 39 try: 40 session.driver = Driver.objects.get(name=session.driver) 41 session.game = Game.objects.get(name=session.game_name) 42 session.session_type = SessionType.objects.get(type=session.session_type) 43 session.car = session.game.cars.get(name=session.car) 44 session.car.car_class = session.game.car_classes.get(name=session.car_class) 45 session.track = session.game.tracks.get(name=session.track) 46 session.record = session.driver.sessions.filter( 47 session_id=session.session_id, 48 session_type=session.session_type, 49 game=session.game, 50 ).first() or Session( 51 session_id=session.session_id, 52 session_type=session.session_type, 53 game=session.game, 54 start=session.start, 55 end=session.end, 56 ) 57 logging.debug(f"{session.session_id}: Fetched session {session_id}") 58 except Exception as e: 59 # TODO add error to session to expire 60 logging.error(f"{session.session_id}: Error fetching session {session_id}: {e}") 61 continue 62 63 def save_sessions(self): 64 session_ids = list(self.firehose.sessions.keys()) 65 for session_id in session_ids: 66 session = self.firehose.sessions.get(session_id) 67 68 # save session to database 69 # TODO: update session details if they change (e.g. end time) 70 if not session.record: 71 try: 72 session.driver, created = Driver.objects.get_or_create(name=session.driver) 73 session.game, created = Game.objects.get_or_create(name=session.game_name) 74 ( 75 session.session_type, 76 created, 77 ) = SessionType.objects.get_or_create(type=session.session_type) 78 session.car, created = session.game.cars.get_or_create(name=session.car) 79 session.car.car_class, created = session.game.car_classes.get_or_create(name=session.car_class) 80 session.track, created = session.game.tracks.get_or_create(name=session.track) 81 ( 82 session.record, 83 created, 84 ) = session.driver.sessions.get_or_create( 85 session_id=session.session_id, 86 session_type=session.session_type, 87 game=session.game, 88 defaults={"start": session.start, "end": session.end}, 89 ) 90 logging.debug(f"{session.session_id}: Saving session {session_id}") 91 except Exception as e: 92 # TODO add error to session to expire 93 logging.error(f"{session.session_id}: Error saving session {session_id}: {e}") 94 continue 95 96 lap_numbers = list(session.laps.keys()) 97 for lap_number in lap_numbers: 98 lap = session.laps.get(lap_number) 99 if session.record and lap.finished and not lap.persisted: 100 # check if lap length is within 98% of the track length 101 track = session.track 102 103 # track_length = track.length 104 # if lap.length < track_length * 0.98: 105 # lstring = ( 106 # f"{lap.number}: {lap.time}s {lap.length}m" 107 # ) 108 # logging.info( 109 # f"{session.session_id}: Discard lap {lstring} - track length {track_length}m" 110 # ) 111 # # FIXME: this is a hack to prevent the lap from being saved again 112 # lap.persisted = True 113 # continue 114 115 try: 116 lap_record = session.record.laps.create( 117 number=lap.number, 118 car=session.car, 119 track=track, 120 start=lap.start, 121 end=lap.end, 122 length=lap.length, 123 valid=lap.valid, 124 time=lap.time, 125 ) 126 logging.info(f"{session.session_id}: Saving lap {lap_record}") 127 session.record.end = session.end 128 session.record.save_dirty_fields() 129 lap.persisted = True 130 except IntegrityError as e: 131 logging.error(f"{session.session_id}: Error saving lap {lap.number}: {e}") 132 lap.persisted = True 133 except Exception as e: 134 logging.error(f"{session.session_id}: Error saving lap {lap.number}: {e}") 135 136 lap_length = int(lap.length) 137 if lap_length > track.length: 138 track.refresh_from_db() 139 if lap_length > track.length: 140 logging.info( 141 f"{session.session_id}: updating {track.name} " 142 + f"length from {track.length} to {lap_length}" 143 ) 144 track.length = lap_length 145 track.save() 146 147 def run(self): 148 self.save_sessions_loop()