traffic_tracking.py 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import logging
  2. import os
  3. import pickle
  4. import threading
  5. import time
  6. from typing import Dict, Set
  7. from starlette.requests import Request
  8. from nicegui import ui
  9. VISITS_FILE = 'traffic_data/visits.pickle'
  10. SESSIONS_FILE = 'traffic_data/sessions.pickle'
  11. class TrafficChard(ui.chart):
  12. def __init__(self) -> None:
  13. self.visits: Dict[int, int] = {}
  14. self.sessions: Dict[int, Set[str]] = {}
  15. self.load()
  16. ui.on_connect(self.on_connect)
  17. ui.timer(10, self.save)
  18. ui.timer(10, self.update_visibility)
  19. super().__init__({
  20. 'title': {'text': 'Page Visits'},
  21. 'navigation': {'buttonOptions': {'enabled': False}},
  22. 'chart': {'type': 'line'},
  23. 'yAxis': {'title': False},
  24. 'xAxis': {
  25. 'type': 'datetime',
  26. 'labels': {'format': '{value:%b %e}', },
  27. },
  28. 'series': [
  29. {'name': 'Views', 'data': []},
  30. {'name': 'Sessions', 'data': []},
  31. ],
  32. })
  33. def on_connect(self, request: Request) -> None:
  34. def seconds_to_day(seconds: float) -> int: return int(seconds / 60 / 60 / 24)
  35. def day_to_milliseconds(day: int) -> float: return day * 24 * 60 * 60 * 1000
  36. today = seconds_to_day(time.time())
  37. self.visits[today] = self.visits.get(today, 0) + 1
  38. self.options.series[0].data[:] = [[day_to_milliseconds(day), count] for day, count in self.visits.items()]
  39. if today not in self.sessions:
  40. self.sessions[today] = set()
  41. self.sessions[today].add(request.session_id)
  42. self.options.series[1].data[:] = [[day_to_milliseconds(day), len(s)] for day, s in self.sessions.items()]
  43. self.update()
  44. def load(self) -> None:
  45. os.makedirs(os.path.dirname(VISITS_FILE), exist_ok=True)
  46. os.makedirs(os.path.dirname(SESSIONS_FILE), exist_ok=True)
  47. try:
  48. with open(VISITS_FILE, 'rb') as f:
  49. self.visits = pickle.load(f)
  50. with open(SESSIONS_FILE, 'rb') as f:
  51. self.sessions = pickle.load(f)
  52. except FileNotFoundError:
  53. pass
  54. except:
  55. logging.exception('Error loading traffic data')
  56. def save(self) -> None:
  57. def _save() -> None:
  58. try:
  59. with open(VISITS_FILE, 'wb') as f:
  60. pickle.dump(self.visits, f)
  61. with open(SESSIONS_FILE, 'wb') as f:
  62. pickle.dump(self.sessions, f)
  63. except:
  64. logging.exception('Error saving traffic data')
  65. t = threading.Thread(target=_save, name='Save Traffic Data')
  66. t.start()
  67. def update_visibility(self) -> None:
  68. self.visible = len(self.visits.keys()) >= 3 and len(self.sessions.keys()) >= 3