Coverage for src / local_deep_research / config / paths.py: 84%

60 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2026-01-11 00:51 +0000

1""" 

2Centralized path configuration for Local Deep Research. 

3Handles database location using platformdirs for proper user data storage. 

4""" 

5 

6import hashlib 

7import os 

8from pathlib import Path 

9 

10import platformdirs 

11from loguru import logger 

12 

13 

14def get_data_directory() -> Path: 

15 """ 

16 Get the appropriate data directory for storing application data. 

17 Uses platformdirs to get platform-specific user data directory. 

18 

19 Environment variable: 

20 LDR_DATA_DIR: Override the default data directory location. 

21 All subdirectories (research_outputs, cache, logs, database) 

22 will be created under this directory. 

23 

24 Returns: 

25 Path to data directory 

26 """ 

27 # Check for explicit override via environment variable 

28 custom_path = os.getenv("LDR_DATA_DIR") 

29 if custom_path: 

30 data_dir = Path(custom_path) 

31 logger.debug( 

32 f"Using custom data directory from LDR_DATA_DIR: {data_dir}" 

33 ) 

34 return data_dir 

35 

36 # Use platformdirs for platform-specific user data directory 

37 # Windows: C:\Users\Username\AppData\Local\local-deep-research 

38 # macOS: ~/Library/Application Support/local-deep-research 

39 # Linux: ~/.local/share/local-deep-research 

40 data_dir = Path(platformdirs.user_data_dir("local-deep-research")) 

41 # Log only the directory pattern, not the full path which may contain username 

42 logger.debug( 

43 f"Using platformdirs data directory pattern: .../{data_dir.name}" 

44 ) 

45 

46 return data_dir 

47 

48 

49def get_research_outputs_directory() -> Path: 

50 """ 

51 Get the directory for storing research outputs (reports, etc.). 

52 

53 Returns: 

54 Path to research outputs directory 

55 """ 

56 # Use subdirectory of main data directory 

57 data_dir = get_data_directory() 

58 outputs_dir = data_dir / "research_outputs" 

59 outputs_dir.mkdir(parents=True, exist_ok=True) 

60 

61 logger.debug(f"Using research outputs directory: {outputs_dir}") 

62 return outputs_dir 

63 

64 

65def get_cache_directory() -> Path: 

66 """ 

67 Get the directory for storing cache files (search cache, etc.). 

68 

69 Returns: 

70 Path to cache directory 

71 """ 

72 # Use subdirectory of main data directory 

73 data_dir = get_data_directory() 

74 cache_dir = data_dir / "cache" 

75 cache_dir.mkdir(parents=True, exist_ok=True) 

76 

77 logger.debug(f"Using cache directory: {cache_dir}") 

78 return cache_dir 

79 

80 

81def get_logs_directory() -> Path: 

82 """ 

83 Get the directory for storing log files. 

84 

85 Returns: 

86 Path to logs directory 

87 """ 

88 # Use subdirectory of main data directory 

89 data_dir = get_data_directory() 

90 logs_dir = data_dir / "logs" 

91 logs_dir.mkdir(parents=True, exist_ok=True) 

92 

93 logger.debug(f"Using logs directory: {logs_dir}") 

94 return logs_dir 

95 

96 

97def get_encrypted_database_path() -> Path: 

98 """Get the path to the encrypted databases directory. 

99 

100 Returns: 

101 Path to the encrypted databases directory 

102 """ 

103 data_dir = get_data_directory() 

104 encrypted_db_path = data_dir / "encrypted_databases" 

105 encrypted_db_path.mkdir(parents=True, exist_ok=True) 

106 return encrypted_db_path 

107 

108 

109def get_user_database_filename(username: str) -> str: 

110 """Get the database filename for a specific user. 

111 

112 Args: 

113 username: The username to generate a filename for 

114 

115 Returns: 

116 The database filename (not full path) for the user 

117 """ 

118 # Use username hash to avoid filesystem issues with special characters 

119 username_hash = hashlib.sha256(username.encode()).hexdigest()[:16] 

120 return f"ldr_user_{username_hash}.db" 

121 

122 

123def get_library_directory() -> Path: 

124 """ 

125 Get the directory for storing library files (documents, PDFs, etc.). 

126 

127 Returns: 

128 Path to library directory 

129 """ 

130 # Use subdirectory of main data directory 

131 data_dir = get_data_directory() 

132 library_dir = data_dir / "library" 

133 library_dir.mkdir(parents=True, exist_ok=True) 

134 

135 logger.debug(f"Using library directory: {library_dir}") 

136 return library_dir 

137 

138 

139def get_config_directory() -> Path: 

140 """ 

141 Get the directory for storing configuration files. 

142 

143 Returns: 

144 Path to config directory 

145 """ 

146 # Use subdirectory of main data directory 

147 data_dir = get_data_directory() 

148 config_dir = data_dir / "config" 

149 config_dir.mkdir(parents=True, exist_ok=True) 

150 

151 logger.debug(f"Using config directory: {config_dir}") 

152 return config_dir 

153 

154 

155def get_models_directory() -> Path: 

156 """ 

157 Get the directory for storing downloaded models. 

158 

159 Returns: 

160 Path to models directory 

161 """ 

162 # Use subdirectory of main data directory 

163 data_dir = get_data_directory() 

164 models_dir = data_dir / "models" 

165 models_dir.mkdir(parents=True, exist_ok=True) 

166 

167 logger.debug(f"Using models directory: {models_dir}") 

168 return models_dir 

169 

170 

171# Convenience functions for backward compatibility 

172def get_data_dir() -> str: 

173 """Get data directory as string for backward compatibility.""" 

174 return str(get_data_directory())