@@ -4,18 +4,14 @@ const router = express.Router();
44const PopularityEnhanced = require ( '../models/popularityEnhanced' ) ;
55const EnhancedPopularityCache = require ( '../utilities/popularityEnhancedCache' ) ;
66
7- /**
8- * Enhanced Popularity Timeline Routes
9- */
7+ /** * Enhanced Popularity Timeline Routes */
108
119// GET: Enhanced popularity timeline with role-based grouping
1210router . get ( '/timeline' , async ( req , res ) => {
1311 try {
14- // Setting default for groupByRole to 'true' as this is the enhanced endpoint
1512 const { range, roles, start, end, groupByRole = 'true' , includeLowVolume = 'true' } = req . query ;
1613
17- const cacheKey = `enhanced_timeline_${ JSON . stringify ( { range, roles, start, end, groupByRole, includeLowVolume } ) } ` ;
18-
14+ const cacheKey = `v2_enhanced_timeline_${ JSON . stringify ( { range, roles, start, end, groupByRole, includeLowVolume } ) } ` ;
1915 const cachedData = EnhancedPopularityCache . get ( cacheKey ) ;
2016 if ( cachedData ) {
2117 return res . json ( {
@@ -26,24 +22,23 @@ router.get('/timeline', async (req, res) => {
2622 } ) ;
2723 }
2824
29- const match = { isActive : true } ; // Enhanced role filtering with partial matching
25+ const match = { isActive : true } ;
3026
3127 if ( roles && roles !== 'All Roles' && roles !== '["All Roles"]' ) {
3228 let parsedRoles ;
3329 try {
34- // Handle both JSON array and comma-separated string
3530 parsedRoles = JSON . parse ( roles ) ;
3631 } catch {
3732 parsedRoles = roles . split ( ',' ) . map ( ( r ) => r . trim ( ) . replace ( / " / g, '' ) ) ;
3833 }
3934
4035 if ( parsedRoles . length > 0 && ! parsedRoles . includes ( 'All Roles' ) ) {
41- // Create regex patterns for partial matching
4236 const rolePatterns = parsedRoles . map ( ( role ) => new RegExp ( role , 'i' ) ) ;
4337 match . role = { $in : rolePatterns } ;
4438 }
45- } // Enhanced date range handling
39+ }
4640
41+ // Date range handling
4742 if ( start && end ) {
4843 const [ startYear , startMonth ] = start . split ( '-' ) . map ( Number ) ;
4944 const [ endYear , endMonth ] = end . split ( '-' ) . map ( Number ) ;
@@ -53,23 +48,26 @@ router.get('/timeline', async (req, res) => {
5348
5449 match . timestamp = { $gte : startDate , $lte : endDate } ;
5550 } else if ( range ) {
56- const months = parseInt ( range , 10 ) || 12 ;
51+ const months = parseInt ( range , 10 ) || 6 ;
5752
58- if ( months <= 6 ) {
59- const availableMonths = [ '2024-01' , '2024-02' , '2024-03' , '2024-04' , '2024-05' , '2024-06' ] ;
60- const recentMonths = availableMonths . slice ( - months ) ;
61- match . month = { $in : recentMonths } ;
62- }
53+ // Dynamic date calculation relative to current date
54+ const startDate = new Date ( ) ;
55+ // Subtract (months - 1) to include the current month in the count
56+ startDate . setMonth ( startDate . getMonth ( ) - ( months - 1 ) ) ;
57+
58+ // Reset to first day of month to be inclusive
59+ startDate . setDate ( 1 ) ;
60+ startDate . setHours ( 0 , 0 , 0 , 0 ) ;
61+
62+ match . timestamp = { $gte : startDate } ;
6363 }
6464
6565 if ( includeLowVolume === 'false' ) {
6666 match . hitsCount = { $gte : 10 } ;
6767 }
6868
69- let result ; // We always run the role-based aggregation as this is the enhanced endpoint
70-
69+ let result ;
7170 if ( groupByRole === 'true' ) {
72- // Role-based aggregation for timeline data (Supports the UX requirement for multiple lines)
7371 result = await PopularityEnhanced . aggregate ( [
7472 { $match : match } ,
7573 {
@@ -140,7 +138,7 @@ router.get('/timeline', async (req, res) => {
140138 } ,
141139 } ,
142140 { $sort : { 'summary.popularityScore' : - 1 } } ,
143- ] ) ; // Sort each role's data by timestamp for correct line plotting order
141+ ] ) ;
144142
145143 result . forEach ( ( roleGroup ) => {
146144 roleGroup . data . sort ( ( a , b ) => new Date ( a . timestamp ) - new Date ( b . timestamp ) ) ;
@@ -150,7 +148,7 @@ router.get('/timeline', async (req, res) => {
150148 success : false ,
151149 error : 'This endpoint only supports role-grouped aggregation (groupByRole=true)' ,
152150 } ) ;
153- } // Cache the result
151+ }
154152
155153 EnhancedPopularityCache . set ( cacheKey , result ) ;
156154
@@ -183,7 +181,7 @@ router.get('/role-pairs', async (req, res) => {
183181 } ) ;
184182 }
185183
186- const cacheKey = `role_pairs_ ${ roles } _${ start } _${ end } ` ;
184+ const cacheKey = `v2_role_pairs_ ${ roles } _${ start } _${ end } ` ;
187185 const cachedData = EnhancedPopularityCache . get ( cacheKey ) ;
188186 if ( cachedData ) return res . json ( { success : true , data : cachedData , cached : true } ) ;
189187
@@ -199,7 +197,7 @@ router.get('/role-pairs', async (req, res) => {
199197 const match = {
200198 role : { $in : rolePatterns } ,
201199 isActive : true ,
202- } ; // Date filtering
200+ } ;
203201
204202 if ( start && end ) {
205203 const [ startYear , startMonth ] = start . split ( '-' ) . map ( Number ) ;
@@ -228,8 +226,8 @@ router.get('/role-pairs', async (req, res) => {
228226 roles : {
229227 $push : {
230228 role : '$_id.role' ,
231- hitsCount : '$hitsCount' ,
232- applicationsCount : '$applicationsCount' ,
229+ hitsCount : { $sum : '$hitsCount' } ,
230+ applicationsCount : { $sum : '$applicationsCount' } ,
233231 pairId : { $concat : [ '$_id.role' , '_' , '$_id.month' ] } ,
234232 } ,
235233 } ,
@@ -341,7 +339,6 @@ router.get('/roles-enhanced', async (req, res) => {
341339 popularityScore : 0 ,
342340 activityLevel : 'All' ,
343341 } ;
344-
345342 if ( allRolesSummary . totalHits > 0 ) {
346343 allRolesSummary . conversionRate =
347344 allRolesSummary . totalApplications / allRolesSummary . totalHits ;
@@ -352,7 +349,6 @@ router.get('/roles-enhanced', async (req, res) => {
352349 const enhancedRoles = [ allRolesSummary , ...roles ] ;
353350
354351 EnhancedPopularityCache . set ( cacheKey , enhancedRoles , 15 * 60 * 1000 ) ; // 15 minutes
355-
356352 res . json ( {
357353 success : true ,
358354 data : enhancedRoles ,
0 commit comments