@@ -28,12 +28,22 @@ class TimeEntry(models.Model):
2828 start_time = models .TimeField ()
2929 end_time = models .TimeField (null = True , blank = True )
3030 total_hours = models .DecimalField (max_digits = 5 , decimal_places = 2 , default = 0 )
31+ entry_type = models .CharField (max_length = 50 , default = 'Regular Work Hours' ,
32+ choices = [('Regular Work Hours' , 'Regular Work Hours' ),
33+ ('Overtime' , 'Overtime' ),
34+ ('Meeting' , 'Meeting' ),
35+ ('Training' , 'Training' ),
36+ ('Other' , 'Other' )])
3137 status = models .CharField (max_length = 20 , default = 'pending' ,
3238 choices = [('pending' , 'Pending' ),
3339 ('approved' , 'Approved' ),
3440 ('rejected' , 'Rejected' )])
3541 created_at = models .DateTimeField (auto_now_add = True )
3642 updated_at = models .DateTimeField (auto_now = True )
43+ # Added fields for better tracking
44+ session_id = models .CharField (max_length = 100 , null = True , blank = True , help_text = "Client-side session ID to prevent manipulation" )
45+ session_verified = models .BooleanField (default = False , help_text = "Whether the session has been verified on the server" )
46+ segment_index = models .IntegerField (default = 0 , help_text = "Index of this segment for multi-segment workdays" )
3747
3848 class Meta :
3949 ordering = ['-date' , '-start_time' ]
@@ -47,15 +57,15 @@ def save(self, *args, **kwargs):
4757 # Handle overnight shifts
4858 if end_dt < start_dt :
4959 end_dt += timedelta (days = 1 )
50-
51- # Calculate duration in hours
60+ # Calculate duration in hours
5261 duration = end_dt - start_dt
5362 self .total_hours = round (duration .total_seconds () / 3600 , 2 )
5463
5564 super ().save (* args , ** kwargs )
56-
65+
5766 @classmethod
5867 def get_weekly_hours (cls , employee , date = None ):
68+ """Get total hours worked in the current week, aggregating all segments."""
5969 if date is None :
6070 date = timezone .now ().date ()
6171
@@ -65,27 +75,54 @@ def get_weekly_hours(cls, employee, date=None):
6575
6676 entries = cls .objects .filter (
6777 employee = employee ,
68- date__range = [week_start , week_end ]
78+ date__range = [week_start , week_end ],
79+ session_verified = True # Only count verified sessions
6980 )
7081 return sum (float (entry .total_hours ) for entry in entries )
7182
7283 @classmethod
7384 def get_daily_average (cls , employee , days = 30 ):
85+ """Calculate true daily average over the past X days, considering all segments."""
7486 end_date = timezone .now ().date ()
7587 start_date = end_date - timedelta (days = days )
7688
7789 entries = cls .objects .filter (
7890 employee = employee ,
79- date__range = [start_date , end_date ]
91+ date__range = [start_date , end_date ],
92+ session_verified = True # Only count verified sessions
8093 )
8194
8295 if not entries :
8396 return 0
8497
85- total_hours = sum (float (entry .total_hours ) for entry in entries )
86- working_days = entries .values ('date' ).distinct ().count ()
98+ # Group by date and sum hours for each day
99+ date_hours = {}
100+ for entry in entries :
101+ date_str = entry .date .strftime ('%Y-%m-%d' )
102+ if date_str not in date_hours :
103+ date_hours [date_str ] = 0
104+ date_hours [date_str ] += float (entry .total_hours )
87105
88- return round (total_hours / working_days , 2 ) if working_days > 0 else 0
106+ # Calculate average using only days with entries
107+ working_days = len (date_hours )
108+ if working_days == 0 :
109+ return 0
110+
111+ total_hours = sum (date_hours .values ())
112+ return round (total_hours / working_days , 2 )
113+
114+ @classmethod
115+ def get_today_hours (cls , employee ):
116+ """Calculate total hours worked today across all segments."""
117+ today = timezone .now ().date ()
118+
119+ entries = cls .objects .filter (
120+ employee = employee ,
121+ date = today ,
122+ session_verified = True # Only count verified sessions
123+ )
124+
125+ return sum (float (entry .total_hours ) for entry in entries )
89126
90127class PersonalNote (models .Model ):
91128 user = models .ForeignKey (User , on_delete = models .CASCADE )
0 commit comments