#!/usr/bin/env python
# DX_APP_WIZARD_NAME DX_APP_WIZARD_VERSION
# Generated by dx-app-wizard.
#
# Parallelized execution pattern (GTable input): Your app will
# subdivide a large chunk of work (in the form of an existing
# GenomicTable) into multiple pieces that can be processed in parallel
# and independently of each other, followed by a final "postprocess"
# stage that will perform any additional computations as necessary.
#
# See https://wiki.dnanexus.com/Developer-Portal for documentation and
# tutorials on how to modify this file.
#
# DNAnexus Python Bindings (dxpy) documentation:
#   http://autodoc.dnanexus.com/bindings/python/current/

import os
import dxpy

# This example will break an input GenomicTable into chunks of 100000
# rows each.
row_chunk_size = 100000

@dxpy.entry_point("postprocess")
def postprocess(process_outputs):
    # Change the following to process whatever input this stage
    # receives.  You may also want to copy and paste the logic to download
    # and upload files here as well if this stage receives file input
    # and/or makes file output.

    for output in process_outputs:
        pass

    return { "answer": "placeholder value" }

@dxpy.entry_point("process")
def process(gtable_id, start_row, end_row):
    DX_APP_WIZARD_PARALLELIZED_INPUT = dxpy.DXGTable(gtable_id)

    # The following loop iterates over each row from start_row to
    # end_row (not including end_row).  You can find documentation on
    # other useful GTable methods (such as iterating over a genomic
    # range query with iterate_query_rows) in the dxpy library here:
    # http://autodoc.dnanexus.com/bindings/python/current/dxpy_dxgtable.html

    for row in DX_APP_WIZARD_PARALLELIZED_INPUT.iterate_rows(start_row, end_row):
        # Fill in code here to perform whatever computation is
        # necessary to process the row.
        #
        # *row* is an array where the first element is the row ID, and
        # the rest of the elements appear in the same order as the
        # GTable's column specification.  You can retrieve the column
        # specifications or names by using
        # DX_APP_WIZARD_PARALLELIZED_INPUT.get_columns() or DX_APP_WIZARD_PARALLELIZED_INPUT.get_col_names().

        pass

    # If your subproblem is to compute some value over the rows it was
    # given, you can return it here:

    return { "output": "placeholder value" }

@dxpy.entry_point("main")
def main(DX_APP_WIZARD_INPUT_SIGNATURE):
DX_APP_WIZARD_INITIALIZE_INPUTDX_APP_WIZARD_DOWNLOAD_ANY_FILES
    # Split your input to be solved by the next stage of your app.
    # The following assumes you are splitting the input by giving,
    # 100000 rows of a GenomicTable per subjob running the "process"
    # entry point.

    num_rows = DX_APP_WIZARD_PARALLELIZED_INPUT.describe()["length"]

    subjobs = []

    for i in range(num_rows / row_chunk_size + (0 if num_rows % row_chunk_size == 0 else 1)):
        subjob_input = { "gtable_id": DX_APP_WIZARD_PARALLELIZED_INPUT.get_id(),
                         "start_row": row_chunk_size * i,
                         "end_row": min(row_chunk_size * (i + 1), num_rows)}
        subjobs.append(dxpy.new_dxjob(subjob_input, "process"))

    # The following line creates the job that will perform the
    # "postprocess" step of your app.  We've given it an input field
    # that is a list of job-based object references created from the
    # "process" jobs we just created.  Assuming those jobs have an
    # output field called "output", these values will be passed to the
    # "postprocess" job.  Because these values are not ready until the
    # "process" jobs finish, the "postprocess" job WILL NOT RUN until
    # all job-based object references have been resolved (i.e. the
    # jobs they reference have finished running).
    #
    # If you do not plan to have the "process" jobs create output that
    # the "postprocess" job will require, then you can explicitly list
    # the dependencies to wait for those jobs to finish by setting the
    # "depends_on" field to the list of subjobs to wait for (it
    # accepts either dxpy handlers or string IDs in the list).  We've
    # included this parameter in the line below as well for
    # completeness, though it is unnecessary if you are providing
    # job-based object references in the input that refer to the same
    # set of jobs.

    postprocess_job = dxpy.new_dxjob(fn_input={"process_outputs": [subjob.get_output_ref("output") for subjob in subjobs]},
                                     fn_name="postprocess",
                                     depends_on=subjobs)

    # If you would like to include any of the output fields from the
    # postprocess_job as the output of your app, you should return it
    # here using a job-based object reference.  If the output field is
    # called "answer", you can pass that on here as follows:
    #
    # return {"app_output_field": postprocess_job.get_output_ref("answer"), ...}
    #
    # Tip: you can include in your output at this point any open
    # objects (such as GTables) which are closed by a job that
    # finishes later.  The system will check to make sure that the
    # output object is closed and will attempt to clone it out as
    # output into the parent container only after all subjobs have
    # finished.

    output = {}
DX_APP_WIZARD_OUTPUT
    return output

dxpy.run()
