Files

Files, in general, are for storing data. In Scriptor, there is a set of typical tasks where files are involved. The File-class provides several methods to do those tasks easily and efficiently.

Note

In the first examples, we’ll use the download-method of File to make your browser download it. This will be explained later in this document.

Creating Files from Data

from_string

This method is used to create a File from a string (aka. a text-file).

#### scriptor ####
from viur.scriptor import *

async def main():
    file = File.from_string("Hello World", "hello_world.txt")
    file.download()
    print("Please check your downloads. You should have a new file called hello_world.txt")

from_bytes

Similar to the from_string-Method, this method creates a File from bytes. This is usually useful for working with files like xlsx, zip, pdf, etc. (Also there are extra convenience-methods for handling tables.)

#### scriptor ####
from viur.scriptor import *

async def main():
    file = File.from_bytes(b"Hello World", "hello_world.txt")
    file.download()
    print("Please check your downloads. You should have a new file called hello_world.txt")

from_url

from_url downloads a file directly from a URL and loads it into Scriptor as a File object. The request goes through the ViUR backend session, so the same authentication cookies are used — which means it works for URLs on the same ViUR server without extra login.

By default the filename is taken from the server response. You can override it with the filename parameter.

#### scriptor ####
from viur.scriptor import *

async def main():
    file = await File.from_url("https://example.com/report.pdf")
    print(file.get_filename())  # filename from the response
    file.download()

With a custom filename:

#### scriptor ####
from viur.scriptor import *

async def main():
    file = await File.from_url(
        "https://example.com/report.pdf",
        filename="my_report.pdf"
    )
    file.download()

from_table

This method can handle two different representations of tables and create a xlsx- or csv-file from them. Tables can either be defined as lists of dicts or lists of lists. In the case of dicts, the keys from the dict must match the headers of the table. If no header is specified explicitly, it is automatically generated from the dictionaries’ keys. In case of a list of lists, the first list in the outer list is considered the header if no header is defined explicitly.

#### scriptor ####
from viur.scriptor import *

async def main():
    tbl_head = ["Name", "Age"]
    tbl_data = [
        ["John", "37"],
        ["Bob", "38"]
    ]
    file = File.from_table(tbl_data, tbl_head, filename="simple_table.xlsx")
    file.download()
    print("Please check your downloads."
          " You should have a new file called simple_table.xlsx")
#### scriptor ####
from viur.scriptor import *

async def main():
    tbl = [
        {"Name": "John", "Age": "37"},
        {"Name": "Bob", "Age": "38"}
    ]
    file = File.from_table(tbl, filename="simple_table2.xlsx")
    file.download()
    print("Please check your downloads."
          " You should have a new file called simple_table2.xlsx")

As you may have noticed, in this example, the ages are defined as strings. This is because tables generally consists of strings only. If you have other types in your data, they can be converted automatically, but you need to explicitly enable this functionality by setting auto_str to True.

#### scriptor ####
from viur.scriptor import *

async def main():
    tbl_head = ["Name", "Age"]
    tbl_data = [
        ["John", 37],
        ["Bob", 38]
    ]
    file = File.from_table(
        tbl_data,
        tbl_head,
        filename="simple_table3.xlsx",
        auto_str=True
    )
    file.download()
    print("Please check your downloads."
          " You should have a new file called simple_table3.xlsx")

Sometimes, especially while developing a new script, you may have incomplete data, that you want to save anyway, just to see what it looks like. To achieve this, you just have to set fill_empty to True.

#### scriptor ####
from viur.scriptor import *

async def main():
    tbl_head = ["Name", "Age"]
    tbl_data = [
        ["John", 37],
        ["Bob"]  # missing age
    ]
    file = File.from_table(
        tbl_data,
        tbl_head,
        filename="simple_table4.xlsx",
        fill_empty=True,
        auto_str=True
    )
    file.download()
    print("Please check your downloads."
          " You should have a new file called simple_table4.xlsx")

Besides xlsx, csv is supported. You can just change the file-name and it will automatically generate the right format.

#### scriptor ####
from viur.scriptor import *

async def main():
    tbl_head = ["Name", "Age"]
    tbl_data = [
        ["John", 37],
        ["Bob"]  # missing age
    ]
    file = File.from_table(
        tbl_data,
        tbl_head,
        filename="simple_table5.csv",
        fill_empty=True,
        auto_str=True
    )
    file.download()
    print("Please check your downloads."
          " You should have a new file called simple_table5.csv")

The default delimiter for csv-files is a comma (,), but sometimes, especially for Excel in europe, you might want to use a semicolon (;). This is simply achieved by passing it as the parameter csv_delimiter (if you pass it while generating xlsx, it is ignored).

#### scriptor ####
from viur.scriptor import *

async def main():
    tbl_head = ["Name", "Age"]
    tbl_data = [
        ["John", 37],
        ["Bob"]  # missing age
    ]
    file = File.from_table(
        tbl_data,
        tbl_head,
        filename="simple_table6.csv",
        fill_empty=True,
        auto_str=True,
        csv_delimiter=";"
    )
    file.download()
    print("Please check your downloads."
          " You should have a new file called simple_table6.csv")

Getting information about the File

get_filename

The get_filename-method returns, as the name suggests, the name of the file.

#### scriptor ####
from viur.scriptor import *

async def main():
    file = File.from_string("Hello World", "hello_world.txt")
    filename = file.get_filename()
    print(f"The file you just created has the name {filename}")

get_size

The get_size-method returns the size of the file in bytes.

#### scriptor ####
from viur.scriptor import *

async def main():
    file = File.from_string("Hello World", "hello_world.txt")
    filesize = file.get_size()
    print(f"The file has a size of {filesize} bytes.")

Using Data from a File

Files can also be user-input (this is explained later, we use demo-data in the following examples). In that case, you want to get data from the file. Files from user-input usually contain text or tabular data, so there are extra methods for handling those easily.

as_bytes

The as_bytes-method returns the complete data of the file as a bytestring.

#### scriptor ####
from viur.scriptor import *

async def main():
    file = File.from_string("Hello World", "hello_world.txt")
    content = file.as_bytes()
    print(content)

as_text

The as_text-method returns the complete data of the file as a decoded string, if possible. If not, it raises an error.

#### scriptor ####
from viur.scriptor import *

async def main():
    file = File.from_string("Hello World", "hello_world.txt")
    content = file.as_text()
    print(content)

as_object_from_json

If the file the user provided is a json-file, this method will convert it to a python-object.

#### scriptor ####
from viur.scriptor import *

async def main():
    file = File.from_string("""
        {"key": "value", "a_list_of_ints": [1,2,3,4], "a_float_value": 1.23}
    """, "demo.json")
    content = file.as_object_from_json()
    print(content)

as_list_table and as_dict_table

Mostly, user-provided data comes in the form of tables. These can be loaded as the aforementioned two representations: a list of dicts or a list of lists.

#### scriptor ####
from viur.scriptor import *

async def main():
    tbl_head = ["Name", "Age"]
    tbl_data = [
        ["John", "37"],
        ["Bob", "38"]
    ]
    file = File.from_table(tbl_data, tbl_head, filename="simple_table.xlsx")
    print(file.as_list_table())
    print(file.as_dict_table())

Getting the file to and from your PC

download

The download-method provides the simplest way to get data from scriptor to your PC. As you have seen from the earlier examples, it just triggers a download so your browser saves the file or asks for where to save it (depending on your browsers settings).

#### scriptor ####
from viur.scriptor import *

async def main():
    file = File.from_string("Hello World", "hello_world.txt")
    file.download()
    print("Please check your downloads. You should have a new file called hello_world.txt")

If you try to download more than one file in the same script, your browser may ask you if you want to allow the website (scriptor) to do this. You can try it out with this code:

#### scriptor ####
from viur.scriptor import *

async def main():
    File.from_string("Hello World", "download1.txt").download()
    File.from_string("Hello World", "download2.txt").download()
    File.from_string("Hello World", "download3.txt").download()

open_dialog

The open_dialog prompts the user to select a file from their PC. The data from the selected file will get loaded into scriptor. The file itself remains as it is. If the user cancels the dialog, an error will be raised and the script stops (unless you handle the error differently).

#### scriptor ####
from viur.scriptor import *

async def main():
    print("Please select a simple text-file.")
    textfile = await File.open_dialog()
    print(textfile.as_text())

save_dialog

The save_dialog is the counterpart to the open_dialog and provides an alternative to the download-method. The user is prompted to save the file. They can choose to select an existing file, which will then get overwritten, or a new filename that doesn’t exist yet, which will then be created. This also means that the file can be saved with a different name than defined in your code. As with the open_dialog, if the user cancels the dialog, an error will be raised.

#### scriptor ####
from viur.scriptor import *

async def main():
    print("Please select where to save the test-file.")
    testfile = File.from_string("Hello World", "save_test.txt")
    await testfile.save_dialog()

upload

The upload-method uploads the file to the ViUR file module (server-side storage). This is useful when you want to store a generated or processed file directly in ViUR instead of sending it to the user’s PC. The method returns the upload key of the newly created file entry. If you need the full file skeleton (e.g. to get the download URL or other metadata), you can pass return_full_skel=True.

#### scriptor ####
from viur.scriptor import *

async def main():
    file = File.from_string("Hello ViUR!", "hello_viur.txt")
    key = await file.upload()
    print(f"File uploaded successfully. Key: {key}")

By default, the file is uploaded to the root folder of the file module. If you want to upload it into a specific folder, pass the key of the target node as node:

#### scriptor ####
from viur.scriptor import *

async def main():
    folder_key = "your-folder-key-here"
    file = File.from_string("Hello ViUR!", "hello_viur.txt")
    key = await file.upload(node=folder_key)
    print(f"File uploaded to folder. Key: {key}")

If you need access to the full file skeleton after upload (e.g. to read the download URL or other metadata), set return_full_skel=True:

#### scriptor ####
from viur.scriptor import *

async def main():
    file = File.from_string("Hello ViUR!", "hello_viur.txt")
    skel = await file.upload(return_full_skel=True)
    print(f"Download URL: {skel['values']['downloadUrl']}")
    print(f"Full skeleton: {skel}")