Skip to content

Contributing: Adding Output Formats

Basile Maret edited this page Jan 16, 2020 · 2 revisions

This page explains how to implement new file output formats for BUVIC.

Output format types

We distinguish two different output types in BUVIC:

  1. "Per section": One output file is created per section of the UV file
  2. "Per file": One output file is created per UV file

An example of "Per section" output are the "qasume" files. Each section of the UV file is converted into one spectrum which is written into one file. Each UV file therefore produces multiple qasume files.

An example of "Per file" output are the "WOUDC" files. One UV file produces exactly one "WOUDC" file which contains multiple spectra.

Creating an Output subclass

The easiest way to create a new output format is to create a subclass of Output for your format. Let's call this subclass NewFormatOutput in this example. Your subclass can be added at the end of the output.py file and should look like this for a "Per file" format:

class NewFormatOutput(Output):
    def _get_name(self, result: Result) -> str:
        """
        Get the name of the output file, inclusive its path relative to BUVIC's output dir

        Note: This method needs to be implemented in subclasses
        :param result: the result from which to get the data to create the name
        :return: the file name
        """
        return "relative/path/your_file_name.csv"

    def _get_output_type(self) -> OutputType:
        """
        Get the type of this output which will define if one file will be generated by section or per UV file.

        Note: This method needs to be implemented in subclasses
        :return: the output type
        """
        return OutputType.PER_FILE

    def _get_multi_result_content(self, results: List[Result]) -> str:
        """
        Get the content of the output file for a given list of results.

        This method needs to be implemented if the type of this output is "PER_FILE".
        :param results: the results for which to create the output file.
        :return: the content of the file
        """
        
        content = "TODO: Generate the file content from the given results"

        return content

For a "Per section" format, the subclass is similar but the method _get_output_type should return OutputType.PER_SECTION and instead of implementing _get_multi_result_content, you will need the following method:

    def _get_single_result_content(self, result: Result) -> str:
        """
        Get the content of the output file for a given result.

        This method needs to be implemented if the type of this output is "PER_SECTION".
        :param result: the result for which to create the output file.
        :return: the content of the file
        """
        
        content = "TODO: Generate the file content from the given result"

        return content

Note that the _get_name method returns the file name with its relative path. The easiest way to get a relative path is to call result.get_relative_path().

Calling Your Output subclass

Calls to Output subclasses are done in method _generate_output of class CalculationUtils. In this method, the results are first grouped by date. For each date, the results of the group are then sorted by time of the measurement and output jobs are created for the group.

You will need to create output jobs for your format and this can be done easily with the class created in the previous section. After the check for the results length (if len(results) != 0:), add the following lines:

# Create the jobs to create the outputs for your format
your_format_jobs = NewFormatOutput(self._output_dir, results).get_jobs()

# Add the jobs to the list of jobs to execute
output_jobs.extend(your_format_jobs)

The jobs will then be executed on a ThreadPoolExecutor later in the method and your output files will be created.

Showing the output files in the GUI

We finally add a download link to the list of generated files.

At the end of the method _create_result_gui of class ResultWidget in file widgets.py, add a new gui.FileDownloader widget with the following lines (for a "Per file" format):

# Create the link
download_button = FileDownloadLink(
    "relative/path/your_file_name.csv",
    True  # Setting this to true will make the link stand apart from the other one by adding an extra margin
)

# Add the link to the container
vbox.append(download_button)

The name (with relative path) should be the same as the one created in NewFormatOutput._get_name().

If you have a "Per section" format, you can loop through the list results with a for loop.

Clone this wiki locally