Beispiel #1
0
    def get_context_data(self, **kwargs):
        context = super(DashboardView, self).get_context_data(**kwargs)
        context['list_fournisseur'] = self.model.objects.values(
            'lignes__produit__fournis',
            'lignes__produit__fournis__user__last_name',
            'lignes__produit__fournis__user__first_name').annotate(
                chiffre_affaire=Sum(
                    ExpressionWrapper(F('lignes__qte') *
                                      F('lignes__produit__prix'),
                                      output_field=fields.FloatField())))
        print(context['list_fournisseur'])
        context['table_fournisseur'] = ChiffreFournisseurTab(
            context['list_fournisseur'])
        context['list_client'] = self.model.objects.all().values(
            'client', 'client__user__last_name',
            'client__user__first_name').annotate(chiffre_affaire=Sum(
                ExpressionWrapper(F('lignes__qte') *
                                  F('lignes__produit__prix'),
                                  output_field=fields.FloatField()))).order_by(
                                      '-chiffre_affaire')

        context['table_client'] = ChiffreClientTab(context['list_client'])
        print(context['list_client'])
        context['chart_jour'] = JourChart()
        context['chart_categorie'] = CategorieChart()
        return context
Beispiel #2
0
    def _add_quality_relevance(self, qs):
        """Annotates query with relevance based on quality score.

        It is calculated by a formula:
            R = log(Q + 1) + 0.1 * v
        Where:
            R - Relevance;
            Q - Quality score (0 to 5);
            v - 1 if collection belongs to a partner namespace, otherwise 0.
        """
        quality_rank_expr = (
            Func(Coalesce(F('latest_version__quality_score'), 0) + 1,
                 function='log') * CONTENT_SCORE_MULTIPLIER)
        vendor_rank_expr = Case(
            When(namespace__is_vendor=True, then=Value(VENDOR_RANK)),
            When(namespace__is_vendor=False, then=Value(0)),
        )
        relevance_expr = F('quality_rank') + F('vendor_rank')
        return qs.annotate(
            quality_rank=Expr(quality_rank_expr,
                              output_field=db_fields.FloatField()),
            vendor_rank=Expr(vendor_rank_expr,
                             output_field=db_fields.FloatField()),
            relevance=Expr(relevance_expr,
                           output_field=db_fields.FloatField()),
        )
Beispiel #3
0
 def get_queryset(self):
     active = Count("optionwheel", filter=Q(optionwheel__is_active=True))
     completed = Count("optionwheel",
                       filter=Q(optionwheel__is_active=False))
     profit = Sum(F("optionwheel__total_profit") *
                  F("optionwheel__quantity"),
                  filter=Q(optionwheel__is_active=False),
                  output_field=fields.DecimalField())
     collateral = Sum(F("optionwheel__collatoral") *
                      F("optionwheel__quantity"),
                      filter=Q(optionwheel__is_active=False),
                      output_field=fields.DecimalField())
     total_wheels = Sum(F("optionwheel__quantity"),
                        filter=Q(optionwheel__is_active=False))
     total_days_active_weighted_by_collateral = Sum(
         F("optionwheel__total_days_active") * F("optionwheel__quantity") *
         F("optionwheel__collatoral"),
         filter=Q(optionwheel__is_active=False))
     average_days = Cast(total_days_active_weighted_by_collateral,
                         fields.FloatField()) / Cast(
                             collateral, fields.FloatField())
     annualized_rate_of_return = Power(
         1 + profit / collateral,
         BUSINESS_DAYS_IN_YEAR / Coalesce(average_days, 252))
     users = User.objects.annotate(
         active=active,
         completed=completed,
         profit=Round(100 * Coalesce(profit, 0)),
         collateral=Round(100 * Coalesce(collateral, 0)),
         return_percentage=Coalesce(profit / collateral, 0),
         total_wheels=Coalesce(total_wheels, 0),
         average_days=Coalesce(average_days, 0),
         annualized_rate_of_return=Coalesce(annualized_rate_of_return, 0),
     )
     return users
Beispiel #4
0
class Game(models.Model):
    guid = fields.UUIDField(primary_key=True)

    n_cells = fields.PositiveIntegerField()
    cell_area = fields.FloatField()

    temperature = fields.FloatField(blank=True, null=True)
    months_elapsed = fields.PositiveIntegerField(blank=True, null=True)

    arrays = models.FileField(blank=True, null=True, upload_to=arrays_upload_location)
Beispiel #5
0
def _front_page(
    paging_size=settings.PAGING_SIZE,
    page=0,
    add_filter={},
    add_q=[],
    as_of=None,
    days_back=50,
):
    # TODO: weighting https://medium.com/hacking-and-gonzo/how-hacker-news-ranking-algorithm-works-1d9b0cf2c08d
    # (P-1) / (T+2)^G
    if as_of is None:
        now = timezone.now()
    else:
        now = as_of
    if connection.vendor == "postgresql":
        now_value = Value(now, output_field=fields.DateTimeField())
        submission_age_float = ExpressionWrapper(
            (now_value - F("created_at")), output_field=fields.DurationField())
        submission_age_hours = ExpressionWrapper(
            Extract(F("tf"), "epoch") / 60 / 60 + 2.1,
            output_field=fields.FloatField())
        real_p = ExpressionWrapper(F("points") - 1,
                                   output_field=fields.FloatField())
        formula = ExpressionWrapper(F("p") / (Power(F("tfh"), F("g")) + 0.001),
                                    output_field=fields.FloatField())
        return (Story.objects.select_related("user").filter(
            duplicate_of__isnull=True).filter(points__gte=1).filter(
                created_at__gte=now -
                datetime.timedelta(days=days_back)).filter(
                    created_at__lte=now).filter(**add_filter).annotate(
                        tf=submission_age_float).
                annotate(tfh=submission_age_hours).annotate(p=real_p).annotate(
                    g=Value(1.8, output_field=fields.FloatField())).annotate(
                        formula=formula).order_by("-formula")[(
                            page * paging_size):(page + 1) * (paging_size)])
    elif connection.vendor == "sqlite":
        now_value = Value(now, output_field=fields.DateTimeField())
        submission_age_float = ExpressionWrapper(
            (now_value - F("created_at")), output_field=fields.FloatField())
        submission_age_hours = ExpressionWrapper(
            F("tf") / 60 / 60 / 1000000 + 2.1,
            output_field=fields.FloatField())
        real_p = ExpressionWrapper(F("points") - 1,
                                   output_field=fields.FloatField())
        formula = ExpressionWrapper(F("p") / (Power(F("tfh"), F("g")) + 0.001),
                                    output_field=fields.FloatField())
        return (Story.objects.select_related("user").filter(
            duplicate_of__isnull=True).filter(points__gte=1).filter(
                created_at__gte=now -
                datetime.timedelta(days=days_back)).filter(
                    created_at__lte=now).filter(**add_filter).annotate(
                        tf=submission_age_float).
                annotate(tfh=submission_age_hours).annotate(p=real_p).annotate(
                    g=Value(1.8, output_field=fields.FloatField())).annotate(
                        formula=formula).order_by("-formula")[(
                            page * paging_size):(page + 1) * (paging_size)])
    else:
        raise NotImplementedError(
            "No frontpage magic for database engine %s implemented" %
            (connection.vendor))
Beispiel #6
0
    def add_relevance(queryset):
        c = 'repository__community_score'
        d = 'repository__download_count'

        # ln((MOD*c + MIN) * d + 1)
        # where c = community_score and d = download_count
        # We're using the community_score as a modifier to the download count
        # instead of just allocating a certain number of points based on the
        # score. The reason for this is that the download score is
        # a logaritmic scale so adding a fixed number of points ended up
        # boosting scores way too much for content with low numbers of
        # downloads. This system allows for the weight of the community score
        # to scale with the number of downloads
        download_count_ln_expr = Func(
            (((Coalesce(F(c), 0) * COMMUNITY_SCORE_MODIFIER) +
                COMMUNITY_SCORE_MODIFIER_MIN)
                * F(d)) + 1,
            function='ln'
        )
        download_rank_expr = (
            F('download_count_ln')
            / (1 + F('download_count_ln'))
            * DOWNLOAD_RANK_MULTIPLIER
        )

        q = 'repository__quality_score'
        # This function is better than using a linear function because it
        # makes it so that the effect of losing the first few points is
        # relatively minor, which reduces the impact of errors in scoring.
        quality_rank_expr = (
            Func(Coalesce(F(q), 0) + 1, function='log')
            * CONTENT_SCORE_MULTIPLIER
        )

        relevance_expr = (
            F('search_rank') + F('download_rank') + F('quality_rank')
        )

        return queryset.annotate(
            download_count_ln=ExpressionWrapper(
                download_count_ln_expr,
                output_field=db_fields.FloatField()),
            download_rank=ExpressionWrapper(
                download_rank_expr,
                output_field=db_fields.FloatField()),
            quality_rank=ExpressionWrapper(
                quality_rank_expr,
                output_field=db_fields.FloatField()),
            relevance=ExpressionWrapper(
                relevance_expr,
                output_field=db_fields.FloatField()),
        )
Beispiel #7
0
    def add_keywords_filter(queryset, keywords):
        if not keywords:
            return queryset.annotate(
                search_rank=Value(0.0, output_field=db_fields.FloatField()))

        tsquery = six.moves.reduce(
            operator.and_,
            (psql_search.SearchQuery(kw) for kw in keywords))

        search_rank_fn = Func(
            F('search_vector'), tsquery, RANK_NORMALIZATION,
            function=RANK_FUNCTION, output_field=db_fields.FloatField())
        return (queryset.annotate(search_rank=search_rank_fn)
                .filter(search_vector=tsquery))
Beispiel #8
0
 def get_context_data(self, *, object_list=None, **kwargs):
     context = super().get_context_data(**kwargs)
     if self.request.user.has_perm('bill.add_client'):
         print("add yes ")
     else:
         print("no")
     if self.request.user.has_perm('bill.change_client'):
         print("change yes")
     else:
         print("no")
     if self.request.user.has_perm('bill.delete_client'):
         print("delete client")
     else:
         print("no")
     if self.request.user.has_perm('bill.view_client'):
         print("view yes ")
     else:
         print("no")
     data_filtred = ClientFilter(self.request.GET,
                                 queryset=self.model.objects.all())
     data_filtredQs = data_filtred.qs
     context["object_list"] = data_filtredQs.annotate(chiffre_affaire=Sum(
         ExpressionWrapper(F('facture__lignes__qte') *
                           F('facture__lignes__produit__prix'),
                           output_field=fields.FloatField())))
     context['object_table'] = self.table_class(context["object_list"])
     context['title'] = "Liste des clients"
     context['option'] = "Ajouter Client"
     context['ajouter_url'] = reverse('client_create')
     context['object_name'] = "Client"
     return context
Beispiel #9
0
    def add_relevance(queryset):
        download_count_ln_expr = Func(F('repository__download_count') + 1,
                                      function='ln')
        download_rank_expr = (F('download_count_ln') /
                              (1 + F('download_count_ln')) *
                              DOWNLOAD_RANK_MULTIPLIER)
        relevance_expr = ExpressionWrapper(F('search_rank') +
                                           F('download_rank'),
                                           output_field=db_fields.FloatField())

        return queryset.annotate(
            download_count_ln=download_count_ln_expr,
            download_rank=ExpressionWrapper(
                download_rank_expr, output_field=db_fields.FloatField()),
            relevance=relevance_expr,
        )
class DeliveryPack(Model):
    pack_id = fields.AutoField(primary_key=True)
    courier = ForeignKey(Courier,
                         on_delete=CASCADE,
                         default=None,
                         blank=True,
                         null=True)
    assign_time = fields.DateTimeField()
    delivery_type = fields.CharField(max_length=4,
                                     choices=Courier.COURIER_TYPE_CHOICES,
                                     default='foot')
    delivery_ended = fields.BooleanField(default=False)
    last_complete_time = fields.DateTimeField()
    total_weight = fields.FloatField(default=0)

    def orders(self):
        return self.order_set.all()

    def assign_order(self, order):
        order.delivery_pack = self
        self.total_weight += order.weight
        order.save()
        self.save()

    def make_order_free(self, order):
        if order not in self.orders():
            return
        order.delivery_pack = None
        order.delivery_time = None
        self.total_weight -= order.weight
        order.save()
        self.save()
Beispiel #11
0
class Order(models.Model):
    order_id = fields.IntegerField(primary_key=True)
    weight = fields.FloatField()
    region = fields.IntegerField()
    delivery_hours = pg_fields.ArrayField(fields.CharField(max_length=12))

    delivery_pack = ForeignKey(DeliveryPack, on_delete=CASCADE, default=None, blank=True, null=True)
    delivery_time = fields.IntegerField(default=None, blank=True, null=True)

    def is_completed(self):
        return self.delivery_time is not None

    def set_completed(self, seconds: int):
        if not self.is_completed():
            self.delivery_time = seconds

    def is_inside_working_time(self, courier: Courier):
        for available_period in self.delivery_hours:
            for delivery_period in courier.working_hours:
                result = time_utils.inside_bounds(available_period, delivery_period)
                if result:
                    return True
        return False

    def __getitem__(self, item):
        return getattr(self, item)

    def __setitem__(self, key, value):
        setattr(self, key, value)

    def get_list_of_fields(self):
        return self._meta.get_fields()
Beispiel #12
0
    def getFivedayWeightList(self):
        #class_name  = ['サービス業','情報・通信']
        basic_company = self.getBasicCompany()
        day = self.getBasicDay()

        result = DailyData.objects.select_related('company_code').all().values(
            'company_code__company_name',
            'company_code__address',
            'company_code__address_url',
        ).annotate(
            company_code=F('company_code'),
            company_name=F('company_code__company_name'),
            feature=F('company_code__feature'),
            fiveday_weight=F('fiveday_weight'),
            class_name=F('company_code__class_name'),
            address=F('company_code__address'),
            address_url=F('company_code__address_url'),
            price=F('finish_value') / 10,
            daily_per=F('daily_per'),
            baisu=ExpressionWrapper(
                F('company_code__kabu_sum_number') * Decimal('1.0') *
                F('finish_value') /
                (basic_company.kabu_sum_number * basic_company.finish_value),
                output_field=fields.FloatField()),
            nikkeichart_url=Concat(
                Value("https://www.nikkei.com/smartchart/?code="),
                F('company_code')),
            localchart_url=Concat(Value("/front/companyinfo/"),
                                  F('company_code')),
        ).filter(
            day=day,
            buy_signal=1,
            #daily_per__lte=30,daily_per__gte=0.01,
        ).order_by('daily_per')
        return result
Beispiel #13
0
    def _add_search_rank(self, qs):
        """Annotates query with search rank value.

        Search rank is calculated as result of `ts_rank` PostgreSQL
        function, which ranks vectors based on the frequency of their matching
        lexemes. Search rank is normalized by dividing it by itself + 1:
        """
        keywords = self.filters.get('keywords')
        if not keywords:
            return qs.annotate(
                search_rank=Value(0.0, output_field=db_fields.FloatField()))
        search_rank_fn = Func(F('search_vector'),
                              psql_search.SearchQuery(keywords),
                              RANK_NORMALIZATION,
                              function=RANK_FUNCTION,
                              output_field=db_fields.FloatField())
        return qs.annotate(search_rank=search_rank_fn)
Beispiel #14
0
class Random(Expression):
    output_field = fields.FloatField()

    def __repr__(self):
        return "Random()"

    def as_sql(self, compiler, connection):
        return connection.ops.random_function_sql(), []
Beispiel #15
0
def ranking_algorithm():
    # Based on Hacker News' post rank formula
    time_diff = functions.Cast(functions.Now() - F('created_time'),
                               fields.DurationField())
    hours = dtime.duration_to_hours(duration=time_diff)
    rank = ExpressionWrapper(
        (F('upvote') - F('devote') + 1) / ((hours + 2)**1.8),
        output_field=fields.FloatField())
    return hours, rank
Beispiel #16
0
    def get_context_data(self, **kwargs):
        context = super(FactureView, self).get_context_data(**kwargs)

        table = LigneFactureTable(
            LigneFacture.objects.all().annotate(total=Sum(
                ExpressionWrapper(F('qte') * F('produit__prix'),
                                  output_field=fields.FloatField()))))
        RequestConfig(self.request, paginate={"per_page": 5}).configure(table)
        context['table'] = table
        return context
Beispiel #17
0
class Address(Model):
    text = fields.CharField(max_length=50, blank=True, null=True)
    phone = fields.CharField(max_length=15, blank=True, null=True)
    city = fields.CharField(max_length=50, blank=True, null=True)
    state = fields.CharField(max_length=50, blank=True, null=True)
    zip_code = fields.CharField(max_length=10, blank=True, null=True)
    country = fields.CharField(max_length=50, blank=True, null=True)
    latitude = fields.FloatField(blank=True, null=True)
    longitude = fields.FloatField(blank=True, null=True)

    def save(self, *args, **kwargs):
        res = requests.get(
            'https://maps.googleapis.com/maps/api/geocode/json?address={},{},{},{}&key={}'
            .format(self.text, self.city, self.country, self.zip_code,
                    API_KEY))
        location = res.json()['results'][0]['geometry']['location']
        self.latitude = location['lat']
        self.longitude = location['lng']
        super().save(*args, **kwargs)
 def __init__(self,
              value: Union[Value, str],
              *expressions,
              output_field=None,
              **extra):
     if value is not Value:
         value = Value(value)
     if output_field is None:
         output_field = fields.FloatField()
     expressions = value, *expressions
     super().__init__(*expressions, output_field=output_field, **extra)
Beispiel #19
0
    def get_context_data(self, **kwargs):
        context = super(ClientDetailView, self).get_context_data(**kwargs)

        table = LigneClientTable(
            Client.objects.all().annotate(chiffre_affaire=Sum(
                ExpressionWrapper(F('facture__lignes__qte') *
                                  F('facture__lignes__produit__prix'),
                                  output_field=fields.FloatField()))))

        context['table'] = table
        return context
Beispiel #20
0
 def get_context_data(self, **kwargs):
     context = super().get_context_data(**kwargs)
     data_filtred = FactureFilter(
         self.request.GET,
         queryset=self.model.objects.filter(client=self.kwargs.get('pk')))
     data_filtredQs = data_filtred.qs
     context['object_list'] = data_filtredQs.annotate(total=Sum(
         ExpressionWrapper(F('lignes__qte') * F('lignes__produit__prix'),
                           output_field=fields.FloatField())))
     context['object_table'] = self.table_class(context['object_list'])
     context['title'] = 'Liste des factures'
     context['object_name'] = "Facture"
     return context
Beispiel #21
0
    def get_context_data(self, **kwargs):
        context = super(DashboardView, self).get_context_data(**kwargs)
        fournisseur_qs = models.Fournisseur.objects.annotate(
            chiffre_affaire=Sum(
                ExpressionWrapper(F('produits_forni__lignefacture__qte') *
                                  F('produits_forni__prix'),
                                  output_field=fields.FloatField())))

        client_qs = models.Client.objects.annotate(chiffre_affaire=Sum(
            ExpressionWrapper(F('facture__lignes__qte') *
                              F('facture__lignes__produit__prix'),
                              output_field=fields.FloatField()))).order_by(
                                  "-chiffre_affaire")

        context[
            "fournisseur_table"] = tables.FournisseurWithChiffreAffaireTable(
                fournisseur_qs)
        context["client_table"] = tables.ClientWithChiffreAffaireTable(
            client_qs)
        context["daily_stat"] = charts.DailyStatChart()
        context["category_stat"] = charts.CategoryStatChart()
        return context
Beispiel #22
0
class ExchangeRate(models.Model):
    # NOTE: again, there is a lot of ways to implement a table for exchange rate montiring
    primary = CurrencyField()
    secondary = CurrencyField()

    rate = fields.FloatField(null=False, blank=False)

    last_update_date_time = fields.DateTimeField(null=False,
                                                 blank=False,
                                                 auto_now=True)

    class Meta:
        constraints = (models.UniqueConstraint(
            fields=('primary', 'secondary'),
            name='db_exchange_rate_primary_secondary_unique'), )
        index_together = ('primary', 'secondary')
Beispiel #23
0
class DailyStatChart(Chart):
    chart_type = 'line'
    qs = Facture.objects.values('date').annotate(chiffre_affaire=Sum(
        ExpressionWrapper(F('lignes__qte') * F('lignes__produit__prix'),
                          output_field=fields.FloatField())))

    def get_datasets(self, **kwargs):

        return [
            DataSet(color=(60, 170, 20),
                    data=list(self.qs.values_list('chiffre_affaire',
                                                  flat=True)),
                    label="l'évolution du chiffre d'affaire")
        ]

    def get_labels(self, **kwargs):
        return list(self.qs.values_list('date', flat=True))
Beispiel #24
0
class CategoryStatChart(Chart):
    chart_type = 'radar'
    qs = Category.objects.values('nom').annotate(chiffre_affaire=Sum(
        ExpressionWrapper(F('produit__lignefacture__qte') * F('produit__prix'),
                          output_field=fields.FloatField())))

    def get_datasets(self, **kwargs):

        return [
            DataSet(color=(245, 66, 170),
                    data=list(self.qs.values_list('chiffre_affaire',
                                                  flat=True)),
                    label="chiffre d'affaire par Categorie")
        ]

    def get_labels(self, **kwargs):
        return list(self.qs.values_list('nom', flat=True))
Beispiel #25
0
class CategorieChart(Chart):
    chart_type = 'radar'
    queryset = Facture.objects.values('lignes__produit__categorie').annotate(
        chiffre_affaire=Sum(
            ExpressionWrapper(F('lignes__qte') * F('lignes__produit__prix'),
                              output_field=fields.FloatField())))

    def get_datasets(self, **kwargs):
        return [
            DataSet(color=(13, 200, 56),
                    data=list(
                        self.queryset.values_list('chiffre_affaire',
                                                  flat=True)),
                    label="chiffre d'affaire par Categorie")
        ]

    def get_labels(self, **kwargs):
        return list(
            self.queryset.values_list('lignes__produit__categorie', flat=True))
Beispiel #26
0
class Transaction(Model):
    account = ForeignKey(Account, on_delete=CASCADE)
    date = fields.DateTimeField('date')
    label = fields.CharField(max_length=1000)
    value = fields.FloatField()
    category = ForeignKey(Category, null=True)

    def categorize(self, category_matching):
        for m, c in category_matching:
            if m in self.label:
                self.category = c
                self.save()
                break

    class Meta:
        unique_together = (('account', 'date', 'label', 'value'), )

    def __str__(self):
        return 'Transaction: {date} {value} {label}'.format(date=self.date,
                                                            label=self.label,
                                                            value=self.value)
Beispiel #27
0
    def filter_by_q(self, queryset, name, value):
        """
        Full text search provided by the 'q' option.

        Args:
            queryset: The query to add the additional full-text search filtering onto
            name: The name of the option specified, i.e. 'q'
            value: The string to search on

        Returns:
            The Django queryset that was passed in, additionally filtered by full-text search.

        """
        search_query = SearchQuery(value)
        qs = queryset.filter(search_vector=search_query)
        ts_rank_fn = Func(
            F("search_vector"),
            search_query,
            32,  # RANK_NORMALIZATION = 32
            function="ts_rank",
            output_field=db_fields.FloatField(),
        )
        return qs.annotate(rank=ts_rank_fn).order_by("-rank")
Beispiel #28
0
 def _resolve_output_field(self):
     if isinstance(self.value, str):
         return fields.CharField()
     if isinstance(self.value, bool):
         return fields.BooleanField()
     if isinstance(self.value, int):
         return fields.IntegerField()
     if isinstance(self.value, float):
         return fields.FloatField()
     if isinstance(self.value, datetime.datetime):
         return fields.DateTimeField()
     if isinstance(self.value, datetime.date):
         return fields.DateField()
     if isinstance(self.value, datetime.time):
         return fields.TimeField()
     if isinstance(self.value, datetime.timedelta):
         return fields.DurationField()
     if isinstance(self.value, Decimal):
         return fields.DecimalField()
     if isinstance(self.value, bytes):
         return fields.BinaryField()
     if isinstance(self.value, UUID):
         return fields.UUIDField()
Beispiel #29
0
 def __init__(self):
     super(Random, self).__init__(output_field=fields.FloatField())
class Activity(OrderedModel):
    """General entity which represents problem/text/video material."""

    TYPES = (
        ('G', _('generic')),
        ('A', _('pre-assessment')),
        ('Z', _('post-assessment')),
    )

    order_with_respect_to = 'atype', 'collection'

    name = models.CharField(max_length=255)
    collection = models.ForeignKey('Collection',
                                   related_name='activities',
                                   null=True,
                                   on_delete=models.CASCADE)
    tags = fields.CharField(
        max_length=255,
        help_text="Provide your tags separated by a comma.",
        blank=True,
        null=True,
    )
    atype = fields.CharField(
        verbose_name="type",
        choices=TYPES,
        default='G',
        max_length=1,
        help_text=
        "Choose 'pre/post-assessment' activity type to pin Activity to the start or the end of "
        "the Collection.")
    difficulty = fields.FloatField(
        default='0.5',
        help_text="Provide float number in the range 0.0 - 1.0",
        validators=[MinValueValidator(0.0),
                    MaxValueValidator(1.0)],
    )
    points = models.FloatField(blank=True, default=1)
    lti_consumer = models.ForeignKey(LtiConsumer,
                                     null=True,
                                     on_delete=models.CASCADE)
    source_launch_url = models.URLField(max_length=255, null=True)
    source_name = fields.CharField(max_length=255, blank=True, null=True)
    source_context_id = fields.CharField(max_length=255, blank=True, null=True)
    # NOTE(wowkalucky): extra field 'order' is available (inherited from OrderedModel)

    # `stype` - means source_type or string_type.
    stype = models.CharField("Type of the activity",
                             help_text="(problem, video, html, etc.)",
                             max_length=25,
                             blank=True,
                             null=True)
    # Number of possible repetition of the activity in the sequence
    repetition = models.PositiveIntegerField(
        default=1,
        help_text=
        "The number of possible repetition of the Activity in the sequence.")

    @property
    def is_problem(self):
        return self.stype in settings.PROBLEM_ACTIVITY_TYPES

    class Meta:
        verbose_name_plural = 'Activities'
        unique_together = ("source_launch_url", "collection")
        ordering = 'atype', 'order'

    def __str__(self):
        return '<Activity: {}>'.format(self.name)

    def get_absolute_url(self):
        return reverse('module:collection-detail',
                       kwargs={'pk': self.collection.pk})

    def save(self, *args, **kwargs):
        """Extension which sends notification to the Adaptive engine that Activity is created/updated."""
        initial_id = self.id
        if initial_id:
            Log.objects.create(log_type=Log.ADMIN,
                               action=Log.ACTIVITY_UPDATED,
                               data=self.get_research_data())
        else:
            Log.objects.create(log_type=Log.ADMIN,
                               action=Log.ACTIVITY_CREATED,
                               data=self.get_research_data())
        super().save(*args, **kwargs)
        self.collection.save()

    def delete(self, *args, **kwargs):
        """Extension which sends notification to the Adaptive engine that Activity is deleted."""
        Log.objects.create(log_type=Log.ADMIN,
                           action=Log.ACTIVITY_DELETED,
                           data=self.get_research_data())
        super().delete(*args, **kwargs)
        self.collection.save()

    @property
    def last_pre(self):
        """
        Has Activity last order number position in certain type sub-collection.

        :return: (bool)
        """
        last_pre = Activity.objects.filter(collection=self.collection,
                                           atype='A').last()
        return self.id == last_pre.id

    def get_research_data(self):
        return {'collection_id': self.collection_id, 'activity_id': self.id}