You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
124 lines
4.9 KiB
124 lines
4.9 KiB
12 months ago
|
#include "tools/cabana/streams/routes.h"
|
||
|
|
||
|
#include <QDateTime>
|
||
|
#include <QDialogButtonBox>
|
||
|
#include <QFormLayout>
|
||
|
#include <QJsonArray>
|
||
|
#include <QJsonDocument>
|
||
|
#include <QListWidget>
|
||
|
#include <QMessageBox>
|
||
|
#include <QPainter>
|
||
|
|
||
|
#include "system/hardware/hw.h"
|
||
|
|
||
|
// The RouteListWidget class extends QListWidget to display a custom message when empty
|
||
|
class RouteListWidget : public QListWidget {
|
||
|
public:
|
||
|
RouteListWidget(QWidget *parent = nullptr) : QListWidget(parent) {}
|
||
|
void setEmptyText(const QString &text) {
|
||
|
empty_text_ = text;
|
||
|
viewport()->update();
|
||
|
}
|
||
|
void paintEvent(QPaintEvent *event) override {
|
||
|
QListWidget::paintEvent(event);
|
||
|
if (count() == 0) {
|
||
|
QPainter painter(viewport());
|
||
|
painter.drawText(viewport()->rect(), Qt::AlignCenter, empty_text_);
|
||
|
}
|
||
|
}
|
||
|
QString empty_text_ = tr("No items");
|
||
|
};
|
||
|
|
||
|
RoutesDialog::RoutesDialog(QWidget *parent) : QDialog(parent) {
|
||
|
setWindowTitle(tr("Remote routes"));
|
||
|
|
||
|
QFormLayout *layout = new QFormLayout(this);
|
||
|
layout->addRow(tr("Device"), device_list_ = new QComboBox(this));
|
||
|
layout->addRow(tr("Duration"), period_selector_ = new QComboBox(this));
|
||
|
layout->addRow(route_list_ = new RouteListWidget(this));
|
||
|
auto button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||
|
layout->addRow(button_box);
|
||
|
|
||
|
device_list_->addItem(tr("Loading..."));
|
||
|
// Populate period selector with predefined durations
|
||
|
period_selector_->addItem(tr("Last week"), 7);
|
||
|
period_selector_->addItem(tr("Last 2 weeks"), 14);
|
||
|
period_selector_->addItem(tr("Last month"), 30);
|
||
|
period_selector_->addItem(tr("Last 6 months"), 180);
|
||
|
|
||
|
// Connect signals and slots
|
||
|
connect(device_list_, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RoutesDialog::fetchRoutes);
|
||
|
connect(period_selector_, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RoutesDialog::fetchRoutes);
|
||
|
connect(route_list_, &QListWidget::itemDoubleClicked, this, &QDialog::accept);
|
||
|
QObject::connect(button_box, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||
|
QObject::connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||
|
|
||
|
// Send request to fetch devices
|
||
|
HttpRequest *http = new HttpRequest(this, !Hardware::PC());
|
||
|
QObject::connect(http, &HttpRequest::requestDone, this, &RoutesDialog::parseDeviceList);
|
||
|
http->sendRequest(CommaApi::BASE_URL + "/v1/me/devices/");
|
||
|
}
|
||
|
|
||
|
void RoutesDialog::parseDeviceList(const QString &json, bool success, QNetworkReply::NetworkError err) {
|
||
|
if (success) {
|
||
|
device_list_->clear();
|
||
|
auto devices = QJsonDocument::fromJson(json.toUtf8()).array();
|
||
|
for (const QJsonValue &device : devices) {
|
||
|
QString dongle_id = device["dongle_id"].toString();
|
||
|
device_list_->addItem(dongle_id, dongle_id);
|
||
|
}
|
||
|
} else {
|
||
|
bool unauthorized = (err == QNetworkReply::ContentAccessDenied || err == QNetworkReply::AuthenticationRequiredError);
|
||
|
QMessageBox::warning(this, tr("Error"), unauthorized ? tr("Unauthorized, Authenticate with tools/lib/auth.py") : tr("Network error"));
|
||
|
reject();
|
||
|
}
|
||
|
sender()->deleteLater();
|
||
|
}
|
||
|
|
||
|
void RoutesDialog::fetchRoutes() {
|
||
|
if (device_list_->currentIndex() == -1 || device_list_->currentData().isNull())
|
||
|
return;
|
||
|
|
||
|
route_list_->clear();
|
||
|
route_list_->setEmptyText(tr("Loading..."));
|
||
|
|
||
|
HttpRequest *http = new HttpRequest(this, !Hardware::PC());
|
||
|
QObject::connect(http, &HttpRequest::requestDone, this, &RoutesDialog::parseRouteList);
|
||
|
|
||
|
// Construct URL with selected device and date range
|
||
|
auto dongle_id = device_list_->currentData().toString();
|
||
|
QDateTime current = QDateTime::currentDateTime();
|
||
|
QString url = QString("%1/v1/devices/%2/routes_segments?start=%3&end=%4")
|
||
|
.arg(CommaApi::BASE_URL).arg(dongle_id)
|
||
|
.arg(current.addDays(-(period_selector_->currentData().toInt())).toMSecsSinceEpoch())
|
||
|
.arg(current.toMSecsSinceEpoch());
|
||
|
http->sendRequest(url);
|
||
|
}
|
||
|
|
||
|
void RoutesDialog::parseRouteList(const QString &json, bool success, QNetworkReply::NetworkError err) {
|
||
|
if (success) {
|
||
|
for (const QJsonValue &route : QJsonDocument::fromJson(json.toUtf8()).array()) {
|
||
|
uint64_t start_time = route["start_time_utc_millis"].toDouble();
|
||
|
uint64_t end_time = route["end_time_utc_millis"].toDouble();
|
||
|
auto datetime = QDateTime::fromMSecsSinceEpoch(start_time);
|
||
|
auto item = new QListWidgetItem(QString("%1 %2min").arg(datetime.toString()).arg((end_time - start_time) / (1000 * 60)));
|
||
|
item->setData(Qt::UserRole, route["fullname"].toString());
|
||
|
route_list_->addItem(item);
|
||
|
}
|
||
|
// Select first route if available
|
||
|
if (route_list_->count() > 0) route_list_->setCurrentRow(0);
|
||
|
} else {
|
||
|
QMessageBox::warning(this, tr("Error"), tr("Failed to fetch routes. Check your network connection."));
|
||
|
reject();
|
||
|
}
|
||
|
route_list_->setEmptyText(tr("No items"));
|
||
|
sender()->deleteLater();
|
||
|
}
|
||
|
|
||
|
void RoutesDialog::accept() {
|
||
|
if (auto current_item = route_list_->currentItem()) {
|
||
|
route_ = current_item->data(Qt::UserRole).toString();
|
||
|
}
|
||
|
QDialog::accept();
|
||
|
}
|