make the 'last_activity' field in /tickets show the actual last activity by making it a virtual field #46
7 changed files with 135 additions and 6 deletions
19
core/files/migrations/0002_alter_file_file.py
Normal file
19
core/files/migrations/0002_alter_file_file.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 4.2.7 on 2024-01-10 19:04
|
||||
|
||||
from django.db import migrations, models
|
||||
import files.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('files', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='file',
|
||||
name='file',
|
||||
field=models.FileField(upload_to=files.models.hash_upload),
|
||||
),
|
||||
]
|
19
core/mail/migrations/0004_alter_emailattachment_file.py
Normal file
19
core/mail/migrations/0004_alter_emailattachment_file.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 4.2.7 on 2024-01-10 19:04
|
||||
|
||||
from django.db import migrations, models
|
||||
import files.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mail', '0003_emailattachment'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='emailattachment',
|
||||
name='file',
|
||||
field=models.FileField(upload_to=files.models.hash_upload),
|
||||
),
|
||||
]
|
|
@ -158,8 +158,8 @@ def receive_email(envelope, log=None):
|
|||
log.warning("Header to does not match envelope to")
|
||||
log.info(f"Header to: {header_to}, envelope to: {envelope.rcpt_tos[0]}")
|
||||
|
||||
recipient = envelope.rcpt_tos[0].lower()
|
||||
sender = envelope.mail_from
|
||||
recipient = envelope.rcpt_tos[0].lower() if envelope.rcpt_tos else header_to.lower()
|
||||
sender = envelope.mail_from if envelope.mail_from else header_from
|
||||
subject = parsed.get('Subject')
|
||||
subject = unescape_and_decode_quoted_printable(subject)
|
||||
subject = unescape_and_decode_base64(subject)
|
||||
|
|
|
@ -13,11 +13,12 @@ from core.settings import MAIL_DOMAIN
|
|||
from mail.models import Email
|
||||
from mail.protocol import send_smtp, make_reply, collect_references
|
||||
from notify_sessions.models import SystemEvent
|
||||
from tickets.models import IssueThread, Comment, STATE_CHOICES, StateChange
|
||||
from tickets.models import IssueThread, Comment, STATE_CHOICES
|
||||
|
||||
|
||||
class IssueSerializer(serializers.ModelSerializer):
|
||||
timeline = serializers.SerializerMethodField()
|
||||
last_activity = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = IssueThread
|
||||
|
@ -36,6 +37,18 @@ class IssueSerializer(serializers.ModelSerializer):
|
|||
raise serializers.ValidationError('invalid state')
|
||||
return attrs
|
||||
|
||||
@staticmethod
|
||||
def get_last_activity(self):
|
||||
try:
|
||||
last_state_change = self.state_changes.order_by('-timestamp').first().timestamp \
|
||||
if self.state_changes.count() > 0 else None
|
||||
last_comment = self.comments.order_by('-timestamp').first().timestamp if self.comments.count() > 0 else None
|
||||
last_mail = self.emails.order_by('-timestamp').first().timestamp if self.emails.count() > 0 else None
|
||||
args = [x for x in [last_state_change, last_comment, last_mail] if x is not None]
|
||||
return max(args)
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_timeline(obj):
|
||||
timeline = []
|
||||
|
@ -65,6 +78,9 @@ class IssueSerializer(serializers.ModelSerializer):
|
|||
})
|
||||
return sorted(timeline, key=lambda x: x['timestamp'])
|
||||
|
||||
def get_queryset(self):
|
||||
return IssueThread.objects.all().order_by('-last_activity')
|
||||
|
||||
|
||||
class IssueViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = IssueSerializer
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 4.2.7 on 2024-01-10 19:04
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('tickets', '0004_remove_issuethread_state_alter_statechange_state'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='issuethread',
|
||||
name='last_activity',
|
||||
),
|
||||
]
|
|
@ -27,7 +27,6 @@ class IssueThread(SoftDeleteModel):
|
|||
id = models.AutoField(primary_key=True)
|
||||
name = models.CharField(max_length=255)
|
||||
assigned_to = models.CharField(max_length=255, null=True)
|
||||
last_activity = models.DateTimeField(auto_now=True)
|
||||
manually_created = models.BooleanField(default=False)
|
||||
|
||||
@property
|
||||
|
|
|
@ -59,7 +59,7 @@ class IssueApiTest(TestCase):
|
|||
self.assertEqual(response.json()[0]['name'], "test issue")
|
||||
self.assertEqual(response.json()[0]['state'], "pending_new")
|
||||
self.assertEqual(response.json()[0]['assigned_to'], None)
|
||||
self.assertEqual(response.json()[0]['last_activity'], issue.last_activity.strftime('%Y-%m-%dT%H:%M:%S.%fZ'))
|
||||
self.assertEqual(response.json()[0]['last_activity'], comment.timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'))
|
||||
self.assertEqual(len(response.json()[0]['timeline']), 4)
|
||||
self.assertEqual(response.json()[0]['timeline'][0]['type'], 'state')
|
||||
self.assertEqual(response.json()[0]['timeline'][1]['type'], 'mail')
|
||||
|
@ -85,6 +85,65 @@ class IssueApiTest(TestCase):
|
|||
self.assertEqual(response.json()[0]['timeline'][3]['timestamp'],
|
||||
comment.timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'))
|
||||
|
||||
def test_issues_incomplete_timeline(self):
|
||||
now = datetime.now()
|
||||
issue1 = IssueThread.objects.create(
|
||||
name="test issue",
|
||||
)
|
||||
issue2 = IssueThread.objects.create(
|
||||
name="test issue",
|
||||
)
|
||||
issue3 = IssueThread.objects.create(
|
||||
name="test issue",
|
||||
)
|
||||
mail1 = Email.objects.create(
|
||||
subject='test',
|
||||
body='test',
|
||||
sender='test',
|
||||
recipient='test',
|
||||
issue_thread=issue2,
|
||||
timestamp=now + timedelta(seconds=2),
|
||||
)
|
||||
comment = Comment.objects.create(
|
||||
issue_thread=issue3,
|
||||
comment="test",
|
||||
timestamp=now + timedelta(seconds=3),
|
||||
)
|
||||
|
||||
response = self.client.get('/api/2/tickets/')
|
||||
self.assertEqual(200, response.status_code)
|
||||
self.assertEqual(3, len(response.json()))
|
||||
self.assertEqual(issue1.id, response.json()[0]['id'])
|
||||
self.assertEqual(issue2.id, response.json()[1]['id'])
|
||||
self.assertEqual(issue3.id, response.json()[2]['id'])
|
||||
self.assertEqual(issue1.state_changes.first().timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
|
||||
response.json()[0]['last_activity'])
|
||||
self.assertEqual(mail1.timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
|
||||
response.json()[1]['last_activity'])
|
||||
self.assertEqual(comment.timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
|
||||
response.json()[2]['last_activity'])
|
||||
self.assertEqual(1, len(response.json()[0]['timeline']))
|
||||
self.assertEqual(2, len(response.json()[1]['timeline']))
|
||||
self.assertEqual(2, len(response.json()[2]['timeline']))
|
||||
self.assertEqual('state', response.json()[0]['timeline'][0]['type'])
|
||||
self.assertEqual('state', response.json()[1]['timeline'][0]['type'])
|
||||
self.assertEqual('mail', response.json()[1]['timeline'][1]['type'])
|
||||
self.assertEqual('state', response.json()[2]['timeline'][0]['type'])
|
||||
self.assertEqual('comment', response.json()[2]['timeline'][1]['type'])
|
||||
self.assertEqual('pending_new', response.json()[0]['timeline'][0]['state'])
|
||||
self.assertEqual('pending_new', response.json()[1]['timeline'][0]['state'])
|
||||
self.assertEqual('test', response.json()[1]['timeline'][1]['sender'])
|
||||
self.assertEqual('test', response.json()[1]['timeline'][1]['recipient'])
|
||||
self.assertEqual('test', response.json()[1]['timeline'][1]['subject'])
|
||||
self.assertEqual('test', response.json()[1]['timeline'][1]['body'])
|
||||
self.assertEqual(mail1.timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
|
||||
response.json()[1]['timeline'][1]['timestamp'])
|
||||
self.assertEqual('pending_new', response.json()[2]['timeline'][0]['state'])
|
||||
self.assertEqual('test', response.json()[2]['timeline'][1]['comment'])
|
||||
self.assertEqual(comment.timestamp.strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
|
||||
response.json()[2]['timeline'][1]['timestamp'])
|
||||
|
||||
|
||||
def test_manual_creation(self):
|
||||
response = self.client.post('/api/2/tickets/manual/',
|
||||
{'name': 'test issue', 'sender': 'test', 'recipient': 'test', 'body': 'test'},
|
||||
|
|
Loading…
Reference in a new issue