def inject_summary_fields(sender, **kw): SiteSummary = rt.models.working.SiteSummary UserSummary = rt.models.working.UserSummary WorkSite = rt.models.tickets.Site Ticket = dd.plugins.working.ticket_model for t in ReportingTypes.get_list_items(): k = t.name + '_hours' dd.inject_field(SiteSummary, k, dd.DurationField(t.text, null=True, blank=True)) dd.inject_field(UserSummary, k, dd.DurationField(t.text, null=True, blank=True)) dd.inject_field(Ticket, k, dd.DurationField(t.text, null=True, blank=True)) def make_getter(t): k = t.name + '_hours' def getter(obj, ar): qs = SiteSummary.objects.filter(master=obj, year__isnull=True) d = qs.aggregate(**{k: models.Sum(k)}) n = d[k] return n return getter dd.inject_field( WorkSite, k, dd.VirtualField(dd.DurationField(t.text), make_getter(t))) if False: # removed 20181211 because useless for ts in TicketStates.get_list_items(): k = ts.get_summary_field() if k is not None: dd.inject_field(SiteSummary, k, models.IntegerField(ts.text)) def make_getter(ts): k = ts.get_summary_field() def getter(obj, ar): if ar is None: return '' qs = SiteSummary.objects.filter(master=obj) d = qs.aggregate(**{k: models.Sum(k)}) n = d[k] if n == 0: return '' sar = rt.models.tickets.TicketsBySite.request( obj, param_values=dict(state=ts, show_active=None)) # n = sar.get_total_count() url = ar.renderer.request_handler(sar) if url is None: return str(n) return E.a(str(n), href='javascript:' + url) return getter dd.inject_field( WorkSite, k, dd.VirtualField(dd.DisplayField(ts.text), make_getter(ts)))
class InvestedTime(dd.Table): @dd.virtualfield(dd.DurationField(_("Time"))) def invested_time(cls, obj, ar): return obj._invested_time @dd.displayfield(_("Description")) def my_description(cls, obj, ar): mi = ar.master_instance if mi is None: return lst = [obj.summary] tpl = u"{0}: {1}" # if obj.site is not None and obj.site == mi.interesting_for: # lst.append(_("site-specific")) if obj.site is not None: # and obj.site != mi.interesting_for: lst.append(tpl.format(ensureUtf(_("Site")), ensureUtf(obj.site))) if obj.user is not None: lst.append(tpl.format(ensureUtf(_("Author")), ensureUtf(obj.user))) # if obj.project is not None: # lst.append(tpl.format( # ensureUtf(_("Project")), ensureUtf(obj.project))) # if obj.topic is not None: # lst.append(tpl.format( # ensureUtf(_("Topic")), ensureUtf(obj.topic))) return E.p(*join_elems(lst, '. '))
class TimeInvestment(Commentable): class Meta: abstract = True closed = models.BooleanField(_("Closed"), default=False) planned_time = dd.DurationField(_("Planned time"), blank=True, null=True)
class EventType(mixins.BabelNamed, Referrable, mixins.Sequenced, MailableType): templates_group = 'cal/Event' ref_max_length = 4 class Meta: app_label = 'cal' abstract = dd.is_abstract_model(__name__, 'EventType') verbose_name = _("Calendar entry type") verbose_name_plural = _("Calendar entry types") ordering = ['seqno'] description = dd.RichTextField(_("Description"), blank=True, format='html') is_appointment = models.BooleanField(_("Appointment"), default=True) all_rooms = models.BooleanField(_("Locks all rooms"), default=False) locks_user = models.BooleanField(_("Locks the user"), default=False) force_guest_states = models.BooleanField(_("Automatic presences"), default=False) fill_presences = models.BooleanField(_("Fill guests"), default=False) start_date = models.DateField(verbose_name=_("Start date"), blank=True, null=True) event_label = dd.BabelCharField(_("Entry label"), max_length=200, blank=True) # , default=_("Calendar entry")) # default values for a Babelfield don't work as expected max_conflicting = models.PositiveIntegerField(_("Simultaneous entries"), default=1) max_days = models.PositiveIntegerField(_("Maximum days"), default=1) transparent = models.BooleanField(_("Transparent"), default=False) planner_column = PlannerColumns.field(blank=True) # default_duration = models.TimeField( # _("Default duration"), blank=True, null=True) default_duration = dd.DurationField(_("Default duration"), blank=True, null=True)
def w(rpttype, verbose_name): def func(fld, obj, ar): return obj._root2tot.get(rpttype, None) return dd.VirtualField(dd.DurationField(verbose_name), func)
def rpttype2vf(func, rpttype, verbose_name): return dd.VirtualField(dd.DurationField(verbose_name), func)
class Site(Referrable, ContactRelated, Starrable, DateRange): class Meta: app_label = 'tickets' verbose_name = pgettext("Ticketing", "Site") verbose_name_plural = pgettext("Ticketing", "Sites") abstract = dd.is_abstract_model(__name__, 'Site') ref_max_length = 20 workflow_state_field = "state" # partner = dd.ForeignKey('contacts.Partner', blank=True, null=True) # # responsible_user = dd.ForeignKey( # # 'users.User', verbose_name=_("Responsible"), # # blank=True, null=True) # name = models.CharField(_("Designation"), max_length=200) child_starrables = [(milestone_model, 'site', None), ('tickets.Ticket', 'site', None)] description = dd.RichTextField(_("Description"), blank=True) remark = models.CharField(_("Remark"), max_length=200, blank=True) name = models.CharField(_("Designation"), max_length=200, unique=True) reporting_type = ReportingTypes.field(blank=True) deadline = models.DateField(verbose_name=_("Deadline"), blank=True, null=True) state = SiteStates.field(default='draft') group = dd.ForeignKey('groups.Group', null=True, blank=True) hours_paid = dd.DurationField(_("Hours paid"), blank=True, null=True) private = models.BooleanField(_("Private"), default=False) def __str__(self): return self.ref or self.name def get_change_observers(self, ar=None): for s in rt.models.tickets.Subscription.objects.filter(site=self): yield (s.user, s.user.mail_mode) def get_row_permission(self, ar, state, ba): """ User has permission if: is tickets staff if a member of it's group is subscribed is contact person is contact person of org Currently only checks for is group member and role... """ if not super(Site, self).get_row_permission(ar, state, ba): return False if ((self.group and self.group.members.filter(user__id=ar.get_user().id).count() == 0) and not ar.get_user().user_type.has_required_roles( [TicketsStaff])): return False return True @classmethod def get_user_queryset(cls, user): qs = super(Site, cls).get_user_queryset(user) if user.is_anonymous: qs = qs.filter(private=False) elif not user.user_type.has_required_roles([TicketsStaff]): qs = qs.filter(Q(private=False) | Q(group__members__user=user)).distinct() return qs @classmethod def add_param_filter(cls, qs, lookup_prefix='', show_exposed=None, **kwargs): qs = super(Site, cls).add_param_filter(qs, **kwargs) exposed_states = SiteStates.filter(is_exposed=True) fkw = dict() fkw[lookup_prefix + 'state__in'] = exposed_states if show_exposed == dd.YesNo.no: qs = qs.exclude(**fkw) elif show_exposed == dd.YesNo.yes: qs = qs.filter(**fkw) return qs @dd.htmlbox(_("Description")) def parsed_description(self, ar): if ar is None: return '' html = '' if self.description: html += ar.parse_memo(self.description) return html def get_overview_elems(self, ar): elems = [] if self.ref: txt = "{} {}".format(self.ref, self.name) else: txt = self.name elems.append(E.h2(txt)) if self.description: elems += rich_text_to_elems(ar, self.description) return elems
class Session(UserAuthored, Started, Ended, Workable): class Meta: app_label = 'working' verbose_name = _("Session") verbose_name_plural = _('Sessions') abstract = dd.is_abstract_model(__name__, 'Session') ticket = dd.ForeignKey(dd.plugins.working.ticket_model, related_name="sessions_by_ticket") session_type = dd.ForeignKey('working.SessionType', null=True, blank=True) summary = models.CharField(_("Summary"), max_length=200, blank=True, help_text=_("Summary of the session.")) description = dd.RichTextField(_("Description"), blank=True) # break_time = models.TimeField( # blank=True, null=True, # verbose_name=_("Break Time")) break_time = dd.DurationField(_("Break Time"), blank=True, null=True) faculty = dd.ForeignKey('skills.Skill', related_name="sessions_by_faculty", blank=True, null=True) reporting_type = ReportingTypes.field(blank=True) is_fixing = models.BooleanField(_("Fixing"), default=False) if settings.USE_TZ: time_zone = TimeZones.field() else: time_zone = dd.DummyField() end_session = EndThisSession() show_today = ShowMySessionsByDay('start_date') # print_activity_report = PrintActivityReport() def __str__(self): if self.start_time and self.end_time: return u"%s %s-%s" % ( self.start_date.strftime(settings.SITE.date_format_strftime), self.start_time.strftime(settings.SITE.time_format_strftime), self.end_time.strftime(settings.SITE.time_format_strftime)) return "%s # %s" % (self._meta.verbose_name, self.pk) def get_ticket(self): return self.ticket def on_create(self, ar): super(Session, self).on_create(ar) if settings.USE_TZ: self.time_zone = self.user.time_zone or \ rt.models.about.TimeZones.default def get_time_zone(self): return self.time_zone def full_clean(self, *args, **kwargs): if self.user_id and not self.time_zone: # can be removed when all production sites have migrated: self.time_zone = self.user.time_zone or \ rt.models.about.TimeZones.default if not settings.SITE.loading_from_dump: if self.start_time is None: self.set_datetime('start', timezone.now()) # value = timezone.now() # if pytz: # tz = pytz.timezone(self.get_timezone()) # value = value.astimezone(tz) # self.start_time = value.time() if self.start_date is None: self.start_date = dd.today() # if self.ticket_id is not None and self.faculty_id is None: # self.faculty = self.ticket.faculty if self.end_time is not None: if self.end_date is None: self.end_date = self.start_date if self.ticket_id: self.ticket.on_worked(self) super(Session, self).full_clean(*args, **kwargs) def unused_save(self, *args, **kwargs): if not settings.SITE.loading_from_dump: if self.start_date is None: self.start_date = dd.today() if self.start_time is None: self.start_time = timezone.now().time() super(Session, self).save(*args, **kwargs) def get_reporting_type(self): if self.reporting_type: return self.reporting_type t = self.get_ticket() if t.ticket_type and t.ticket_type.reporting_type: return t.ticket_type.reporting_type if t.site and t.site.reporting_type: return t.site.reporting_type # if t.project and t.project.reporting_type: # return t.project.reporting_type return dd.plugins.working.default_reporting_type # def after_ui_save(self, ar, cw): # super(Session, self).after_ui_save(ar, cw) # if self.ticket_id: # self.ticket.on_worked(self, ar, cw) def get_root_project(self): """Return the root project for this session (or None if session has no ticket). """ if self.ticket and self.ticket.project: return self.ticket.project.get_parental_line()[0] def get_duration(self): """Return the duration in hours as a :class:`lino.utils.quantities.Quantity`. This inherits from :meth:`StartedEnded <lino_xl.lib.cal.mixins.StartedEnded.get_duration>` but removes :attr:`break_time` if specified. """ diff = super(Session, self).get_duration() if diff and self.break_time: diff -= self.break_time return diff # if self.end_time is None: # diff = datetime.timedelta() # else: # diff = self.get_datetime('end') - self.get_datetime('start') # if self.break_time is not None: # diff -= self.break_time # return Duration(diff) @dd.displayfield(_("Ticket #")) def ticket_no(self, ar): if ar is None: return self.ticket_id return self.ticket.obj2href(ar) # self.ticket_id) @dd.displayfield(_("Site")) def site_ref(self, ar): if not self.ticket: return '' site = self.ticket.site if site is None: return '' if ar is None: return str(site) return site.obj2href(ar)