Coverage for server / routes / student / student_theme.py: 100%

21 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-10-04 09:33 +0000

1""" 

2Student Theme Routes 

3 

4This module defines the API routes for student profile themes, allowing students to 

5customize their profile appearance with different visual themes. 

6 

7Routes: 

8- GET /v1/student/account/theme/fetch - Get current theme 

9- POST /v1/student/account/theme/apply - Apply a new theme 

10- PUT /v1/student/account/theme/update - Update theme settings 

11- DELETE /v1/student/account/theme/delete - Delete theme and revert to default 

12""" 

13 

14from fastapi import APIRouter, Body, Depends, Request, status 

15from typing import Dict 

16 

17# Authentication 

18from server.authentication.auth0_bearer import Auth0Bearer 

19from server.services.growthbook import require_feature 

20 

21_THEME_DEPS = [ 

22 Depends(Auth0Bearer(access_levels=["student"])), 

23 Depends(require_feature("student.theme_management")), 

24] 

25 

26# Models 

27from server.models.themes import ThemeApply, ThemeResponse, ThemeUpdate 

28 

29# Services 

30from server.services.student.student_theme import studentThemeService 

31 

32router = APIRouter() 

33theme_service = studentThemeService() 

34 

35@router.get( 

36 "/fetch", 

37 dependencies=_THEME_DEPS, 

38 status_code=status.HTTP_200_OK, 

39 response_model=Dict, 

40 description="As a student, I can fetch my current profile theme information", 

41 summary="Get current profile theme information", 

42 responses={ 

43 200: { 

44 "description": "Successfully retrieved theme details", 

45 "content": { 

46 "application/json": { 

47 "example": { 

48 "theme_id": "dark-mode", 

49 "theme_name": "Dark Mode", 

50 "colors": { 

51 "primary": "#7b68ee", 

52 "secondary": "#6c757d", 

53 "accent": "#00c6a9", 

54 "text": "#f8f9fa", 

55 "background": "#212529" 

56 }, 

57 "font": { 

58 "family": "Roboto, sans-serif", 

59 "size": "16px", 

60 "heading_scale": 1.2 

61 }, 

62 "layout": { 

63 "spacing": "16px", 

64 "border_radius": "4px", 

65 "card_style": "shadow" 

66 }, 

67 "is_default": False, 

68 "created_at": "2025-02-15T12:00:00Z", 

69 "updated_at": "2025-02-15T12:00:00Z" 

70 } 

71 } 

72 } 

73 }, 

74 401: {"description": "Invalid or missing authentication"}, 

75 404: {"description": "No theme found"}, 

76 500: {"description": "Server-side issue"} 

77 } 

78) 

79async def fetch_theme(request: Request) -> Dict: 

80 """ 

81 Get the current theme for the authenticated student. 

82  

83 Args: 

84 request (Request): The HTTP request containing user authentication 

85  

86 Returns: 

87 ThemeResponse: The current theme details 

88 """ 

89 return await theme_service.get_current_theme(request) 

90 

91 

92@router.post( 

93 "/apply", 

94 dependencies=_THEME_DEPS, 

95 status_code=status.HTTP_201_CREATED, 

96 response_model=ThemeResponse, 

97 description="As a student, I can apply a new theme to my profile", 

98 summary="Apply a new theme to profile", 

99 responses={ 

100 201: { 

101 "description": "New theme successfully applied", 

102 "content": { 

103 "application/json": { 

104 "example": { 

105 "theme_id": "light-blue", 

106 "theme_name": "Light Blue", 

107 "colors": { 

108 "primary": "#0d6efd", 

109 "secondary": "#6c757d", 

110 "accent": "#0dcaf0", 

111 "text": "#212529", 

112 "background": "#f8f9fa" 

113 }, 

114 "font": { 

115 "family": "Open Sans, sans-serif", 

116 "size": "16px", 

117 "heading_scale": 1.2 

118 }, 

119 "layout": { 

120 "spacing": "14px", 

121 "border_radius": "6px", 

122 "card_style": "shadow" 

123 }, 

124 "is_default": False, 

125 "created_at": "2025-01-10T10:30:00Z", 

126 "updated_at": "2025-01-10T10:30:00Z" 

127 } 

128 } 

129 } 

130 }, 

131 400: {"description": "Invalid theme ID"}, 

132 401: {"description": "Invalid or missing authentication"}, 

133 404: {"description": "Theme not found"}, 

134 500: {"description": "Server-side issue"} 

135 } 

136) 

137async def apply_theme( 

138 request: Request, 

139 theme_data: ThemeApply = Body(...) 

140) -> ThemeResponse: 

141 """ 

142 Apply a theme to the authenticated student's profile. 

143  

144 Args: 

145 request (Request): The HTTP request containing user authentication 

146 theme_data (ThemeApply): The theme ID to apply 

147  

148 Returns: 

149 ThemeResponse: The applied theme details 

150 """ 

151 return await theme_service.apply_theme(request, theme_data.theme_id, theme_data.color_mode) 

152 

153 

154@router.put( 

155 "/update", 

156 dependencies=_THEME_DEPS, 

157 status_code=status.HTTP_200_OK, 

158 response_model=ThemeResponse, 

159 description="As a student, I can update my current theme settings", 

160 summary="Update current theme settings", 

161 responses={ 

162 200: { 

163 "description": "Theme settings updated successfully", 

164 }, 

165 400: {"description": "Invalid theme data"}, 

166 401: {"description": "Invalid or missing authentication"}, 

167 404: {"description": "No active theme found"}, 

168 500: {"description": "Server-side issue"} 

169 } 

170) 

171async def update_theme( 

172 request: Request, 

173 theme_data: ThemeUpdate = Body(...) 

174) -> ThemeResponse: 

175 """ 

176 Update the current theme settings for the authenticated student. 

177 

178 Args: 

179 request (Request): The HTTP request containing user authentication 

180 theme_data (ThemeUpdate): Partial theme data to update 

181 

182 Returns: 

183 ThemeResponse: The updated theme details 

184 """ 

185 return await theme_service.update_theme(request, theme_data) 

186 

187 

188@router.delete( 

189 "/delete", 

190 dependencies=_THEME_DEPS, 

191 status_code=status.HTTP_200_OK, 

192 response_model=Dict, 

193 description="As a student, I can delete my theme and revert to default", 

194 summary="Delete theme and revert to default", 

195 responses={ 

196 200: { 

197 "description": "Theme deleted, reverted to default", 

198 }, 

199 401: {"description": "Invalid or missing authentication"}, 

200 404: {"description": "No active theme found to delete"}, 

201 500: {"description": "Server-side issue"} 

202 } 

203) 

204async def delete_theme(request: Request) -> Dict: 

205 """ 

206 Delete the active theme for the authenticated student (soft delete). 

207 

208 Args: 

209 request (Request): The HTTP request containing user authentication 

210 

211 Returns: 

212 Dict: Default theme details after deletion 

213 """ 

214 return await theme_service.delete_theme(request)