Coverage for src/local_deep_research/web/warning_checks/backup.py: 100%

30 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-22 21:20 +0000

1"""Backup status warning checks. 

2 

3Pure functions that take primitive values and return warning dicts (or None). 

4""" 

5 

6from typing import Optional 

7 

8from ...config.paths import MODE_NOT_KEPT, OWNED_BY_ANOTHER_ACCOUNT 

9 

10_REPORT_BACKUP_CAUSES = { 

11 OWNED_BY_ANOTHER_ACCOUNT: ( 

12 "another OS account owns the server's research_outputs folder, as " 

13 "with Docker Desktop bind mounts of Windows folders or after the app " 

14 "ran as a different user" 

15 ), 

16 MODE_NOT_KEPT: ( 

17 "the filesystem holding the server's research_outputs folder does not " 

18 "keep owner-only permissions, as on WSL drives such as /mnt/c, " 

19 "CIFS/SMB shares and exFAT" 

20 ), 

21} 

22 

23 

24def check_report_backup_unavailable( 

25 backup_enabled: bool, problem: Optional[str] 

26) -> Optional[dict]: 

27 """Warn that report file backups are refused although they are enabled. 

28 

29 ``problem`` is None when the folder is private, a 

30 ``DirectoryNotPrivateError.reason``, or any other string when the folder 

31 could not be prepared at all. Not dismissible: it shows while the 

32 setting is on and FileReportStorage would refuse the folder. 

33 """ 

34 if not backup_enabled or problem is None: 

35 return None 

36 cause = _REPORT_BACKUP_CAUSES.get( 

37 problem, 

38 "the server could not make its research_outputs folder private " 

39 "(the server log names the cause when a report is saved)", 

40 ) 

41 return { 

42 "type": "report_backup_unavailable", 

43 "icon": "⚠️", 

44 "title": "Report File Backups Disabled", 

45 "message": ( 

46 "Report file backup is on, but no report files are being " 

47 f"written because {cause}. Reports are still saved in the " 

48 "database. Keep the data directory (LDR_DATA_DIR) on a Linux " 

49 "filesystem path or a Docker named volume owned by the account " 

50 "that runs the app, or turn off report file backup." 

51 ), 

52 "dismissKey": None, 

53 "actionUrl": "/settings#setting-report-enable_file_backup", 

54 "actionLabel": "Open report backup setting", 

55 } 

56 

57 

58def check_backup_disabled( 

59 backup_enabled: bool, dismissed: bool 

60) -> Optional[dict]: 

61 """Warn if automatic backups are disabled.""" 

62 if backup_enabled: 

63 return None 

64 if dismissed: 

65 return None 

66 

67 return { 

68 "type": "backup_disabled", 

69 "icon": "💾", 

70 "title": "Database Backups Disabled", 

71 "message": ( 

72 "Automatic database backups are disabled. Without backups, " 

73 "data cannot be recovered if the database is corrupted. " 

74 "Enable backups in Settings \u203a Backup." 

75 ), 

76 "dismissKey": "app.warnings.dismiss_backup_disabled", 

77 } 

78 

79 

80def check_no_backups_exist( 

81 backup_enabled: bool, backup_count: int, dismissed: bool 

82) -> Optional[dict]: 

83 """Warn if backups are enabled but none exist yet.""" 

84 if not backup_enabled: 

85 return None 

86 if backup_count > 0: 

87 return None 

88 if dismissed: 

89 return None 

90 

91 return { 

92 "type": "no_backups", 

93 "icon": "📁", 

94 "title": "No Backups Found", 

95 "message": ( 

96 "No database backups have been created yet. A backup will " 

97 "be created automatically on your next login. You can " 

98 "configure backup settings in Settings \u203a Backup." 

99 ), 

100 "dismissKey": "app.warnings.dismiss_no_backups", 

101 } 

102 

103 

104def check_backup_healthy( 

105 backup_enabled: bool, 

106 backup_count: int, 

107 total_size_human: str, 

108 dismissed: bool, 

109) -> Optional[dict]: 

110 """Show positive backup status info (dismissable).""" 

111 if not backup_enabled: 

112 return None 

113 if backup_count == 0: 

114 return None 

115 if dismissed: 

116 return None 

117 

118 return { 

119 "type": "backup_info", 

120 "icon": "✅", 

121 "title": "Database Backup Active", 

122 "message": ( 

123 f"{backup_count} backup{'s' if backup_count > 1 else ''} " 

124 f"({total_size_human}) on disk. Encrypted backups cannot be " 

125 "compressed, so each backup equals your database size " 

126 "(which can be large if PDFs are stored in the database). " 

127 "Backups are created once per day on login. Manage backups " 

128 "in Settings \u203a Backup." 

129 ), 

130 "dismissKey": "app.warnings.dismiss_backup_info", 

131 }