Django:创建一个custom_filter.py来计算总工作时间

b5buobof  于 2023-10-21  发布在  Go
关注(0)|答案(1)|浏览(138)

我是新手。先谢谢你了。
我正在做一个每日工作时间记录(DTR),计算每天的总工作时间,并显示上班时间和下班时间(AM)以及上班时间和下班时间(PM)。如果所有的超时和超时都发生在同一天,我已经可以显示超时和超时,并显示总工作时间,但我的问题是,当超时和超时跨越多天时,例如:

# If the attendance happened on the same day, I can compute the total duty hours, in this case, it is 8 hours (hours only)
[datetime.datetime(1900, 1, 1, 7, 51, 57), datetime.datetime(1900, 1, 1, 12, 0, 18), datetime.datetime(1900, 1, 1, 12, 19, 24), datetime.datetime(1900, 1, 1, 17, 0, 8)]

# But, if the time-in and time-out was on separate day, I was not able to compute the total duty hours.
[datetime.datetime(1900, 1, 1, 7, 57, 30)] # Time-in
[datetime.datetime(1900, 1, 1, 6, 5, 36)] # Time-out 

# My initial plan to compute this, is to add an "end_time" and "start_time".
# Initially, I want the data to be like this.
# With this, I can compute each day by adding an "end_time" and "start_time"
# It is also desirable to indicate the computed total duty hours in the time-out day.
[datetime.datetime(1900, 1, 1, 7, 57, 30), datetime.datetime(1900, 1, 1, 23, 59, 59)]
[datetime.datetime(1900, 1, 1, 00, 00, 01), datetime.datetime(1900, 1, 1, 6, 5, 36)]

# With the custom_filter.py applied, the data looks like this.
# I was not able to insert the start date in the second line.
[datetime.datetime(1900, 1, 1, 7, 57, 30), datetime.datetime(1900, 1, 1, 23, 59, 59)]
[datetime.datetime(1900, 1, 1, 6, 5, 36), datetime.datetime(1900, 1, 1, 23, 59, 59)]

至于数据的格式,请参考我下面的代码。任何建议都是非常欢迎和赞赏。

dtr/model.py
from django.db import models
from django.utils.translation import gettext_lazy as _

class DailyAttendance(models.Model):
    date = models.DateField(verbose_name=_('Date'))
    profile = models.ForeignKey("employee_profile.PersonalInformation", related_name="daily_attendance_records", on_delete=models.CASCADE, verbose_name=_('Profile'), blank=True, null=True)
    attendance_records = models.ManyToManyField("AttendanceRecord", related_name="daily_attendance_records", verbose_name=_('Attendance Records'), blank=True)

    class Meta:
        verbose_name_plural = "Daily Attendance Records"

    def __str__(self):
        return f"{self.profile} - {self.date}"

punch = [
    ('0', 'In'),
    ('1', 'Out'),
    ('2', 'Break-Out'),
    ('3', 'Break-In'),
    ('4', 'Overtime-In'),
    ('5', 'Overtime-Out'),
]

status = [
    ('1', 'Finger print'),
    ('4', 'Card'),
    ('15', 'Face recognition'),
]

class AttendanceRecord(models.Model):
    profile = models.ForeignKey("employee_profile.PersonalInformation", related_name="attendance_records_profile", on_delete=models.CASCADE, verbose_name=_('Profile'), blank=True, null=True)
    daily_attendance = models.ForeignKey("DailyAttendance", related_name="attendance_record_daily", on_delete=models.CASCADE, verbose_name=_('Daily Attendance'), blank=True, null=True)
    uid = models.CharField(verbose_name=_('UID'), max_length=255, blank=True, null=True)
    date = models.DateField(verbose_name=_('Date'), blank=True, null=True)
    time = models.TimeField(verbose_name=_('Time'), blank=True, null=True)
    punch = models.CharField(verbose_name=_('Punch'), max_length=255, choices=punch, blank=True, null=True)
    status = models.CharField(verbose_name=_('Status'), max_length=255, choices=status, blank=True, null=True)

    class Meta:
        verbose_name_plural = "Attendance Records"

    def __str__(self):
        if self.punch == '0':
            punch = 'In'
        elif self.punch == '1':
            punch = 'Out'
        elif self.punch == '2':
            punch = 'Break-Out'
        elif self.punch == '3':
            punch = 'Break-In'
        elif self.punch == '4':
            punch = 'Overtime-In'
        elif self.punch == '5':
            punch = 'Overtime-Out'

        return f"{self.profile} - {self.date} - {punch} - {self.time}"
dtr/view.py
@login_required(login_url='/admin')
def dtr(request, profile):
    try:
        profile = PersonalInformation.objects.get(employee_no=profile)
    except PersonalInformation.DoesNotExist:
        profile = None

    attendance_records = DailyAttendance.objects.filter(profile=profile)

    date_time_data = {}  # Initialize an empty dictionary to store date-time data

    for day in range(1, 32):  # Loop through days 01 to 31
        day_str = str(day).zfill(2)  # Convert day to a zero-padded string (e.g., "01", "02", ..., "31")

        # Initialize an empty list for the day
        date_time_data[day_str] = []

        # Filter attendance_records for the current day
        for daily_attendance in attendance_records:
            if daily_attendance.date.strftime('%d') == day_str:
                for record in daily_attendance.attendance_records.all():
                    time_obj = record.time.strftime('%H:%M:%S')
                    punch_obj = record.punch # Not in-use
                    date_time_data[day_str].append(time_obj) # Append the time to the list for the day

    context = {
        "title": "DTR",
        "profile": profile,
        "attendance_records": attendance_records,
        "date_time_data": date_time_data,
    }

    return render(request, "dtr/dtr.html", context)
dtr/templatetags/custom_filters.py
from django import template
from datetime import datetime, timedelta

register = template.Library()

@register.filter(name='calculate_daily_duration')
def calculate_daily_duration(time_list):
    total_duration = timedelta()
    
    time_objects = [datetime.strptime(time_str, '%H:%M:%S') for time_str in time_list]

    # This part is the issue and is the responsible to append or insert an end_time or start_time depending if the the attendance data has only one data.
    # Start#
    if len(time_objects) == 1:
        time_str = time_objects[0].strftime('%H:%M:%S')
        time_obj = datetime.strptime(time_str, '%H:%M:%S')
        et = datetime.strptime('23:59:59', '%H:%M:%S')
        st = datetime.strptime('00:00:01', '%H:%M:%S')
        
        if time_obj.time() < et.time():
            time_objects.append(et)
            
        elif time_obj.time() > st.time():
            time_objects.insert(0, st)
    # End#
    
    # This part computes the time data
    for i in range(0, len(time_objects), 2):      
        if i + 1 < len(time_objects):
            start_time = time_objects[i]
            end_time = time_objects[i + 1]
            duration = end_time - start_time
            total_duration += duration
                
    print(time_objects)
    
    total_hours, remainder = divmod(total_duration.total_seconds(), 3600)
    
    return f'{int(total_hours)}'
`dtr template (HTML)`
<div class="c x0 y30 wa ha">
    <div class="t m0 x11 hb ye ff2 fs3 fc0 sc0 ls0 ws0">28</div>
</div>
<div class="c xc y30 wb ha">
    <div class="t m0 x6 hb ye ff3 fs3 fc0 sc0 ls0 ws0">{% for daily_attendance in attendance_records %}{% if daily_attendance.date|date:"d" == "28" %}{% for record in daily_attendance.attendance_records.all %}{% if record.punch == "0" and record.time|time:"H:i" >= "00:00" and record.time|time:"H:i" <= "11:59" %}{{ record.time|time:"H:i" }}{% endif %}{% endfor %}{% endif %}{% endfor %}</div>
</div>
<div class="c x10 y30 wb ha">
    <div class="t m0 x6 hb ye ff3 fs3 fc0 sc0 ls0 ws0">{% for daily_attendance in attendance_records %}{% if daily_attendance.date|date:"d" == "28" %}{% for record in daily_attendance.attendance_records.all %}{% if record.punch == "1" and record.time|time:"H:i" >= "00:00" and record.time|time:"H:i" < "12:59" %}{{ record.time|time:"H:i" }}{% endif %}{% endfor %}{% endif %}{% endfor %}</div>
</div>
<div class="c x5 y30 wb ha">
    <div class="t m0 x6 hb ye ff3 fs3 fc0 sc0 ls0 ws0">{% for daily_attendance in attendance_records %}{% if daily_attendance.date|date:"d" == "28" %}{% for record in daily_attendance.attendance_records.all %}{% if record.punch == "0" and record.time|time:"H:i" >= "12:00" %}{{ record.time|time:"H:i" }}{% endif %}{% endfor %}{% endif %}{% endfor %}</div>
</div>
<div class="c x12 y30 wb ha">
    <div class="t m0 x6 hb ye ff3 fs3 fc0 sc0 ls0 ws0">{% for daily_attendance in attendance_records %}{% if daily_attendance.date|date:"d" == "28" %}{% for record in daily_attendance.attendance_records.all %}{% if record.punch == "1" and record.time|time:"H:i" >= "13:00" and record.time|time:"H:i" <= "23:59" %}{{ record.time|time:"H:i" }}{% endif %}{% endfor %}{% endif %}{% endfor %}</div>
</div>
<div class="c xe y30 wc hf">
    <div class="t m0 xd hb ye ff3 fs3 fc0 sc0 ls0 ws0">{% for day, time_list in date_time_data.items %}{% if day == "28" %}{{time_list|calculate_daily_duration}}{% endif %}{% endfor %}</div>
</div>

截图:

ss2ws0br

ss2ws0br1#

幸运的是,我的问题解决了...这是解决问题的代码。注意:custom_filter.py已被删除。所有操作都在view.py上。因为我是个新手,所以不能详细解释。

data = date_time_data

    duty_hours_per_day = {}

    # Initialize variables
    current_day = None
    start_time = None

    # Iterate through the data and calculate duty hours
    for entry in data:
        day = entry['day']
        time = datetime.strptime(entry['time'], '%H:%M:%S')
        punch = entry['punch']
        
        if punch == 'In':
            start_time = time
        elif punch == 'Out':
            if start_time:
                duty_hours_per_day[day] = duty_hours_per_day.get(day, timedelta()) + (time - start_time)
            start_time = None  # Reset start time
        current_day = day

    # Handle negative day values by adding one day
    for day, duration in duty_hours_per_day.items():
        if duration.days < 0:
            duty_hours_per_day[day] += timedelta(days=1)

    # print(duty_hours_per_day)

    # Calculate total duty hours for each day and format as hours, minutes, and seconds
    total_duty_hours_formatted = {}

    for day, duration in duty_hours_per_day.items():
        total_hours, remainder = divmod(duration.total_seconds(), 3600)
        total_minutes, total_seconds = divmod(remainder, 60)
        total_duty_hours_formatted[day] = {
        'hours': int(total_hours),
        'minutes': int(total_minutes),
        'seconds': int(total_seconds)
    }

这是整个view.py

@login_required(login_url='/admin')
def dtr(request, profile):
    try:
        profile = PersonalInformation.objects.get(employee_no=profile)
    except PersonalInformation.DoesNotExist:
        profile = None

    attendance_records = DailyAttendance.objects.filter(profile=profile)

    date_time_data = []
    for daily_attendance in attendance_records:
        for record in daily_attendance.attendance_records.all():
            record_dict = {
                'day': record.date.strftime('%d'),
                'time': record.time.strftime('%H:%M:%S'),
                'punch': 'In' if record.punch == '0' else 'Out', # 0 means Time-in, 1 means Time-out
            }
            
            date_time_data.append(record_dict)
    
    data = date_time_data

    duty_hours_per_day = {}

    # Initialize variables
    current_day = None
    start_time = None

    # Iterate through the data and calculate duty hours
    for entry in data:
        day = entry['day']
        time = datetime.strptime(entry['time'], '%H:%M:%S')
        punch = entry['punch']
        
        if punch == 'In':
            start_time = time
        elif punch == 'Out':
            if start_time:
                duty_hours_per_day[day] = duty_hours_per_day.get(day, timedelta()) + (time - start_time)
            start_time = None  # Reset start time
        current_day = day

    # Handle negative day values by adding one day
    for day, duration in duty_hours_per_day.items():
        if duration.days < 0:
            duty_hours_per_day[day] += timedelta(days=1)

    # Calculate total duty hours for each day and format as hours, minutes, and seconds
    total_duty_hours_formatted = {}

    for day, duration in duty_hours_per_day.items():
        total_hours, remainder = divmod(duration.total_seconds(), 3600)
        total_minutes, total_seconds = divmod(remainder, 60)
        total_duty_hours_formatted[day] = {
        'hours': int(total_hours),
        'minutes': int(total_minutes),
        'seconds': int(total_seconds)
    }
        
    total_hours = 0
    total_minutes = 0
    total_seconds = 0

    for day, duty_hours in total_duty_hours_formatted.items():
        total_hours += duty_hours['hours']
        total_minutes += duty_hours['minutes']
        total_seconds += duty_hours['seconds']

    total_minutes += total_seconds // 60
    total_seconds = total_seconds % 60
    total_hours += total_minutes // 60
    total_minutes = total_minutes % 60

    context = {
        "title": "DTR",
        "profile": profile,
        "attendance_records": attendance_records,
        "total_duty_hours_formatted": total_duty_hours_formatted,
        "total_hours": total_hours,
        "total_minutes": total_minutes,
    }

    return render(request, "dtr/dtr.html", context)

截图:

相关问题