diff --git a/core/admin.py b/core/admin.py index 92a1a23..d2a4052 100644 --- a/core/admin.py +++ b/core/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin from django.contrib.auth.admin import UserAdmin -from .models import PawUser +from .models import PawUser, MailTemplate, GoogleSSOUser @admin.register(PawUser) @@ -9,3 +9,12 @@ class CustomUserAdmin(UserAdmin): ('Extended Data', { 'fields': ('profile_picture', 'language', 'telegram_username')}), ) + +@admin.register(MailTemplate) +class MailTemplateAdmin(admin.ModelAdmin): + list_display = ('name', 'event', 'language') + list_filter = ('event', 'language') + +@admin.register(GoogleSSOUser) +class GoogleSSOUserAdmin(admin.ModelAdmin): + list_display = ('paw_user', 'google_id') \ No newline at end of file diff --git a/core/migrations/0006_mailtemplate.py b/core/migrations/0006_mailtemplate.py new file mode 100644 index 0000000..dd7a6af --- /dev/null +++ b/core/migrations/0006_mailtemplate.py @@ -0,0 +1,39 @@ +# Generated by Django 5.0.3 on 2024-03-23 14:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("core", "0005_remove_googlessouser_id_alter_googlessouser_paw_user"), + ] + + operations = [ + migrations.CreateModel( + name="MailTemplate", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("event", models.CharField(max_length=100)), + ( + "language", + models.CharField( + choices=[("en", "English"), ("fr", "French"), ("de", "German")], + default="en", + max_length=2, + ), + ), + ("name", models.CharField(max_length=200)), + ("subject", models.CharField(max_length=200)), + ("content", models.TextField()), + ], + ), + ] diff --git a/core/migrations/0007_seed_mailtemplates.py b/core/migrations/0007_seed_mailtemplates.py new file mode 100644 index 0000000..16a2823 --- /dev/null +++ b/core/migrations/0007_seed_mailtemplates.py @@ -0,0 +1,82 @@ +from django.db import migrations +from core.models import MailTemplate + +def add_initial_templates(apps, schema_editor): + MailTemplate.objects.create( + event='ticket_status_change', + name='Ticket Status Change', + language='en', + subject='Your ticket status has changed', + content='''\ +Hello {ticket_creator_username}, + +This is to inform you that the status of your ticket #{ticket_id} {ticket_title} has been changed from "{ticket_status_old} to "{ticket_status}". + +Thank you for your patience. + +Best regards, +[Your Organization Name] +''' + ) + + MailTemplate.objects.create( + event='new_ticket', + name='New Ticket', + language='en', + subject='Your ticket has been created successfully', + content='''\ +Dear {ticket_creator_username}, + +We're writing to inform you that your ticket #{ticket_id} has been created successfully. + +Details: +Title: {ticket_title} +Category: {ticket_category} +Description: +{ticket_description} + +Our team will review your ticket and respond as soon as possible. You can track the status of your ticket by logging into your account. + +Thank you for reaching out to us. + +Best regards, +[Your Organization Name] +''' + ) + + MailTemplate.objects.create( + event='ticket_assigned', + name='Ticket Assignment', + language='en', + subject='A new ticket has been assigned to your team', + content='''\ +Hello Team, + +A new ticket has been created with the following details: + +Ticket ID: #{ticket_id} +Title: {ticket_title} +Priority: {ticket_priority} +Category: {ticket_category} +Created By: {ticket_creator_username} +Description: +{ticket_description} + +Please review the ticket and take appropriate action. + +Thank you for your attention. + +Best regards, +[Your Organization Name] +''' + ) + + + +class Migration(migrations.Migration): + + dependencies = [ + ("core", "0006_mailtemplate"), + ] + + operations = [migrations.RunPython(add_initial_templates),] diff --git a/core/models.py b/core/models.py index 96f5c37..3193d14 100644 --- a/core/models.py +++ b/core/models.py @@ -1,6 +1,8 @@ from django.db import models from django.contrib.auth.models import AbstractUser from django.utils.translation import gettext_lazy as _ +from django.conf import settings +from django.core.mail import send_mail class PawUser(AbstractUser): @@ -25,3 +27,32 @@ class GoogleSSOUser(models.Model): class Meta: db_table = "google_sso_user" verbose_name = _("Google SSO User") + +class MailTemplate(models.Model): + event = models.CharField(max_length=100) + language = models.CharField(max_length=2, default='en', choices=settings.LANGUAGES) + name = models.CharField(max_length=200) + subject = models.CharField(max_length=200) + content = models.TextField() + + @classmethod + def get_template(cls, event, language='en'): + template = cls.objects.filter(event=event, language=language).first() + if not template: + template = cls.objects.filter(event=event, language='en').first() + return template + + def send_mail(self, to, context): + try: + send_mail( + self.subject.format(**context), + self.content.format(**context), + settings.DEFAULT_FROM_EMAIL, + [to], + fail_silently=False, + ) + except Exception as e: + print(e) + + def __str__(self): + return f"{self.name} - [{self.event}]" \ No newline at end of file diff --git a/paw/__init__.py b/paw/__init__.py index 5d08ea7..b2e3edd 100644 --- a/paw/__init__.py +++ b/paw/__init__.py @@ -1,5 +1,5 @@ from django import get_version -VERSION = (0, 2, 0, "beta", 1) +VERSION = (0, 3, 0, "beta", 1) __version__ = get_version(VERSION) diff --git a/ticketing/admin.py b/ticketing/admin.py index 39bff25..83d7caf 100644 --- a/ticketing/admin.py +++ b/ticketing/admin.py @@ -1,9 +1,18 @@ from django.contrib import admin from .models import Category, Ticket, Comment, Template, Team, FileAttachment -# Register your models here. + admin.site.register(Category) -admin.site.register(Ticket) + +@admin.register(Ticket) +class TicketAdmin(admin.ModelAdmin): + list_display = ('title', 'status', 'priority', 'category', 'created_at', 'updated_at') + list_filter = ('status', 'priority', 'category') + search_fields = ('title', 'description') + date_hierarchy = 'created_at' + ordering = ('-created_at',) + readonly_fields = ('created_at', 'updated_at') + admin.site.register(Comment) admin.site.register(Template) admin.site.register(Team) diff --git a/ticketing/models.py b/ticketing/models.py index 6e49029..3bb8c67 100644 --- a/ticketing/models.py +++ b/ticketing/models.py @@ -2,9 +2,10 @@ from django.db import models from core.models import PawUser from django.utils import timezone from django.utils.translation import gettext_lazy as _ -from django.db.models.signals import post_save +from django.db.models.signals import post_save, pre_save from django.dispatch import receiver from uuid import uuid4 +from core.models import MailTemplate def ticket_directory_path(instance, filename): @@ -89,12 +90,55 @@ class Ticket(models.Model): @receiver(post_save, sender=Ticket, dispatch_uid="team_auto_assignment") def update_team_assignment(sender, instance, created, **kwargs): - if not created or not instance.category or not instance.category.team: - return - + if not created: + return None + + if not instance.category or not instance.category.team: + mail_template = MailTemplate.get_template('ticket_assigned') + if not mail_template: + return None + #TODO send mail to all supporters + return None + + # assign team to ticket instance.assigned_team = instance.category.team instance.save() + mail_template = MailTemplate.get_template('ticket_assigned') + if not mail_template: + return None + mail_template.send_mail(instance.category.team.members.values_list('email', flat=True), { + 'ticket_id': instance.id, 'ticket_title': instance.title, 'ticket_description': instance.description, + 'ticket_priority': instance.get_priority(), 'ticket_category': instance.category.name if instance.category else _('General'), + 'ticket_creator_username': instance.user.username}) + + + +@receiver(post_save, sender=Ticket, dispatch_uid="mail_notification") +def send_mail_notification(sender, instance, created, **kwargs): + if created: + mail_template = MailTemplate.get_template('new_ticket', instance.user.language) + if not mail_template: + return None + mail_template.send_mail(instance.user.email, { + 'ticket_id': instance.id, 'ticket_creator_username': instance.user.username, 'ticket_title': instance.title, + 'ticket_description': instance.description, 'ticket_category': instance.category.name if instance.category else _('General')}) + +@receiver(pre_save, sender=Ticket, dispatch_uid="mail_change_notification") +def send_mail_change_notification(sender, instance, update_fields=None, **kwargs): + try: + old_instance = Ticket.objects.get(id=instance.id) + except Ticket.DoesNotExist: + return None + + if old_instance.status != instance.status: + mail_template = MailTemplate.get_template('ticket_status_change', instance.user.language) + if not mail_template: + return None + mail_template.send_mail(instance.user.email, { + 'ticket_id': instance.id, 'ticket_creator_username': instance.user.username, 'ticket_status': instance.status, + 'ticket_status_old': old_instance.status, 'ticket_title': instance.title + }) class Comment(models.Model): ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE) @@ -131,4 +175,4 @@ class Template(models.Model): Category, on_delete=models.CASCADE, null=True, blank=True) def __str__(self): - return self.name + return self.name \ No newline at end of file