mirror of
https://github.com/TurTaskProject/TurTaskWeb.git
synced 2025-12-19 05:54:07 +01:00
Merge branch 'main' into feature/calendar
This commit is contained in:
commit
5299fb2eb7
@ -7,8 +7,8 @@ from rest_framework.response import Response
|
|||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
|
||||||
from tasks.utils import get_service
|
from tasks.utils import get_service
|
||||||
from tasks.models import Task
|
from tasks.models import Todo, RecurrenceTask
|
||||||
from tasks.serializers import TaskUpdateSerializer
|
from tasks.serializers import TodoUpdateSerializer, RecurrenceTaskUpdateSerializer
|
||||||
|
|
||||||
|
|
||||||
class GoogleCalendarEventViewset(viewsets.ViewSet):
|
class GoogleCalendarEventViewset(viewsets.ViewSet):
|
||||||
@ -17,28 +17,30 @@ class GoogleCalendarEventViewset(viewsets.ViewSet):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.current_time = datetime.now(tz=timezone.utc).isoformat()
|
self.current_time = datetime.now(tz=timezone.utc).isoformat()
|
||||||
self.event_fields = 'items(id,summary,description,created,updated,start,end)'
|
self.event_fields = 'items(id,summary,description,created,recurringEventId,updated,start,end)'
|
||||||
|
|
||||||
def _validate_serializer(self, serializer):
|
def _validate_serializer(self, serializer):
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
serializer.save()
|
serializer.save()
|
||||||
return Response("Task Sync Successfully", status=200)
|
return Response("Validate Successfully", status=200)
|
||||||
return Response(serializer.errors, status=400)
|
return Response(serializer.errors, status=400)
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
service = get_service(request)
|
service = get_service(request)
|
||||||
events = service.events().list(calendarId='primary', fields=self.event_fields).execute()
|
events = service.events().list(calendarId='primary', fields=self.event_fields).execute()
|
||||||
for event in events.get('items', []):
|
for event in events.get('items', []):
|
||||||
|
if event.get('recurringEventId'):
|
||||||
|
continue
|
||||||
try:
|
try:
|
||||||
task = Task.objects.get(google_calendar_id=event['id'])
|
task = Todo.objects.get(google_calendar_id=event['id'])
|
||||||
serializer = TaskUpdateSerializer(instance=task, data=event)
|
serializer = TodoUpdateSerializer(instance=task, data=event)
|
||||||
return self._validate_serializer(serializer)
|
return self._validate_serializer(serializer)
|
||||||
except Task.DoesNotExist:
|
except Todo.DoesNotExist:
|
||||||
serializer = TaskUpdateSerializer(data=event, user=request.user)
|
serializer = TodoUpdateSerializer(data=event, user=request.user)
|
||||||
return self._validate_serializer(serializer)
|
return self._validate_serializer(serializer)
|
||||||
|
|
||||||
def list(self, request, days=7):
|
def list(self, request, days=7):
|
||||||
max_time = (datetime.now(tz=timezone.utc) + timedelta(days=3)).isoformat()
|
max_time = (datetime.now(tz=timezone.utc) + timedelta(days=days)).isoformat()
|
||||||
|
|
||||||
service = get_service(request)
|
service = get_service(request)
|
||||||
events = []
|
events = []
|
||||||
@ -49,11 +51,11 @@ class GoogleCalendarEventViewset(viewsets.ViewSet):
|
|||||||
calendarId='primary',
|
calendarId='primary',
|
||||||
timeMin=self.current_time,
|
timeMin=self.current_time,
|
||||||
timeMax=max_time,
|
timeMax=max_time,
|
||||||
maxResults=20,
|
maxResults=200,
|
||||||
singleEvents=True,
|
singleEvents=True,
|
||||||
orderBy='startTime',
|
orderBy='startTime',
|
||||||
pageToken=next_page_token,
|
pageToken=next_page_token,
|
||||||
fields='items(id,summary,description,created,updated,start,end)',
|
fields='items(id,summary,description,created,recurringEventId,updated,start,end)',
|
||||||
)
|
)
|
||||||
|
|
||||||
page_results = query.execute()
|
page_results = query.execute()
|
||||||
|
|||||||
@ -4,3 +4,6 @@ from django.apps import AppConfig
|
|||||||
class TasksConfig(AppConfig):
|
class TasksConfig(AppConfig):
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
name = 'tasks'
|
name = 'tasks'
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
import tasks.signals
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
# Generated by Django 4.2.6 on 2023-11-06 15:01
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('tasks', '0009_alter_task_options_task_importance_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Todo',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('title', models.TextField()),
|
||||||
|
('notes', models.TextField(default='')),
|
||||||
|
('importance', models.PositiveSmallIntegerField(choices=[(1, '1'), (2, '2'), (3, '3'), (4, '4'), (5, '5')], default=1)),
|
||||||
|
('difficulty', models.PositiveSmallIntegerField(choices=[(1, 'Easy'), (2, 'Normal'), (3, 'Hard'), (4, 'Very Hard'), (5, 'Devil')], default=1)),
|
||||||
|
('challenge', models.BooleanField(default=False)),
|
||||||
|
('fromSystem', models.BooleanField(default=False)),
|
||||||
|
('creation_date', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('last_update', models.DateTimeField(auto_now=True)),
|
||||||
|
('google_calendar_id', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('start_event', models.DateTimeField(null=True)),
|
||||||
|
('end_event', models.DateTimeField(null=True)),
|
||||||
|
('priority', models.PositiveSmallIntegerField(choices=[(1, 'Important & Urgent'), (2, 'Important & Not Urgent'), (3, 'Not Important & Urgent'), (4, 'Not Important & Not Urgent')], default=4)),
|
||||||
|
('tags', models.ManyToManyField(blank=True, to='tasks.tag')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='subtask',
|
||||||
|
name='parent_task',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tasks.todo'),
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='Task',
|
||||||
|
),
|
||||||
|
]
|
||||||
39
backend/tasks/migrations/0011_recurrencetask.py
Normal file
39
backend/tasks/migrations/0011_recurrencetask.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# Generated by Django 4.2.6 on 2023-11-06 16:14
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('tasks', '0010_todo_alter_subtask_parent_task_delete_task'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='RecurrenceTask',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('title', models.TextField()),
|
||||||
|
('notes', models.TextField(default='')),
|
||||||
|
('importance', models.PositiveSmallIntegerField(choices=[(1, '1'), (2, '2'), (3, '3'), (4, '4'), (5, '5')], default=1)),
|
||||||
|
('difficulty', models.PositiveSmallIntegerField(choices=[(1, 'Easy'), (2, 'Normal'), (3, 'Hard'), (4, 'Very Hard'), (5, 'Devil')], default=1)),
|
||||||
|
('challenge', models.BooleanField(default=False)),
|
||||||
|
('fromSystem', models.BooleanField(default=False)),
|
||||||
|
('creation_date', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('last_update', models.DateTimeField(auto_now=True)),
|
||||||
|
('google_calendar_id', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('start_event', models.DateTimeField(null=True)),
|
||||||
|
('end_event', models.DateTimeField(null=True)),
|
||||||
|
('recurrence_rule', models.TextField()),
|
||||||
|
('tags', models.ManyToManyField(blank=True, to='tasks.tag')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -1,9 +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.utils import timezone
|
|
||||||
|
|
||||||
class Tag(models.Model):
|
class Tag(models.Model):
|
||||||
"""
|
"""
|
||||||
@ -16,21 +12,16 @@ class Tag(models.Model):
|
|||||||
|
|
||||||
class Task(models.Model):
|
class Task(models.Model):
|
||||||
"""
|
"""
|
||||||
Represents a task, such as Habit, Daily, Todo, or Reward.
|
Represents a Abstract of task, such as Habit, Daily, Todo, or Reward.
|
||||||
|
|
||||||
:param type: The type of the tasks
|
:param user: The user who owns the task.
|
||||||
:param title: Title of the task.
|
:param title: Title of the task.
|
||||||
:param notes: Optional additional notes for the task.
|
:param notes: Optional additional notes for the task.
|
||||||
:param tags: Associated tags for the task.
|
:param tags: Associated tags for the task.
|
||||||
:param completed: A boolean field indicating whether the task is completed.
|
:param completed: A boolean field indicating whether the task is completed.
|
||||||
:param exp: The experience values user will get from the task.
|
|
||||||
:param priority: The priority of the task (1, 2, .., 4), using Eisenhower Matrix Idea.
|
|
||||||
:param importance: The importance of the task (range: 1 to 5)
|
:param importance: The importance of the task (range: 1 to 5)
|
||||||
:param difficulty: The difficulty of the task (range: 1 to 5).
|
:param difficulty: The difficulty of the task (range: 1 to 5).
|
||||||
:param attribute: The attribute linked to the task
|
|
||||||
:param user: The user who owns the task.
|
|
||||||
:param challenge: Associated challenge (optional).
|
:param challenge: Associated challenge (optional).
|
||||||
:param reminders: A Many-to-Many relationship with Reminder.
|
|
||||||
:param fromSystem: A boolean field indicating if the task is from System.
|
:param fromSystem: A boolean field indicating if the task is from System.
|
||||||
:param creation_date: Creation date of the task.
|
:param creation_date: Creation date of the task.
|
||||||
:param last_update: Last updated date of the task.
|
:param last_update: Last updated date of the task.
|
||||||
@ -38,87 +29,49 @@ class Task(models.Model):
|
|||||||
:param start_event: Start event of the task.
|
:param start_event: Start event of the task.
|
||||||
:param end_event: End event(Due Date) of the task.
|
:param end_event: End event(Due Date) of the task.
|
||||||
"""
|
"""
|
||||||
TASK_TYPES = [
|
class Difficulty(models.IntegerChoices):
|
||||||
('daily', 'Daily'),
|
EASY = 1, 'Easy'
|
||||||
('habit', 'Habit'),
|
NORMAL = 2, 'Normal'
|
||||||
('todo', 'Todo'),
|
HARD = 3, 'Hard'
|
||||||
('Long Term Goal', 'Long Term Goal'),
|
VERY_HARD = 4, 'Very Hard'
|
||||||
]
|
DEVIL = 5, 'Devil'
|
||||||
|
|
||||||
DIFFICULTY_CHOICES = [
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
||||||
(1, 'Easy'),
|
|
||||||
(2, 'Normal'),
|
|
||||||
(3, 'Hard'),
|
|
||||||
(4, 'Very Hard'),
|
|
||||||
(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')
|
|
||||||
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)
|
|
||||||
exp = models.FloatField(default=0)
|
|
||||||
priority = models.PositiveSmallIntegerField(choices=EISENHOWER_MATRIX, default=4)
|
|
||||||
importance = models.PositiveSmallIntegerField(choices=[(i, str(i)) for i in range(1, 6)], default=1)
|
importance = models.PositiveSmallIntegerField(choices=[(i, str(i)) for i in range(1, 6)], default=1)
|
||||||
difficulty = models.PositiveSmallIntegerField(choices=DIFFICULTY_CHOICES, default=1)
|
difficulty = models.PositiveSmallIntegerField(choices=Difficulty.choices, default=Difficulty.EASY)
|
||||||
attribute = models.CharField(max_length=15, choices=ATTRIBUTE, default='str')
|
|
||||||
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)
|
||||||
creation_date = models.DateTimeField(auto_now_add=True)
|
creation_date = models.DateTimeField(auto_now_add=True)
|
||||||
last_update = models.DateTimeField(auto_now=True)
|
last_update = models.DateTimeField(auto_now=True)
|
||||||
google_calendar_id = models.CharField(blank=True, null=True, max_length=255)
|
google_calendar_id = models.CharField(max_length=255, null=True, blank=True)
|
||||||
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:
|
class Meta:
|
||||||
verbose_name = 'Task'
|
abstract = True
|
||||||
verbose_name_plural = 'Tasks'
|
|
||||||
|
|
||||||
|
|
||||||
|
class Todo(Task):
|
||||||
|
|
||||||
|
class EisenhowerMatrix(models.IntegerChoices):
|
||||||
|
IMPORTANT_URGENT = 1, 'Important & Urgent'
|
||||||
|
IMPORTANT_NOT_URGENT = 2, 'Important & Not Urgent'
|
||||||
|
NOT_IMPORTANT_URGENT = 3, 'Not Important & Urgent'
|
||||||
|
NOT_IMPORTANT_NOT_URGENT = 4, 'Not Important & Not Urgent'
|
||||||
|
|
||||||
|
priority = models.PositiveSmallIntegerField(choices=EisenhowerMatrix.choices, default=EisenhowerMatrix.NOT_IMPORTANT_NOT_URGENT)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
class RecurrenceTask(Task):
|
||||||
|
recurrence_rule = models.TextField()
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"{self.title} ({self.recurrence_rule})"
|
||||||
|
|
||||||
class Subtask(models.Model):
|
class Subtask(models.Model):
|
||||||
"""
|
"""
|
||||||
@ -127,9 +80,9 @@ class Subtask(models.Model):
|
|||||||
:param completed: A boolean field indicating whether the subtask is completed.
|
:param completed: A boolean field indicating whether the subtask is completed.
|
||||||
:param parent_task: The parent task of the subtask.
|
:param parent_task: The parent task of the subtask.
|
||||||
"""
|
"""
|
||||||
|
parent_task = models.ForeignKey(Todo, on_delete=models.CASCADE)
|
||||||
description = models.TextField()
|
description = models.TextField()
|
||||||
completed = models.BooleanField(default=False)
|
completed = models.BooleanField(default=False)
|
||||||
parent_task = models.ForeignKey(Task, on_delete=models.CASCADE)
|
|
||||||
|
|
||||||
|
|
||||||
class UserNotification(models.Model):
|
class UserNotification(models.Model):
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
from rest_framework import serializers
|
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 Todo, RecurrenceTask
|
||||||
|
|
||||||
|
|
||||||
class GoogleCalendarEventSerializer(serializers.Serializer):
|
class GoogleCalendarEventSerializer(serializers.Serializer):
|
||||||
@ -10,7 +10,7 @@ class GoogleCalendarEventSerializer(serializers.Serializer):
|
|||||||
description = serializers.CharField(required=False)
|
description = serializers.CharField(required=False)
|
||||||
|
|
||||||
|
|
||||||
class TaskUpdateSerializer(serializers.ModelSerializer):
|
class TodoUpdateSerializer(serializers.ModelSerializer):
|
||||||
id = serializers.CharField(source="google_calendar_id")
|
id = serializers.CharField(source="google_calendar_id")
|
||||||
summary = serializers.CharField(source="title")
|
summary = serializers.CharField(source="title")
|
||||||
description = serializers.CharField(source="notes", required=False)
|
description = serializers.CharField(source="notes", required=False)
|
||||||
@ -21,15 +21,41 @@ class TaskUpdateSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Task
|
model = Todo
|
||||||
fields = ('id', 'summary', 'description', 'created', 'updated', 'start_datetime', 'end_datetime')
|
fields = ('id', 'summary', 'description', 'created', 'updated', 'start_datetime', 'end_datetime')
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.user = kwargs.pop('user', None)
|
self.user = kwargs.pop('user', None)
|
||||||
super(TaskUpdateSerializer, self).__init__(*args, **kwargs)
|
super(TodoUpdateSerializer, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
validated_data['user'] = self.user
|
validated_data['user'] = self.user
|
||||||
task = Task.objects.create(**validated_data)
|
task = Todo.objects.create(**validated_data)
|
||||||
|
|
||||||
|
return task
|
||||||
|
|
||||||
|
|
||||||
|
class RecurrenceTaskUpdateSerializer(serializers.ModelSerializer):
|
||||||
|
id = serializers.CharField(source="google_calendar_id")
|
||||||
|
summary = serializers.CharField(source="title")
|
||||||
|
description = serializers.CharField(source="notes", required=False)
|
||||||
|
created = serializers.DateTimeField(source="creation_date")
|
||||||
|
updated = serializers.DateTimeField(source="last_update")
|
||||||
|
recurrence = serializers.DateTimeField(source="recurrence_rule")
|
||||||
|
start_datetime = serializers.DateTimeField(source="start_event", required=False)
|
||||||
|
end_datetime = serializers.DateTimeField(source="end_event", required=False)
|
||||||
|
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = RecurrenceTask
|
||||||
|
fields = ('id', 'summary', 'description', 'created', 'updated', 'recurrence', 'start_datetime', 'end_datetime')
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.user = kwargs.pop('user', None)
|
||||||
|
super(RecurrenceTaskUpdateSerializer, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
validated_data['user'] = self.user
|
||||||
|
task = RecurrenceTask.objects.create(**validated_data)
|
||||||
|
|
||||||
return task
|
return task
|
||||||
25
backend/tasks/signals.py
Normal file
25
backend/tasks/signals.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
from django.db.models.signals import pre_save
|
||||||
|
from django.dispatch import receiver
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from tasks.models import Todo
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(pre_save, sender=Todo)
|
||||||
|
def update_priority(sender, instance, **kwargs):
|
||||||
|
if instance.end_event:
|
||||||
|
time_until_due = (instance.end_event - timezone.now()).days
|
||||||
|
else:
|
||||||
|
time_until_due = float('inf')
|
||||||
|
|
||||||
|
urgency_threshold = 3
|
||||||
|
importance_threshold = 3
|
||||||
|
|
||||||
|
if time_until_due <= urgency_threshold and instance.importance >= importance_threshold:
|
||||||
|
instance.priority = Todo.EisenhowerMatrix.IMPORTANT_URGENT
|
||||||
|
elif time_until_due > urgency_threshold and instance.importance >= importance_threshold:
|
||||||
|
instance.priority = Todo.EisenhowerMatrix.IMPORTANT_NOT_URGENT
|
||||||
|
elif time_until_due <= urgency_threshold and instance.importance < importance_threshold:
|
||||||
|
instance.priority = Todo.EisenhowerMatrix.NOT_IMPORTANT_URGENT
|
||||||
|
else:
|
||||||
|
instance.priority = Todo.EisenhowerMatrix.NOT_IMPORTANT_NOT_URGENT
|
||||||
@ -1,21 +1,21 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from ..models import Task
|
from ..models import Todo
|
||||||
|
|
||||||
class TaskCreateSerializer(serializers.ModelSerializer):
|
class TaskCreateSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Task
|
model = Todo
|
||||||
# fields = '__all__'
|
# fields = '__all__'
|
||||||
exclude = ('tags',)
|
exclude = ('tags',)
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
# Create a new task with validated data
|
# Create a new task with validated data
|
||||||
return Task.objects.create(**validated_data)
|
return Todo.objects.create(**validated_data)
|
||||||
|
|
||||||
class TaskGeneralSerializer(serializers.ModelSerializer):
|
class TaskGeneralSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Task
|
model = Todo
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
# Create a new task with validated data
|
# Create a new task with validated data
|
||||||
return Task.objects.create(**validated_data)
|
return Todo.objects.create(**validated_data)
|
||||||
@ -2,11 +2,11 @@ from rest_framework import status
|
|||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.generics import CreateAPIView, RetrieveAPIView, RetrieveUpdateAPIView, DestroyAPIView
|
from rest_framework.generics import CreateAPIView, RetrieveAPIView, RetrieveUpdateAPIView, DestroyAPIView
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
from ..models import Task
|
from ..models import Todo
|
||||||
from .serializers import TaskCreateSerializer, TaskGeneralSerializer
|
from .serializers import TaskCreateSerializer, TaskGeneralSerializer
|
||||||
|
|
||||||
class TaskCreateView(CreateAPIView):
|
class TaskCreateView(CreateAPIView):
|
||||||
queryset = Task.objects.all()
|
queryset = Todo.objects.all()
|
||||||
serializer_class = TaskCreateSerializer
|
serializer_class = TaskCreateSerializer
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
@ -21,17 +21,17 @@ class TaskCreateView(CreateAPIView):
|
|||||||
|
|
||||||
|
|
||||||
class TaskRetrieveView(RetrieveAPIView):
|
class TaskRetrieveView(RetrieveAPIView):
|
||||||
queryset = Task.objects.all()
|
queryset = Todo.objects.all()
|
||||||
serializer_class = TaskGeneralSerializer
|
serializer_class = TaskGeneralSerializer
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
|
|
||||||
class TaskUpdateView(RetrieveUpdateAPIView):
|
class TaskUpdateView(RetrieveUpdateAPIView):
|
||||||
queryset = Task.objects.all()
|
queryset = Todo.objects.all()
|
||||||
serializer_class = TaskGeneralSerializer
|
serializer_class = TaskGeneralSerializer
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
|
|
||||||
class TaskDeleteView(DestroyAPIView):
|
class TaskDeleteView(DestroyAPIView):
|
||||||
queryset = Task.objects.all()
|
queryset = Todo.objects.all()
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
@ -5,8 +5,8 @@ from django.test import TestCase
|
|||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from tasks.tests.utils import create_test_user, login_user
|
from tasks.tests.utils import create_test_user, login_user
|
||||||
from tasks.serializers import TaskUpdateSerializer
|
from tasks.serializers import TodoUpdateSerializer
|
||||||
from tasks.models import Task
|
from tasks.models import Todo
|
||||||
|
|
||||||
class TaskUpdateSerializerTest(TestCase):
|
class TaskUpdateSerializerTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -25,14 +25,14 @@ class TaskUpdateSerializerTest(TestCase):
|
|||||||
'end_datetie': self.end_time,
|
'end_datetie': self.end_time,
|
||||||
}
|
}
|
||||||
|
|
||||||
serializer = TaskUpdateSerializer(data=data, user=self.user)
|
serializer = TodoUpdateSerializer(data=data, user=self.user)
|
||||||
self.assertTrue(serializer.is_valid())
|
self.assertTrue(serializer.is_valid())
|
||||||
serializer.is_valid()
|
serializer.is_valid()
|
||||||
task = serializer.save()
|
task = serializer.save()
|
||||||
self.assertIsInstance(task, Task)
|
self.assertIsInstance(task, Todo)
|
||||||
|
|
||||||
def test_serializer_update(self):
|
def test_serializer_update(self):
|
||||||
task = Task.objects.create(title='Original Task', notes='Original description', user=self.user)
|
task = Todo.objects.create(title='Original Task', notes='Original description', user=self.user)
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'id': '32141cwaNcapufh8jq2conw',
|
'id': '32141cwaNcapufh8jq2conw',
|
||||||
@ -44,7 +44,7 @@ class TaskUpdateSerializerTest(TestCase):
|
|||||||
'end_datetie': self.end_time,
|
'end_datetie': self.end_time,
|
||||||
}
|
}
|
||||||
|
|
||||||
serializer = TaskUpdateSerializer(instance=task, data=data)
|
serializer = TodoUpdateSerializer(instance=task, data=data)
|
||||||
self.assertTrue(serializer.is_valid())
|
self.assertTrue(serializer.is_valid())
|
||||||
updated_task = serializer.save()
|
updated_task = serializer.save()
|
||||||
|
|
||||||
|
|||||||
@ -1,38 +0,0 @@
|
|||||||
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)
|
|
||||||
@ -5,9 +5,9 @@ from rest_framework import status
|
|||||||
from rest_framework.test import APITestCase
|
from rest_framework.test import APITestCase
|
||||||
|
|
||||||
from tasks.tests.utils import create_test_user, login_user
|
from tasks.tests.utils import create_test_user, login_user
|
||||||
from ..models import Task
|
from ..models import Todo
|
||||||
|
|
||||||
class TaskCreateViewTests(APITestCase):
|
class TodoCreateViewTests(APITestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
||||||
self.user = create_test_user()
|
self.user = create_test_user()
|
||||||
@ -16,7 +16,7 @@ class TaskCreateViewTests(APITestCase):
|
|||||||
self.due_date = datetime.now() + timedelta(days=5)
|
self.due_date = datetime.now() + timedelta(days=5)
|
||||||
|
|
||||||
|
|
||||||
def test_create_valid_task(self):
|
def test_create_valid_todo(self):
|
||||||
"""
|
"""
|
||||||
Test creating a valid task using the API.
|
Test creating a valid task using the API.
|
||||||
"""
|
"""
|
||||||
@ -32,10 +32,10 @@ class TaskCreateViewTests(APITestCase):
|
|||||||
}
|
}
|
||||||
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)
|
||||||
self.assertEqual(Task.objects.count(), 1)
|
self.assertEqual(Todo.objects.count(), 1)
|
||||||
self.assertEqual(Task.objects.get().title, 'Test Task')
|
self.assertEqual(Todo.objects.get().title, 'Test Task')
|
||||||
|
|
||||||
def test_create_invalid_task(self):
|
def test_create_invalid_todo(self):
|
||||||
"""
|
"""
|
||||||
Test creating an invalid task using the API.
|
Test creating an invalid task using the API.
|
||||||
"""
|
"""
|
||||||
@ -45,7 +45,7 @@ class TaskCreateViewTests(APITestCase):
|
|||||||
|
|
||||||
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_400_BAD_REQUEST)
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertEqual(Task.objects.count(), 0) # No task should be created
|
self.assertEqual(Todo.objects.count(), 0) # No task should be created
|
||||||
|
|
||||||
def test_missing_required_fields(self):
|
def test_missing_required_fields(self):
|
||||||
"""
|
"""
|
||||||
@ -58,7 +58,7 @@ class TaskCreateViewTests(APITestCase):
|
|||||||
|
|
||||||
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_400_BAD_REQUEST)
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertEqual(Task.objects.count(), 0) # No task should be created
|
self.assertEqual(Todo.objects.count(), 0) # No task should be created
|
||||||
|
|
||||||
def test_invalid_user_id(self):
|
def test_invalid_user_id(self):
|
||||||
"""
|
"""
|
||||||
@ -76,4 +76,4 @@ class TaskCreateViewTests(APITestCase):
|
|||||||
|
|
||||||
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_400_BAD_REQUEST)
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertEqual(Task.objects.count(), 0) # No task should be created
|
self.assertEqual(Todo.objects.count(), 0) # No task should be created
|
||||||
36
backend/tasks/tests/test_todo_eisenhower.py
Normal file
36
backend/tasks/tests/test_todo_eisenhower.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
from datetime import datetime, timedelta, timezone
|
||||||
|
from django.test import TestCase
|
||||||
|
from tasks.models import Todo
|
||||||
|
from tasks.tests.utils import create_test_user
|
||||||
|
|
||||||
|
class TodoPriorityTest(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.user = create_test_user()
|
||||||
|
|
||||||
|
def test_priority_calculation(self):
|
||||||
|
# Important = 2, Till Due = none
|
||||||
|
todo = Todo(importance=2, end_event=None, user=self.user)
|
||||||
|
todo.save()
|
||||||
|
# 'Not Important & Not Urgent'
|
||||||
|
self.assertEqual(todo.priority, Todo.EisenhowerMatrix.NOT_IMPORTANT_NOT_URGENT)
|
||||||
|
|
||||||
|
due_date = datetime.now(timezone.utc) + timedelta(days=1)
|
||||||
|
# Important = 4, Till Due = 1
|
||||||
|
todo = Todo(importance=4, end_event=due_date, user=self.user)
|
||||||
|
todo.save()
|
||||||
|
# 'Important & Urgent'
|
||||||
|
self.assertEqual(todo.priority, Todo.EisenhowerMatrix.IMPORTANT_URGENT)
|
||||||
|
|
||||||
|
due_date = datetime.now(timezone.utc) + timedelta(days=10)
|
||||||
|
# Important = 3, Till Due = 10
|
||||||
|
todo = Todo(importance=3, end_event=due_date, user=self.user)
|
||||||
|
todo.save()
|
||||||
|
# 'Important & Not Urgent'
|
||||||
|
self.assertEqual(todo.priority, Todo.EisenhowerMatrix.IMPORTANT_NOT_URGENT)
|
||||||
|
|
||||||
|
due_date = datetime.now(timezone.utc) + timedelta(days=2)
|
||||||
|
# Important = 1, Till Due = 2
|
||||||
|
todo = Todo(importance=1, end_event=due_date, user=self.user)
|
||||||
|
todo.save()
|
||||||
|
# 'Not Important & Urgent'
|
||||||
|
self.assertEqual(todo.priority, Todo.EisenhowerMatrix.NOT_IMPORTANT_URGENT)
|
||||||
@ -1,7 +1,7 @@
|
|||||||
from rest_framework.test import APIClient
|
from rest_framework.test import APIClient
|
||||||
|
|
||||||
from users.models import CustomUser
|
from users.models import CustomUser
|
||||||
from ..models import Task
|
from ..models import Todo
|
||||||
|
|
||||||
|
|
||||||
def create_test_user(email="testusertestuser@example.com", username="testusertestuser",
|
def create_test_user(email="testusertestuser@example.com", username="testusertestuser",
|
||||||
@ -61,4 +61,4 @@ def create_test_task(user, **kwargs):
|
|||||||
|
|
||||||
task_attributes = {**defaults, **kwargs}
|
task_attributes = {**defaults, **kwargs}
|
||||||
|
|
||||||
return Task.objects.create(user=user, **task_attributes)
|
return Todo.objects.create(user=user, **task_attributes)
|
||||||
Loading…
Reference in New Issue
Block a user