Source code for jmb.ticket.models

# coding: utf-8
"""
.. autoclass:: Ticket


"""
import datetime, os, tempfile
from appy.pod.renderer import Renderer

from django.db.models import Q
from django.conf import settings
from django.core import serializers
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.comments.models import Comment
from django.core.urlresolvers import reverse
from django.db import models
from django.db.models.query_utils import Q
from django.utils.translation import ugettext_lazy as _
from django.forms.models import model_to_dict
from django.contrib.auth.models import User, Group

from jmb.core.utils.functions import get_template_abspath
from jmb.core.db.fields import HTMLField, HTMLFieldTinyMCE
from jmb.core.db.models import DateModel, UserModel, StatusModel, OrderedModel, SEOModel
from jmb.core.middleware.thread_local import get_current_user
from jmb.i18n.models import TranslationModel, I18NModel
from jmb.organization.models import (
    Contact as OrganizationContact,
    Organization as OrganizationOrganization, OrganizationAddress
)
from jmb.project.models import Project
from jmb.core.utils.ooutils import OOUtils

class AllSet(set):
    """A set that can produce a queryset so as to play well with forms
    """
    @property
    def qs(self):
        return Contact.objects.filter(pk__in=[c.pk for c in self])


class Organization(OrganizationOrganization):
    """
    Classe che aggiunge al modello ``jmb.organization.Organization`` alcuni
    metodi comodi per gestire i permessi e la visibilità dei ticket
    """

    class Meta:
        proxy = True

    def __repr__(self):
        return u"<TicketOrganization: %s>" % self

    @property
    def ticket_managers(self):
        managers = AllSet(potential_managers().filter(ticketperm__project_manager=True))
        managers.update(list(potential_managers().filter(
            managed_ticketprojects__organization=self)))
        return managers
                        
    @property
    def ticket_staff(self):
        managers = AllSet(potential_staff().filter(ticketperm__project_staff=True))
        managers.update(list(potential_managers().filter(
            managed_ticketprojects__organization=self)))
        return managers
        # return potential_staff().filter(
        #     assigned_ticketprojects__organization=self
        # ).distinct()

    @property
    def ticket_customers(self):
        return potential_customers().filter(
            visible_ticketprojects__organization=self
        ).distinct()

    @property
    def ticket_submitters(self):
        return Contact.objects.filter(
            Q(managed_ticketprojects__organization=self) |
            Q(assigned_ticketprojects__organization=self) |
            Q(visible_ticketprojects__organization=self),
            status=1
        ).distinct()

    @property
    def ticketprojects(self):
        return TicketProject.active.filter(organization_id=self.id)

    
  
  
class Contact(OrganizationContact):
    """
    Classe che aggiunge al modello ``jmb.organization.Contact`` alcuni
    metodi comodi per gestire i permessi e la visibilità dei ticket
    """

    class Meta:
        proxy = True

    def __repr__(self):
        return u"<TicketContact: %s>" % self
    
    
    
    @property
    def managed_organizations(self):
        if self.is_site_manager():
            return Organization.customers_owners.all()
        return Organization.customers_owners.filter(
            projects__ticketproject__managers=self
        ).distinct()

    @property
    def assigned_organizations(self):
        if self.is_site_staff():
            return Organization.customers_owners.all()
        return Organization.customers_owners.filter(
            projects__ticketproject__staff=self
        ).distinct()

    @property
    def visible_organizations(self):
        return Organization.customers_owners.filter(
            projects__ticketproject__customers=self
        ).distinct()

    @property
    def ticket_organizations(self):
        if self.is_site_manager() or self.is_site_staff():
            return Organization.customers_owners.all()
        return Organization.customers_owners.filter(
            Q(projects__ticketproject__managers=self) |
            Q(projects__ticketproject__staff=self) |
            Q(projects__ticketproject__customers=self)
        ).distinct()

    @property
    def managed_ticketprojects(self):
        if self.is_site_manager():
            return TicketProject.active.all()
        return TicketProject.active.filter(
            managers=self
        ).distinct()

    @property
    def assigned_ticketprojects(self):
        if self.is_site_staff():
            return TicketProject.active.all()

        return TicketProject.active.filter(
            staff=self
        ).distinct()

    @property
    def visible_ticketprojects(self):
        return TicketProject.active.filter(
            customers=self
        ).distinct()

    @property
    def visible_tickets(self):
        return Ticket.active.filter(
            customers=self
        ).distinct()

    @property
    def ticketprojects(self):
        if self.is_site_manager() or self.is_site_staff():
            return TicketProject.active.all()
        return TicketProject.active.filter(
            Q(managers=self) |
            Q(staff=self) |
            Q(customers=self)
        ).distinct()

    def get_managed_organization_ticketprojects(self, organization):
        qs = TicketProject.active.filter(
            project__organization_id=organization.id
        )
        if self.is_site_manager():
            return qs
        return qs.filter(managers=self).distinct()

    def get_assigned_organization_ticketprojects(self, organization):
        qs = TicketProject.active.filter(
            organization_id=organization.id)
        
        if self.is_site_staff():
            return qs
        return qs.filter(staff=self).distinct()

    def get_visible_organization_ticketprojects(self, organization):
        return TicketProject.active.filter(
            customers=self,
            organization_id=organization.id
        ).distinct()

    def get_organization_ticketprojects(self, organization):
        if self.is_site_manager() or self.is_site_staff():
            return TicketProject.active.filter(organization_id=organization.id).distinct()
        return TicketProject.active.filter(
            Q(managers=self) |
            Q(staff=self) |
            Q(customers=self),
            organization_id=organization.id
        ).distinct()

    def get_staff_tickets(self):
        if self.is_site_staff():
            return Ticket.objects.all()
        else:
            return Ticket.objects.filter(
                staff=self,
            )

    def is_manager(self):
        return self.managed_ticketprojects.all().count() > 0

    def is_staff(self):
        return self.assigned_ticketprojects.all().count() > 0

    def is_site_manager(self):
        from jmb.ticket import utils
        user, contact = utils.get_current_contact()
        return user.has_perm('ticket.ticket_site_manager')

    def is_site_staff(self):
        from jmb.ticket import utils
        user, contact = utils.get_current_contact()
        return user.has_perm('ticket.ticket_site_staff')

    def is_customer(self):
        return self.visible_ticketprojects.all().count() > 0
    
    def is_owner(self):
        return bool(self.__class__.owners.filter(pk=self.id))

    def is_submitter(self):
        active_ticket_projects = TicketProject.active.all()
        return active_ticket_projects.filter(
            Q(managers=self) |
            Q(staff=self) |
            Q(customers=self)
        ).count() > 0

    def get_role(self, organization, project):
        """
        return the role of contact

        :arg organization: limit the role for that organization
        :arg project: limit the role to that project
        """
        if self.is_site_manager():
            return 'manager'
        elif self.is_site_staff():
            return 'staff'
        elif organization and project:
            mtp = self.managed_ticketprojects.filter(
                organization__id=organization.id,
                id=project.id
            )
            stp = self.assigned_ticketprojects.filter(
                organization__id=organization.id,
                id=project.id
            )
            ctp = self.visible_ticketprojects.filter(
                organization__id=organization.id,
                id=project.id
            )

            if mtp.count() > 0:
                return "manager"
            elif stp.count() > 0:
                return "staff"
            elif ctp.count() > 0:
                return "customer"
        return None

    def get_role_by_ticket(self, ticket):
        """
        return the role of contact for passed ticket

        :arg ticket: id or instance ticket
        """
        if isinstance(ticket, (int, basestring)):
            ticket = Ticket.objects.get(pk=ticket)
        return self.get_role(ticket.project.organization, ticket.project)

TicketContact = Contact 

def potential_managers():
    return Contact.owners.all()

def potential_staff():
    return Contact.workers.all()


def potential_customers():
    return Contact.customers.all()


class Typology(DateModel, UserModel, StatusModel, I18NModel):
    name = models.CharField(
        max_length=30,
        verbose_name=_("Name"),
    )
    description = HTMLFieldTinyMCE(
        null=True,
        verbose_name=_("Description"),
    )
    
    
    def __unicode__(self):
        return self.translate().name

    class Meta:
        verbose_name = _("Typology")
        verbose_name_plural = _("Typologies")


class TypologyTranslation(TranslationModel):
    typology = models.ForeignKey(
        Typology,
        related_name='translations',
    )
    name = models.CharField(
        max_length=30,
        verbose_name=_("Name"),
    )
    
    
    
    class Meta:
        verbose_name = _('Translation')
        verbose_name_plural = _('Translations')
        unique_together = (('typology', 'language',),)

    def __unicode__(self):
        return self.name
    

class Category(DateModel, UserModel, StatusModel, I18NModel):
    name = models.CharField (
        max_length=30,
        verbose_name=_("Name"),
    )
    description = HTMLFieldTinyMCE(
        null=True,
        verbose_name=_("Description"),
    )

    def __unicode__(self):
        return self.translate().name
    
    
    
    class Meta:
        verbose_name = _("Category")
        verbose_name_plural = _("Categories")


class CategoryTranslation(TranslationModel):
    category = models.ForeignKey(
        Category,
        related_name='translations',
    )
    name = models.CharField(
        max_length=30,
        verbose_name=_("Name"),
    )

    
    
    class Meta:
        verbose_name = _('Translation')
        verbose_name_plural = _('Translations')
        unique_together = (('category', 'language',),)

    def __unicode__(self):
        return self.name


class TicketProjectActiveManager(models.Manager):
    def get_query_set(self):
        today = datetime.datetime.today()
        qs = super(TicketProjectActiveManager, self).get_query_set().filter(
            status=1,
            date_start__lte=today,
        ).filter(
            Q(date_end__gte=today) | Q(date_end__isnull=True)
        )
        return qs


class TicketProjectManager(models.Manager):
    def get_query_set(self):
        from jmb.ticket import utils

        user, contact = utils.get_current_contact()
        qs = super(TicketProjectManager, self).get_query_set()

        if not user:
            return qs.none()

        if not user.is_authenticated():
            return qs.none()

        if user.is_superuser or user.has_perm('ticket.list_all_tickets'):
            return qs

        if contact:
            if contact.is_site_manager():
                return qs
            elif contact.is_site_staff():
                return qs  # TODO: dovrebbe essere rifinita?
            else:
                if contact.organization:
                    if Contact.owners.filter(pk=contact.id):
                        qs = qs.filter(
                            Q(managers=contact) | Q(staff=contact),
                            organization=contact.organization,
                        )
                    else:
                        qs = qs.filter(customers=contact,)
                    return qs

        return qs.none()

# TODO:
#        user = get_current_user()
#        if not user:
#            return qs.none()
#
#        if not user.is_authenticated():
#            return qs.none()
#
#        if user.is_superuser or user.has_perm('ticket.list_all_projects'):
#            return qs
#
#        try:
#            if user.contact and user.contact.organization:
#                return qs.filter(organization = user.contact.organization)
#        except:
#            pass
#
#        qs = qs.filter(Q(managers=user) | Q(staff=user))
#
#        qs = qs.distinct()
        return qs


class TicketProject(Project):
    """Project: each user can have multiple projects"""
    managers = models.ManyToManyField(
        Contact,
        related_name='managed_ticketprojects',
        verbose_name=_("Managers")
    )
    staff = models.ManyToManyField(
        Contact,
        related_name='assigned_ticketprojects',
        verbose_name=_("Staff")
    )
    customers = models.ManyToManyField(
        Contact,
        related_name='visible_ticketprojects',
        verbose_name=_("Customers")
    )
    assistance_email = models.EmailField(
        verbose_name=_("Assistance Email"),
        blank=True,
        null=True,
    )
    icon = models.ImageField(
        blank=True, null=True,
        upload_to=settings.TICKET_TICKETPROJECT_PATH_ICON,
        verbose_name=_("Icon")
    )
    address = models.ForeignKey (
        OrganizationAddress,
        related_name = 'ticket_projects',
        null = True, blank = True,
        db_index = True,
        verbose_name = _("Address"),
    )
    objects = TicketProjectManager()
    active = TicketProjectActiveManager()
    
    #per il serialize
    def natural_key(self):
        return model_to_dict(self, exclude=['icon',])
    
    @property
    def all_managers(self):
        "Return managers and site_managers"
        managers = AllSet(list(self.managers.filter(status=1)))
        managers.update(list(Contact.objects.filter(ticketperm__project_manager=True, status=1)))        
        return managers

    @property
    def all_staff(self):
        "Return managers and site_managers"
        managers = AllSet(list(self.staff.filter(status=1)), )
        managers.update(list(Contact.objects.filter(ticketperm__project_staff=True, status=1)))        
        return managers

    def get_string_managers(self):
        managers = [str(manager) for manager in self.managers.all()]
        return ', '.join(managers)

    def get_manager_emails(self):
        manager_emails = [manager.email for manager in self.managers.filter(email__isnull=False)]
        return manager_emails

    def get_list_managers(self):
        return self.managers.all()

    def get_all_workers(self):
        """Return all workers from staff"""
        workers_ids = set()
        for worker in self.staff.all():
            if not worker.id in workers_ids:
                workers_ids.add(worker.id)
                yield worker
                    

    def get_string_staff(self):
        staff = [str(staff) for staff in self.staff.all()]
        return ', '.join(staff)

    def get_staff_emails(self):
        staff_emails = [staff.email for staff in self.staff.filter(email__isnull = False)]
        return staff_emails

    def get_string_customers(self):
        customers = [str(customer) for customer in self.customers.all()]
        return ', '.join(customers)

    def get_customer_emails(self):
        customer_emails = [customer.email for customer in self.customers.filter(email__isnull = False)]
        return customer_emails

    @property
    def submitters(self):
        # la versione con Q() | Q() | Q() e` circa 1000 volte più lenta
        # per il fatto che OR e` molto poco performante
        submitters = AllSet(list(self.managers.all()))
        submitters.update(list(self.staff.all()))
        submitters.update(list(self.customers.all()))
        submitters.update(list(Contact.objects.filter(ticketperm__project_manager=True)))
        submitters.update(list(Contact.objects.filter(ticketperm__project_staff=True)))
        return submitters

    def get_full_name(self):
        return self.tickets.count()
    full_name=property(get_full_name)

    def get_total_tickets(self):
        return self.tickets.count()
    total_tickets=property(get_total_tickets)
    get_total_tickets.short_description = _('Total Tickets')

    def get_open_tickets(self):
        return self.tickets.filter(status=7).count()
    open_tickets=property(get_open_tickets)
    get_open_tickets.short_description = _('Open Tickets')

    def get_close_tickets(self):
        return self.total_tickets - self.open_tickets
    close_tickets = property(get_close_tickets)
    get_close_tickets.short_description = _('Close Tickets')

    def get_percentage_done(self):
        percentage = 100
        if self.total_tickets:
            percentage = (100 * self.close_tickets) / self.total_tickets
        return int(percentage)
    percentage_done=property(get_percentage_done)
    get_percentage_done.short_description = _('Percentage Done')

    def get_total_work_hours(self):
        assistances = TicketAssistance.objects.filter(ticket__project=self)
        totale_ore = datetime.timedelta(0)
        for assistance in assistances:
            totale_ore += assistance.real_duration
        s = totale_ore.days * 86400 + totale_ore.seconds
        hours, remainder = divmod(s, 3600)
        minutes, seconds = divmod(remainder, 60)
        str_hours = 'ora'
        if hours > 1:
            str_hours = 'ore'
        str_minutes = 'minuto'
        if minutes > 1:
            str_minutes = 'minuti'
        return '%s %s, %s %s' % (hours, str_hours, minutes, str_minutes)
    total_work_hours=property(get_total_work_hours)
    get_total_work_hours.short_description = _("Total Work Hours")

    def get_total_assistances(self):
        return TicketAssistance.objects.filter(ticket__project=self).count()
    total_assistances=property(get_total_assistances)
    get_total_assistances.short_description = _("Total Assistances")

    class Meta:
        verbose_name = _("Project")
        verbose_name_plural = _("Projects")
        permissions = (
            ("can_view_ticket_projects"   , "Can view ticket projects"),
            ("list_all_ticket_projects"   , "Can list all ticket projects"),
            ("ticket_site_manager"        , "Is site manager"),
            ("ticket_site_staff"          , "Is site staff")
        )
        ordering = ['name']

    def __unicode__(self):
        if self.organization:
            return "%s (%s)" % (unicode(self.name), unicode(self.organization))
        else:
            return unicode(self.name)


class AllTicketManager(models.Manager):
    pass


class TicketManager(models.Manager):
    def get_query_set(self):
        # TODO: anche solo con dj, si passa da qui 4 volte...
        from jmb.ticket import utils

        user, contact = utils.get_current_contact()
        qs = super(TicketManager, self).get_query_set()

        if not user:
            return qs.none()

        if not user.is_authenticated():
            return qs.none()

        if user.is_superuser or user.has_perm('ticket.list_all_tickets'):
            return qs

        if contact:
            if contact.is_site_manager():
                return qs
            elif contact.is_site_staff():
                return qs  # TODO: dovrebbe essere rifinita?
            else:
                if contact.organization:
                    if Contact.owners.filter(pk=contact.id):
                        qs = qs.filter(
                            Q(project__managers=contact) | Q(project__staff=contact),
                            project__organization=contact.organization,
                        )
                    else:
                        qs = qs.filter(public=True, project__customers=contact,)
                    return qs

        return qs.none()

    # TODO Lascio il codice precedente per ua riflessione se l'attuale iplementazione e` corretta
    # if user.has_perm("ticket.list_own_contact_tickets"):
    #     if user.contact and user.contact.organization:
    #         return qs.filter(public=True, organization = user.contact.organization)

    # p = TicketProject.objects.all()

    # #q1 = Q(assigned_to__isnull=True) & Q(project__in=p)
    # q1 = Q(assigned_to__isnull=True)
    # q2 = Q(assigned_to=user) | Q(submitter=user)

    # qs = qs.filter(q1 | q2)
    # qs = qs.distinct()


class Priority(DateModel, UserModel):
    """
    Priorità del ticket
    """
    name = models.CharField (
        max_length = 100,
        verbose_name = _("Name"),
    )
    
    priority = models.IntegerField(
        verbose_name=_('Priority'),
        unique = True
    )
    
    color = models.CharField(
        max_length = 100,
        verbose_name = _("Color"),
    )
    
    color_text = models.CharField(
        max_length = 100,
        verbose_name = _("Color Text"),
    )
    
    def __unicode__(self):
        return unicode(self.name)
    
    class Meta:
        verbose_name = _('Priority')
        verbose_name_plural = _('Priority')
        ordering = ['priority']
[docs]class Ticket(DateModel, UserModel): """ Il ticket ha due attributi di stato: ``status``: aperto/chiuso o ``progress_status`` che ha stati definibili nei settings """ project = models.ForeignKey ( TicketProject, null = True, blank = True, db_index = True, related_name = "tickets", verbose_name = _("Project"), ) organization = models.ForeignKey ( Organization, null = True, blank = True, db_index = True, related_name = "tickets", verbose_name = _("Organization"), ) address = models.ForeignKey ( OrganizationAddress, related_name = 'tickets', null = True, blank = True, db_index = True, verbose_name = _("Address"), ) submitter = models.ForeignKey ( Contact, db_index = True, related_name = "tickets_summitted", verbose_name = _("Submitter"), ) assigned_by = models.ForeignKey ( Contact, blank = True, null = True, db_index = True, related_name = "tickets_assigned_by_me", verbose_name = _("Assigned by"), ) assigned_to = models.ManyToManyField ( Contact, blank = True, null = True, db_index = True, related_name = "tickets_assigned_to_me", verbose_name = _("Assigned to"), ) category = models.ForeignKey ( Category, db_index = True, blank = True, null = True, related_name = "tickets", verbose_name = _("Category"), ) typology = models.ForeignKey ( Typology, db_index = True, blank = True, null = True, related_name = "tickets", verbose_name = _("Typology"), ) priority = models.ForeignKey ( Priority, default = 1, to_field = "priority", related_name = "tickets", verbose_name = _("Priority"), ) status = models.IntegerField( choices=settings.TICKET_TICKET_STATUS_CODES, default=1, verbose_name=_('Status'), help_text=None, ) progress_status = models.IntegerField( choices=settings.TICKET_TICKET_PROGRESS_STATUS_CODES, default=1, null=True, blank=False, verbose_name=_('Progress Status'), help_text=None, ) title = models.CharField ( max_length = 100, verbose_name = _("Title"), ) keywords = models.CharField ( max_length=150, blank = True, null = True, verbose_name = _("Keywords"), help_text="keywords to categorize the ticket" ) description = HTMLFieldTinyMCE ( verbose_name = _("Description"), ) duration = models.IntegerField ( null = True, blank = True, verbose_name = _("Duration"), ) deadline = models.DateField( null = True, blank = True, verbose_name = _("Deadline"), ) triggerline = models.DateField( null = True, blank = True, default=datetime.date.today, verbose_name = _("Triggerline"), ) note = models.TextField ( verbose_name = _("Note"), null = True, blank = True, ) public = models.BooleanField( verbose_name = _("Visible by customer"), help_text = _("Customers will have visibility of this ticket"), default = False, ) to_be_planned = models.BooleanField( null=False, default=True, verbose_name = _("To be planned"), help_text = _("If True the ticket will appear in the ticket board"), ) objects = TicketManager() all_objects = AllTicketManager() #per il serialize #def natural_key(self): # return model_to_dict(self, exclude=['icon',]) def label(self): return str(self) def label_description(self): return self.description def get_from_email(self): email = settings.TICKET_GENERIC_MAIL_TICKET if self.project and self.project.assistance_email: email = self.project.assistance_email return email def get_assigned_to_emails(self): if self.assigned_to: return [ u.user.email for u in self.assigned_to.all() if u.user.email ] else: if self.project: return [ u.user.email for u in self.project.get_list_staff() if u.user.email ] def get_string_assigned_to(self): list_assigned_to = [] for assigned_to in self.assigned_to.all(): list_assigned_to.append(unicode(assigned_to)) if list_assigned_to: return ', '.join(list_assigned_to) return '' string_assigned_to = property(get_string_assigned_to) get_string_assigned_to.short_description = _('Assigned To') def get_all_address_to_send_email(self): ### No longer used s = set() if self.submitter.email: s.add(self.submitter.email) for email in self.get_assigned_to_emails(): s.add(email) if self.project: for email in self.project.get_manager_emails(): s.add(email) return s def save(self, *args, **kwargs): if not self.id and self.submitter.is_customer(): self.public = True super(Ticket, self).save() def is_closed(self): if self.status in (0, 5): return True return False def get_total_assistances(self): return self.assistances.count() total_assistances=property(get_total_assistances) get_total_assistances.short_description = _("Total Assistances") def get_comments_count(self): ctype = ContentType.objects.get_for_model(self) num_comments = Comment.objects.filter(content_type=ctype, is_public = True, object_pk=str(self.id)).count() return num_comments def send_close_mail(self): send_mail = get_settings("ticket.settings", "SEND_MAIL") if send_mail: TicketCloseMail(self).send() def get_address(self): return self.address or self.project.address or self.project.organization.get_first_address() class Meta: verbose_name = _("Ticket") verbose_name_plural = _("Tickets") permissions = ( ("can_view_tickets" , "Can view tickets"), ("can_reopen_tickets" , "Can reopen tickets"), ("list_all_tickets" , "Can list all tickets"), ("list_own_contact_tickets" , "Can list own contact tickets"), ("list_own_organization_tickets" , "Can list own organization tickets"), ) def __unicode__(self): return unicode(self.title)
class TicketAssistanceManager(models.Manager): def get_query_set(self): qs = super(TicketAssistanceManager, self).get_query_set() user = get_current_user() if not user: return qs.none() if not user.is_authenticated(): return qs.none() if user.is_superuser or user.has_perm('ticket.list_all_ticketassistances'): return qs t = Ticket.objects.all() qs = qs.filter(ticket__in=t) return qs class TicketAssistance (DateModel, UserModel): ticket = models.ForeignKey ( Ticket, related_name = 'assistances', db_index = True, verbose_name = _("ticket"), ) worker = models.ForeignKey ( Contact, related_name = 'assistances', db_index = True, verbose_name = _("worker"), ) date_begin = models.DateTimeField ( db_index = True, blank = True, null = True, verbose_name = _("date begin"), ) date_end = models.DateTimeField ( db_index = True, blank = True, null = True, verbose_name = _("date end"), ) work_duration = models.IntegerField ( blank = True, null = True, verbose_name = _("work duration"), help_text = _("In Minutes") ) job_description = HTMLFieldTinyMCE ( blank = True, null = True, verbose_name = _("Job Description"), ) extra_duration = models.IntegerField ( verbose_name = _("extra duration"), help_text = _("In Minutes"), blank = True, null = True, ) travel_duration = models.IntegerField ( verbose_name = _("travel duration"), help_text = _("In Minutes"), blank = True, null = True, ) kilometers = models.IntegerField ( verbose_name = _("kilometers"), blank = True, null = True, ) overnight_stay = models.BooleanField ( verbose_name = _("overnight stay"), default = False ) carrying = models.CharField ( max_length = 3, choices = settings.TICKET_TICKETASSISTANCE_CARRYING, db_index = True, verbose_name = _("carrying"), blank = True, null = True, ) pay_exit = models.BooleanField ( db_index = True, verbose_name = _("pay exit"), ) procedure = models.BooleanField ( ## preventivo già trasformato in procedure default = False, db_index = True, verbose_name = _("Ordered"), ) #objects = TicketAssistanceManager() def label(self): return str(self) def get_real_duration(self): real_duration = timedelta(0) if self.date_begin and self.date_end: real_duration = self.date_end - self.date_begin return real_duration real_duration=property(get_real_duration) def get_calculated_duration(self): calc_duration = timedelta(0) if self.work_duration and self.extra_duration and self.travel_duration: calc_duration = timedelta(seconds=( self.work_duration + self.extra_duration + self.travel_duration)*60) return calc_duration calculated_duration=property(get_calculated_duration) def get_status(self): status = ugt("open") if self.date_begin and self.date_end: status = ugt("close") return status get_status=property(get_status) class Meta: verbose_name = _("assistance ticket") verbose_name_plural = _("assistances ticket") permissions = ( ("can_view_ticketassistances", "Can view ticket assistances"), ("list_all_ticketassistances", "Can list all ticket assistances"), ("list_own_ticketassistances", "Can list own ticket assistances"), ("can_modify_all_ticketassistances", "Can modify all ticket assistances"), ) def __unicode__(self): return u"%s --> %s" % (self.ticket, self.worker) class TicketAttachment(DateModel, UserModel, StatusModel): ticket = models.ForeignKey ( Ticket, db_index = True, related_name = "attachments", verbose_name = _("Ticket"), ) file = models.FileField ( upload_to = settings.TICKET_PATH_ATTACHMENT, verbose_name = _("File"), ) description = models.CharField( max_length = 255, verbose_name = _("Description"), blank = True, null = True ) def get_display_name(self): if self.description: return self.description else: return os.path.basename(self.file.name) class Plan (DateModel, UserModel): ticket = models.ForeignKey ( Ticket, related_name = 'plans', db_index = True, verbose_name = _("ticket"), ) workers = models.ManyToManyField ( Contact, null=True, blank=True, related_name = 'plans', db_index = True, verbose_name = _("workers"), ) date_begin = models.DateTimeField ( db_index = True, blank = True, null = True, verbose_name = _("date begin"), ) date_end = models.DateTimeField ( db_index = True, blank = True, null = True, verbose_name = _("date end"), ) description = HTMLFieldTinyMCE ( blank = True, null = True, verbose_name = _("Job Description"), ) status = models.IntegerField( choices=settings.TICKET_TICKET_STATUS_CODES, default=1, verbose_name=_('Status'), help_text=None, ) priority = models.ForeignKey ( Priority, blank = True, null = True, to_field = "priority", related_name = "plans", verbose_name = _("Priority"), ) address = models.ForeignKey ( OrganizationAddress, null = True, blank = True, verbose_name = _("Address"), ) class Meta: verbose_name = _("plan ticket") verbose_name_plural = _("plans ticket") permissions = ( ("can_list_plan", "Can list ticket plans"), ("can_list_all_plan", "Can list all ticket plans"), ("can_list_own_plan", "Can list own ticket plans"), ("can_modify_all_plan", "Can modify all ticket plans"), ) def save(self, *args, **kwargs): if settings.TICKET_SINGLE_PLAN: self.ticket.to_be_planned = False self.ticket.save() super(Plan, self).save() def delete(self, *args, **kwargs): if settings.TICKET_SINGLE_PLAN: self.ticket.to_be_planned = True self.ticket.save() super(Plan, self).delete() #label dell evento def label(self): return "<em>%s</em>" % str(self.ticket) + "<br>" + "<br>".join([str(w) for w in self.workers.all()]) #per il serialize def natural_key(self): return model_to_dict(self, fields=[field.name for field in instance._meta.fields], exclude=['icon']) def get_workers_string(self): return ",".join([tc.full_name for tc in self.workers.all()]) def as_json(self): return dict( id=self.id, triggerline=self.triggerline, deadline=self.deadline, title=self.title, status=self.status, priority=self.priority, project=self.project, organization=self.organization.id, address=self.address, assigned_to=self.assigned_to, assigned_by=self.assigned_by, ) def get_address(self): return self.address or self.ticket.get_address() def get_report(self, file_type='pdf'): """Return a report of this plan :arg file_type: ``pdf`` (default) or ``odt`` """ file_modello = get_template_abspath('admin/ticket/plan/plan.odt') output_file = tempfile.NamedTemporaryFile(suffix='.pdf', prefix='plan').name renderer = Renderer(file_modello, {'obj': self, 'ticket': self.ticket}, output_file, ooPort=8100, overwriteExisting=True) renderer.run() return output_file def get_priority(self): return self.priority or self.ticket.priority def total_hours_work(self): delta = self.date_begin - self.date_end total = delta * self.workers.count() return "%d:%d" % (total.seconds/3600, total.seconds%3600/60) # per app pod def get_date_begin(self): return self.date_begin.strftime("%d/%m/%Y %H:%M") def __unicode__(self): #TODO WORKERS FARE IL LOOP ? return u"%s --> %s" % (self.ticket, self.workers.all()) class TicketPerm(DateModel, UserModel): contact = models.ForeignKey( Contact, unique=True, related_name = "ticketperm", verbose_name = _("Contact"), ) project_manager = models.BooleanField( verbose_name = _("Contact is manager of all projects"), help_text = _("THis contact is managers of all projects"), default = False, ) project_staff = models.BooleanField( verbose_name = _("Contact is staff of all tickets"), help_text = _("Contact is staff of all tickets"), default = False, ) class Meta: verbose_name = _("ticket permission") verbose_name_plural = _("ticket permission") class Log(DateModel, UserModel): ticket = models.ForeignKey ( Ticket, related_name='logs', db_index=True, verbose_name=_("Ticket"), ) field = models.CharField( max_length=100, verbose_name=_("Field"), ) old_value = models.TextField( verbose_name=_("Old Value"), ) new_value = models.TextField( verbose_name=_("New Value"), ) class Meta: verbose_name = _("ticket log") verbose_name_plural = _("ticket logs") def __unicode__(self): return u"%s --> modificato %s" % (self.ticket, self.field) def get_ticket_staff(q=None, json=False): """ Return a set of all contacts in staff of all tickets in q :arg q: a QuerySet of tickets as returned by an advanced_search of ticket changelist :arg json: boolean. If True return a json serialized result. Default False. """ if not q: if json: return serializers.serialize('json', []) else: return [] ## Fixme: non dovrebbe essere reversed in generale contacts = list(reversed(Contact.owners.all().order_by('role__ordering', 'last_name'))) # Probabilmente non particolarmente efficente, remotamente usa IN # select_related non funziona con many2many q = q.filter(project__isnull=False).prefetch_related('project__staff') for ticket in q.all(): for contact in ticket.project.staff.all(): if not contact in contacts: contacts += [contact] if json: return serializers.serialize('json', contacts, extras=('get_full_name',)) else: return contacts