コード例 #1
0
ファイル: scores.py プロジェクト: djaodjin/envconnect
def freeze_scores(sample, includes=None, excludes=None,
                  collected_by=None, created_at=None, segment_path=None):
    #pylint:disable=too-many-arguments,disable=too-many-locals
    # This function must be executed in a `transaction.atomic` block.
    LOGGER.info("freeze scores for %s based of sample %s",
        sample.account, sample.slug)
    created_at = datetime_or_now(created_at)
    if not segment_path:
        segment_path = '/'
    score_sample = Sample.objects.create(
        created_at=created_at,
        campaign=sample.campaign,
        account=sample.account,
        extra=sample.extra,
        is_frozen=True)
    # Copy the actual answers
    score_metric_id = Metric.objects.get(slug='score').pk
    for answer in Answer.objects.filter(
            sample=sample,
            question__path__startswith=segment_path).exclude(
            metric_id=score_metric_id):
        answer.pk = None
        answer.created_at = created_at
        answer.sample = score_sample
        answer.save()
        LOGGER.debug("save(created_at=%s, question_id=%s, metric_id=%s,"\
            " measured=%s, denominator=%s, collected_by=%s,"\
            " sample=%s)",
            answer.created_at, answer.question_id, answer.metric_id,
            answer.measured, answer.denominator, answer.collected_by,
            answer.sample)
    # Create frozen scores for answers we can derive a score from
    # (i.e. assessment).
    assessment_metric_id = Metric.objects.get(slug='assessment').pk
    calculator = get_score_calculator(segment_path)
    scored_answers = calculator.get_scores(
        sample.campaign, assessment_metric_id, prefix=segment_path,
        includes=includes, excludes=excludes)
    for decorated_answer in scored_answers:
        if (decorated_answer.answer_id and
            decorated_answer.is_planned == sample.extra):
            numerator = decorated_answer.numerator
            denominator = decorated_answer.denominator
            LOGGER.debug("create(created_at=%s, question_id=%s,"\
                " metric_id=%s, measured=%s, denominator=%s,"\
                " collected_by=%s, sample=%s)",
                created_at, decorated_answer.id, score_metric_id,
                numerator, denominator, collected_by, score_sample)
            _ = Answer.objects.create(
                created_at=created_at,
                question_id=decorated_answer.id,
                metric_id=score_metric_id,
                measured=numerator,
                denominator=denominator,
                collected_by=collected_by,
                sample=score_sample)
    sample.created_at = datetime_or_now()
    sample.save()
    return score_sample
コード例 #2
0
 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
コード例 #3
0
ファイル: dashboards.py プロジェクト: dwino92/envconnect
 def paginate_queryset(self, queryset, request, view=None):
     expired_at = datetime_or_now() - relativedelta(year=1)
     self.no_assessment = 0
     self.abandoned = 0
     self.expired = 0
     self.assessment_phase = 0
     self.improvement_phase = 0
     self.completed = 0
     for account in queryset:
         last_activity_at = account.get('last_activity_at', None)
         if last_activity_at:
             if account.get('assessment_completed', False):
                 if account.get('improvement_completed', False):
                     if last_activity_at < expired_at:
                         self.expired += 1
                     else:
                         self.completed += 1
                 else:
                     if last_activity_at < expired_at:
                         self.abandoned += 1
                     else:
                         self.improvement_phase += 1
             else:
                 if last_activity_at < expired_at:
                     self.abandoned += 1
                 else:
                     self.assessment_phase += 1
         else:
             self.no_assessment += 1
     return super(CompletionSummaryPagination, self).paginate_queryset(
         queryset, request, view=view)
コード例 #4
0
    def populate_historical_scores(self, organization):
        if not isinstance(organization, Organization):
            organization = Organization.objects.get(slug=organization)
        assessment_sample = Sample.objects.filter(
            extra__isnull=True, survey=self.survey,
            account=organization).order_by('-created_at').first()

        # Backup current answers
        backups = {}
        for answer in Answer.objects.filter(sample=assessment_sample,
                                            metric_id=1):
            backups[answer.pk] = answer.measured

        today = datetime_or_now()
        for months in [6, 12, 24]:
            for answer in Answer.objects.filter(sample=assessment_sample,
                                                metric_id=1):
                choices = [1, 2, 3, 4]
                answer.measured = random.choice(choices[(answer.measured -
                                                         1):])
                answer.save()
            created_at = today - relativedelta(months=months)
            score_sample = freeze_scores(assessment_sample,
                                         includes=[assessment_sample.pk],
                                         excludes=get_testing_accounts(),
                                         created_at=created_at)
            # XXX Sample.created_at is using `auto_now_add`
            score_sample.created_at = created_at
            score_sample.save()

        # Restore backup
        for answer in Answer.objects.filter(sample=assessment_sample,
                                            metric_id=1):
            answer.measured = backups[answer.pk]
            answer.save()
コード例 #5
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()
コード例 #6
0
 def get_context_data(self, **kwargs):
     context = super(SuppliersView, self).get_context_data(**kwargs)
     root, trail = self.breadcrumbs
     update_context_urls(context, {
         'api_suppliers': reverse('api_suppliers',
             args=(self.account, root)),
         'api_accessibles': site_prefixed(
             "/api/profile/%(account)s/plans/%(account)s-report/"\
             "subscriptions/" % {'account': self.account}),
         'api_organizations': site_prefixed("/api/profile/"),
         'api_organization_profile': site_prefixed(
             "/api/profile/%(account)s/" % {'account': self.account}),
         'download': reverse('reporting_organization_download',
                             args=(self.account, root)),
         'improvements_download': reverse(
             'reporting_organization_improvements_download',
             args=(self.account, root))
     })
     try:
         extra = json.loads(self.account.extra)
     except (IndexError, TypeError, ValueError) as err:
         extra = {}
     start_at = extra.get('start_at', None)
     context.update({
         'score_toggle': True,
         'account_extra': self.account.extra,
         'date_range': {
             'start_at': start_at,
             'ends_at': (datetime_or_now() + relativedelta(days=1)
             ).isoformat(),
         }
     })
     return context
コード例 #7
0
ファイル: mixins.py プロジェクト: MorganShorter/envconnect
 def ends_at(self):
     if not hasattr(self, '_ends_at'):
         if self.sample.is_frozen:
             self._ends_at = self.sample.created_at
         else:
             self._ends_at = datetime_or_now()
     return self._ends_at
コード例 #8
0
ファイル: suppliers.py プロジェクト: MorganShorter/envconnect
def get_supplier_managers(account):
    ends_at = datetime_or_now()
    queryset = Subscription.objects.filter(
        ends_at__gt=ends_at,
        organization=account).select_related('plan__organization').values_list(
            'plan__organization__slug', 'plan__organization__full_name')
    supplier_managers = [{
        'slug': supplier_manager[0],
        'printable_name': supplier_manager[1]
    } for supplier_manager in queryset]
    return supplier_managers
コード例 #9
0
 def get_context_data(self, **kwargs):
     context = {'base_url': self.get_base_url()}
     organization = self.kwargs.get('organization', None)
     if organization:
         for accessible in self.get_accessibles(self.request):
             if accessible['slug'] == organization:
                 context.update({'organization': accessible})
                 break
     from_root, trail = self.breadcrumbs
     root = None
     if trail:
         root = self._build_tree(trail[-1][0],
                                 from_root,
                                 cut=TransparentCut())
         # Flatten icons and practices (i.e. Energy Efficiency) to produce
         # the list of charts.
         for element in six.itervalues(root[1]):
             for chart in self.score_charts:
                 # We use `score_charts`, not `get_printable_charts` because
                 # not all top level icons might show up in the benchmark
                 # graphs, yet we need to display the scores under the icons.
                 if element[0]['slug'] == chart['slug']:
                     if 'normalized_score' in chart:
                         element[0][
                             'normalized_score'] = "%s%%" % chart.get(
                                 'normalized_score')
                     else:
                         element[0]['normalized_score'] = "N/A"
                     element[0]['score_weight'] = chart.get(
                         'score_weight', "N/A")
                     break
         charts = self.get_printable_charts()
         for chart in charts:
             if chart['slug'] == 'totals':
                 context.update({
                     'total_chart':
                     chart,
                     'nb_respondents':
                     chart.get('nb_respondents', "N/A")
                 })
                 break
         context.update({
             'charts':
             [chart for chart in charts if chart['slug'] != 'totals'],
             'breadcrumbs':
             trail,
             'root':
             root,
             'at_time':
             datetime_or_now()
         })
     return context
コード例 #10
0
    def post(self, request, *args, **kwargs):
        """
        Uploads a static asset file.

        **Examples

        .. code-block:: http

            POST /api/assets/ HTTP/1.1

        """
        #pylint: disable=unused-argument,too-many-locals
        uploaded_file = request.data['file']
        if self.content_type:
            # We optionally force the content_type because S3Store uses
            # mimetypes.guess and surprisingly it doesn't get it correct
            # for 'text/css'.
            uploaded_file.content_type = self.content_type
        sha1 = hashlib.sha1(uploaded_file.read()).hexdigest()

        # Store filenames with forward slashes, even on Windows
        filename = force_text(uploaded_file.name.replace('\\', '/'))
        sha1_filename = sha1 + os.path.splitext(filename)[1]
        storage = get_default_storage(self.request, self.account)
        stored_filename = sha1_filename if self.store_hash else filename
        prefix = request.data.get('prefix', None)
        if prefix is not None:
            stored_filename = urljoin(prefix, stored_filename)

        result = {}
        if storage.exists(stored_filename):
            if self.replace_stored:
                storage.delete(stored_filename)
                storage.save(stored_filename, uploaded_file)
                response_status = status.HTTP_201_CREATED
            else:
                result = {
                    "message": "%s is already in the gallery." % filename
                }
                response_status = status.HTTP_200_OK
        else:
            storage.save(stored_filename, uploaded_file)
            response_status = status.HTTP_201_CREATED
        result.update({
            'location': storage.url(stored_filename),
            'updated_at': datetime_or_now(),
            'tags': []
        })
        return Response(self.get_serializer().to_representation(result),
                        status=response_status)
コード例 #11
0
 def freeze_scores(sample,
                   includes=None,
                   excludes=None,
                   collected_by=None,
                   created_at=None):
     LOGGER.info("freeze scores for %s", sample.account)
     created_at = datetime_or_now(created_at)
     scored_answers = get_scored_answers(
         population=Consumption.objects.get_active_by_accounts(
             excludes=excludes),
         includes=includes)
     score_sample = Sample.objects.create(created_at=created_at,
                                          survey=sample.survey,
                                          account=sample.account,
                                          extra='completed',
                                          is_frozen=True)
     with connection.cursor() as cursor:
         cursor.execute(scored_answers, params=None)
         col_headers = cursor.description
         decorated_answer_tuple = namedtuple(
             'DecoratedAnswerTuple', [col[0] for col in col_headers])
         for decorated_answer in cursor.fetchall():
             decorated_answer = decorated_answer_tuple(*decorated_answer)
             if decorated_answer.answer_id:
                 numerator = decorated_answer.numerator
                 denominator = decorated_answer.denominator
                 _ = Answer.objects.create(created_at=created_at,
                                           question_id=decorated_answer.id,
                                           metric_id=2,
                                           measured=numerator,
                                           denominator=denominator,
                                           collected_by=collected_by,
                                           sample=score_sample,
                                           rank=decorated_answer.rank)
     sample.created_at = datetime_or_now()
     sample.save()
     return score_sample
コード例 #12
0
    def post(self, request, *args, **kwargs):
        """
        Uploads a static asset file.

        **Examples

        .. code-block:: http

            POST /api/assets/ HTTP/1.1

        """
        #pylint: disable=unused-argument,too-many-locals
        uploaded_file = request.data['file']
        if self.content_type:
            # We optionally force the content_type because S3Store uses
            # mimetypes.guess and surprisingly it doesn't get it correct
            # for 'text/css'.
            uploaded_file.content_type = self.content_type
        sha1 = hashlib.sha1(uploaded_file.read()).hexdigest()

        # Store filenames with forward slashes, even on Windows
        filename = force_text(uploaded_file.name.replace('\\', '/'))
        sha1_filename = sha1 + os.path.splitext(filename)[1]
        storage = get_default_storage(self.request, self.account)
        stored_filename = sha1_filename if self.store_hash else filename
        prefix = request.data.get('prefix', None)
        if prefix is not None:
            stored_filename = urljoin(prefix, stored_filename)

        result = {}
        if storage.exists(stored_filename):
            if self.replace_stored:
                storage.delete(stored_filename)
                storage.save(stored_filename, uploaded_file)
                response_status = status.HTTP_201_CREATED
            else:
                result = {
                    "message": "%s is already in the gallery." % filename}
                response_status = status.HTTP_200_OK
        else:
            storage.save(stored_filename, uploaded_file)
            response_status = status.HTTP_201_CREATED
        result.update({
            'location': storage.url(stored_filename),
            'updated_at': datetime_or_now(),
            'tags': []
            })
        return Response(self.get_serializer().to_representation(result),
            status=response_status)
コード例 #13
0
ファイル: suppliers.py プロジェクト: djaodjin/envconnect
def get_supplier_managers(account, ends_at=None):
    ends_at = datetime_or_now(ends_at)
    queryset = Subscription.objects.filter(ends_at__gt=ends_at,
                                           organization=account)
    if is_testing(account):
        queryset = queryset.filter(
            plan__organization__extra__contains='testing')
    else:
        queryset = queryset.exclude(
            plan__organization__extra__contains='testing')
    queryset = queryset.select_related('plan__organization').values_list(
        'plan__organization__slug', 'plan__organization__full_name')
    supplier_managers = [{
        'slug': supplier_manager[0],
        'printable_name': supplier_manager[1]
    } for supplier_manager in queryset]
    return supplier_managers
コード例 #14
0
 def get_filename(self):
     return datetime_or_now().strftime(self.basename + '-%Y%m%d.xlsx')
コード例 #15
0
ファイル: reactions.py プロジェクト: VanL/djaodjin-pages
 def perform_create(self, serializer):
     if not is_authenticated(self.request):
         raise PermissionDenied()
     serializer.save(created_at=datetime_or_now(),
         element=self.element, user=self.request.user)
コード例 #16
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)