class Contact(dd.Model): class Meta: #~ abstract = True verbose_name = _("Contact") verbose_name_plural = _("Contacts") person = models.ForeignKey(Person, null=True, blank=True) company = models.ForeignKey(Company, null=True, blank=True) role = models.ForeignKey(Role, null=True, blank=True) language = dd.LanguageField() #~ address = models.ForeignKey(Address,null=True,blank=True) #~ address_type = models.ForeignKey(AddressType,blank=True,null=True) email = models.EmailField(_('E-Mail'), blank=True) # ,null=True) url = models.URLField(_('URL'), blank=True) phone = models.CharField(_('Phone'), max_length=200, blank=True) gsm = models.CharField(_('GSM'), max_length=200, blank=True) fax = models.CharField(_('Fax'), max_length=200, blank=True) def send_mail(self, subject, message, from_email=None): "Sends an e-mail to this User." from django.core.mail import send_mail send_mail(subject, message, from_email, [self.email])
class Entry(UserAuthored, Controllable, CombinedDateTime): """A blog entry is a short article with a title, published on a given date and time by a given user. """ class Meta: app_label = 'blogs' verbose_name = _("Blog Entry") verbose_name_plural = _("Blog Entries") title = models.CharField(_("Heading"), max_length=200, blank=True) body = dd.RichTextField(_("Body"), blank=True, format='html') pub_date = models.DateField(_("Publication date"), blank=True, null=True) pub_time = models.TimeField(_("Publication time"), blank=True, null=True) entry_type = dd.ForeignKey('blogs.EntryType', blank=True, null=True) language = dd.LanguageField() def __str__(self): return u'%s #%s' % (self._meta.verbose_name, self.pk) def on_create(self, ar): """ Sets the :attr:`pub_date` and :attr:`pub_time` to now. """ if not settings.SITE.loading_from_dump: self.set_datetime('pub', timezone.now()) self.language = ar.get_user().language super(Entry, self).on_create(ar)
class SalesDocument(VatDocument, Certifiable): """Common base class for `orders.Order` and :class:`VatProductInvoice`. Subclasses must either add themselves a `date` field (as does Order) or inherit it from Voucher (as does VatProductInvoice) """ auto_compute_totals = True print_items_table = None """Which table (column layout) to use in the printed document. :class:`ItemsByInvoicePrint` :class:`ItemsByInvoicePrintNoQtyColumn` """ class Meta: abstract = True language = dd.LanguageField() subject = models.CharField(_("Subject line"), max_length=200, blank=True) intro = models.TextField("Introductive Text", blank=True) paper_type = dd.ForeignKey('sales.PaperType', null=True, blank=True) # channel = Channels.field(default=Channels.paper.as_callable()) def get_printable_type(self): return self.journal def get_print_language(self): return self.language or self.partner.language or \ dd.get_default_language() def get_trade_type(self): return TradeTypes.sales def add_voucher_item(self, product=None, qty=None, **kw): Product = rt.modules.products.Product if product is not None: if not isinstance(product, Product): product = Product.objects.get(pk=product) # if qty is None: # qty = Duration(1) kw['product'] = product kw['qty'] = qty return super(SalesDocument, self).add_voucher_item(**kw) def get_excerpt_templates(self, bm): """Overrides :meth:`lino_xl.lib.excerpts.mixins.Certifiable.get_excerpt_templates`. """ pt = self.paper_type or self.partner.paper_type if pt and pt.template: return [pt.template]
class Note(TypedPrintable, UserAuthored, Controllable, ContactRelated, mixins.ProjectRelated, Mailable): """A **note** is a dated and timed document written by its author (a user). For example a report of a meeting or a phone call, or just some observation. Notes are usually meant for internal use. .. attribute:: date .. attribute:: time .. attribute:: type .. attribute:: event_type .. attribute:: subject .. attribute:: body .. attribute:: language """ manager_roles_required = dd.login_required(OfficeStaff) class Meta: app_label = 'notes' abstract = dd.is_abstract_model(__name__, 'Note') verbose_name = _("Note") verbose_name_plural = _("Notes") date = models.DateField(verbose_name=_('Date'), default=dd.today) time = models.TimeField(blank=True, null=True, verbose_name=_("Time"), default=timezone.now) type = dd.ForeignKey('notes.NoteType', blank=True, null=True, verbose_name=_('Note Type (Content)')) event_type = dd.ForeignKey('notes.EventType', blank=True, null=True, verbose_name=_('Event Type (Form)')) subject = models.CharField(_("Subject"), max_length=200, blank=True) body = dd.RichTextField(_("Body"), blank=True, format='html') language = dd.LanguageField() def __str__(self): return u'%s #%s' % (self._meta.verbose_name, self.pk) def summary_row(self, ar, **kw): #~ s = super(Note,self).summary_row(ui,rr) s = super(Note, self).summary_row(ar) #~ s = contacts.ContactDocument.summary_row(self,ui,rr) if self.subject: s += [' ', self.subject] return s def get_mailable_type(self): return self.type def get_print_language(self): return self.language
class Entry(mixins.TypedPrintable, mixins.CreatedModified, UserAuthored, Controllable): """ Deserves more documentation. """ class Meta: verbose_name = _("Blog Entry") verbose_name_plural = _("Blog Entries") language = dd.LanguageField() type = models.ForeignKey(EntryType, blank=True, null=True) # ,null=True) title = models.CharField(_("Heading"), max_length=200, blank=True) body = dd.RichTextField(_("Body"), blank=True, format='html') def __unicode__(self): return u'%s #%s' % (self._meta.verbose_name, self.pk)
class SalesDocument(VatVoucher, Certifiable): class Meta: abstract = True edit_totals = False print_items_table = None language = dd.LanguageField() subject = models.CharField(_("Subject line"), max_length=200, blank=True) intro = models.TextField("Introductive Text", blank=True) paper_type = dd.ForeignKey('sales.PaperType', null=True, blank=True) # channel = Channels.field(default=Channels.as_callable('paper')) def get_printable_type(self): return self.journal def get_print_language(self): return self.language or self.partner.language or \ dd.get_default_language() def get_trade_type(self): return TradeTypes.sales def add_voucher_item(self, product=None, qty=None, **kw): Product = rt.models.products.Product if product is not None: if not isinstance(product, Product): product = Product.objects.get(pk=product) # if qty is None: # qty = Duration(1) kw['product'] = product kw['qty'] = qty return super(SalesDocument, self).add_voucher_item(**kw) def get_excerpt_templates(self, bm): # Overrides lino_xl.lib.excerpts.mixins.Certifiable.get_excerpt_templates pt = self.paper_type or get_paper_type(self.partner) if pt and pt.template: # print(20190506, pt.template) return [pt.template]
class Entry(UserAuthored, Controllable, CombinedDateTime, Previewable, Publishable): """A blog entry is a short article with a title, published on a given date and time by a given user. """ class Meta: app_label = 'blogs' verbose_name = _("Blog Entry") verbose_name_plural = _("Blog Entries") title = models.CharField(_("Heading"), max_length=200, blank=True) pub_date = models.DateField( _("Publication date"), blank=True, null=True) pub_time = dd.TimeField( _("Publication time"), blank=True, null=True) entry_type = dd.ForeignKey('blogs.EntryType', blank=True, null=True) language = dd.LanguageField() def __str__(self): if self.pub_date: return _("{} by {}").format(self.pub_date, self.user) return u'%s #%s' % (self._meta.verbose_name, self.pk) def on_create(self, ar): """ Sets the :attr:`pub_date` and :attr:`pub_time` to now. """ if not settings.SITE.loading_from_dump: self.set_datetime('pub', timezone.now()) self.language = ar.get_user().language super(Entry, self).on_create(ar) add_interest = AddInterestField() @classmethod def get_dashboard_items(cls, user): qs = cls.objects.filter(Q(pub_date__isnull=False)).order_by("-pub_date") return qs[:5]
class User(CreatedModified): """Represents a user of this site. .. attribute:: username The primary key. .. attribute:: profile The profile of a user is what defines her or his permissions. Users with an empty `profile` field are considered inactive and cannot log in. .. attribute:: partner Pointer to the :class:`lino.modlib.contacts.models.Partner` instance related to this user. This is a DummyField when :mod:`lino.modlib.contacts` is not installed. """ class Meta: verbose_name = _('User') verbose_name_plural = _('Users') abstract = dd.is_abstract_model(__name__, 'User') ordering = ['last_name', 'first_name', 'username'] preferred_foreignkey_width = 15 hidden_columns = 'password remarks' authenticated = True """This is always `True`. See also :attr:`lino.modlib.users.utils.AnonymousUser.authenticated`. """ username = models.CharField(_('Username'), max_length=30, unique=True, help_text=_("""Required. Must be unique.""")) password = models.CharField(_('Password'), max_length=128) profile = UserProfiles.field( blank=True, help_text=_("Users with an empty `profile` field are considered " "inactive and cannot log in.")) initials = models.CharField(_('Initials'), max_length=10, blank=True) first_name = models.CharField(_('First name'), max_length=30, blank=True) last_name = models.CharField(_('Last name'), max_length=30, blank=True) email = models.EmailField(_('e-mail address'), blank=True) remarks = models.TextField(_("Remarks"), blank=True) # ,null=True) language = dd.LanguageField(default=models.NOT_PROVIDED, blank=True) if dd.is_installed('contacts'): partner = models.ForeignKey('contacts.Partner', blank=True, null=True, on_delete=models.PROTECT) else: partner = dd.DummyField() def __unicode__(self): return self.get_full_name() def get_full_name(self): "Returns the first_name plus the last_name, with a space in between." if not self.first_name and not self.last_name: return self.username return u'%s %s' % (self.first_name.strip(), self.last_name.strip()) @dd.displayfield(_("Name"), max_length=15) def name_column(self, request): #~ return join_words(self.last_name.upper(),self.first_name) return unicode(self) if dd.is_installed('contacts'): def get_person(self): if self.partner: return self.partner.get_mti_child('person') else: def get_person(self): return None person = property(get_person) def get_row_permission(self, ar, state, ba): """ Only system managers may edit other users. See also :meth:`User.disabled_fields`. """ #~ print 20120621, self, user, state, action if not ba.action.readonly: user = ar.get_user() if user != self: if not isinstance(user.profile.role, dd.SiteAdmin): return False return super(User, self).get_row_permission(ar, state, ba) #~ return False def disabled_fields(self, ar): """ Only System admins may change the `profile` of users. See also :meth:`Users.get_row_permission`. """ rv = super(User, self).disabled_fields(ar) if not isinstance(ar.get_user().profile.role, dd.SiteAdmin): rv.add('profile') return rv def full_clean(self, *args, **kw): p = self.person if p is not None: for n in ('first_name', 'last_name', 'email', 'language'): if not getattr(self, n): setattr(self, n, getattr(p, n)) #~ self.language = p.language if not self.language: #~ self.language = settings.SITE.DEFAULT_LANGUAGE.django_code self.language = settings.SITE.get_default_language() if not self.password: self.set_unusable_password() if not self.initials: if self.first_name and self.last_name: self.initials = self.first_name[0] + self.last_name[0] super(User, self).full_clean(*args, **kw) def get_received_mandates(self): #~ return [ [u.id,_("as %s")%u] for u in self.__class__.objects.all()] return [[u.id, unicode(u)] for u in self.__class__.objects.all()] #~ return self.__class__.objects.all() change_password = ChangePassword() # the following methods are unchanged copies from Django's User # model def set_password(self, raw_password): self.password = make_password(raw_password) def check_password(self, raw_password): """ Returns a boolean of whether the raw_password was correct. Handles hashing formats behind the scenes. """ def setter(raw_password): self.set_password(raw_password) self.save() return check_password(raw_password, self.password, setter) def set_unusable_password(self): # Sets a value that will never be a valid hash self.password = make_password(None) def has_usable_password(self): return is_password_usable(self.password) def as_list_item(self, ar): return E.li(E.strong(self.username), ' : ', unicode(self), ', ', unicode(self.profile), ', ', E.strong(settings.SITE.LANGUAGE_DICT.get(self.language))) @classmethod def get_by_username(cls, username, default=models.NOT_PROVIDED): """ `User.get_by_username(x)` is equivalent to `User.objects.get(username=x)` except that the text of the DoesNotExist exception is more useful. """ try: return cls.objects.get(username=username) except cls.DoesNotExist: if default is models.NOT_PROVIDED: raise cls.DoesNotExist( "No %s with username %r" % (unicode(cls._meta.verbose_name), username)) return default
class Excerpt(TypedPrintable, UserAuthored, Controllable, mixins.ProjectRelated, ContactRelated, Mailable, Postable): """A printable document that describes some aspect of the current situation. .. attribute:: excerpt_type The type of this excerpt (ForeignKey to :class:`ExcerptType`). .. attribute:: owner The object being printed by this excerpt. See :attr:`Controllable.owner <lino.modlib.gfks.mixins.Controllable.owner>`. .. attribute:: company The optional company of the :attr:`recipient` of this excerpt. See :attr:`ContactRelated.company <lino_xl.lib.contacts.mixins.ContactRelated.company>`. .. attribute:: contact_person The optional contact person of the :attr:`recipient` of this excerpt. See :attr:`ContactRelated.contact_person <lino_xl.lib.contacts.mixins.ContactRelated.contact_person>`. .. attribute:: recipient The recipient of this excerpt. See :attr:`ContactRelated.recipient <lino_xl.lib.contacts.mixins.ContactRelated.recipient>` .. attribute:: language The language used for printing this excerpt. .. attribute:: date .. attribute:: time .. method:: get_address_html See :meth:`lino_xl.lib.contacts.mixins.ContactRelated.get_address_html`. Return the address of the :attr:`recipient` of this excerpt. """ manager_roles_required = dd.login_required(OfficeStaff) # manager_level_field = 'office_level' allow_cascaded_delete = "owner" class Meta: app_label = 'excerpts' abstract = dd.is_abstract_model(__name__, 'Excerpt') verbose_name = _("Excerpt") verbose_name_plural = _("Excerpts") excerpt_type = dd.ForeignKey('excerpts.ExcerptType') body_template_content = BodyTemplateContentField(_("Body template")) language = dd.LanguageField() # if dd.is_installed('outbox'): # mails_by_owner = dd.ShowSlaveTable('outbox.MailsByController') def get_body_template(self): """Return the body template to use for this excerpt.""" owner = self.owner # owner is None e.g. if is a broken GFK if owner is not None: assert self.__class__ is not owner.__class__ tplname = owner.get_body_template() if tplname: return tplname return self.excerpt_type.body_template def get_body_template_filename(self): tplname = self.get_body_template() if not tplname: return None mc = self.excerpt_type.content_type.model_class() tplgroup = mc.get_template_group() return rt.find_config_file(tplname, tplgroup) def get_body_template_name(self): tplname = self.get_body_template() if not tplname: return None mc = self.excerpt_type.content_type.model_class() tplgroup = mc.get_template_group() return tplgroup + '/' + tplname def disabled_fields(self, ar): rv = super(Excerpt, self).disabled_fields(ar) rv = rv | set(['excerpt_type', 'project']) if self.build_time: rv |= self.PRINTABLE_FIELDS return rv def __str__(self): if self.build_time: return naturaltime(self.build_time) # return _("%(owner)s (printed %(time)s)") % dict( # owner=self.owner, time=naturaltime(self.build_time)) return _("Unprinted %s #%s") % (self._meta.verbose_name, self.pk) def get_mailable_type(self): return self.excerpt_type def get_mailable_subject(self): return six.text_type(self.owner) # .get_mailable_subject() def get_template_groups(self): ptype = self.get_printable_type() if ptype is None: raise Exception("20140520 Must have excerpt_type.") grp = ptype.content_type.model_class().get_template_group() return [grp, 'excerpts'] def filename_root(self): # mainly because otherwise we would need to move files around on # existing sites et = self.excerpt_type if et is None or not et.certifying: return super(Excerpt, self).filename_root() assert et.certifying o = self.owner if o is None: return super(Excerpt, self).filename_root() name = o._meta.app_label + '.' + o.__class__.__name__ if not et.primary: name += '.' + str(et.pk) name += '-' + str(o.pk) return name def get_print_templates(self, bm, action): """Overrides :meth:`lino.modlib.printing.mixins.Printable.get_print_templates`. When printing an excerpt, the controlling database objects gets a chance to decide which template to use. """ et = self.excerpt_type if et is not None and et.certifying: if isinstance(self.owner, Certifiable): tpls = self.owner.get_excerpt_templates(bm) if tpls is not None: return tpls return super(Excerpt, self).get_print_templates(bm, action) # ptype = self.get_printable_type() # # raise Exception("20150710 %s" % self.owner) # if ptype is not None and ptype.template: # return [ptype.template] # # return [bm.get_default_template(self)] # return [dd.plugins.excerpts.get_default_template(bm, self.owner)] # def get_recipient(self): # rec = super(Excerpt, self).get_recipient() # if rec is None and hasattr(self.owner, 'recipient'): # return self.owner.recipient # return rec # recipient = property(get_recipient) def get_printable_type(self): return self.excerpt_type def get_print_language(self): return self.language def unused_on_create(self, ar): # replaced by signal below super(Excerpt, self).on_create(ar) if not self.owner_id: if self.project: self.owner = self.project self.language = self.owner.get_print_language() @dd.chooser() def excerpt_type_choices(cls, owner): # logger.info("20150702 %s", owner) qs = rt.modules.excerpts.ExcerptType.objects.order_by('name') if owner is None: # e.g. when choosing on the *parameter* field # return qs.filter(content_type__isnull=True) return qs.filter() ct = ContentType.objects.get_for_model(owner.__class__) return qs.filter(content_type=ct) @property def date(self): "Used in templates" if self.build_time: return self.build_time.date() return dd.today() @property def time(self): "Used in templates" if self.build_time: return self.build_time.time() return timezone.now() @dd.virtualfield(dd.HtmlBox(_("Preview"))) def preview(self, ar): if ar is None: return '' with translation.override(self.get_print_language()): ctx = self.get_printable_context(ar) return ar.html_text(ctx['body']) # return '<div class="htmlText">%s</div>' % ctx['body'] def get_printable_context(self, ar=None, **kw): """Adds a series of names to the context used when rendering printable documents. Extends :meth:`lino.core.model.Model.get_printable_context`. """ if self.owner is not None: kw = self.owner.get_printable_context(ar, **kw) kw = super(Excerpt, self).get_printable_context(**kw) kw.update(obj=self.owner) body = '' if self.excerpt_type_id is not None: etype = self.excerpt_type if etype.backward_compat: kw.update(this=self.owner) tplname = self.get_body_template_name() if tplname and ar is not None: body = settings.SITE.plugins.jinja.render_jinja( ar, tplname, kw) # env = settings.SITE.plugins.jinja.renderer.jinja_env # template = env.get_template(tplname) # # logger.info("body template %s (%s)", tplname, template) # body = ar.render_jinja(template, **kw) # # logger.info("20160311 body template %s (%s) -> %s", # # tplname, template, body) kw.update(body=body) return kw @classmethod def on_analyze(cls, site): cls.PRINTABLE_FIELDS = dd.fields_list( cls, "project excerpt_type " "body_template_content " "company contact_person language " "user build_method") super(Excerpt, cls).on_analyze(site)
class Partner(mixins.Polymorphic, AddressLocation, Addressable): """ A Partner is any physical or moral person for which you want to keep contact data (address, phone numbers, ...). A :class:`Partner` can act as the recipient of a sales invoice, as the sender of an incoming purchases invoice, ... A Partner has at least a name and usually also an "official" address. Predefined subclasses of Partners are :class:`Person` for physical persons and :class:`Company` for companies, organisations and any kind of non-formal Partners. .. attribute:: name The full name of this partner. Used for alphabetic sorting. Subclasses may fill this field automatically, e.g. saving a :class:`Person` will automatically set her `name` field to "last_name, first_name". .. attribute:: email The primary email address. """ preferred_foreignkey_width = 20 # preferred width for ForeignKey fields to a Partner class Meta: app_label = 'contacts' # avoid RemovedInDjango19Warning abstract = dd.is_abstract_model(__name__, 'Partner') verbose_name = _("Partner") verbose_name_plural = _("Partners") name = models.CharField(max_length=200, verbose_name=_('Name')) language = dd.LanguageField() email = models.EmailField(_('E-Mail'), blank=True) # ,null=True) url = models.URLField(_('URL'), blank=True) phone = models.CharField(_('Phone'), max_length=200, blank=True) gsm = models.CharField(_('GSM'), max_length=200, blank=True) fax = models.CharField(_('Fax'), max_length=200, blank=True) remarks = models.TextField(_("Remarks"), blank=True) # ,null=True) print_labels = dd.PrintLabelsAction() def on_create(self, ar): self.language = ar.get_user().language if not self.country: sc = settings.SITE.site_config if sc.site_company: self.country = sc.site_company.country super(Partner, self).on_create(ar) def save(self, *args, **kw): if self.id is None: sc = settings.SITE.site_config if sc.next_partner_id is not None: try: self.__class__.objects.get(id=sc.next_partner_id) raise ValidationError( "Cannot create partner with id={0}. " "Check your next_partner_id in SiteConfig!".format( sc.next_partner_id)) except self.__class__.DoesNotExist: self.id = sc.next_partner_id sc.next_partner_id += 1 sc.save() #~ logger.info("20120327 Partner.save(%s,%s)",args,kw) super(Partner, self).save(*args, **kw) def __unicode__(self): #~ return self.name return self.get_full_name() def address_person_lines(self): #~ yield self.name yield self.get_full_name() def get_full_name(self, *args, **kw): """\ Returns a one-line string representing this Partner. The default returns simply the `name` field, ignoring any parameters, but e.g. :class:`Human` overrides this. """ return self.name full_name = property(get_full_name) @dd.displayfield(_("Name")) def name_column(self, request): #~ return join_words(self.last_name.upper(),self.first_name) return unicode(self) def get_partner_instance(self): return self # compatibility with lino.modlib.partners @dd.displayfield() def overview(self, ar): return E.div(*self.get_overview_elems(ar)) def get_overview_elems(self, ar): elems = [] if ar is None: return elems buttons = self.get_mti_buttons(ar) # buttons = join_elems(buttons, ', ') elems.append( E.p(unicode(_("See as ")), *buttons, style="font-size:8px;text-align:right;padding:3pt;")) elems += self.get_name_elems(ar) elems.append(E.br()) elems += join_elems(list(self.address_location_lines()), sep=E.br) elems = [ E.div(*elems, style="font-size:18px;font-weigth:bold;" "vertical-align:bottom;text-align:middle") ] return elems def get_name_elems(self, ar): return [E.b(self.name)] def get_print_language(self): return self.language
#~ def setup_handle(self,lh): #~ lh.config.label = _("Site Parameters") #~ lh.about.label = _("About") class SiteConfigs(dd.Table): """ The table used to present the :class:`SiteConfig` row in a Detail form. See also :meth:`lino.Lino.get_site_config`. Deserves more documentation. """ model = 'system.SiteConfig' required_roles = dd.required(SiteStaff) default_action = actions.ShowDetailAction() #~ has_navigator = False hide_top_toolbar = True #~ can_delete = perms.never detail_layout = """ default_build_method # lino.ModelsBySite """ do_build = BuildSiteCache() if settings.SITE.user_model == 'auth.User': dd.inject_field(settings.SITE.user_model, 'profile', UserProfiles.field()) dd.inject_field(settings.SITE.user_model, 'language', dd.LanguageField())
class Excerpt(TypedPrintable, UserAuthored, Controllable, mixins.ProjectRelated, ContactRelated, Mailable, Postable): manager_roles_required = dd.login_required(OfficeStaff) # manager_level_field = 'office_level' allow_cascaded_delete = "owner" class Meta: app_label = 'excerpts' abstract = dd.is_abstract_model(__name__, 'Excerpt') verbose_name = _("Excerpt") verbose_name_plural = _("Excerpts") excerpt_type = dd.ForeignKey('excerpts.ExcerptType') body_template_content = BodyTemplateContentField(_("Body template")) language = dd.LanguageField() # if dd.is_installed('outbox'): # mails_by_owner = dd.ShowSlaveTable('outbox.MailsByController') def get_body_template(self): """Return the body template to use for this excerpt.""" owner = self.owner # owner is None e.g. if is a broken GFK if owner is not None: assert self.__class__ is not owner.__class__ tplname = owner.get_body_template() if tplname: return tplname return self.excerpt_type.body_template def get_body_template_filename(self): tplname = self.get_body_template() if not tplname: return None mc = self.excerpt_type.content_type.model_class() tplgroup = mc.get_template_group() return rt.find_config_file(tplname, tplgroup) def get_body_template_name(self): tplname = self.get_body_template() if not tplname: return None mc = self.excerpt_type.content_type.model_class() tplgroup = mc.get_template_group() return tplgroup + '/' + tplname def disabled_fields(self, ar): rv = super(Excerpt, self).disabled_fields(ar) rv = rv | set(['excerpt_type', 'project']) if self.build_time: rv |= self.PRINTABLE_FIELDS return rv def __str__(self): if self.build_time: return str(naturaltime(self.build_time)) # return _("%(owner)s (printed %(time)s)") % dict( # owner=self.owner, time=naturaltime(self.build_time)) return gettext("Unprinted %s #%s") % (self._meta.verbose_name, self.pk) def get_mailable_type(self): return self.excerpt_type def get_mailable_subject(self): return str(self.owner) # .get_mailable_subject() def get_template_groups(self): ptype = self.get_printable_type() if ptype is None: raise Exception("20140520 Must have excerpt_type.") grp = ptype.content_type.model_class().get_template_group() return [grp, u'excerpts'] def filename_root(self): # mainly because otherwise we would need to move files around on # existing sites et = self.excerpt_type if et is None or not et.certifying: return super(Excerpt, self).filename_root() assert et.certifying o = self.owner if o is None: return super(Excerpt, self).filename_root() name = o._meta.app_label + '.' + o.__class__.__name__ if not et.primary: name += '.' + str(et.pk) name += '-' + str(o.pk) return name def get_print_templates(self, bm, action): """When printing a certifying excerpt, the controlling database object gets a chance to decide which template to use. Overrides :meth:`lino.modlib.printing.Printable.get_print_templates`. """ et = self.excerpt_type if et is not None and et.certifying: if isinstance(self.owner, Certifiable): tpls = self.owner.get_excerpt_templates(bm) if tpls is not None: # print("20190506 Excerpt.get_print_templates()", tpls) return tpls return super(Excerpt, self).get_print_templates(bm, action) # ptype = self.get_printable_type() # # raise Exception("20150710 %s" % self.owner) # if ptype is not None and ptype.template: # return [ptype.template] # # return [bm.get_default_template(self)] # return [dd.plugins.excerpts.get_default_template(bm, self.owner)] # def get_recipient(self): # rec = super(Excerpt, self).get_recipient() # if rec is None and hasattr(self.owner, 'recipient'): # return self.owner.recipient # return rec # recipient = property(get_recipient) def get_printable_type(self): return self.excerpt_type def get_print_language(self): return self.language @dd.chooser() def excerpt_type_choices(cls, owner): # logger.info("20150702 %s", owner) qs = rt.models.excerpts.ExcerptType.objects.order_by('name') if owner is None: # e.g. when choosing on the *parameter* field # return qs.filter(content_type__isnull=True) return qs.filter() ct = ContentType.objects.get_for_model(owner.__class__) return qs.filter(content_type=ct) @property def date(self): "Used in templates" if self.build_time: return self.build_time.date() return dd.today() @property def time(self): "Used in templates" if self.build_time: return self.build_time.time() return timezone.now() @dd.virtualfield(dd.HtmlBox(_("Preview"))) def preview(self, ar): if ar is None or self.owner is None: # body templates should not need to test whether obj is defined return '' lang = self.get_print_language() or \ settings.SITE.DEFAULT_LANGUAGE.django_code with translation.override(lang): ctx = self.get_printable_context(ar) return ar.html_text(ctx['body']) def before_printable_build(self, bm): super(Excerpt, self).before_printable_build(bm) if self.owner is not None: return self.owner.before_printable_build(bm) def get_printable_context(self, ar=None, **kw): """Adds a series of names to the context used when rendering printable documents. Extends :meth:`lino.core.model.Model.get_printable_context`. """ if self.owner is not None: kw = self.owner.get_printable_context(ar, **kw) kw = super(Excerpt, self).get_printable_context(**kw) kw.update(obj=self.owner) kw.update(excerpt=self) body = '' # logger.info("20180114 get_printable_context() %s", self) if self.excerpt_type_id is not None: etype = self.excerpt_type if etype.backward_compat: kw.update(this=self.owner) tplname = self.get_body_template_name() # logger.info("20180114 tplname is %s -", # tplname) if tplname and ar is not None: body = settings.SITE.plugins.jinja.render_jinja( ar, tplname, kw) # env = settings.SITE.plugins.jinja.renderer.jinja_env # template = env.get_template(tplname) # # logger.info("body template %s (%s)", tplname, template) # body = ar.render_jinja(template, **kw) # logger.info("20160311 body template %s -> %s", # tplname, body) kw.update(body=body) return kw @classmethod def on_analyze(cls, site): cls.PRINTABLE_FIELDS = dd.fields_list( cls, "project excerpt_type " "body_template_content " "company contact_person language " "user build_method") super(Excerpt, cls).on_analyze(site)
class Note(TypedPrintable, UserAuthored, Controllable, Feasible, ContactRelated, mixins.ProjectRelated, ChangeNotifier, UploadController, Mailable): manager_roles_required = dd.login_required(OfficeStaff) class Meta: app_label = 'notes' abstract = dd.is_abstract_model(__name__, 'Note') verbose_name = _("Note") verbose_name_plural = _("Notes") date = models.DateField( verbose_name=_('Date'), default=dd.today) time = dd.TimeField( blank=True, null=True, verbose_name=_("Time"), default=timezone.now) type = dd.ForeignKey( 'notes.NoteType', blank=True, null=True, verbose_name=_('Note Type (Content)')) event_type = dd.ForeignKey( 'notes.EventType', blank=True, null=True, verbose_name=_('Event Type (Form)')) subject = models.CharField(_("Subject"), max_length=200, blank=True) body = dd.RichTextField(_("Body"), blank=True, format='html') language = dd.LanguageField() def __str__(self): if self.event_type_id: return u'%s #%s' % (self.event_type, self.pk) return u'%s #%s' % (self._meta.verbose_name, self.pk) def summary_row(self, ar, **kw): #~ s = super(Note,self).summary_row(ui,rr) s = super(Note, self).summary_row(ar) #~ s = contacts.ContactDocument.summary_row(self,ui,rr) if self.subject: s += [' ', self.subject] return s def get_mailable_type(self): return self.type def get_print_language(self): return self.language def get_change_owner(self): return self.project # def get_change_observers(self, ar=None): # # in lino_welfare the project is pcsw.Client # prj = self.project # if isinstance(prj, ChangeNotifier): # for u in prj.get_change_observers(ar): # yield u def get_change_info(self, ar, cw): yield E.p( gettext("Subject"), ': ', self.subject, E.br(), gettext("Client"), ': ', ar.obj2memo(self.project))