Coverage for dao/season_dao.py: 28.00%

98 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2026-04-15 13:02 +0000

1""" 

2Season Data Access Object. 

3 

4Handles all database operations related to seasons and age groups including: 

5- Season CRUD operations 

6- Age group CRUD operations 

7- Current/active season queries 

8""" 

9 

10from datetime import date 

11 

12import structlog 

13 

14from dao.base_dao import BaseDAO, dao_cache, invalidates_cache 

15 

16logger = structlog.get_logger() 

17 

18# Cache patterns for invalidation 

19SEASONS_CACHE_PATTERN = "mt:dao:seasons:*" 

20AGE_GROUPS_CACHE_PATTERN = "mt:dao:age_groups:*" 

21 

22 

23class SeasonDAO(BaseDAO): 

24 """Data access object for season and age group operations.""" 

25 

26 # === Age Group Query Methods === 

27 

28 @dao_cache("age_groups:all") 

29 def get_all_age_groups(self) -> list[dict]: 

30 """Get all age groups.""" 

31 try: 

32 response = self.client.table("age_groups").select("*").order("name").execute() 

33 return response.data 

34 except Exception: 

35 logger.exception("Error querying age groups") 

36 return [] 

37 

38 def get_age_group_by_name(self, name: str) -> dict | None: 

39 """Get an age group by name (case-insensitive exact match). 

40 

41 Returns the age group record (id, name). 

42 For match-scraper integration, this helps look up age groups by name. 

43 No caching - used for scraper lookups. 

44 """ 

45 try: 

46 response = ( 

47 self.client.table("age_groups") 

48 .select("id, name") 

49 .ilike("name", name) # Case-insensitive match 

50 .limit(1) 

51 .execute() 

52 ) 

53 

54 if response.data and len(response.data) > 0: 

55 return response.data[0] 

56 return None 

57 

58 except Exception: 

59 logger.exception("Error getting age group by name", age_group_name=name) 

60 return None 

61 

62 # === Age Group CRUD Methods === 

63 

64 @invalidates_cache(AGE_GROUPS_CACHE_PATTERN) 

65 def create_age_group(self, name: str) -> dict: 

66 """Create a new age group.""" 

67 try: 

68 result = self.client.table("age_groups").insert({"name": name}).execute() 

69 return result.data[0] 

70 except Exception as e: 

71 logger.exception("Error creating age group") 

72 raise e 

73 

74 @invalidates_cache(AGE_GROUPS_CACHE_PATTERN) 

75 def update_age_group(self, age_group_id: int, name: str) -> dict | None: 

76 """Update an age group.""" 

77 try: 

78 result = self.client.table("age_groups").update({"name": name}).eq("id", age_group_id).execute() 

79 return result.data[0] if result.data else None 

80 except Exception as e: 

81 logger.exception("Error updating age group") 

82 raise e 

83 

84 @invalidates_cache(AGE_GROUPS_CACHE_PATTERN) 

85 def delete_age_group(self, age_group_id: int) -> bool: 

86 """Delete an age group.""" 

87 try: 

88 result = self.client.table("age_groups").delete().eq("id", age_group_id).execute() 

89 return len(result.data) > 0 

90 except Exception as e: 

91 logger.exception("Error deleting age group") 

92 raise e 

93 

94 # === Season Query Methods === 

95 

96 @dao_cache("seasons:all") 

97 def get_all_seasons(self) -> list[dict]: 

98 """Get all seasons.""" 

99 try: 

100 response = self.client.table("seasons").select("*").order("start_date", desc=True).execute() 

101 return response.data 

102 except Exception: 

103 logger.exception("Error querying seasons") 

104 return [] 

105 

106 @dao_cache("seasons:current") 

107 def get_current_season(self) -> dict | None: 

108 """Get the current active season based on today's date.""" 

109 try: 

110 today = date.today().isoformat() 

111 

112 response = ( 

113 self.client.table("seasons") 

114 .select("*") 

115 .lte("start_date", today) 

116 .gte("end_date", today) 

117 .single() 

118 .execute() 

119 ) 

120 

121 return response.data 

122 except Exception as e: 

123 logger.info("No current season found", error=str(e)) 

124 return None 

125 

126 @dao_cache("seasons:active") 

127 def get_active_seasons(self) -> list[dict]: 

128 """Get active seasons (current and future) for scheduling new matches.""" 

129 try: 

130 today = date.today().isoformat() 

131 

132 response = ( 

133 self.client.table("seasons") 

134 .select("*") 

135 .gte("end_date", today) 

136 .order("start_date", desc=False) 

137 .execute() 

138 ) 

139 

140 return response.data 

141 except Exception: 

142 logger.exception("Error querying active seasons") 

143 return [] 

144 

145 # === Season CRUD Methods === 

146 

147 @invalidates_cache(SEASONS_CACHE_PATTERN) 

148 def create_season(self, name: str, start_date: str, end_date: str) -> dict: 

149 """Create a new season.""" 

150 try: 

151 result = ( 

152 self.client.table("seasons") 

153 .insert({"name": name, "start_date": start_date, "end_date": end_date}) 

154 .execute() 

155 ) 

156 return result.data[0] 

157 except Exception as e: 

158 logger.exception("Error creating season") 

159 raise e 

160 

161 @invalidates_cache(SEASONS_CACHE_PATTERN) 

162 def update_season(self, season_id: int, name: str, start_date: str, end_date: str) -> dict | None: 

163 """Update a season.""" 

164 try: 

165 result = ( 

166 self.client.table("seasons") 

167 .update({"name": name, "start_date": start_date, "end_date": end_date}) 

168 .eq("id", season_id) 

169 .execute() 

170 ) 

171 return result.data[0] if result.data else None 

172 except Exception as e: 

173 logger.exception("Error updating season") 

174 raise e 

175 

176 @invalidates_cache(SEASONS_CACHE_PATTERN) 

177 def delete_season(self, season_id: int) -> bool: 

178 """Delete a season.""" 

179 try: 

180 result = self.client.table("seasons").delete().eq("id", season_id).execute() 

181 return len(result.data) > 0 

182 except Exception as e: 

183 logger.exception("Error deleting season") 

184 raise e