/ components / paddock / telemetry / pitcrew / session_saver.py
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()