def migrate_completion_status(): created_at = datetime_or_now() with transaction.atomic(): scores_api = SuppliersAPIView() rollup_tree = scores_api.rollup_scores() for account in get_account_model().objects.all(): accounts = rollup_tree[0].get('accounts', {}) if account.pk in accounts: scores = accounts.get(account.pk, None) if scores: normalized_score = scores.get('normalized_score', None) if normalized_score is not None: assess_api = AssessmentAPIView() sample = Sample.objects.filter( extra__isnull=True, survey__title=ReportMixin.report_title, account=account).order_by( '-created_at').first() assess_api.freeze_scores( sample, includes=[sample], excludes=settings.TESTING_RESPONSE_IDS, created_at=created_at) improvement_score = scores.get('improvement_score', None) if improvement_score is not None: for sample in Sample.objects.filter( extra='is_planned', survey__title=ReportMixin.report_title, account=account): sample.is_frozen = True sample.save()
def aggregate_scores(self, metric, cohorts, cut=None, accounts=None): #pylint:disable=unused-argument if accounts is None: accounts = get_account_model().objects.all() scores = {} rollup_tree = self.rollup_scores() rollup_scores = self.get_drilldown(rollup_tree, metric.slug) for cohort in cohorts: score = 0 if isinstance(cohort, EditableFilter): if metric.slug == 'totals': # Hard-coded: on the totals matrix we want to use # a different metric for each cohort/column shown. rollup_scores = self.get_drilldown( rollup_tree, self.as_metric_candidate(cohort.slug)) includes, excludes = cohort.as_kwargs() nb_accounts = 0 for account in accounts.filter(**includes).exclude(**excludes): account_score = rollup_scores.get(account.pk, None) if account_score is not None: score += account_score.get('normalized_score', 0) nb_accounts += 1 if nb_accounts > 0: score = score / nb_accounts else: account = cohort account_score = rollup_scores.get(account.pk, None) if account_score is not None: score = account_score.get('normalized_score', 0) scores.update({str(cohort): score}) return scores
def handle(self, *args, **options): title = options['title'] slug = options['slug'] if not slug: slug = slugify(title) with transaction.atomic(): metric = EditableFilter.objects.create(slug=slug, title=title, tags='metric') EditablePredicate.objects.create( rank=1, editable_filter=metric, operator='startsWith', field='path', selector='keepmatching', operand='/%(slug)s/sustainability-%(slug)s' % {'slug': slug}) matrix = Matrix.objects.create( slug=slug, title=title, metric=metric, account=get_account_model().objects.get( slug=settings.APP_NAME)) cohort = EditableFilter.objects.create(slug="%s-1" % slug, title=title, tags='cohort') EditablePredicate.objects.create(rank=1, editable_filter=cohort, operator='contains', field='extra', selector='keepmatching', operand='"%(slug)s"' % {'slug': slug}) matrix.cohorts.add(cohort)
def print_updated_scores(self): """ Displays the scores for each account/scorecard as a text list. """ api = SuppliersAPIView() rollup_tree = api.rollup_scores() for account in get_account_model().objects.all(): self.stdout.write('"%s"' % account.printable_name) self.print_account_updated_scores(account, "/", rollup_tree)
class DashboardMixin(BenchmarkMixin): account_model = get_account_model() def get_requested_accounts(self): return [AccountType._make(val) for val in Subscription.objects.filter( plan__organization=self.account).select_related( 'organization').values_list('organization__pk', 'organization__slug', 'organization__full_name', 'organization__email', 'grant_key')] def get_accounts(self): return [AccountType._make(val) for val in Subscription.objects.filter( grant_key__isnull=True, plan__organization=self.account).select_related( 'organization').values_list('organization__pk', 'organization__slug', 'organization__full_name', 'organization__email', 'grant_key')]
def get_testing_accounts(): return [ val['pk'] for val in get_account_model().objects.filter( extra__contains='testing').values('pk') ]
class AccountMixin(deployutils_mixins.AccountMixin): account_queryset = get_account_model().objects.all() account_lookup_field = 'slug' account_url_kwarg = 'organization'
class DashboardMixin(BenchmarkMixin): account_model = get_account_model() @property def ends_at(self): if not hasattr(self, '_ends_at'): self._ends_at = self.request.GET.get('ends_at', None) if self._ends_at: self._ends_at = self._ends_at.strip('"') try: self._ends_at = datetime_or_now(self._ends_at) except ValueError: self._ends_at = datetime_or_now() return self._ends_at @property def is_frozen(self): return True @staticmethod def _get_answers(samples, metric_id, prefix=None, choice=None, includes=None): answers = """WITH samples AS (%(samples)s ), expected_opportunities AS ( SELECT survey_question.id AS question_id, samples.account_id AS account_id, samples.id AS sample_id, samples.extra AS is_planned, samples.slug AS sample_slug, samples.is_frozen AS is_completed FROM samples INNER JOIN survey_enumeratedquestions ON samples.survey_id = survey_enumeratedquestions.campaign_id INNER JOIN survey_question ON survey_question.id = survey_enumeratedquestions.question_id WHERE survey_question.path LIKE '%(prefix)s%%' ) SELECT expected_opportunities.account_id AS account_id, expected_opportunities.sample_slug AS sample_id, expected_opportunities.is_planned AS is_planned, CAST(survey_answer.measured AS FLOAT) AS numerator, CAST(survey_answer.denominator AS FLOAT) AS denominator, survey_answer.created_at AS last_activity_at, survey_answer.id AS answer_id, expected_opportunities.is_completed AS is_completed FROM expected_opportunities LEFT OUTER JOIN survey_answer ON expected_opportunities.question_id = survey_answer.question_id AND expected_opportunities.sample_id = survey_answer.sample_id WHERE survey_answer.metric_id = %(metric_id)s AND survey_answer.measured = %(choice)s""" % { 'samples': samples, 'metric_id': metric_id, 'choice': choice, 'prefix': prefix } _show_query_and_result(answers) return answers def _get_na_answers(self, population, metric_id, prefix=None, includes=None): latest_assessments = Consumption.objects.get_latest_samples_by_prefix( before=self.ends_at, prefix=prefix) return self._get_answers(latest_assessments, metric_id, prefix=prefix, choice=Consumption.NOT_APPLICABLE, includes=includes) def _get_planned_improvements(self, population, metric_id, prefix=None, includes=None): latest_improvements = Consumption.objects.get_latest_samples_by_prefix( before=self.ends_at, prefix=prefix, tag='is_planned') return self._get_answers( latest_improvements, metric_id, prefix=prefix, choice=Consumption.NEEDS_SIGNIFICANT_IMPROVEMENT, includes=includes) @property def requested_accounts(self): if not hasattr(self, '_requested_accounts'): ends_at = self.ends_at self._requested_accounts = [] level = set([self.account.pk]) next_level = level | set([ rec['organization'] for rec in Subscription.objects.filter( plan__organization__in=level).exclude( organization__in=get_testing_accounts()).values( 'organization').distinct() ]) try: extra = json.loads(self.account.extra) except (TypeError, ValueError): extra = None if extra and extra.get('supply_chain', None): while len(level) < len(next_level): level = next_level next_level = level | set([ rec['organization'] for rec in Subscription.objects.filter( plan__organization__in=level).exclude( organization__in=get_testing_accounts()). values('organization').distinct() ]) prev = None self._requested_accounts = [] for val in Subscription.objects.filter( ends_at__gt=ends_at, # from `SubscriptionMixin.get_queryset` plan__organization__in=level).select_related( 'organization').values_list( 'organization__pk', 'organization__slug', 'organization__full_name', 'organization__email', 'organization__phone', 'grant_key', 'extra', 'created_at', 'plan__organization__slug', 'plan__organization__full_name').order_by( 'organization__full_name', 'organization__pk'): account = AccountType._make(val) created_at = val[7] provider_slug = val[8] provider_full_name = val[9] if not prev: prev = account elif prev.pk != account.pk: self._requested_accounts += [prev] prev = account # aggregate grant_key, extra and reports_to if not account.request_key: prev.request_key = None try: extra = json.loads(account.extra) if not prev.extra: prev.extra = {} elif isinstance(prev.extra, six.string_types): try: prev.extra = json.loads(prev.extra) except (TypeError, ValueError): prev.extra = {} prev.extra.update(extra) except (TypeError, ValueError): pass prev.reports_to += [(provider_slug, provider_full_name, created_at)] if prev: self._requested_accounts += [prev] return self._requested_accounts def get_accounts(self): return [val for val in self.requested_accounts if not val.grant_key]
def create(self, request, *args, **kwargs): #pylint:disable=too-many-locals,too-many-statements if request.data: serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) supplier_managers = [serializer.validated_data] else: supplier_managers = get_supplier_managers(self.account) last_activity_at = Answer.objects.filter( sample=self.assessment_sample).aggregate(Max('created_at')).get( 'created_at__max', None) if not last_activity_at: raise ValidationError({'detail': "You cannot share a scorecard"\ " before completing the assessment."}) last_scored_assessment = Sample.objects.filter( is_frozen=True, extra__isnull=True, survey=self.survey, account=self.account).order_by('-created_at').first() if (not last_scored_assessment or last_scored_assessment.created_at < last_activity_at): # New activity since last record, let's freeze the assessment # and planning. with transaction.atomic(): last_scored_assessment = freeze_scores( self.assessment_sample, includes=self.get_included_samples(), excludes=self._get_filter_out_testing(), collected_by=self.request.user) if self.improvement_sample: freeze_scores(self.improvement_sample, includes=self.get_included_samples(), excludes=self._get_filter_out_testing(), collected_by=self.request.user) # send assessment updated and invite notifications data = supplier_managers status_code = None for supplier_manager in supplier_managers: supplier_manager_slug = supplier_manager.get('slug', None) if supplier_manager_slug: try: matrix = Matrix.objects.filter( account__slug=supplier_manager_slug, metric__slug='totals').select_related('account').get() # Supplier manager already has a dashboard LOGGER.info("%s shared %s assessment (%s) with %s", self.request.user, self.account, last_scored_assessment, supplier_manager_slug) # Update or create dashboard entry ends_at = datetime_or_now() + relativedelta(years=1) subscription_query = Subscription.objects.filter( organization=self.account, plan=Plan.objects.get(organization=matrix.account)) if subscription_query.exists(): # The Subscription already exists. The metadata ( # either requested by supplier manager or pro-actively # shared) was set on creation. We thus just need to # extend the end date and clear the grant_key. subscription_query.update(grant_key=None, ends_at=ends_at) else: # Create the subscription with a request_key, and # a extra tag to keep track of originator. Subscription.objects.create( organization=self.account, plan=Plan.objects.get(organization=matrix.account), ends_at=ends_at, extra='{"originator":"supplier"}') # send assessment updated. reason = supplier_manager.get('message', None) if reason: reason = force_text(reason) signals.assessment_completed.send( sender=__name__, assessment=last_scored_assessment, path=self.kwargs.get('path'), notified=matrix.account, reason=reason, request=self.request) if status_code is None: status_code = status.HTTP_201_CREATED except Matrix.DoesNotExist: # Registered supplier manager but no dashboard # XXX send hint to get paid version. LOGGER.error("%s shared %s assessment (%s) with matrix %s", self.request.user, self.account, last_scored_assessment, supplier_manager_slug) # XXX Should technically add all managers # of `supplier_manager_slug` account_model = get_account_model() try: dashboard_account = account_model.objects.get( slug=supplier_manager_slug) data = {} data.update(supplier_manager) data.update({ 'slug': dashboard_account.email, 'email': dashboard_account.email }) if status_code is None: status_code = status.HTTP_404_NOT_FOUND except account_model.DoesNotExist: raise ValidationError({ 'detail': _("Cannot find account '%s'") % supplier_manager_slug }) else: # Organization profile cannot be found. contact_email = supplier_manager.get('email', None) LOGGER.error("%s shared %s assessment (%s) with %s", self.request.user, self.account, last_scored_assessment, contact_email) data = {} data.update(supplier_manager) data.update({'slug': contact_email}) if status_code is None: status_code = status.HTTP_404_NOT_FOUND return Response(data, status=status_code)