Skip to main content

How to convert a Python Project to a VIKTOR app

info

Level: Beginners
Time: 30 min

Prerequisites:

  • You have some experience with reading Python code
  • You have completed the installation and onboarding process of the VIKTOR platform
  • You have a Python project or script that you would like to convert to a VIKTOR app

Introduction

Most Python projects start off with a simple script to help with a task, whether it is for automating or calculation purposes. These scripts can evolve into valuable tools that can have value far beyond your own use.

In general, what keeps these scripts from being utilized by others is that it often does not have a user-friendly way of being operated (for example an intuitive interface), it generally requires an extensive user manual, and that distribution is nearly impossible, as these scripts almost always require a set of dependencies that need to be installed, not mentioning the rights that need to be allocated to the user to be able to install these programs and packages.

The VIKTOR platform allows one to overcome these challenges by providing a platform, SDK and integration possibilities. In this piece we will provide a guide to easily convert your script into a VIKTOR web application, which will allow others to also share the value that you created, without the bottlenecks usually encountered.

In this guide, we will explore how to convert your Python project to a VIKTOR app. We will cover:

  1. Where to start
  2. Let's convert your script!
  3. What's next?

By the end of this guide, you will have a clear understanding how to convert your Python project to a VIKTOR app.

info

If you do not have a suitable Python script, consider writing something that you are familiar with such as a basic plot. This will remove the chance of knowledge gaps that you would have if you are given a script and allows you to focus on the VIKTOR elements.

Throughout the guide, we added some links with additional information; but don't let them distract you too much. Stay focused on understanding the content. After this, you will know everything you need to convert your Python project to an app.

Where to start

For the sake of this guide it is important to understand the fundamentals and typical layout of a VIKTOR app. These fundamentals are, however, elaborated upon earlier during the VIKTOR onboarding. So, if you want to directly get started in the steps to convert your script to an app, skip to the Let’s convert your script!!

VIKTOR framework fundamentals

As the goal with this guide is to help you convert your script to a VIKTOR app, it is helpful to understand the fundamentals that make up the VIKTOR framework. In an attempt to give the clearest overview of the fundamentals, without oversimplifying, let us start with what we believe a good starting point is in understanding the VIKTOR framework. There are three aspects that are important to keep in mind when converting your script to an app, which are:

  1. Input: The variables that can be adjusted for a calculation
  2. Results: Results can either be presented visually or downloaded in a given file format.
  3. Stateless and containerized calculation: A stateless calculation’s outcomes are not influenced by previous events, except if part of the calculation’s outcome is to alter the input (which will then alter the outcome of the next calculation). A deployed app also runs in isolation, which is what is meant by being containerized.

These three points are important to remember when considering converting a script to a VIKTOR application.

VIKTOR code layout

Now that we know how VIKTOR’s framework fundamentally works, let us look at an example of a VIKTOR app to explain a typical VIKTOR app code layout:

An example of a VIKTOR app's code and interface.

Be aware that this is just an example to explain the fundamentals from a code’s perspective, and that the VIKTOR platform provides an endless number of possibilities.

Noting above’s example, let us try to connect the fundamentals to this example.

Input

As a fundamental, the VIKTOR platform forces the developer to determine what the input variables of the calculation should be. This is done by defining all input parameters in the Parametrization class. There is an extensive amount of input, so this should not limit the developer. Refer to the User input, fields and buttons section in the documentation for a clear overview of the possibilities.

Example of VIKTOR input widgets

Results

Whether the result of the calculation is a visualization of sorts or a download, this is a result of a method that is defined in the Controller class. A download is simply a file of bytes or strings, so any format can be downloaded. For visualizations, the following is possible:

  • Images (PNG, JPEG, SVG, GIF): these formats need to be saved to memory by using BytesIO, StringIO or VIKTOR’s File
  • Maps
  • 3D geometries (any object that can be converted to a glb/gltf format, or by using the primitive geometries available in the VIKTOR SDK)
  • PDFs
  • Any result in HTML
  • Plotly

Refer to the Results and visualizations section in the documentation to read up more on this.

The range of output that can be generated with VIKTOR

Stateless calculation

Any method that produces a result, presents a visualization, or produces a download must contain some logic. This logic can be defined directly in the method, split up into different methods, or defined in functions or classes in other files or directories within the app project. The requirement is that the code is written in such a way that the input provided (which can be retrieved from the params argument of the Controller class) always produces the same result when the input remains unchanged.

In the event where a stateful event is wanted, the VIKTOR platform provides a Storage capability that allows developers to write data to a storage database that can be retrieved for the next calculation.

Containerized calculation

As the intent of building a web app is to run it on a server rather than locally, the app with all its logic and files needs to be able to run in an isolated environment. Therefore, one needs to make sure that the entire development is done within the project folder, and that, when referring to files and folders, that these paths are referenced relatively.

What is stateless, containerized code, and what alternatives does VIKTOR bring.

Other things to consider…

  • If your code is written such that files are written to your local drive, it is suggested to convert this to saving the file to memory, or to use the VIKTOR SDK Storage functionality to store the file to a database.
  • If the Python code uses system dependencies or software that cannot be installed using pip install, we suggest using the integrations we provide. Or rewrite your code to use code that does not require these dependencies.

Let’s convert your script!

Now that we know the VIKTOR platform from fundamentals, and we have an idea how the code structure works for a VIKTOR application, let’s take that knowledge to convert a script to a VIKTOR application.

The steps to convert a simple script to an app.

Take the following steps:

1. Create a VIKTOR app project.

To create a new VIKTOR application project, use the viktor-cli to generate a project file structure automatically. The command to use is:

viktor-cli create-app –-app-type editor <name-of-your-project>

2. Collect all requirements of the python script.

A good place to start would be to collect a list of all the python packages that are being used in the script. Some examples of frequently used Python packages are:

  • Numpy
  • Scipy
  • Matplotlib
  • Pandas
  • Etc.

Add these packages to the requirements.txt file.

info

If the Python script only works for a given Python version, make sure to indicate this in the viktor.config.toml file.

3. Convert script to a function.

A script can be converted to a VIKTOR app in many ways. One of the simpler ways of converting a script would be to rewrite the code such that it can run by calling a single function. This would entail you only need to consider what should be the result of the calculation, and then make sure that the result that you want to produce is returned by the function. E.g. if the result of the code is to produce a matplotlib plot, return a matplotlib figure, or if the result is a pandas dataframe, return that. If your code writes a file to a local drive, rewrite the code such that it returns the file as an object saved to memory, e.g. BytesIO, StringIO or File.

4. Create a view/download in the VIKTOR Controller

In this step we will create a view or download functionality that can be seen in the interface of the VIKTOR app. Here are the steps:

To use the newly created function in your VIKTOR app, import the function into the app.py file where the bare-bones VIKTOR app code structure resides.

Depending on whether you would like to create a download functionality or visualization, follow the steps described in the subsection:

4.1 Download

Here are the steps for a download functionality:

  1. Create a download button: In the Parametrization, create a field with a DownloadButton, similar to the code snippet presented below:
from viktor.parametrization import ViktorParametrization, DownloadButton

class Parametrization(ViktorParametrization):
download_btn = DownloadButton('Download file', method='download_file')
  1. In the Controller, create a method with the same name as defined in the method argument (in the given example, download_file). Here is an example how this would look like:
from viktor import ViktorController
from viktor.result import DownloadResult


class Controller(ViktorController):
label = '...'
parametrization = Parametrization

def download_file(self, params, **kwargs):

return DownloadResult(file_content='', file_name='my_file.txt')
  1. Add your function to the method. The file_content that the VIKTOR platform expects is either string, bytes, File or BytesIO. Therefore, make sure to convert the type produced by the function if it does not suit any of the given types.
from viktor import ViktorController
from viktor.result import DownloadResult
from .my_project import my_function # assuming that your function is within another file


# Assuming that your function is within this project
def my_function():
...
return


class Controller(ViktorController):
label = '...'
parametrization = Parametrization

def download_file(self, params, **kwargs):
result = my_function()
return DownloadResult(file_content=result, file_name='my_file.txt')

4.2 Visualization

Here are the steps for a visualization:

  1. Determine what type of visualization you want to create from the result of your function.
  2. In the Controller, create a method with your name of choice. The result to be returned by the method is dependent on the type of visualization.
  3. Add a decorator to your method that suits your visualization.
  4. Add your function to the method. Depending on the View, the result may differ. Therefore, make sure to check the documentation for that given view, and convert the type produced by the function to the format that is expected.

Here is an example of the result of the four steps:

from viktor import ViktorController
from viktor.views import WebView, WebResult
from .my_project import my_function # assuming that your function is within another file


# Assuming that your function is within this project
def my_function():
...
return


class Controller(ViktorController):
label = '...'
parametrization = Parametrization

@WebView("My View", duration_guess=1)
def create_plot(self, params, **kwargs):
my_result = my_function()
return WebResult(my_result)

Here is a table overview of the steps, with the possible options:

StepsImageMap3D GeometryPDFHTMLPlotly
1. Determine type of visualizationThis could be static plots such as Matplotlib, Seaborn, etc. or other type of images.GeoJSONs or locations that can be visualized on a mapAny type of geometry that can be converted to glb/gltf, or some simple parameters that can be converted to simple VIKTOR geometries.Word and Excel files can be converted to PDFs within VIKTOR!HTML or URLs can be imbedded.Plotly is a Python package with which you can create dynamic plots.
2. Create method with correct return typeImageResultMapResult or GeoJSONResultGeometryResultPDFResultWebResultPlotlyResult
3. Add decorator to your methodImageViewMapView or GeoJSONViewGeometryViewPDFViewWebViewPlotlyView
4. Add your function to the method.BytesIO for PNG, JPG/JPEG and GIF, StringIO for SVGMapView: a list of VIKTOR map objects
GeoJSONView: a GeoJSON dict
This could either be a VIKTOR Group object, consisting of VIKTOR primitive Geometry objects, or...
a glTF/GLB or 3DM file as a VIKTOR File ojbect
You could reference a PDF from a URL, a path, or, probably the most common one, as a VIKTOR FileThe HTML could be retrieved from a URL, static file as a path, or as an HTML stringA Plotly JSON using the .to_json method on the Plotly Figure object

If all went well, you will now have an application that displays the result of your function.

5. Create fields as input for calculation.

We’ve created a visualization, but ideally, we would like to make the app dynamic based on input. To do this, the following steps can be followed:

  1. Start by determining which variables you would like the user of the app to change. Add these variables as input to the function you’ve created from your script.
  2. Add fields to the parametrization that correspond with these variables. For example, if there are three variables named a, b and c that can be adjusted, we can add these fields to the Parametrization as well:
VariableTypeField
aFloatNumberField
bOptions of stringsOptionField
cFileFileField
Etc.
from viktor.parametrization import ViktorParametrization, NumberField, OptionField, FileField


class Parametrization(ViktorParametrization):
a = NumberField('a')
b = OptionField('b', options=['apple', 'pear', 'lemon'])
c = FileField('Upload file')
  1. Add the parameters to the function. You may have noticed that the methods defined in the Controller have a params argument. You can access the parameters from that variable. Here is an example:
from viktor import ViktorController
from viktor.views import WebView, WebResult
from .my_project import my_function # assuming that your function is within another file


# Assuming that your function is within this project
def my_function(a, b, c):
...
return


class Controller(ViktorController):
label = '...'
parametrization = Parametrization

@WebView("My View", duration_guess=1)
def create_plot(self, params, **kwargs):
result = my_function(
a=params.a,
b=params.b,
c=params.c
)
return WebResult(result)

What's next?

And that's it! By following these steps, one should be able to convert a Python script to a VIKTOR app in no time.

The VIKTOR platform provides much, much more in terms of functionalities and features. Therefore, if you want to explore the possibilities to improve your app, consider consulting the documentation. Topics that are of great interest to developers are:

There are also resources other than the documentation available that could help in the app development journey, such as: