Exemplo n.º 1
0
 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()
Exemplo n.º 2
0
 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
Exemplo n.º 3
0
 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)
Exemplo n.º 4
0
 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)
Exemplo n.º 5
0
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')]
Exemplo n.º 6
0
def get_testing_accounts():
    return [
        val['pk'] for val in get_account_model().objects.filter(
            extra__contains='testing').values('pk')
    ]
Exemplo n.º 7
0
class AccountMixin(deployutils_mixins.AccountMixin):

    account_queryset = get_account_model().objects.all()
    account_lookup_field = 'slug'
    account_url_kwarg = 'organization'
Exemplo n.º 8
0
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]
Exemplo n.º 9
0
    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)