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.
		
		
		
		
		
			
		
			
				
					
					
						
							138 lines
						
					
					
						
							5.4 KiB
						
					
					
				
			
		
		
	
	
							138 lines
						
					
					
						
							5.4 KiB
						
					
					
				#include "tools/cabana/streams/routes.h"
 | 
						|
 | 
						|
#include <QDateTime>
 | 
						|
#include <QDialogButtonBox>
 | 
						|
#include <QFormLayout>
 | 
						|
#include <QJsonArray>
 | 
						|
#include <QJsonDocument>
 | 
						|
#include <QListWidget>
 | 
						|
#include <QMessageBox>
 | 
						|
#include <QPainter>
 | 
						|
 | 
						|
class OneShotHttpRequest : public HttpRequest {
 | 
						|
public:
 | 
						|
  OneShotHttpRequest(QObject *parent) : HttpRequest(parent, false) {}
 | 
						|
  void send(const QString &url) {
 | 
						|
    if (reply) {
 | 
						|
      reply->disconnect();
 | 
						|
      reply->abort();
 | 
						|
      reply->deleteLater();
 | 
						|
      reply = nullptr;
 | 
						|
    }
 | 
						|
    sendRequest(url);
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
// 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), route_requester_(new OneShotHttpRequest(this)) {
 | 
						|
  setWindowTitle(tr("Remote routes"));
 | 
						|
 | 
						|
  QFormLayout *layout = new QFormLayout(this);
 | 
						|
  layout->addRow(tr("Device"), device_list_ = new QComboBox(this));
 | 
						|
  layout->addRow(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);
 | 
						|
  period_selector_->addItem(tr("Preserved"), -1);
 | 
						|
 | 
						|
  // Connect signals and slots
 | 
						|
  QObject::connect(route_requester_, &HttpRequest::requestDone, this, &RoutesDialog::parseRouteList);
 | 
						|
  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, false);
 | 
						|
  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..."));
 | 
						|
  // Construct URL with selected device and date range
 | 
						|
  QString url = QString("%1/v1/devices/%2").arg(CommaApi::BASE_URL, device_list_->currentText());
 | 
						|
  int period = period_selector_->currentData().toInt();
 | 
						|
  if (period == -1) {
 | 
						|
    url += "/routes/preserved";
 | 
						|
  } else {
 | 
						|
    QDateTime now = QDateTime::currentDateTime();
 | 
						|
    url += QString("/routes_segments?start=%1&end=%2")
 | 
						|
               .arg(now.addDays(-period).toMSecsSinceEpoch())
 | 
						|
               .arg(now.toMSecsSinceEpoch());
 | 
						|
  }
 | 
						|
  route_requester_->send(url);
 | 
						|
}
 | 
						|
 | 
						|
void RoutesDialog::parseRouteList(const QString &json, bool success, QNetworkReply::NetworkError err) {
 | 
						|
  if (success) {
 | 
						|
    for (const QJsonValue &route : QJsonDocument::fromJson(json.toUtf8()).array()) {
 | 
						|
      QDateTime from, to;
 | 
						|
      if (period_selector_->currentData().toInt() == -1) {
 | 
						|
        from = QDateTime::fromString(route["start_time"].toString(), Qt::ISODateWithMs);
 | 
						|
        to = QDateTime::fromString(route["end_time"].toString(), Qt::ISODateWithMs);
 | 
						|
      } else {
 | 
						|
        from = QDateTime::fromMSecsSinceEpoch(route["start_time_utc_millis"].toDouble());
 | 
						|
        to = QDateTime::fromMSecsSinceEpoch(route["end_time_utc_millis"].toDouble());
 | 
						|
      }
 | 
						|
      auto item = new QListWidgetItem(QString("%1    %2min").arg(from.toString()).arg(from.secsTo(to) / 60));
 | 
						|
      item->setData(Qt::UserRole, route["fullname"].toString());
 | 
						|
      route_list_->addItem(item);
 | 
						|
    }
 | 
						|
    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"));
 | 
						|
}
 | 
						|
 | 
						|
QString RoutesDialog::route() {
 | 
						|
  auto current_item = route_list_->currentItem();
 | 
						|
  return current_item ? current_item->data(Qt::UserRole).toString() : "";
 | 
						|
}
 | 
						|
 |