'This will calculate the vulnerability_aggregates for the specified
records. Either all (when rebuild_all is invoked) or for the
individual parameter(s) at the specified location(s) when run
onapprove - which currently happens inside the approve_report()
controller.
This will get the raw data from vulnerability_data and generate a
vulnerability_aggregate record for the given time period.
The reason for doing this is so that all aggregated data can be
obtained from a single table. So when displaying data for a
particular location it will not be necessary to try the aggregate
table, and if it\'s not there then try the data table. Rather just
look at the aggregate table.
Once this has run then a complete set of aggregate records should
exists for this parameter_id and location for every time period from
the first data item until the current time period.
Where appropriate add test cases to modules/unit_tests/s3db/vulnerability.py'

@staticmethod
def vulnerability_update_aggregates(records=None):

    if (not records):
        return
    import datetime
    from dateutil.rrule import rrule, YEARLY
    db = current.db
    s3db = current.s3db
    dtable = s3db.vulnerability_data
    atable = db.vulnerability_aggregate
    gtable = db.gis_location
    param_location_dict = {}
    location_dict = {}
    vulnerability_pids = s3db.vulnerability_pids()
    aggregated_period = S3VulnerabilityModel.vulnerability_aggregated_period
    if isinstance(records, basestring):
        records = json.loads(records)
    if isinstance(records[0]['date'], (datetime.date, datetime.datetime)):
        from_json = False
    else:
        from_json = True
        from dateutil.parser import parse
    for record in records:
        data_id = record['data_id']
        location_id = record['location_id']
        parameter_id = record['parameter_id']
        if ((not location_id) or (not parameter_id)):
            current.log.warning(('Skipping    bad    vulnerability_data    record    with    data_id    %s    ' % data_id))
            continue
        if from_json:
            date = parse(record['date'])
        else:
            date = record['date']
        (start_date, end_date) = aggregated_period(date)
        query = ((((dtable.location_id == location_id) & (dtable.parameter_id == parameter_id)) & (dtable.deleted != True)) & (dtable.approved_by != None))
        data_rows = db(query).select(dtable.data_id, dtable.date, dtable.value)
        earliest_period = start_date
        (last_period, end_date) = aggregated_period(None)
        data = {}
        data[start_date] = Storage(date=date, id=data_id, value=record['value'])
        for row in data_rows:
            if (row.data_id == data_id):
                continue
            row_date = row.date
            (start_date, end_date) = aggregated_period(row_date)
            if (start_date in data):
                if (row_date <= data[start_date]['date']):
                    continue
            elif (start_date < earliest_period):
                earliest_period = start_date
            data[start_date] = Storage(date=row_date, id=row.data_id, value=row.value)
        query = ((atable.location_id == location_id) & (atable.parameter_id == parameter_id))
        aggr_rows = db(query).select(atable.id, atable.agg_type, atable.date, atable.end_date, atable.mean)
        aggr = {}
        for row in aggr_rows:
            (start_date, end_date) = aggregated_period(row.date)
            aggr[start_date] = Storage(id=row.id, type=row.agg_type, end_date=row.end_date, mean=row.mean)
        last_data_period = earliest_period
        last_type_agg = False
        last_data_value = None
        changed_periods = []
        for dt in rrule(YEARLY, dtstart=earliest_period, until=last_period):
            dt = dt.date()
            if (dt != last_period):
                (start_date, end_date) = aggregated_period(dt)
            else:
                start_date = dt
                end_date = None
            if (dt in aggr):
                agg_type = aggr[dt]['type']
                if (agg_type == 2):
                    last_type_agg = True
                    last_data_value = aggr[dt]['mean']
                    continue
                query = (atable.id == aggr[dt]['id'])
                if (agg_type == 3):
                    if (dt in data):
                        value = data[dt]['value']
                        last_data_value = value
                        db(query).update(agg_type=1, reported_count=1, ward_count=1, end_date=end_date, sum=value, min=value, max=value, mean=value, median=value)
                        changed_periods.append((start_date, end_date))
                    elif last_type_agg:
                        continue
                    elif (aggr[dt]['mean'] != last_data_value):
                        value = last_data_value
                        db(query).update(agg_type=3, reported_count=1, ward_count=1, end_date=end_date, sum=value, min=value, max=value, mean=value, median=value)
                        changed_periods.append((start_date, end_date))
                elif (agg_type == 1):
                    if (dt in data):
                        value = data[dt]['value']
                        last_data_value = value
                        if (aggr[dt]['mean'] != value):
                            db(query).update(agg_type=1, reported_count=1, ward_count=1, end_date=end_date, sum=value, min=value, max=value, mean=value, median=value)
                            changed_periods.append((start_date, end_date))
                    else:
                        value = last_data_value
                        db(query).update(agg_type=3, reported_count=1, ward_count=1, end_date=end_date, sum=value, min=value, max=value, mean=value, median=value)
                        changed_periods.append((start_date, end_date))
            else:
                if (dt in data):
                    value = data[dt]['value']
                    agg_type = 1
                    last_data_value = value
                else:
                    value = last_data_value
                    agg_type = 3
                atable.insert(parameter_id=parameter_id, location_id=location_id, agg_type=agg_type, reported_count=1, ward_count=1, date=start_date, end_date=end_date, sum=value, min=value, max=value, mean=value, median=value)
                changed_periods.append((start_date, end_date))
        if (changed_periods == []):
            continue
        if (parameter_id not in param_location_dict):
            param_location_dict[parameter_id] = {location_id: changed_periods}
        elif (location_id not in param_location_dict[parameter_id]):
            param_location_dict[parameter_id][location_id] = changed_periods
        elif (changed_periods[0][0] < param_location_dict[parameter_id][location_id][0][0]):
            param_location_dict[parameter_id][location_id] = changed_periods
        if (parameter_id in vulnerability_pids):
            if (location_id not in location_dict):
                location_dict[location_id] = changed_periods
            elif (changed_periods[0][0] < location_dict[location_id][0][0]):
                location_dict[location_id] = changed_periods
    parents = {}
    get_parents = current.gis.get_parents
    for loc_id in location_dict.keys():
        parents[loc_id] = get_parents(loc_id)
    parents_data = {}
    for (param_id, loc_dict) in param_location_dict.items():
        for (loc_id, periods) in loc_dict.items():
            if (loc_id in parents):
                for p_loc_row in parents[loc_id]:
                    p_loc_id = p_loc_row.id
                    if (param_id in parents_data):
                        if (p_loc_id in parents_data[param_id]):
                            if (periods[0][0] < parents_data[param_id][p_loc_id][0][0][0]):
                                parents_data[param_id][p_loc_id][0] = periods
                        else:
                            parents_data[param_id][p_loc_id] = [periods]
                    else:
                        parents_data[param_id] = {p_loc_id: [periods]}
    async = current.s3task.async
    for (param_id, loc_dict) in parents_data.items():
        for (loc_id, (changed_periods,)) in loc_dict.items():
            for (start_date, end_date) in changed_periods:
                (s, e) = (str(start_date), str(end_date))
                async('vulnerability_update_location_aggregate', args=[loc_id, param_id, s, e], timeout=1800)
    resilence_parents = {}
    for (loc_id, periods) in location_dict.items():
        resilence_parents[loc_id] = (periods, True)
        for p_loc_row in parents[loc_id]:
            p_loc_id = p_loc_row.id
            if (p_loc_id in resilence_parents):
                if (periods[0][0] < resilence_parents[p_loc_id][0][0][0]):
                    resilence_parents[p_loc_id][0] = periods
            else:
                resilence_parents[p_loc_id] = [periods, False]
    vulnerability_resilience = S3VulnerabilityModel.vulnerability_resilience
    resilience_pid = s3db.vulnerability_resilience_id()
    for (location_id, (period, use_location)) in resilence_parents.items():
        for (start_date, end_date) in changed_periods:
            vulnerability_resilience(location_id, resilience_pid, vulnerability_pids, start_date, end_date, use_location)
