Skip to content

Customize Kpimark

In order to customize course output you’re able to use Kpimark extensions API.

Basically, the only API for extending Kpimark are @register_renderer(renderer) and @register_validator decorators. Register renderer takes one argument, which is basically renderer, which is going to be replaced during runtime Kpimark initialization in it4kt-builder.

Example

Kpimark gives you possibility to take control of markdown parsing process. While building a course you have option to provide init.py file from root of your course directory.

Let’s say that we would like to add new block token for solution admonition. Generally, there are two main goals that needs to be done:

  1. Create a new token for document parsing
  2. Provide renderer method which translates token’s content to desired output.

Declare a new token

In order to achieve these goals you can start with following code inside init.py:

from it4kt.kpimark.tokens.admonitions import AdmonitionBlock


class SolutionBlock(AdmonitionBlock):
    ALERT_TYPES = ["solution", "riešenie"]

    @classmethod
    def start(cls, line):
        return super().start(line, cls.ALERT_TYPES)

In this first step we are importing some basic stuff from it4kt-builder. We aim to provide new admonition so our token’s class shoudl extend AdmonitionBlock. Then, we provide match keywords to ALERT_TYPES static class variable, which our block is going to use during matching process.

Note

Every block token can implement start(cls, line) to match beginning of the block from document lines and read(cls, lines) method in order to declare way in which your token is parsing lines from document.

Declare renderer method

We have declared our new Token and first goal is achieved. Now, for the second goal we are going to implement a new HTML renderer, which is resposible for HTML output generation:

...

from it4kt.kpimark.extension import register_renderer
from it4kt.kpimark.renderers.html import KpimarkHTMLRenderer

...

@register_renderer(KpimarkHTMLRenderer)
class ExtendedRenderer(KpimarkHTMLRenderer):
    TOKENS_EXTENDED = [SolutionBlock]

    def render_solution_block(self, token):
        return self.render_admonition(token, "solution", heading="Solution")

We’ve just created our custom HTML renderer, which is going to replace basic KpimarkHTMLRenderer during runtime. We implemented rendering method for SolutionBlock, which we’ve defined earlier.

Warning

User can provide only one custom renderer for every output-related renderer. In any other case an exception is going to be raised.

As our goal was to implement admonition we are able to use base render_admonition() method from KpimarkHTMLRenderer.

Tip

User has also possibility to edit only rendering process of the token if he needs to. To achieve this behavior you only need to override already existing token’s rendering method.

Internationalization

If you would like to provide localized keyword you can use i18n global instance.

...

from it4kt import i18n

...

@register_renderer(KpimarkHTMLRenderer)
class CustomHTMLRenderer(KpimarkHTMLRenderer):
    TOKENS_EXTENDED = [SolutionBlock]

    def render_solution_block(self, token):
        if i18n.language == "sk":
            heading = "Riešenie"
        else:
            heading = "Solution"
        return self.render_admonition(token, "solution", heading=heading)

In example above we are setting admonition header by current translation language.

Note

Keep in mind that you can provide translation string for every language defined in your course configuration and supported by it4kt-builder. For more about localization check it’s recipe page.

Validate defined token

User is also able to define validation method for any of the tokens.

...

from it4kt.kpimark.extension import register_validator
from it4kt.kpimark.validation.validator import Validator

...

@register_validator
class ExtendedValidator(Validator):
    TOKENS_EXTENDED = [SolutionBlock]

    def validate_solution_block(self, token):
        pass

Warning

User can provide only provide single validator class. In any other case an exception is going to be raised.

Code used in example

from it4kt.kpimark.tokens.admonitions import AdmonitionBlock
from it4kt.kpimark.extension import register_validator
from it4kt.kpimark.validation.validator import Validator
from it4kt import i18n


class SolutionBlock(AdmonitionBlock):
    ALERT_TYPES = ["solution", "riešenie"]

    @classmethod
    def start(cls, line):
        return super().start(line, cls.ALERT_TYPES)


@register_renderer(KpimarkHTMLRenderer)
class ExtendedRenderer(KpimarkHTMLRenderer):
    TOKENS_EXTENDED = [SolutionBlock]

    def render_solution_block(self, token):
        if i18n.language == "sk":
            heading = "Riešenie"
        else:
            heading = "Solution"
        return self.render_admonition(token, "solution", heading=heading)


@register_validator
class ExtendedValidator(Validator):
    TOKENS_EXTENDED = [SolutionBlock]

    def validate_solution_block(self, token):
        pass