mirror of
https://github.com/TurTaskProject/TurTaskWeb.git
synced 2025-12-19 05:54:07 +01:00
Merge pull request #21 from TurTaskProject/feature/tasks-logic
Add task prioritize calculator
This commit is contained in:
commit
72df95242c
@ -0,0 +1,27 @@
|
|||||||
|
# Generated by Django 4.2.6 on 2023-11-04 10:20
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('tasks', '0008_task_end_event_task_start_event'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='task',
|
||||||
|
options={'verbose_name': 'Task', 'verbose_name_plural': 'Tasks'},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='task',
|
||||||
|
name='importance',
|
||||||
|
field=models.PositiveSmallIntegerField(choices=[(1, '1'), (2, '2'), (3, '3'), (4, '4'), (5, '5')], default=1),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='task',
|
||||||
|
name='priority',
|
||||||
|
field=models.PositiveSmallIntegerField(choices=[(1, 'Important & Urgent'), (2, 'Important & Not Urgent'), (3, 'Not Important & Urgent'), (4, 'Not Important & Not Urgent')], default=4),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -1,3 +1,5 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
@ -6,30 +8,35 @@ from django.utils import timezone
|
|||||||
class Tag(models.Model):
|
class Tag(models.Model):
|
||||||
"""
|
"""
|
||||||
Represents a tag that can be associated with tasks.
|
Represents a tag that can be associated with tasks.
|
||||||
Fields:
|
|
||||||
- name: The unique name of the tag.
|
:param name: The unique name of the tag.
|
||||||
"""
|
"""
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
|
|
||||||
|
|
||||||
class Task(models.Model):
|
class Task(models.Model):
|
||||||
"""
|
"""
|
||||||
Represents a task, such as Habit, Daily, Todo, or Reward.
|
Represents a task, such as Habit, Daily, Todo, or Reward.
|
||||||
Fields:
|
|
||||||
- type: The type of the tasks
|
:param type: The type of the tasks
|
||||||
- title: Title of the task.
|
:param title: Title of the task.
|
||||||
- notes: Optional additional notes for the task.
|
:param notes: Optional additional notes for the task.
|
||||||
- tags: Associated tags for the task.
|
:param tags: Associated tags for the task.
|
||||||
- completed: A boolean field indicating whether the task is completed.
|
:param completed: A boolean field indicating whether the task is completed.
|
||||||
- exp: The experience values user will get from the task.
|
:param exp: The experience values user will get from the task.
|
||||||
- priority: The priority of the task (range: 0.1 to 2).
|
:param priority: The priority of the task (1, 2, .., 4), using Eisenhower Matrix Idea.
|
||||||
- difficulty: The difficulty of the task (range: 1 to 5).
|
:param importance: The importance of the task (range: 1 to 5)
|
||||||
- attribute: The attribute linked to the task
|
:param difficulty: The difficulty of the task (range: 1 to 5).
|
||||||
- user: The user who owns the task.
|
:param attribute: The attribute linked to the task
|
||||||
- challenge: Associated challenge (optional).
|
:param user: The user who owns the task.
|
||||||
- reminders: A Many-to-Many relationship with Reminder.
|
:param challenge: Associated challenge (optional).
|
||||||
- fromSystem: A boolean field indicating if the task is from System.
|
:param reminders: A Many-to-Many relationship with Reminder.
|
||||||
- creation_date: Creation date of the task.
|
:param fromSystem: A boolean field indicating if the task is from System.
|
||||||
- last_update: Last updated date of the task.
|
:param creation_date: Creation date of the task.
|
||||||
|
:param last_update: Last updated date of the task.
|
||||||
|
:param: google_calendar_id: Google Calendar Event ID of the task.
|
||||||
|
:param start_event: Start event of the task.
|
||||||
|
:param end_event: End event(Due Date) of the task.
|
||||||
"""
|
"""
|
||||||
TASK_TYPES = [
|
TASK_TYPES = [
|
||||||
('daily', 'Daily'),
|
('daily', 'Daily'),
|
||||||
@ -46,24 +53,31 @@ class Task(models.Model):
|
|||||||
(5, 'Devil'),
|
(5, 'Devil'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
ATTRIBUTE = [
|
||||||
|
('str', 'Strength'),
|
||||||
|
('int', 'Intelligence'),
|
||||||
|
('end', 'Endurance'),
|
||||||
|
('per', 'Perception'),
|
||||||
|
('luck', 'Luck'),
|
||||||
|
]
|
||||||
|
|
||||||
|
EISENHOWER_MATRIX = [
|
||||||
|
(1, 'Important & Urgent'),
|
||||||
|
(2, 'Important & Not Urgent'),
|
||||||
|
(3, 'Not Important & Urgent'),
|
||||||
|
(4, 'Not Important & Not Urgent'),
|
||||||
|
]
|
||||||
|
|
||||||
type = models.CharField(max_length=15, choices=TASK_TYPES, default='habit')
|
type = models.CharField(max_length=15, choices=TASK_TYPES, default='habit')
|
||||||
title = models.TextField()
|
title = models.TextField()
|
||||||
notes = models.TextField(default='')
|
notes = models.TextField(default='')
|
||||||
tags = models.ManyToManyField(Tag, blank=True)
|
tags = models.ManyToManyField(Tag, blank=True)
|
||||||
completed = models.BooleanField(default=False)
|
completed = models.BooleanField(default=False)
|
||||||
exp = models.FloatField(default=0)
|
exp = models.FloatField(default=0)
|
||||||
priority = models.FloatField(default=1, validators=[
|
priority = models.PositiveSmallIntegerField(choices=EISENHOWER_MATRIX, default=4)
|
||||||
validators.MinValueValidator(0.1),
|
importance = models.PositiveSmallIntegerField(choices=[(i, str(i)) for i in range(1, 6)], default=1)
|
||||||
validators.MaxValueValidator(2),
|
|
||||||
])
|
|
||||||
difficulty = models.PositiveSmallIntegerField(choices=DIFFICULTY_CHOICES, default=1)
|
difficulty = models.PositiveSmallIntegerField(choices=DIFFICULTY_CHOICES, default=1)
|
||||||
attribute = models.CharField(max_length=15, choices=[
|
attribute = models.CharField(max_length=15, choices=ATTRIBUTE, default='str')
|
||||||
('str', 'Strength'),
|
|
||||||
('int', 'Intelligence'),
|
|
||||||
('end', 'Endurance'),
|
|
||||||
('per', 'Perception'),
|
|
||||||
('luck', 'Luck'),
|
|
||||||
], default='str')
|
|
||||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
||||||
challenge = models.BooleanField(default=False)
|
challenge = models.BooleanField(default=False)
|
||||||
fromSystem = models.BooleanField(default=False)
|
fromSystem = models.BooleanField(default=False)
|
||||||
@ -73,13 +87,45 @@ class Task(models.Model):
|
|||||||
start_event = models.DateTimeField(null=True)
|
start_event = models.DateTimeField(null=True)
|
||||||
end_event = models.DateTimeField(null=True)
|
end_event = models.DateTimeField(null=True)
|
||||||
|
|
||||||
|
def calculate_eisenhower_matrix_category(self):
|
||||||
|
"""
|
||||||
|
Classify the task into one of the four categories in the Eisenhower Matrix.
|
||||||
|
|
||||||
|
:return: The category of the task (1, 2, 3, or 4).
|
||||||
|
"""
|
||||||
|
if self.end_event:
|
||||||
|
time_until_due = (self.end_event - datetime.now(timezone.utc)).days
|
||||||
|
else:
|
||||||
|
time_until_due = float('inf')
|
||||||
|
|
||||||
|
urgency_threshold = 3
|
||||||
|
importance_threshold = 3
|
||||||
|
|
||||||
|
if time_until_due <= urgency_threshold and self.importance >= importance_threshold:
|
||||||
|
return 1
|
||||||
|
elif time_until_due > urgency_threshold and self.importance >= importance_threshold:
|
||||||
|
return 2
|
||||||
|
elif time_until_due <= urgency_threshold and self.importance < importance_threshold:
|
||||||
|
return 3
|
||||||
|
else:
|
||||||
|
return 4
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
self.priority = self.calculate_eisenhower_matrix_category()
|
||||||
|
super(Task, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = 'Task'
|
||||||
|
verbose_name_plural = 'Tasks'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Subtask(models.Model):
|
class Subtask(models.Model):
|
||||||
"""
|
"""
|
||||||
Represents a subtask associated with a task.
|
Represents a subtask associated with a task.
|
||||||
- description: Description of the subtask.
|
:param description: Description of the subtask.
|
||||||
- completed: A boolean field indicating whether the subtask is completed.
|
:param completed: A boolean field indicating whether the subtask is completed.
|
||||||
- parent_task: The parent task of the subtask.
|
:param parent_task: The parent task of the subtask.
|
||||||
"""
|
"""
|
||||||
description = models.TextField()
|
description = models.TextField()
|
||||||
completed = models.BooleanField(default=False)
|
completed = models.BooleanField(default=False)
|
||||||
@ -89,10 +135,10 @@ class Subtask(models.Model):
|
|||||||
class UserNotification(models.Model):
|
class UserNotification(models.Model):
|
||||||
"""
|
"""
|
||||||
Represents a user notification.
|
Represents a user notification.
|
||||||
Fields:
|
|
||||||
- type: The type of the notification (e.g., 'NEW_CHAT_MESSAGE').
|
:param type: The type of the notification (e.g., 'NEW_CHAT_MESSAGE').
|
||||||
- data: JSON data associated with the notification.
|
:param data: JSON data associated with the notification.
|
||||||
- seen: A boolean field indicating whether the notification has been seen.
|
:param seen: A boolean field indicating whether the notification has been seen.
|
||||||
"""
|
"""
|
||||||
NOTIFICATION_TYPES = (
|
NOTIFICATION_TYPES = (
|
||||||
('LEVEL_UP', 'Level Up'),
|
('LEVEL_UP', 'Level Up'),
|
||||||
@ -124,12 +170,12 @@ class UserNotification(models.Model):
|
|||||||
class Transaction(models.Model):
|
class Transaction(models.Model):
|
||||||
"""
|
"""
|
||||||
Represents a transaction involving currencies in the system.
|
Represents a transaction involving currencies in the system.
|
||||||
Fields:
|
|
||||||
- currency: The type of currency used in the transaction
|
:param currency: The type of currency used in the transaction
|
||||||
- transactionType: The type of the transaction
|
:param transactionType: The type of the transaction
|
||||||
- description: Additional text.
|
:param description: Additional text.
|
||||||
- amount: The transaction amount.
|
:param amount: The transaction amount.
|
||||||
- user: The user involved in the transaction.
|
:param user: The user involved in the transaction.
|
||||||
"""
|
"""
|
||||||
CURRENCIES = (('gold', 'Gold'),)
|
CURRENCIES = (('gold', 'Gold'),)
|
||||||
TRANSACTION_TYPES = (
|
TRANSACTION_TYPES = (
|
||||||
|
|||||||
@ -2,7 +2,6 @@ from rest_framework import serializers
|
|||||||
from django.utils.dateparse import parse_datetime
|
from django.utils.dateparse import parse_datetime
|
||||||
from .models import Task
|
from .models import Task
|
||||||
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
class GoogleCalendarEventSerializer(serializers.Serializer):
|
class GoogleCalendarEventSerializer(serializers.Serializer):
|
||||||
summary = serializers.CharField()
|
summary = serializers.CharField()
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.test import APITestCase
|
from rest_framework.test import APITestCase
|
||||||
@ -11,6 +13,8 @@ class TaskCreateViewTests(APITestCase):
|
|||||||
self.user = create_test_user()
|
self.user = create_test_user()
|
||||||
self.client = login_user(self.user)
|
self.client = login_user(self.user)
|
||||||
self.url = reverse("add-task")
|
self.url = reverse("add-task")
|
||||||
|
self.due_date = datetime.now() + timedelta(days=5)
|
||||||
|
|
||||||
|
|
||||||
def test_create_valid_task(self):
|
def test_create_valid_task(self):
|
||||||
"""
|
"""
|
||||||
@ -21,9 +25,10 @@ class TaskCreateViewTests(APITestCase):
|
|||||||
'type': 'habit',
|
'type': 'habit',
|
||||||
'exp': 10,
|
'exp': 10,
|
||||||
'attribute': 'str',
|
'attribute': 'str',
|
||||||
'priority': 1.5,
|
'priority': 1,
|
||||||
'difficulty': 1,
|
'difficulty': 1,
|
||||||
'user': self.user.id,
|
'user': self.user.id,
|
||||||
|
'end_event': self.due_date,
|
||||||
}
|
}
|
||||||
response = self.client.post(self.url, data, format='json')
|
response = self.client.post(self.url, data, format='json')
|
||||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||||
@ -63,9 +68,10 @@ class TaskCreateViewTests(APITestCase):
|
|||||||
'title': 'Test Task',
|
'title': 'Test Task',
|
||||||
'type': 'habit',
|
'type': 'habit',
|
||||||
'exp': 10,
|
'exp': 10,
|
||||||
'priority': 1.5,
|
'priority': 1,
|
||||||
'difficulty': 1,
|
'difficulty': 1,
|
||||||
'user': 999, # Invalid user ID
|
'user': 999, # Invalid user ID
|
||||||
|
'end_event': self.due_date,
|
||||||
}
|
}
|
||||||
|
|
||||||
response = self.client.post(self.url, data, format='json')
|
response = self.client.post(self.url, data, format='json')
|
||||||
|
|||||||
38
backend/tasks/tests/test_task_eisenhower.py
Normal file
38
backend/tasks/tests/test_task_eisenhower.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
from datetime import datetime, timedelta, timezone
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from tasks.models import Task
|
||||||
|
from tasks.tests.utils import create_test_user
|
||||||
|
|
||||||
|
class TaskModelTest(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.user = create_test_user()
|
||||||
|
|
||||||
|
def test_eisenhower_matrix_category(self):
|
||||||
|
task = Task(importance=2, end_event=None, user=self.user)
|
||||||
|
task.save()
|
||||||
|
|
||||||
|
# 'Not Important & Not Urgent' category (category 4)
|
||||||
|
self.assertEqual(task.calculate_eisenhower_matrix_category(), 4)
|
||||||
|
|
||||||
|
due_date = datetime.now(timezone.utc) + timedelta(days=1)
|
||||||
|
task = Task(importance=4, end_event=due_date, user=self.user)
|
||||||
|
task.save()
|
||||||
|
|
||||||
|
# 'Important & Urgent' category (category 1)
|
||||||
|
self.assertEqual(task.calculate_eisenhower_matrix_category(), 1)
|
||||||
|
|
||||||
|
due_date = datetime.now(timezone.utc) + timedelta(days=10)
|
||||||
|
task = Task(importance=3, end_event=due_date, user=self.user)
|
||||||
|
task.save()
|
||||||
|
|
||||||
|
# 'Important & Not Urgent' category (category 2)
|
||||||
|
self.assertEqual(task.calculate_eisenhower_matrix_category(), 2)
|
||||||
|
|
||||||
|
due_date = datetime.now(timezone.utc) + timedelta(days=4)
|
||||||
|
task = Task(importance=1, end_event=due_date, user=self.user)
|
||||||
|
task.save()
|
||||||
|
|
||||||
|
# 'Not Important & Urgent' category (category 3)
|
||||||
|
self.assertEqual(task.calculate_eisenhower_matrix_category(), 3)
|
||||||
Loading…
Reference in New Issue
Block a user