Coverage for lino/modlib/printing/mixins.py : 39%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# -*- coding: UTF-8 -*- # Copyright 2009-2016 Luc Saffre # License: BSD (see file COPYING for details)
:doc:`/admin/printing`.
"""
# from lino.core import actions
# not used. BuildMethods.add_item_instance(bm)
return BuildMethods.choices
""" Base class for all "Print" actions. """
if not dbutils.resolve_app('system'): return False # if actor.__name__ == 'ExcerptsByProject': # logger.info("20140401 attach_to_actor() %r", self) return super(BasePrintAction, self).attach_to_actor(actor, name)
# including ShowEmptyTable which is subclass of # ShowDetailAction. But not callable from InsertRow. return isinstance(caller, (GridEdit, ShowDetailAction))
return elem.get_print_templates(bm, self)
"""Return the target filename if a document needs to be built, otherwise return ``None``. """ elem.before_printable_build(bm) filename = bm.get_target_name(self, elem) if not filename: return if os.path.exists(filename): logger.debug(u"%s %s -> overwrite existing %s.", bm, elem, filename) os.remove(filename) else: #~ logger.info("20121221 makedirs_if_missing %s",os.path.dirname(filename)) rt.makedirs_if_missing(os.path.dirname(filename)) logger.debug(u"%s : %s -> %s", bm, elem, filename) return filename
help_url = ar.get_help_url("print", target='_blank') msg = _("Your printable document (filename %(doc)s) " "should now open in a new browser window. " "If it doesn't, please consult %(help)s " "or ask your system administrator.") msg %= dict(doc=leaf, help=E.tostring(help_url)) kw.update(message=msg, alert=True) if bm.use_webdav and has_davlink and ar.request is not None: kw.update( open_davlink_url=ar.request.build_absolute_uri(url)) else: kw.update(open_url=url) ar.success(**kw) return
"""Print using a hard-coded template and no cache.
"""
#~ assert bm is self.build_method if self.tplname: return [self.tplname + bm.template_ext] return obj.get_print_templates(bm, self)
elem = ar.selected_rows[0] bm = elem.get_build_method() bm.build(ar, self, elem) mf = bm.get_target(self, elem) # if ar.request is not None and bm.use_webdav and has_davlink: # url = ar.request.build_absolute_uri(url) # kw.update(open_davlink_url=url) # else: # kw.update(open_url=url) # ar.success(**kw) leaf = mf.parts[-1] self.notify_done(ar, bm, leaf, mf.url, **kw)
"""A print action which uses a cache for the generated printable document and builds is only when it doesn't yet exist.
"""
# select_rows = False
if elem.build_time: return return BasePrintAction.before_build(self, bm, elem)
if len(ar.selected_rows) == 1: obj = ar.selected_rows[0] bm = obj.get_build_method() mf = bm.get_target(self, obj) leaf = mf.parts[-1] if obj.build_time is None: obj.build_target(ar) ar.info("%s has been built.", leaf) else: ar.info("Reused %s from cache.", leaf)
self.notify_done(ar, bm, leaf, mf.url, **kw) ar.set_response(refresh=True) return
def ok(ar2): #~ qs = [ar.actor.get_row_by_pk(pk) for pk in ar.selected_pks] mf = self.print_multiple(ar, ar.selected_rows) ar2.success(open_url=mf.url) #~ kw.update(refresh_all=True) #~ return kw msg = _("This will print %d rows.") % len(ar.selected_rows) ar.confirm(ok, msg, _("Are you sure?"))
pdfs = [] for obj in qs: #~ assert isinstance(obj,CachedPrintable) if obj.printed_by_id is None: obj.build_target(ar) pdf = obj.get_target_name() assert pdf is not None pdfs.append(pdf)
mf = TmpMediaFile(ar, 'pdf') rt.makedirs_if_missing(os.path.dirname(mf.name)) merge_pdfs(pdfs, mf.name) return mf
"""Edit the print template, i.e. the file specified by :meth:`Printable.get_print_templates`.
The action available only when :mod:`lino.modlib.davlink` is installed, and only for users with `SiteStaff` role.
If it is available, then it still works only when
- your site has a local config directory - your :xfile:`webdav` directory (1) is published by your server under "/webdav" and (2) has a symbolic link named `config` which points to your local config directory. - the local config directory is writable by `www-data`
**Factory template versus local template**
The action automatically copies a factory template to the local config tree if necessary. Before doing so, it will ask for confirmation: :message:`Before you can edit this template we must create a local copy on the server. This will exclude the template from future updates.`
"""
lcd = settings.SITE.confdirs.LOCAL_CONFIG_DIR if lcd is None: # ar.info("No local config directory in %s " % # settings.SITE.confdirs) raise Warning("No local config directory. " "Contact your system administrator.")
elem = ar.selected_rows[0] bm = elem.get_build_method() leaf = bm.get_template_leaf(self, elem)
filename = bm.get_template_file(ar, self, elem) local_file = None groups = elem.get_template_groups() assert len(groups) > 0 for grp in reversed(groups): # subtle: if there are more than 1 groups parts = [grp, leaf] local_file = join(lcd.name, *parts) if filename == local_file: break
parts = ['webdav', 'config'] + parts url = settings.SITE.build_media_url(*parts) if ar.request is not None: url = ar.request.build_absolute_uri(url)
if not has_davlink: msg = "cp %s %s" % (filename, local_file) ar.info(msg) raise Warning("Java is not enabled. " "Contact your system administrator.")
def doit(ar): ar.info("Going to open url: %s " % url) ar.success(open_davlink_url=url) # logger.info('20140313 EditTemplate %r', kw)
if filename == local_file: doit(ar) else: def ok(ar2): logger.info( "%s made local template copy %s", ar.user, local_file) rt.makedirs_if_missing(dirname(local_file)) shutil.copy(filename, local_file) doit(ar2)
msg = _( "Before you can edit this template we must create a " "local copy on the server. " "This will exclude the template from future updates.") ar.info("Gonna copy %s to %s", rt.relpath(filename), rt.relpath(local_file)) ar.confirm(ok, msg, _("Are you sure?"))
""" Defines the :guilabel:`Clear cache` button on a Printable record.
The `run_from_ui` method has an optional keyword argmuent `force`. This is set to True in `docs/tests/debts.rst` to avoid compliations.
"""
#~ def disabled_for(self,obj,request): #~ if not obj.build_time: #~ return True
# obj may be None when Lino asks whether this action # should be visible in the UI if obj is not None and not obj.build_time: return False return super(ClearCacheAction, self).get_action_permission( ar, obj, state)
elem = ar.selected_rows[0]
def doit(ar): elem.clear_cache() ar.success(_("%s printable cache has been cleared.") % elem, refresh=True)
t = elem.get_cache_mtime() if t is not None: # set microseconds to those of the stored field because # Django DateTimeField can have microseconds precision or # not depending on the database backend.
t = datetime.datetime( t.year, t.month, t.day, t.hour, t.minute, t.second, elem.build_time.microsecond) if settings.USE_TZ: t = make_aware(t) if t != elem.build_time: # logger.info("20140313 %r != %r", t, elem.build_time) return ar.confirm( doit, _("This will discard all changes in the generated file."), _("Are you sure?")) return doit(ar)
"""Base class for models that specify the :attr:`TypedPrintable.type`.
.. attribute:: build_method
A pointer to an item of :class:`lino.modlib.printing.choicelists.BuildMethods`.
.. attribute:: template
The name of the file to be used as template.
If this field is empty, Lino will use the filename returned by :meth:`lino.modlib.printing.Plugin.get_default_template`.
The list of choices for this field depend on the :attr:`build_method`. Ending must correspond to the :attr:`build_method`.
"""
""" Default value for `templates_group` is the model's full name. """
def get_template_groups(cls): """Note that `get_template_groups` is a **class method** on `PrintableType` but an **instance method** on `Printable`.
""" return [cls.templates_group] # or full_model_name(cls)
def template_choices(cls, build_method): return cls.get_template_choices( build_method, cls.get_template_groups())
def get_template_choices(cls, build_method, template_groups): if not build_method: build_method = BuildMethods.get_system_default() return rt.find_template_config_files( build_method.template_ext, *template_groups)
"""Mixin for models whose instances have a "print" action (i.e. for which Lino can generate a printable document).
Extended by :class:`CachedPrintable` and :class:`TypedPrintable`.
.. attribute:: do_print
The action used to print this object. This is an instance of :class:`DirectPrintAction` or :class:`CachedPrintAction` by default. And if :mod:`lino_xl.lib.excerpts` is installed, then :func:`set_excerpts_actions <lino_xl.lib.excerpts.set_excerpts_actions>` possibly replaces :attr:`do_print` by a :class:`lino_xl.lib.excerpts.CreateExcerpt` instance.
.. attribute:: edit_template
"""
pass
return [self.__class__.get_template_group()]
return self._meta.app_label + '.' + self.__class__.__name__ \ + '-' + str(self.pk)
""" Return a list of filenames of templates for the specified build method. Returning an empty list means that this item is not printable. For subclasses of :class:`SimpleBuildMethod` the returned list may not contain more than 1 element.
""" return [bm.get_default_template(self)]
return BuildMethods.get_system_default()
"""Return the build method to use when printing this object.
This is expected to rather raise an exception than return `None`.
""" # TypedPrintable overrides this return self.get_default_build_method()
""" Mixin for Models that generate a unique external file at a determined place when being printed.
Adds a "Print" button, a "Clear cache" button and a `build_time` field.
The "Print" button of a :class:`CachedPrintable <lino.mixins.printable.CachedPrintable>` transparently handles the case when multiple rows are selected. If multiple rows are selected (which is possible only when :attr:`cell_edit <lino.core.tables.AbstractTable.cell_edit>` is True), then it will automatically:
- build the cached printable for those objects who don't yet have one
- generate a single temporary pdf file which is a merge of these individual cached printable docs
.. attribute:: build_time
Timestamp of the built target file. Contains `None` if no build hasn't been called yet.
"""
_("build time"), null=True, editable=False)
if not self.build_method: self.build_method = self.get_default_build_method() super(CachedPrintable, self).full_clean(*args, **kwargs)
super(CachedPrintable, self).on_duplicate(ar, master) self.build_time = None self.build_method = None
if self.build_time: return self.build_method.get_target_name( self.do_print, self)
return self.build_method or self.get_default_build_method()
return self.build_method.get_target_url( self.do_print, self)
"""Return the modification time (a `datetime`) of the generated cache file, or `None` if no such file exists.
""" filename = self.get_target_name() if not filename: return None try: t = os.path.getmtime(filename) except OSError: return None return datetime.datetime.fromtimestamp(t)
self.build_time = None self.save()
bm = elem.get_build_method() t = bm.build(ar, elem.__class__.do_print, elem) if t is None: raise Exception("%s : build() returned None?!") elem.build_time = datetime.datetime.fromtimestamp(t) elem.save()
"""A :class:`CachedPrintable` that uses a "Type" for deciding which template to use on a given instance.
A TypedPrintable model must define itself a field ``type`` which is a ForeignKey to a Model that implements :class:`PrintableType`.
Alternatively you can override :meth:`get_printable_type` if you want to name the field differently. An example of this is :attr:`ml.sales.SalesDocument.imode`.
"""
return self.type
ptype = self.get_printable_type() if ptype is None: return super(TypedPrintable, self).get_template_groups() return ptype.get_template_groups()
ptype = self.get_printable_type() if ptype and ptype.build_method: return ptype.build_method return super(TypedPrintable, self).get_default_build_method()
# def get_build_method(self): # if not self.build_method: # return self.get_default_build_method() # return self.build_method # ptype = self.get_printable_type() # if ptype and ptype.build_method: # return ptype.build_method # return super(TypedPrintable, self).get_build_method()
ptype = self.get_printable_type() if ptype is None: return super(TypedPrintable, self).get_print_templates(bm, action)
if ptype.template: return [ptype.template] return [bm.get_default_template(self)] |