# 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