Skip to content

Commit 9dfc028

Browse files
committed
ustable version 0.0.8 + polishing
1 parent 0cdcfdc commit 9dfc028

File tree

8 files changed

+1055
-110
lines changed

8 files changed

+1055
-110
lines changed
1.26 KB
Binary file not shown.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Generated by Django 5.2.1 on 2025-05-15 06:09
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('PunchClock', '0014_companysettings_company_logo'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='timeentry',
15+
name='segment_index',
16+
field=models.IntegerField(default=0, help_text='Index of this segment for multi-segment workdays'),
17+
),
18+
migrations.AddField(
19+
model_name='timeentry',
20+
name='session_id',
21+
field=models.CharField(blank=True, help_text='Client-side session ID to prevent manipulation', max_length=100, null=True),
22+
),
23+
migrations.AddField(
24+
model_name='timeentry',
25+
name='session_verified',
26+
field=models.BooleanField(default=False, help_text='Whether the session has been verified on the server'),
27+
),
28+
]
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Generated by Django 4.2.5
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('PunchClock', '0015_timeentry_segment_index_timeentry_session_id_and_more'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='timeentry',
15+
name='entry_type',
16+
field=models.CharField(
17+
choices=[
18+
('Regular Work Hours', 'Regular Work Hours'),
19+
('Overtime', 'Overtime'),
20+
('Meeting', 'Meeting'),
21+
('Training', 'Training'),
22+
('Other', 'Other'),
23+
],
24+
default='Regular Work Hours',
25+
max_length=50
26+
),
27+
),
28+
]

PunchClock/models.py

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -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

90127
class PersonalNote(models.Model):
91128
user = models.ForeignKey(User, on_delete=models.CASCADE)

0 commit comments

Comments
 (0)