Only regenerate actions.json if the action modules have changed. Compares the modification time of the actions directory against the existing actions.json file. If no .py file is newer, we also verify that every action .py has a corresponding entry in the JSON — a p
(self)
| 1365 | f"ports={self.portnbr}" |
| 1366 | ) |
| 1367 | |
| 1368 | def load_gamification_data(self): |
| 1369 | """Load persistent gamification progress from disk.""" |
| 1370 | os.makedirs(self.datadir, exist_ok=True) |
| 1371 | |
| 1372 | default_data = { |
| 1373 | "version": 1, |
| 1374 | "total_points": 0, |
| 1375 | "level": 1, |
| 1376 | "mac_points": {}, |
| 1377 | "lifetime_counts": {} |
| 1378 | } |
| 1379 | |
| 1380 | loaded_data = {} |
| 1381 | if os.path.exists(self.gamification_file): |
| 1382 | try: |
| 1383 | with open(self.gamification_file, 'r', encoding='utf-8') as fp: |
| 1384 | raw_data = json.load(fp) |
| 1385 | if isinstance(raw_data, dict): |
| 1386 | loaded_data = raw_data |
| 1387 | except json.JSONDecodeError: |
| 1388 | logger.warning("Gamification file is corrupted; starting with defaults") |
| 1389 | except Exception as exc: |
| 1390 | logger.warning(f"Unable to load gamification file: {exc}") |
| 1391 | |
| 1392 | self.gamification_data = {**default_data, **loaded_data} |
| 1393 | if not isinstance(self.gamification_data.get("mac_points"), dict): |
| 1394 | self.gamification_data["mac_points"] = {} |
| 1395 | if not isinstance(self.gamification_data.get("lifetime_counts"), dict): |
| 1396 | self.gamification_data["lifetime_counts"] = {} |
| 1397 | |
| 1398 | self._update_gamification_state() |
| 1399 | |
| 1400 | def save_gamification_data(self): |
| 1401 | """Persist gamification progress to disk.""" |
| 1402 | try: |
| 1403 | os.makedirs(os.path.dirname(self.gamification_file), exist_ok=True) |
| 1404 | data_to_save = dict(self.gamification_data) |
| 1405 | data_to_save["total_points"] = int(self.gamification_data.get("total_points", 0) or 0) |
| 1406 | data_to_save["level"] = int(self.gamification_data.get("level", 1) or 1) |
| 1407 | with open(self.gamification_file, 'w', encoding='utf-8') as fp: |
| 1408 | json.dump(data_to_save, fp, indent=4) |
| 1409 | except Exception as exc: |
| 1410 | logger.error(f"Failed to save gamification data: {exc}") |
| 1411 | |
| 1412 | def calculate_level(self, total_points: int) -> int: |
| 1413 | """Calculate the level from total points using a slower progression curve.""" |
| 1414 | if total_points < 0: |
| 1415 | total_points = 0 |
| 1416 | return max(1, 1 + total_points // max(self.points_per_level, 1)) |
| 1417 | |
| 1418 | def _update_gamification_state(self): |
| 1419 | """Synchronize in-memory level/points from gamification data.""" |
| 1420 | total_points = int(self.gamification_data.get("total_points", 0) or 0) |
no test coverage detected