| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| |
| import difflib |
| import functools |
| import logging |
| import uuid |
| |
| from django.db import models |
| from django.urls import reverse |
| |
| logger = logging.getLogger(__name__) |
| |
| |
| class Product(models.Model): |
| prefix = models.TextField(primary_key=True) |
| name = models.TextField() |
| description = models.TextField(blank=True, null=True) |
| owner = models.TextField(blank=True, null=True) |
| |
| class Meta: |
| db_table = 'bloodhound_product' |
| |
| |
| class ProductConfig(models.Model): |
| """Possibly legacy table - keeping for now""" |
| product = models.ForeignKey(Product, on_delete=models.CASCADE) |
| section = models.TextField() |
| option = models.TextField() |
| value = models.TextField(blank=True, null=True) |
| |
| class Meta: |
| db_table = 'bloodhound_productconfig' |
| unique_together = (('product', 'section', 'option'),) |
| |
| |
| class ProductResourceMap(models.Model): |
| """Possibly legacy model - keeping for now""" |
| product_id = models.ForeignKey(Product, on_delete=models.CASCADE) |
| resource_type = models.TextField(blank=True, null=True) |
| resource_id = models.TextField(blank=True, null=True) |
| |
| class Meta: |
| db_table = 'bloodhound_productresourcemap' |
| |
| |
| class ModelCommon(models.Model): |
| id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) |
| created = models.DateTimeField(auto_now_add=True, editable=False) |
| |
| class Meta: |
| abstract = True |
| |
| |
| class Ticket(ModelCommon): |
| |
| def api_url(self): |
| return reverse('ticket-detail', args=(self.id,)) |
| |
| def api_events_url(self): |
| return reverse('changeevent-list', args=(self.id,)) |
| |
| def last_update(self): |
| last_event = self.changeevent_set.order_by('created').last() |
| return self.created if last_event is None else last_event.created |
| |
| def add_field_event(self, field, newvalue): |
| current_lines = self.get_field_value(field).splitlines(keepends=True) |
| replace_lines = newvalue.splitlines(keepends=True) |
| result = '\n'.join(difflib.ndiff(current_lines, replace_lines)) |
| |
| tfield, created = TicketField.objects.get_or_create(name=field) |
| c = ChangeEvent(ticket=self, field=tfield, diff=result) |
| c.save() |
| |
| def get_field_value(self, field): |
| try: |
| tfield = TicketField.objects.get(name=field) |
| except TicketField.DoesNotExist as e: |
| return '' |
| |
| event = self.changeevent_set.filter(field=tfield).order_by('created').last() |
| return '' if event is None else event.value() |
| |
| |
| class TicketField(ModelCommon): |
| name = models.CharField(max_length=32) |
| |
| def api_url(self): |
| return reverse('ticketfield-detail', args=(self.id,)) |
| |
| |
| class ChangeEvent(ModelCommon): |
| ticket = models.ForeignKey(Ticket, models.CASCADE, null=False) |
| field = models.ForeignKey(TicketField, models.CASCADE) |
| diff = models.TextField() |
| |
| def value(self, which=2): |
| return ''.join(difflib.restore(self.diff.splitlines(keepends=True), which)).strip() |
| |
| old_value = functools.partialmethod(value, which=1) |
| |
| def __str__(self): |
| return "Change to: {}; Field: {}; Diff: {}".format( |
| self.ticket, self.field, self.diff) |
| |
| def api_url(self): |
| return reverse('changeevent-detail', args=(self.ticket.id, self.id,)) |
| |
| def api_ticket_url(self): |
| return reverse('ticket-detail', args=(self.ticket.id,)) |