diff --git a/ai/__pycache__/__init__.cpython-311.pyc b/ai/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..8bcb2fb Binary files /dev/null and b/ai/__pycache__/__init__.cpython-311.pyc differ diff --git a/ai/__pycache__/local_ai_api.cpython-311.pyc b/ai/__pycache__/local_ai_api.cpython-311.pyc new file mode 100644 index 0000000..fa9977d Binary files /dev/null and b/ai/__pycache__/local_ai_api.cpython-311.pyc differ diff --git a/core/__pycache__/forms.cpython-311.pyc b/core/__pycache__/forms.cpython-311.pyc new file mode 100644 index 0000000..a5ebf5c Binary files /dev/null and b/core/__pycache__/forms.cpython-311.pyc differ diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index 9aa598b..af9630e 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index 1f807fa..e6775df 100644 Binary files a/core/__pycache__/urls.cpython-311.pyc and b/core/__pycache__/urls.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 6867ddf..aae512f 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/forms.py b/core/forms.py new file mode 100644 index 0000000..2ce8565 --- /dev/null +++ b/core/forms.py @@ -0,0 +1,13 @@ +from django import forms +from .models import Task + +class TaskForm(forms.ModelForm): + class Meta: + model = Task + fields = ['title', 'category', 'due_date', 'due_time'] + widgets = { + 'title': forms.Textarea(attrs={'class': 'form-control', 'rows': 3, 'placeholder': 'Cosa devi fare?'}), + 'category': forms.Select(attrs={'class': 'form-select'}), + 'due_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), + 'due_time': forms.TimeInput(attrs={'class': 'form-control', 'type': 'time'}), + } diff --git a/core/management/__init__.py b/core/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/management/__pycache__/__init__.cpython-311.pyc b/core/management/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..8c466d9 Binary files /dev/null and b/core/management/__pycache__/__init__.cpython-311.pyc differ diff --git a/core/management/commands/__init__.py b/core/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/management/commands/send_due_date_reminders.py b/core/management/commands/send_due_date_reminders.py new file mode 100644 index 0000000..375adb8 --- /dev/null +++ b/core/management/commands/send_due_date_reminders.py @@ -0,0 +1,37 @@ +import os +from django.core.management.base import BaseCommand +from django.core.mail import send_mail +from django.utils import timezone +from core.models import Task + +class Command(BaseCommand): + help = 'Sends email reminders for tasks that are due today.' + + def handle(self, *args, **options): + today = timezone.now().date() + due_tasks = Task.objects.filter(due_date=today, status__in=['DA_FARE', 'IN_SOSPESO']) + + if not due_tasks: + self.stdout.write(self.style.SUCCESS('No tasks are due today.')) + return + + recipient_list = os.environ.get('CONTACT_EMAIL_TO', '').split(',') + if not recipient_list or not recipient_list[0]: + self.stdout.write(self.style.ERROR('No recipient email configured. Please set CONTACT_EMAIL_TO environment variable.')) + return + + for task in due_tasks: + subject = f'Reminder: Task "{task.title}" is due today!' + message = f'Hello,\n\nThis is a reminder that your task "{task.title}" is due today, {today}.\n\nCategory: {task.get_category_display()}\nStatus: {task.get_status_display()}\n\nThank you!' + + try: + send_mail( + subject, + message, + os.environ.get('DEFAULT_FROM_EMAIL'), + recipient_list, + fail_silently=False, + ) + self.stdout.write(self.style.SUCCESS(f'Successfully sent reminder for task: "{task.title}"')) + except Exception as e: + self.stdout.write(self.style.ERROR(f'Failed to send email for task: "{task.title}". Error: {e}')) diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py new file mode 100644 index 0000000..85a8fd0 --- /dev/null +++ b/core/migrations/0001_initial.py @@ -0,0 +1,31 @@ +# Generated by Django 5.2.7 on 2026-01-07 21:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Task', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200, verbose_name='Titolo')), + ('description', models.TextField(blank=True, null=True, verbose_name='Descrizione')), + ('due_date', models.DateField(blank=True, null=True, verbose_name='Data di Scadenza')), + ('priority', models.CharField(choices=[('L', 'Bassa'), ('M', 'Media'), ('H', 'Alta')], default='M', max_length=1, verbose_name='Priorità')), + ('category', models.CharField(blank=True, max_length=100, verbose_name='Categoria')), + ('completed', models.BooleanField(default=False, verbose_name='Completato')), + ], + options={ + 'verbose_name': 'Attività', + 'verbose_name_plural': 'Attività', + 'ordering': ['due_date'], + }, + ), + ] diff --git a/core/migrations/0002_alter_task_options_remove_task_completed_and_more.py b/core/migrations/0002_alter_task_options_remove_task_completed_and_more.py new file mode 100644 index 0000000..4f20dd6 --- /dev/null +++ b/core/migrations/0002_alter_task_options_remove_task_completed_and_more.py @@ -0,0 +1,48 @@ +# Generated by Django 5.2.7 on 2026-01-07 21:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='task', + options={'ordering': ['status', 'category'], 'verbose_name': 'Attività', 'verbose_name_plural': 'Attività'}, + ), + migrations.RemoveField( + model_name='task', + name='completed', + ), + migrations.RemoveField( + model_name='task', + name='description', + ), + migrations.RemoveField( + model_name='task', + name='due_date', + ), + migrations.RemoveField( + model_name='task', + name='priority', + ), + migrations.AddField( + model_name='task', + name='status', + field=models.CharField(choices=[('DA_FARE', 'Da Fare'), ('IN_SOSPESO', 'In Sospeso'), ('COMPLETATO', 'Completato')], default='DA_FARE', max_length=10, verbose_name='Stato'), + ), + migrations.AlterField( + model_name='task', + name='category', + field=models.CharField(choices=[('LAVORO', 'Lavoro'), ('FAMIGLIA', 'Famiglia'), ('PERSONALE', 'Personale')], default='PERSONALE', max_length=10, verbose_name='Categoria'), + ), + migrations.AlterField( + model_name='task', + name='title', + field=models.TextField(verbose_name='Cosa devi fare?'), + ), + ] diff --git a/core/migrations/0003_task_due_date_task_due_time.py b/core/migrations/0003_task_due_date_task_due_time.py new file mode 100644 index 0000000..d16039d --- /dev/null +++ b/core/migrations/0003_task_due_date_task_due_time.py @@ -0,0 +1,23 @@ +# Generated by Django 5.2.7 on 2026-01-07 21:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0002_alter_task_options_remove_task_completed_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='task', + name='due_date', + field=models.DateField(blank=True, null=True, verbose_name='Data di scadenza'), + ), + migrations.AddField( + model_name='task', + name='due_time', + field=models.TimeField(blank=True, null=True, verbose_name='Ora di scadenza'), + ), + ] diff --git a/core/migrations/__pycache__/0001_initial.cpython-311.pyc b/core/migrations/__pycache__/0001_initial.cpython-311.pyc new file mode 100644 index 0000000..0dca65a Binary files /dev/null and b/core/migrations/__pycache__/0001_initial.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/0002_alter_task_options_remove_task_completed_and_more.cpython-311.pyc b/core/migrations/__pycache__/0002_alter_task_options_remove_task_completed_and_more.cpython-311.pyc new file mode 100644 index 0000000..6947e82 Binary files /dev/null and b/core/migrations/__pycache__/0002_alter_task_options_remove_task_completed_and_more.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/0003_task_due_date_task_due_time.cpython-311.pyc b/core/migrations/__pycache__/0003_task_due_date_task_due_time.cpython-311.pyc new file mode 100644 index 0000000..8d0aa54 Binary files /dev/null and b/core/migrations/__pycache__/0003_task_due_date_task_due_time.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index 71a8362..89604f1 100644 --- a/core/models.py +++ b/core/models.py @@ -1,3 +1,28 @@ from django.db import models -# Create your models here. +class Task(models.Model): + CATEGORY_CHOICES = [ + ('LAVORO', 'Lavoro'), + ('FAMIGLIA', 'Famiglia'), + ('PERSONALE', 'Personale'), + ] + + STATUS_CHOICES = [ + ('DA_FARE', 'Da Fare'), + ('IN_SOSPESO', 'In Sospeso'), + ('COMPLETATO', 'Completato'), + ] + + title = models.TextField(verbose_name="Cosa devi fare?") + category = models.CharField(max_length=10, choices=CATEGORY_CHOICES, default='PERSONALE', verbose_name="Categoria") + status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='DA_FARE', verbose_name="Stato") + due_date = models.DateField(verbose_name="Data di scadenza", null=True, blank=True) + due_time = models.TimeField(verbose_name="Ora di scadenza", null=True, blank=True) + + def __str__(self): + return self.title + + class Meta: + ordering = ['status', 'category'] + verbose_name = "Attività" + verbose_name_plural = "Attività" \ No newline at end of file diff --git a/core/templates/base.html b/core/templates/base.html index 1e7e5fb..35cfad9 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -14,12 +14,17 @@ {% endif %} {% load static %} + + + + {% block head %}{% endblock %}
{% block content %}{% endblock %} +