У меня есть объект Peewee, который выглядит следующим образом:
class Status(peewee.Model):
host = peewee.ForeignKeyField(
Host,
backref='checks',
on_delete='CASCADE')
check_date = peewee.DateTimeField()
status = peewee.TextField()
Он записывает результаты выполнения некоторых проверок службы на нескольких хостах. Каждая строка содержит один результат для одного хоста, состоящий из даты проверки и статуса. Получившаяся таблица выглядит примерно так:
+----+---------+----------------------------+---------+
| id | host_id | check_date | status |
+----+---------+----------------------------+---------+
| 1 | 123 | 2020-02-04 17:52:28.716036 | UP |
| 2 | 321 | 2020-02-04 17:52:28.716036 | REFUSED |
| 3 | 555 | 2020-02-04 17:52:28.716036 | UP |
...
| 50 | 123 | 2020-02-04 21:21:48.319062 | TIMEOUT |
| 51 | 321 | 2020-02-04 21:21:48.319062 | UNKNOWN |
| 52 | 555 | 2020-02-04 21:21:48.319062 | UP |
+----+---------+----------------------------+---------+
Я хочу создать сводный вид, например:
+----------------------------+-----+---------+---------+---------+-------+
| check_date | UP | REFUSED | TIMEOUT | UNKNOWN | TOTAL |
+----------------------------+-----+---------+---------+---------+-------+
| 2020-02-04 17:52:28.716036 | 221 | 34 | 10 | 2 | 267 |
| 2020-02-04 21:21:48.319062 | 230 | 30 | 15 | 4 | 279 |
+----------------------------+-----+---------+---------+---------+-------+
Я могу сделать это в SQL, например:
select
check_date,
count(*) filter (where status = 'UP') as UP,
count(*) filter (where status = 'REFUSED') as REFUSED,
count(*) filter (where status = 'TIMEOUT') as TIMEOUT,
count(*) filter (where status = 'UNKNOWN') as UNKNOWN,
count(*) as TOTAL
from status
group by check_date
Как мне структурировать аналогичный запрос, используя peewee ? Я знаю, что есть доступ к sql функциям через пространство имен peewee.fn
, но я не уверен, возможно ли структурировать эти filter
подзапросы с использованием этого синтаксиса.
Я решил это для теперь, начав с:
status_summary = (
Status.select(Status.check_date,
Status.status,
peewee.fn.Count(Status.id).alias('count'))
.group_by(Status.check_date, Status.status)
.order_by(Status.check_date, Status.status)
)
, который получает меня:
+----------------------------+---------+-------+
| check_date | status | count |
+----------------------------+---------+-------+
| 2020-02-04 17:52:28.716036 | UP | 221 |
| 2020-02-04 17:52:28.716036 | REFUSED | 34 |
| 2020-02-04 17:52:28.716036 | TIMEOUT | 10 |
| 2020-02-04 17:52:28.716036 | UNKNOWN | 34 |
| 2020-02-04 21:21:48.319062 | UP | 230 |
| 2020-02-04 21:21:48.319062 | REFUSED | 30 |
| 2020-02-04 21:21:48.319062 | TIMEOUT | 15 |
| 2020-02-04 21:21:48.319062 | UNKNOWN | 4 |
+----------------------------+---------+-------+
который я затем обрабатываю в Python, используя itertools.groupby
:
status_summary = itertools.groupby(status_summary, lambda x: x.check_date)
status_summary = [
{'date': date, 'summary': {x.status: x.count for x in results}}
for date, results in status_summary
]
который достает меня:
[
{
"date": "2020-02-04 17:52:28.716036",
"summary": {
"OPEN": 538,
"REFUSED": 13,
"TIMEOUT": 41,
"UNKNOWN": 4,
"UNREACHABLE": 2
}
},
{
"date": "2020-02-04 17:55:22.655965",
"summary": {
"OPEN": 533,
"REFUSED": 15,
"TIMEOUT": 42,
"UNKNOWN": 5,
"UNREACHABLE": 3
}
},
{
"date": "2020-02-04 18:51:31.937254",
"summary": {
"OPEN": 541,
"REFUSED": 11,
"TIMEOUT": 41,
"UNKNOWN": 4,
"UNREACHABLE": 1
}
},
{
"date": "2020-02-04 21:21:48.319062",
"summary": {
"OPEN": 544,
"REFUSED": 9,
"TIMEOUT": 39,
"UNKNOWN": 4,
"UNREACHABLE": 2
}
},
{
"date": "2020-02-05 00:11:23.377746",
"summary": {
"OPEN": 547,
"REFUSED": 8,
"TIMEOUT": 37,
"UNKNOWN": 5,
"UNREACHABLE": 1
}
}
]
Это фактически то, чего я хочу, но я чувствую, что процесс попадания сюда был излишне сложным.