Django dados Filtrar timestamp GROUP BY dia, semana, mês, ano

votos
30

I têm uma aplicação Django (DRF) em que o armazenamento de dados TimeSeries periódicos baseados na resposta de API. Aqui é a minha model.py

# Model to store the Alexa API Data
class Alexa(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    extra = jsonfield.JSONField(null=True)
    rank =  models.PositiveIntegerField(default=0, null=True)

Eu estou usando django-filtros para dados de consulta com base em um intervalo (__lte, __gte). Como /api/alexa/?created_at__lte=2020-02-14T09:15:52.329641Zdevolver todos os dados criados antes2020-02-14T09:15:52.329641Z

[
    {
        id: 1,
        created_at: 2020-02-03T19:30:57.868588Z,
        extra: {'load_time': 00, 'backlink': 0},
        rank: 0
    },
    ...
 ]

Existe uma maneira de construir um terminal para retornar dados agregados agrupadas por dia, semana, mês e ano com base nos parâmetros de consulta que eu passar. Por exemplo, /api/alexa/?created_at__lte=2020-02-14T09:15:52.329641Z&group_by=monthretornaria

[
    {
        created_at: 2020-01-01T00:00:00.000000Z,
        extra: {'load_time': 00, 'backlink': 0}, <- Aggregated Data 
        rank: 0                                    <- Aggregated Data
    },
    {
        created_at: 2020-02-01T00:00:00.000000Z,
        extra: {'load_time': 00, 'backlink': 0}, <- Aggregated Data 
        rank: 0                                    <- Aggregated Data 
    },
 ]

Aqui está a minha serializer.py atual

class AlexaViewSet(viewsets.ModelViewSet):
    queryset = Alexa.objects.all()
    filter_fields = {'created_at' : ['iexact', 'lte', 'gte']}
    http_method_names = ['get', 'post', 'head']

Tenho visto vários trechos fazendo agregação mas nenhuma cumprindo totalmente as minhas necessidades nem me dar uma idéia completa sobre o tema.

Eu sou novo para Django e construção de análise dashboards em geral, se houver qualquer outra forma de representação de dados tais TimeSeries para consumo em gráficos de front-end, eu gostaria de receber suas sugestões para que tão bem.

Publicado 15/02/2020 em 08:48
usuário
Em outras línguas...                            


1 respostas

votos
0

Em primeiro lugar, a classe AlexaViewSetnão é um serializer mas um ViewSet. Você não especificou a classe serializer em que ViewSet então eu você precisa especificar isso.

Por outro lado, se você quer passar um parâmetro de consulta personalizada no URL, em seguida, você deve substituir o listmétodo desta ViewSet e analisar a cadeia de consulta passada no requestobjeto para recuperar o valor de group_by, validá-lo, e depois perfom a agregação o youself .

Outro problema que eu vejo é que você também precisa definir o que é agregar um campo JSON, que não é suportado no SQL e é muito relativo, de modo que você pode querer considerar redesenhar como você armazenar as informações deste campo JSON se você quiser para agregações perfom em campos dentro dela. Gostaria de sugerir a extração dos campos que deseja agregar do JSON (quando armazená-los no banco de dados) e colocá-los em uma coluna SQL separadamente para que você possa executar agregações mais tarde. O cliente também poderia passar a operação de agregação como um parâmetro de consulta, por exemplo aggregation=sum, ou aggregation=avg.

Em um caso simples, onde você só precisa da média da classifica este deve ser útil como um exemplo (você pode adicionar TruncQuarter, etc.):

class AlexaViewSet(viewsets.ModelViewSet):
    serializer_class = AlexaSerializer
    queryset = Alexa.objects.all()
    filter_fields = {'created_at': ['iexact', 'lte', 'gte']}
    http_method_names = ['get', 'post', 'head']

    GROUP_CASTING_MAP = {  # Used for outputing the reset datetime when grouping
        'day': Cast(TruncDate('created_at'), output_field=DateTimeField()),
        'month': Cast(TruncMonth('created_at'), output_field=DateTimeField()),
        'week': Cast(TruncWeek('created_at'), output_field=DateTimeField()),
        'year': Cast(TruncYear('created_at'), output_field=DateTimeField()),
    }

    GROUP_ANNOTATIONS_MAP = {  # Defines the fields used for grouping
        'day': {
            'day': TruncDay('created_at'),
            'month': TruncMonth('created_at'),
            'year': TruncYear('created_at'),
        },
        'week': {
            'week': TruncWeek('created_at')
        },
        'month': {
            'month': TruncMonth('created_at'),
            'year': TruncYear('created_at'),
        },
        'year': {
            'year': TruncYear('created_at'),
        },
    }

    def list(self, request, *args, **kwargs):
        group_by_field = request.GET.get('group_by', None)
        if group_by_field and group_by_field not in self.GROUP_CASTING_MAP.keys():  # validate possible values
            return Response(status=status.HTTP_400_BAD_REQUEST)

        queryset = self.filter_queryset(self.get_queryset())

        if group_by_field:
            queryset = queryset.annotate(**self.GROUP_ANNOTATIONS_MAP[group_by_field]) \
                .values(*self.GROUP_ANNOTATIONS_MAP[group_by_field]) \
                .annotate(rank=Avg('rank'), created_at=self.GROUP_CASTING_MAP[group_by_field]) \
                .values('rank', 'created_at')

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

Para estes valores:

GET /alexa
[
    {
        "id": 1,
        "created_at": "2020-03-16T12:04:59.096098Z",
        "extra": "{}",
        "rank": 2
    },
    {
        "id": 2,
        "created_at": "2020-02-15T12:05:01.907920Z",
        "extra": "{}",
        "rank": 64
    },
    {
        "id": 3,
        "created_at": "2020-02-15T12:05:03.890150Z",
        "extra": "{}",
        "rank": 232
    },
    {
        "id": 4,
        "created_at": "2020-02-15T12:05:06.357748Z",
        "extra": "{}",
        "rank": 12
    }
]
GET /alexa/?group_by=day
[
    {
        "created_at": "2020-02-15T00:00:00Z",
        "extra": null,
        "rank": 102
    },
    {
        "created_at": "2020-03-16T00:00:00Z",
        "extra": null,
        "rank": 2
    }
]
GET /alexa/?group_by=week
[
    {
        "created_at": "2020-02-10T00:00:00Z",
        "extra": null,
        "rank": 102
    },
    {
        "created_at": "2020-03-16T00:00:00Z",
        "extra": null,
        "rank": 2
    }
]

GET /alexa/?group_by=month
[
    {
        "created_at": "2020-02-01T00:00:00Z",
        "extra": null,
        "rank": 102
    },
    {
        "created_at": "2020-03-01T00:00:00Z",
        "extra": null,
        "rank": 2
    }
]
GET /alexa/?group_by=year
[
    {
        "created_at": "2020-01-01T00:00:00Z",
        "extra": null,
        "rank": 77
    }
]
Respondeu 15/02/2020 em 20:34
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more