diff --git a/osinaweb/db.sqlite3 b/osinaweb/db.sqlite3 index 51adef77..9a7133f5 100644 Binary files a/osinaweb/db.sqlite3 and b/osinaweb/db.sqlite3 differ diff --git a/osinaweb/osinacore/__pycache__/models.cpython-310.pyc b/osinaweb/osinacore/__pycache__/models.cpython-310.pyc index 492cc0de..43ad0fe4 100644 Binary files a/osinaweb/osinacore/__pycache__/models.cpython-310.pyc and b/osinaweb/osinacore/__pycache__/models.cpython-310.pyc differ diff --git a/osinaweb/osinacore/__pycache__/views.cpython-310.pyc b/osinaweb/osinacore/__pycache__/views.cpython-310.pyc index 1aeb19b9..1ec7e94e 100644 Binary files a/osinaweb/osinacore/__pycache__/views.cpython-310.pyc and b/osinaweb/osinacore/__pycache__/views.cpython-310.pyc differ diff --git a/osinaweb/osinacore/migrations/0060_pointactivity_total_time.py b/osinaweb/osinacore/migrations/0060_pointactivity_total_time.py new file mode 100644 index 00000000..91b02047 --- /dev/null +++ b/osinaweb/osinacore/migrations/0060_pointactivity_total_time.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.5 on 2024-03-19 20:36 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('osinacore', '0059_alter_task_requirement'), + ] + + operations = [ + migrations.AddField( + model_name='pointactivity', + name='total_time', + field=models.DurationField(blank=True, null=True), + ), + ] diff --git a/osinaweb/osinacore/migrations/0061_alter_task_end_date_alter_task_start_date.py b/osinaweb/osinacore/migrations/0061_alter_task_end_date_alter_task_start_date.py new file mode 100644 index 00000000..7f6e7edf --- /dev/null +++ b/osinaweb/osinacore/migrations/0061_alter_task_end_date_alter_task_start_date.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.5 on 2024-03-19 21:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('osinacore', '0060_pointactivity_total_time'), + ] + + operations = [ + migrations.AlterField( + model_name='task', + name='end_date', + field=models.DateField(null=True), + ), + migrations.AlterField( + model_name='task', + name='start_date', + field=models.DateField(null=True), + ), + ] diff --git a/osinaweb/osinacore/migrations/__pycache__/0060_pointactivity_total_time.cpython-310.pyc b/osinaweb/osinacore/migrations/__pycache__/0060_pointactivity_total_time.cpython-310.pyc new file mode 100644 index 00000000..b755edf2 Binary files /dev/null and b/osinaweb/osinacore/migrations/__pycache__/0060_pointactivity_total_time.cpython-310.pyc differ diff --git a/osinaweb/osinacore/migrations/__pycache__/0061_alter_task_end_date_alter_task_start_date.cpython-310.pyc b/osinaweb/osinacore/migrations/__pycache__/0061_alter_task_end_date_alter_task_start_date.cpython-310.pyc new file mode 100644 index 00000000..e1912431 Binary files /dev/null and b/osinaweb/osinacore/migrations/__pycache__/0061_alter_task_end_date_alter_task_start_date.cpython-310.pyc differ diff --git a/osinaweb/osinacore/models.py b/osinaweb/osinacore/models.py index 97ec3c2d..4a4cb71e 100644 --- a/osinaweb/osinacore/models.py +++ b/osinaweb/osinacore/models.py @@ -212,8 +212,8 @@ class Task(models.Model): status_date = models.DateTimeField(default=timezone.now) extra = models.BooleanField(default=False) description = models.TextField() - start_date = models.CharField(max_length=200) - end_date = models.CharField(max_length=200) + start_date = models.DateField(null=True) + end_date = models.DateField(null=True) assigned_to = models.ForeignKey(StaffProfile, on_delete=models.CASCADE, null=True) requirement = models.ForeignKey(ProjectRequirement, on_delete=models.SET_NULL, null=True, blank=True) task_id = models.CharField(max_length=20, null=True, blank=True) @@ -233,15 +233,19 @@ class Task(models.Model): super(Task, self).save(*args, **kwargs) - def formatted_start_date(self): - # Assuming start_date is stored as "day-month-year" - parts = self.start_date.split('-') - return f"{parts[2]}-{parts[1]}-{parts[0]}" + def total_task_time(self): + total_time_seconds = 0 + for point in self.point_set.all(): + if point: + point_hours, point_minutes, point_seconds = point.total_activity_time() + total_time_seconds += (point_hours * 3600) + (point_minutes * 60) + point_seconds + + total_time_hours = total_time_seconds // 3600 + total_time_minutes = (total_time_seconds % 3600) // 60 + total_time_seconds = total_time_seconds % 60 + + return total_time_hours, total_time_minutes, total_time_seconds - def formatted_end_date(self): - # Assuming end_date is stored as "day-month-year" - parts = self.end_date.split('-') - return f"{parts[2]}-{parts[1]}-{parts[0]}" @@ -255,37 +259,15 @@ class Point(models.Model): ) status = models.CharField(max_length=200, choices=STATUS_CHOICES, null=True) task = models.ForeignKey(Task, on_delete=models.CASCADE, null=True) - def total_time(self): - # Calculate the total time for ongoing activities - ongoing_total_time_timedelta = timedelta() - ongoing_activities = self.pointactivity_set.filter(end_time__isnull=True) - - for activity in ongoing_activities: - start_time_aware = activity.start_time - current_time_aware = timezone.now() - ongoing_total_time_timedelta += current_time_aware - start_time_aware - - # Calculate the total time for completed activities - completed_total_time_timedelta = self.pointactivity_set.filter(end_time__isnull=False).aggregate( - total_time=Sum(F('end_time') - F('start_time')) - )['total_time'] - - # Handle the case when there are no ongoing or completed activities - if not ongoing_activities.exists() and completed_total_time_timedelta is None: - return 0, 0, 0 - - # Add the total times together - total_time_timedelta = ongoing_total_time_timedelta + (completed_total_time_timedelta or timedelta()) - - # Convert total seconds to timedelta - total_time_timedelta = timedelta(seconds=total_time_timedelta.total_seconds()) - - # Extract hours, minutes, and seconds from timedelta - hours, remainder = divmod(total_time_timedelta.seconds, 3600) - minutes, seconds = divmod(remainder, 60) - - # Return the total time as a tuple (hours, minutes, seconds) - return hours, minutes, seconds + def total_activity_time(self): + total_time_seconds = 0 + for activity in self.pointactivity_set.all(): + if activity.total_time: + total_time_seconds += activity.total_time.total_seconds() + total_time_hours = int(total_time_seconds // 3600) + total_time_minutes = int((total_time_seconds % 3600) // 60) + total_time_seconds = int(total_time_seconds % 60) + return total_time_hours, total_time_minutes, total_time_seconds @@ -293,6 +275,24 @@ class PointActivity(models.Model): point = models.ForeignKey(Point, on_delete=models.CASCADE, null=True) start_time = models.DateTimeField() end_time = models.DateTimeField(null=True, blank=True) + total_time = models.DurationField(null=True, blank=True) + def save(self, *args, **kwargs): + if self.start_time and not self.end_time: + self.total_time = datetime.now() - self.start_time + elif self.start_time and self.end_time: + self.total_time = self.end_time - self.start_time + super(PointActivity, self).save(*args, **kwargs) + + def total_time_in_hours_minutes_seconds(self): + if self.total_time: + total_seconds = self.total_time.total_seconds() + hours = int(total_seconds // 3600) + minutes = int((total_seconds % 3600) // 60) + seconds = int(total_seconds % 60) + return hours, minutes, seconds + return 0, 0, 0 + + diff --git a/osinaweb/osinacore/views.py b/osinaweb/osinacore/views.py index d7dccf3c..8404437c 100644 --- a/osinaweb/osinacore/views.py +++ b/osinaweb/osinacore/views.py @@ -6,7 +6,7 @@ from django.contrib import messages from .forms import * from django.utils import timezone from django.urls import reverse -from django.http import HttpResponse, HttpResponseServerError, Http404 +from django.http import HttpResponse from django.db.models import Q from django.http import JsonResponse from .models import Task, Epic @@ -75,16 +75,16 @@ def home(request, *args, **kwargs): +from django.db.models import Sum, F + -from django.db.models import Subquery, OuterRef -#Listing Pages @login_required def my_projects(request, *args, **kwargs): user = request.user if user.is_superuser: - # Superadmin can see all projects + # Superadmin can see all projects with total time worked on each project projects = Project.objects.all().order_by('-project_id') else: # Non-superuser, filter projects where the user is either the manager or a member @@ -92,25 +92,25 @@ def my_projects(request, *args, **kwargs): Q(manager=user.staffprofile) | Q(members=user.staffprofile) ).distinct().order_by('-project_id') - # Calculate total time for each project for project in projects: - total_hours, total_minutes, total_seconds = 0, 0, 0 - - for task in project.task_set.all(): - for point in task.point_set.all(): - hours, minutes, seconds = point.total_time() - total_hours += hours - total_minutes += minutes - total_seconds += seconds + total_time_seconds = 0 + # Modify task queryset based on user role + if user.is_superuser: + tasks = project.task_set.all() + else: + tasks = project.task_set.filter(assigned_to=user.staffprofile) - # Convert excess seconds to minutes and excess minutes to hours - total_minutes += total_seconds // 60 - total_seconds %= 60 - total_hours += total_minutes // 60 - total_minutes %= 60 + for task in tasks: + total_time_hours, total_time_minutes, total_time_seconds_task = task.total_task_time() + total_time_seconds += (total_time_hours * 3600) + (total_time_minutes * 60) + total_time_seconds_task - project.total_time = f"{total_hours}hr {total_minutes}min {total_seconds}sec" + total_time_hours = total_time_seconds // 3600 + total_time_minutes = (total_time_seconds % 3600) // 60 + total_time_seconds = total_time_seconds % 60 + project.total_time_worked_hours = total_time_hours + project.total_time_worked_minutes = total_time_minutes + project.total_time_worked_seconds = total_time_seconds context = { 'projects': projects, @@ -119,6 +119,9 @@ def my_projects(request, *args, **kwargs): + + + @login_required def my_tasks(request, *args, **kwargs): if request.user.is_superuser: @@ -1822,27 +1825,26 @@ def mark_point_working_on_task_page(request, point_id, task_id): + @login_required def mark_point_paused(request, point_id, task_id): task = get_object_or_404(Task, id=task_id) point = get_object_or_404(Point, id=point_id) point.status = 'Paused' - current_datetime = datetime.now() - + current_datetime = timezone.now() point.save() last_activity = PointActivity.objects.filter(point=point).last() - last_activity.end_time = datetime.now() - last_activity.save() - + if last_activity: + last_activity.end_time = current_datetime + last_activity.save() status_text = f'{point.text} - Paused' status = Status(text=status_text, date=current_datetime.date(), time=current_datetime.strftime("%I:%M %p"), staff=request.user.staffprofile) status.save() - task_id_str = task.task_id showpoints_url = reverse('showpoints', args=[task_id_str]) @@ -1852,31 +1854,33 @@ def mark_point_paused(request, point_id, task_id): -from datetime import datetime @login_required def mark_point_completed(request, point_id, task_id): task = get_object_or_404(Task, id=task_id) point = get_object_or_404(Point, id=point_id) point.status = 'Completed' - current_datetime = datetime.now() + + current_datetime = timezone.now() point.save() - + # Update the end time of the last activity to the current time last_activity = PointActivity.objects.filter(point=point).last() if last_activity: - last_activity.end_time = datetime.now() + last_activity.end_time = current_datetime last_activity.save() - total_time = point.total_time() + + total_time_hours, total_time_minutes, total_time_seconds = point.total_activity_time() + formatted_time = "" - if total_time[0] > 0: - formatted_time += f"{total_time[0]}{'hr' if total_time[0] == 1 else 'hrs'}" - if total_time[1] > 0: - formatted_time += f" {total_time[1]}{'min' if total_time[1] == 1 else 'mins'}" - if total_time[2] > 0: - formatted_time += f" {total_time[2]}{'sec' if total_time[2] == 1 else 'secs'}" + if total_time_hours > 0: + formatted_time += f"{total_time_hours}{'hr' if total_time_hours == 1 else 'hrs'}" + if total_time_minutes > 0: + formatted_time += f" {total_time_minutes}{'min' if total_time_minutes == 1 else 'mins'}" + if total_time_seconds > 0: + formatted_time += f" {total_time_seconds}{'sec' if total_time_seconds == 1 else 'secs'}" status_text = f'{point.text} - Completed' if formatted_time: @@ -1920,9 +1924,6 @@ def mark_point_completed_task_page(request, point_id, task_id): - - - # CUSTOMER DASHBOARD @login_required def customer_index(request, *args, **kwargs): diff --git a/osinaweb/templates/listing_pages/projects.html b/osinaweb/templates/listing_pages/projects.html index 2cadcb5c..a153e885 100644 --- a/osinaweb/templates/listing_pages/projects.html +++ b/osinaweb/templates/listing_pages/projects.html @@ -201,13 +201,15 @@
-

Total Time: {{project.total_time}}

+

Total Time: {{project.total_time_worked_hours}}hr {{project.total_time_worked_hours}}min {{project.total_time_worked_seconds}}sec

- {% if project.recent_note_date %} -

{{ project.recent_note_text }}

+ {% if project.note_set.exists %} + {% with last_note=project.note_set.last %} +

{{ last_note.text }}

+ {% endwith %} {% else %}

No recent note

{% endif %} diff --git a/osinaweb/templates/popup_modals/showpoints-modal.html b/osinaweb/templates/popup_modals/showpoints-modal.html index e16b5fb0..bb6bffce 100644 --- a/osinaweb/templates/popup_modals/showpoints-modal.html +++ b/osinaweb/templates/popup_modals/showpoints-modal.html @@ -94,23 +94,9 @@

Total Time: - {% if point.total_time.0 > 0 %} - {{ point.total_time.0 }}hr - {% if point.total_time.1 > 0 %} - , - {% endif %} - {% endif %} - - {% if point.total_time.1 > 0 %} - {{ point.total_time.1 }}min - {% if point.total_time.2 > 0 %} - , - {%endif%} - {% endif %} - - {% if point.total_time.2 > 0 %} - {{ point.total_time.2 }}sec - {% endif %} + {{ point.total_activity_time.0 }} hours, + {{ point.total_activity_time.1 }} minutes, + {{ point.total_activity_time.2 }} seconds