@staticmethod
    @task
    def createreport(planid, stamp, request, language=None):
        logger.debug('Starting task to create a report.')
        try:
            plan = Plan.objects.get(pk=planid)
        except:
            logger.warn("Couldn't retrieve plan information for plan %d.", planid)
            return 

        tempdir = settings.WEB_TEMP
        filename = '%s_p%d_v%d_%s' % (plan.owner.username, plan.id, plan.version, stamp)

        logger.debug('Getting base geounits.')

        # Get the district mapping and order by geounit id
        mapping = plan.get_base_geounits()
        mapping.sort(key=lambda unit: unit[0])

        # Get the geounit ids we'll be iterating through
        geolevel = plan.legislative_body.get_base_geolevel()
        geounits = Geounit.objects.filter(geolevel=geolevel)
        max_and_min = geounits.aggregate(Min('id'), Max('id'))
        min_id = int(max_and_min['id__min'])
        max_id = int(max_and_min['id__max'])

        logger.debug('Getting district mapping.')

        # Iterate through the query results to create the district_id list
        # This ordering depends on the geounits in the shapefile matching the 
        # order of the imported geounits. If this ordering is the same, the 
        # geounits' ids don't have to match their fids in the shapefile
        sorted_district_list = list()
        row = None
        if len(mapping) > 0:
             row = mapping.pop(0)
        for i in range(min_id, max_id + 1):
            if row and row[0] == i:
                district_id = row[2]
                row = None
                if len(mapping) > 0:
                    row = mapping.pop(0)
            else:
                district_id = 'NA'
            sorted_district_list.append(str(district_id))

        logger.debug('Getting POST variables and settings.')
        
        info = plan.get_district_info()
        names = map(lambda i:i[0], info)
        nseats = map(lambda i:i[1], info) # can't do it in the lambda
        nseats = reduce(lambda x,y: x+y, nseats, 0)
        # needs to be a str because of join() below
        magnitude = map(lambda i:str(i[1]), info)

        logger.debug('Firing web worker task.')

        dispatcher = HttpDispatchTask()

        # Callbacks do not fire for HttpDispatchTask -- why not?
        #
        #def failure(self, exc, task_id, args, kwargs, einfo=None):
        #    self.log.get_default_logger().info('  CALLBACK: Failure!')
        #def success(self, retval, task_id, args, kwargs):
        #    self.log.get_default_logger().info('  CALLBACK: Success!')
        #
        #dispatcher.on_failure = failure
        #dispatcher.on_success = success

        # Increase the default timeout, just in case
        socket.setdefaulttimeout(600)

        result = dispatcher.delay(
            url = settings.BARD_SERVER + '/getreport/',
            method='POST',
            plan_id=planid,
            plan_owner=plan.owner.username,
            plan_version=plan.version,
            district_list=';'.join(sorted_district_list),
            district_names=';'.join(names),
            district_mags=';'.join(magnitude),
            nseats=nseats,
            pop_var=request['popVar'],
            pop_var_extra=request['popVarExtra'],
            ratio_vars=';'.join(request['ratioVars[]']),
            split_vars=request['splitVars'],
            block_label_var=request['blockLabelVar'],
            rep_comp=request['repComp'],
            rep_comp_extra=request['repCompExtra'],
            rep_spatial=request['repSpatial'],
            rep_spatial_extra=request['repSpatialExtra'],
            stamp=stamp) # TODO: Add language when BARD supports it.
        return
Create the data structures required for a BARD report, and call
dataset/ETHPy150Open PublicMapping/DistrictBuilder/django/publicmapping/redistricting/tasks.py/PlanReport.createreport