Source code for akvo.rsr.management.commands.remove_disaggregation_targets_dupes

# -*- coding: utf-8 -*-

# Akvo Reporting is covered by the GNU Affero General Public License.
# See more details in the license.txt file located at the root folder of the Akvo RSR module.
# For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.


from django.core.management.base import BaseCommand
from django.db.models import Count
from akvo.rsr.models import Indicator, IndicatorPeriod, DisaggregationTarget, IndicatorDisaggregationTarget


[docs]def find_duplicates(objects, project_relation): return objects.all()\ .values('id', 'disaggregation_targets__dimension_value', 'disaggregation_targets__dimension_value__value', project_relation)\ .annotate(duplicates=Count('disaggregation_targets__dimension_value'))\ .filter(duplicates__gt=1)
[docs]def handle_duplicates(type, perform_deletion=False, verbosity=1): if type == 'period': container_objects = IndicatorPeriod.objects target_objects = DisaggregationTarget.objects elif type == 'indicator': container_objects = Indicator.objects target_objects = IndicatorDisaggregationTarget.objects else: raise Exception('Type must be period or indicator, {} given.'.format(type)) project_relation = 'result__project' if type == 'indicator' else 'indicator__result__project' duplicates = find_duplicates(container_objects, project_relation) if verbosity > 0: print('Found {} duplicate disaggregation targets in {} {}s'.format( duplicates.count(), duplicates.values_list('id', flat=True).distinct().count(), type )) projects = set() if verbosity > 1: for dup in duplicates: print('{}: {}, dimension_value: {}, duplicates: {}, project: {}'.format(type, dup['id'], dup['disaggregation_targets__dimension_value'], dup['duplicates'], dup[project_relation])) projects.add(dup[project_relation]) print(f'Affected projects: {projects}') all_targets = [] tobe_removed = [] for dup in duplicates: object_key = '{}__id'.format(type) filters = { object_key: dup['id'], 'dimension_value__id': dup['disaggregation_targets__dimension_value'] } candidates = target_objects.filter(**filters).values_list('id', flat=True) all_targets.extend(candidates) tobe_removed.extend(candidates[1:]) if verbosity > 1: print('problematic {} disaggregation targets: {}, to be removed: {}'.format(type, len(all_targets), len(tobe_removed))) assert duplicates.count() == (len(all_targets) - len(tobe_removed)) if verbosity > 0: print('{} duplicate {} disaggregation targets will be removed...'.format(len(tobe_removed), type)) if perform_deletion: result = target_objects.filter(id__in=tobe_removed).delete() if verbosity > 0: print(result)
[docs]class Command(BaseCommand): help = ( "Script to delete disaggregation target duplication at indicator and period level." "\n\n" "Use the --delete option to actually delete the duplicate objects" )
[docs] def add_arguments(self, parser): parser.add_argument( '--delete', action='store_true', dest='delete', default=False, help='Actually delete duplicates' )
[docs] def handle(self, *args, **options): # parse options verbosity = int(options['verbosity']) perform_deletion = options['delete'] handle_duplicates('period', perform_deletion, verbosity) handle_duplicates('indicator', perform_deletion, verbosity)