How to use a CSV file upload to change an OptionFields' input dynamically

I have completed the computer science tutorial. I saw the extra tasks where it mentions to make the app more dynamic by asking a user to upload a file to analyze and decided to try it out.

Since the variable file is a FileField and FileField returns a type file I assumed the best approach would be to add a parameter in the extract_data() function where the file is passed. That did not work as it gives an error that says: “‘FileField’ object has no attribute ‘file’” (note the files are csv)

Here is the code:

def extract_data(file):
data = pd.read_csv(file, sep=‘;’)
return data

class Parametrization(ViktorParametrization):
#uploading file
file = FileField(‘Upload a CSV’, file_types=[‘.csv’])

#optionfields
main_column = OptionField('Choose main property', options=extract_data(file.file).columns.values.tolist())
count_column = OptionField('Choose property to analyse', options=extract_data(file.file).columns.values.tolist())
1 Like

Hi Ibrahim,

Welcome to the community! And thank you for your question. If I understand you correctly, you would like to have a dynamic list of options based on the uploaded csv file. To achieve this, some things need to be understood first:

  1. Callback functions

One of the ways to create a dynamic option field is by defining a function that produces a list of options based on certain input and logic. These functions need to be defined in a certain format before they can be added to the options argument of the OptionField object. The format is as follows:

def name_of_callback_function(params, **kwargs):
    ...
    return [...]

The input that can be passed on in the key word arguments are:

  • params: the parameters defined in the parametrization
  • entity_id
  • entity_name

For your example, only the params will be necessary.

  1. The FileField

The FileField is used to if a developer wants to upload a file of sorts. To be able to retrieve the uploaded file, this can be retrieved from the name of the variable on which the FileField is defined. In your case, the name file. The object that is obtained, however, is not a File object, but rather a FileResource object. A FileResource object holds both the properties file (which returns a File object) and filename.

  1. Combining both

Knowing about this, to achieve your case, let us first rewrite your callback function:

import StringIO
import pandas as pd
from viktor import File
from viktor.api_v1 import FileResource

def extract_data(params, **kwargs):
    my_csv_file_resource: FileResource = params.file
    if not my_csv_file_resource:
        # in the case of no file uploaded, return an empty list of options
        return []
    my_csv_file: File = my_csv_file_resource.file
    # convert `File` to `StringIO`
    csv_stringio = StringIO(my_csv_file.getvalue())
    # convert `StringIO` to `DataFrame`
    df = pd.read_csv(csv_stringio, sep=‘;’)
    options = df.columns.values.tolist()
    return options

(For more information on converting a string to a pandas DataFrame object, refer to this link.)

And then, the parametrization:

from viktor.parametrization import ViktorParametrization, FileField, OptionField

class Parametrization(ViktorParametrization):
    #uploading file
    file = FileField(‘Upload a CSV’, file_types=[‘.csv’])

    #optionfields
    main_column = OptionField('Choose main property', options=extract_data)
    count_column = OptionField('Choose property to analyse', options=extract_data)

For more information, refer to the following link, which also describes how callback functions can be used to make an OptionField dynamic.

I hope this answers your question.

2 Likes

I’ve also changed the title of this topic to better suit the discussion.