-
Notifications
You must be signed in to change notification settings - Fork 0
Progress statements
By stating the progress of a job at specific points in the code, DP2 clients can display a progressbar or a percentage to the end-user.
Due to the nature of XProc and XSLT, there is not always a clear execution order of the different parts of a script. So the way to state progress in Pipeline 2 is to state the amount of work that you expect will take place until the next progress statement arrives.
In addition, when stating the expected amount of work that will take place, another "level" of progress statements can be added, which will allow you to further refine the progress information and reuse them across scripts in a modular way.
For instance, say you have the script px:dtbook-to-epub3, then
you can state the following:
- loading the DTBook takes 10% of the total job running time
- converting from DTBook to ZedAI takes 40% of the total time
- converting from ZedAI to EPUB 3 takes 40% of the total time
- storing the EPUB 3 takes 10% of the total time
Within the "DTBook to ZedAI" step however, you can add more statements like the following:
- loading external resources takes 30% of the time spent in DTBook to ZedAI, (translating to 12% of the total job time since 30% of 40% is 12%)
- The XSLT that converts from DTBook to ZedAI uses the remaining 70% of the time, (translating to 28% of the total job time since 70% of 40% is 28%)
And you may nest progress statements deeper inside the DTBook to ZedAI XSLT or wherever you wish.
This mechanism allows the client to know where in the process a job is, and estimate how much time is remaining.
Progress statements are sent to the client as part of the execution log. Messages in the execution log starting with the following pattern are progress statements:
[progress NAME PROGRESS SUBNAME]
From XProc, messages can be sent to the execution log by using the
px:message step:
<px:message message="[progress NAME PROGRESS SUBNAME]"/>
If the following step contains pipes, for instance if the result of
another step is piped into a port or an option of the step you
are writing a progress message for, then you will want to use
cx:depends-on on <px:message> to make sure that no other progress
messages from the current XProc file are sent to the execution log
before the step you are writing a progress message for finishes.
From XSLT, messages can be sent to the execution log by using the
xsl:message step:
<xsl:message select="'[progress NAME PROGRESS SUBNAME]'"/>
There is currently no mechanism to send messages to the execution log from Java. See: https://github.com/daisy/pipeline-issues/issues/477
[progress NAME PROGRESS SUBNAME]
The PROGRESS is a fraction on the form NUMERATOR/DENOMINATOR.
Both the numerator and denominator must be integers.
The denominator part can be omitted, in which case the denominator
will default to 100 so that the number in practice represents a percentage
([progress 5] = 5%, [progress 5/50] = 10%).
(It is also possible to use a ranged PROGRESS on the form FROM-TO,
where FROM and TO are integers in the range [0,100]. This is a way of stating
that the current progress is FROM % and the next step will take TO-FROM % of
the current progress. However, this is rarely useful, since this requires you to
know the current progress.)
NAME is the name of the current progress level.
SUBNAME is the name of the next progress level.
The top-level progress level does not have a NAME.
SUBNAME is optional, but recommended.
Progress statements whose NAME does not equal
the SUBNAME of the first preceding progress
statement with a NAME different than itself are ignored.
(Unless the NAME is empty.)
Note that there cannot be two progress statements with
the same NAME at different levels because there would
be no way to know which of the two levels the progress
statement belongs to. In case this does occur, it is always
assumed that the progress statement belongs to the top-most
level of the two. For recursive steps/templates/methods this
means that you should not create a new level for each recursion.
- for public XProc steps, use the step type including the namespace prefix,
such as
px:dtbook-to-pef. - for public XSLTs, use the filename of the XSLT, such as
dtbook-to-pef.xsl. - for public Java classes, use the package name + class name + method name, such as
org.daisy.saxon.functions.file.FileExists.exists - for internal structures such as
xsl:for-each,p:group,for () {}etc., append a descriptive string, such aspx:dtbook-to-pef.check-arguments,dtbook-to-pef.xsl.iterate-paragraphsororg.daisy.saxon.functions.file.FileExists.exists.iterate-files.
dtbook-to-pef.xpl
<p:declare-step type="px:dtbook-to-pef" ...>
(...)
<px:message message="[progress 40 px:dtbook.load] Converting from DTBook to PEF"/>
<px:dtbook.load>
(...)
</px:dtbook.load>
<px:message message="[progress 40 px:dtbook-to-pef.convert] Converting from DTBook to PEF"/>
<px:dtbook-to-pef.convert>
(...)
</px:dtbook-to-pef.convert>
<px:message message="[progress 20 px:pef.store] Converting from DTBook to PEF"/>
<px:pef.store>
(...)
</px:pef.store>
</p:declare-step>dtbook-to-pef.convert.xpl
<p:declare-step type="px:dtbook-to-pef.convert" ...>
(...)
<px:message message="Translating text to braille"/>
<px:message message="[progress px:dtbook-to-pef.convert 30 text-transform.xsl]"/>
<p:xslt>
(...)
<p:input port="stylesheet">
<p:document href="../xslt/text-transform.xsl"/>
</p:input>
</p:xslt>
<px:message message="Formatting DTBook as PEF"/>
<px:message message="[progress px:dtbook-to-pef.convert 60 px:dtbook-to-pef.convert.for-each-document]"/>
<p:for-each name="for-each-document">
<px:message>
<p:with-option name="message" select="concat('[progress px:dtbook-to-pef.convert.for-each-document 1/',p:iteration-size(),'] px:format-dtbook-as-pef')"/>
</px:message>
<px:format-dtbook-as-pef>
(...)
</px:format-dtbook-as-pef>
</p:for-each>
<px:message message="Finalizing"/>
<px:message message="[progress px:dtbook-to-pef.convert 10]"/>
<p:group>
(...)
</p:group>
</p:declare-step>text-transform.xsl
<xsl:stylesheet ...>
(...)
<xsl:variable name="total-paragraphs" select="count(//dtbook:p)"/>
<!-- a single paragraph -->
<xsl:template match="dtbook:p">
<pef:row>
<xsl:message select="concat('[progress text-transform.xsl 1/',$total-paragraphs,' org.daisy.saxon.functions.braille.Transform.transformText]')"/>
<pf:transform-text>
<xsl:with-param name="text" select="text()"/>
</pf:transform-text>
</pef:row>
</xsl:template>
<!-- many paragraphs at once -->
<xsl:template match="dtbook:poem">
<pef:page>
<xsl:message select="concat('[progress text-transform.xsl ',count(.//dtbook:p),'/',$total-paragraphs,' org.daisy.saxon.functions.braille.Transform.transformPoem]')"/>
<pf:transform-text>
<xsl:with-param name="poem" select="."/>
</pf:transform-text>
</pef:page>
</xsl:template>
</xsl:stylesheet>pipeline-clientlib-java filters out \[progress[^\]*\] * from the messages before displaying
them, so progress statements are never rendered for the end-user. It also calculates the
percentage and provides an API for estimating (interpolating) the current progress.