/******************************************************************************* * gui/statistics/GxsTransportStatistics.cpp * * * * Copyright (c) 2011 Retroshare Team * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Affero General Public License as * * published by the Free Software Foundation, either version 3 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Affero General Public License for more details. * * * * You should have received a copy of the GNU Affero General Public License * * along with this program. If not, see . * * * *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "GxsTransportStatistics.h" #include "gui/Identity/IdDetailsDialog.h" #include "gui/settings/rsharesettings.h" #include "util/QtVersion.h" #include "gui/common/UIStateHelper.h" #include "util/misc.h" #include "util/qtthreadsutils.h" #include "gui/gxs/GxsIdLabel.h" #include "gui/gxs/GxsIdDetails.h" #include "gui/gxs/GxsIdTreeWidgetItem.h" #include "gui/Identity/IdDialog.h" #include "gui/MainWindow.h" #include "gui/common/FilesDefs.h" #define COL_PENDING_ID 0 #define COL_PENDING_DESTINATION 1 #define COL_PENDING_DATASTATUS 2 #define COL_PENDING_DATASIZE 3 #define COL_PENDING_DATAHASH 4 #define COL_PENDING_SEND 5 #define COL_PENDING_GROUP_ID 6 #define COL_PENDING_SENDTIME 7 #define COL_PENDING_DESTINATION_ID 8 #define COL_GROUP_GRP_ID 0 #define COL_GROUP_PUBLISHTS 1 #define COL_GROUP_NUM_MSGS 2 #define COL_GROUP_SIZE_MSGS 3 #define COL_GROUP_SUBSCRIBED 4 #define COL_GROUP_POPULARITY 5 #define COL_GROUP_UNIQUE_ID 6 #define COL_GROUP_AUTHOR_ID 7 //static const int PARTIAL_VIEW_SIZE = 9 ; //static const int MAX_TUNNEL_REQUESTS_DISPLAY = 10 ; //static const int GXSTRANS_STATISTICS_DELAY_BETWEEN_GROUP_REQ = 30 ; // never request more than every 30 secs. #define GXSTRANS_GROUP_META 0x01 #define GXSTRANS_GROUP_DATA 0x02 #define GXSTRANS_GROUP_STAT 0x03 #define GXSTRANS_MSG_META 0x04 //#define DEBUG_GXSTRANS_STATS 1 GxsTransportStatistics::GxsTransportStatistics(QWidget *parent) : MainPage(parent) { setupUi(this) ; mStateHelper = new UIStateHelper(this); mStateHelper->addWidget(GXSTRANS_GROUP_META, treeWidget); m_bProcessSettings = false; mLastGroupReqTS = 0 ; /* Set header resize modes and initial section sizes Uploads TreeView*/ QHeaderView_setSectionResizeMode(treeWidget->header(), QHeaderView::ResizeToContents); QHeaderView_setSectionResizeMode(groupTreeWidget->header(), QHeaderView::ResizeToContents); connect(treeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(CustomPopupMenu(QPoint))); connect(groupTreeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(CustomPopupMenuGroups(QPoint))); treeWidget->setColumnHidden(COL_PENDING_DESTINATION_ID,true); groupTreeWidget->setColumnHidden(COL_GROUP_AUTHOR_ID,true); // load settings processSettings(true); updateDisplay(true); } GxsTransportStatistics::~GxsTransportStatistics() { // save settings processSettings(false); } void GxsTransportStatistics::processSettings(bool bLoad) { m_bProcessSettings = true; Settings->beginGroup(QString("GxsTransportStatistics")); if (bLoad) { // load settings // state of splitter //splitter->restoreState(Settings->value("Splitter").toByteArray()); } else { // save settings // state of splitter //Settings->setValue("Splitter", splitter->saveState()); } Settings->endGroup(); m_bProcessSettings = false; } void GxsTransportStatistics::CustomPopupMenu( QPoint ) { QMenu contextMnu( this ); QTreeWidgetItem *item = treeWidget->currentItem(); if (item) { contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/info16.png"), tr("View details"), this, SLOT(personDetails())); } contextMnu.exec(QCursor::pos()); } void GxsTransportStatistics::CustomPopupMenuGroups( QPoint ) { QMenu contextMnu( this ); QTreeWidgetItem *item = groupTreeWidget->currentItem(); if (item) { contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/info16.png"), tr("View details"), this, SLOT(showAuthorInPeople())); } contextMnu.exec(QCursor::pos()); } void GxsTransportStatistics::updateDisplay(bool) { time_t now = time(NULL) ; #ifdef DEBUG_GXSTRANS_STATS std::cerr << "GxsTransportStatistics::updateDisplay()" << std::endl; #endif loadGroups(); mLastGroupReqTS = now ; } QString GxsTransportStatistics::getPeerName(const RsPeerId &peer_id) { static std::map names ; std::map::const_iterator it = names.find(peer_id) ; if( it != names.end()) return it->second ; else { RsPeerDetails detail ; if(!rsPeers->getPeerDetails(peer_id,detail)) return tr("Unknown Peer"); return (names[peer_id] = QString::fromUtf8(detail.name.c_str())) ; } } static QString getStatusString(GxsTransSendStatus status) { switch(status) { case GxsTransSendStatus::PENDING_PROCESSING : return QObject::tr("Processing") ; case GxsTransSendStatus::PENDING_PREFERRED_GROUP : return QObject::tr("Choosing group") ; case GxsTransSendStatus::PENDING_RECEIPT_CREATE : return QObject::tr("Creating receipt") ; case GxsTransSendStatus::PENDING_RECEIPT_SIGNATURE : return QObject::tr("Signing receipt") ; case GxsTransSendStatus::PENDING_SERIALIZATION : return QObject::tr("Serializing") ; case GxsTransSendStatus::PENDING_PAYLOAD_CREATE : return QObject::tr("Creating payload") ; case GxsTransSendStatus::PENDING_PAYLOAD_ENCRYPT : return QObject::tr("Encrypting payload") ; case GxsTransSendStatus::PENDING_PUBLISH : return QObject::tr("Publishing") ; case GxsTransSendStatus::PENDING_RECEIPT_RECEIVE : return QObject::tr("Waiting for receipt") ; case GxsTransSendStatus::RECEIPT_RECEIVED : return QObject::tr("Receipt received") ; case GxsTransSendStatus::FAILED_RECEIPT_SIGNATURE : return QObject::tr("Receipt signature failed") ; case GxsTransSendStatus::FAILED_ENCRYPTION : return QObject::tr("Encryption failed") ; case GxsTransSendStatus::UNKNOWN : default : return QObject::tr("Unknown") ; } } void GxsTransportStatistics::updateContent() { RsGxsTrans::GxsTransStatistics transinfo ; rsGxsTrans->getDataStatistics(transinfo) ; // clear treeWidget->clear(); //time_t now = time(NULL) ; // 1 - fill the table for pending packets time_t now = time(NULL) ; groupBox->setTitle(tr("Pending data items")+": " + QString::number(transinfo.outgoing_records.size()) ); for(uint32_t i=0;iaddTopLevelItem(item); RsIdentityDetails details ; rsIdentity->getIdDetails(rec.recipient,details); QString nickname = QString::fromUtf8(details.mNickname.c_str()); if(nickname.isEmpty()) nickname = tr("Unknown"); item -> setId(rec.recipient,COL_PENDING_DESTINATION, false) ; item -> setData(COL_PENDING_ID, Qt::DisplayRole, QString::number(rec.trans_id,16).rightJustified(8,'0')); item -> setData(COL_PENDING_DATASTATUS, Qt::DisplayRole, getStatusString(rec.status)); item -> setData(COL_PENDING_DATASIZE, Qt::DisplayRole, misc::friendlyUnit(rec.data_size)); item -> setData(COL_PENDING_DATAHASH, Qt::DisplayRole, QString::fromStdString(rec.data_hash.toStdString())); item -> setData(COL_PENDING_SEND, Qt::DisplayRole, QDateTime::fromTime_t(rec.send_TS).toString()); item -> setData(COL_PENDING_GROUP_ID, Qt::DisplayRole, QString::fromStdString(rec.group_id.toStdString())); item -> setData(COL_PENDING_DESTINATION_ID, Qt::DisplayRole, QString::fromStdString(rec.recipient.toStdString())); item -> setData(COL_PENDING_SENDTIME, Qt::DisplayRole, QString::number(now - rec.send_TS)); item->setTextAlignment(COL_PENDING_DATASIZE, Qt::AlignRight ); item->setTextAlignment(COL_PENDING_SEND, Qt::AlignRight ); } // 2 - fill the table for pending group data // record openned groups std::set openned_groups ; for(int i=0; itopLevelItemCount(); ++i) if( groupTreeWidget->isItemExpanded(groupTreeWidget->topLevelItem(i)) ) openned_groups.insert(RsGxsGroupId(groupTreeWidget->topLevelItem(i)->data(COL_GROUP_GRP_ID, Qt::DisplayRole).toString().toStdString())); groupTreeWidget->clear(); for(std::map::const_iterator it(mGroupStats.begin());it!=mGroupStats.end();++it) { const RsGxsTransGroupStatistics& stat(it->second) ; QTreeWidgetItem *item ; { QString unique_id = QString::fromStdString(stat.mGrpId.toStdString()); QList iteml = groupTreeWidget->findItems(unique_id,Qt::MatchExactly,COL_GROUP_UNIQUE_ID) ; if(iteml.empty()) item = new QTreeWidgetItem; else item = *iteml.begin(); } groupTreeWidget->addTopLevelItem(item); groupTreeWidget->setItemExpanded(item,openned_groups.find(it->first) != openned_groups.end()); QString msg_time_string = (stat.last_publish_TS>0)?QString("(Last msg: %1)").arg(QDateTime::fromTime_t((uint)stat.last_publish_TS).toString()):"" ; item->setData(COL_GROUP_PUBLISHTS, Qt::DisplayRole, msg_time_string) ; item->setData(COL_GROUP_NUM_MSGS, Qt::DisplayRole, QString::number(stat.mNumMsgs) ) ; item->setData(COL_GROUP_GRP_ID, Qt::DisplayRole, QString::fromStdString(it->first.toStdString())) ; item->setData(COL_GROUP_SIZE_MSGS, Qt::DisplayRole, QString::number(stat.mTotalSizeOfMsgs)) ; item->setData(COL_GROUP_SUBSCRIBED,Qt::DisplayRole, stat.subscribed?tr("Yes"):tr("No")) ; item->setData(COL_GROUP_POPULARITY,Qt::DisplayRole, QString::number(stat.popularity)) ; item->setData(COL_GROUP_UNIQUE_ID, Qt::DisplayRole, QString::fromStdString(it->first.toStdString())) ; for(std::map::const_iterator msgIt(stat.messages_metas.begin());msgIt!=stat.messages_metas.end();++msgIt) { const RsMsgMetaData& meta(msgIt->second); QTreeWidgetItem *sitem ; { QString unique_id = QString::fromStdString(meta.mMsgId.toStdString()); QList iteml = groupTreeWidget->findItems(unique_id,Qt::MatchExactly,COL_GROUP_UNIQUE_ID) ; if(iteml.empty()) sitem = new QTreeWidgetItem(item) ; else sitem = *iteml.begin(); } GxsIdLabel *label = new GxsIdLabel(); label->setId(meta.mAuthorId) ; groupTreeWidget->setItemWidget(sitem,COL_GROUP_GRP_ID,label) ; RsIdentityDetails idDetails ; rsIdentity->getIdDetails(meta.mAuthorId,idDetails); QPixmap pixmap ; QDateTime qdatetime; qdatetime.setTime_t(meta.mPublishTs); if(idDetails.mAvatar.mSize == 0 || !GxsIdDetails::loadPixmapFromData(idDetails.mAvatar.mData, idDetails.mAvatar.mSize, pixmap,GxsIdDetails::SMALL)) pixmap = GxsIdDetails::makeDefaultIcon(meta.mAuthorId,GxsIdDetails::SMALL); sitem->setIcon(COL_GROUP_GRP_ID, QIcon(pixmap)); sitem->setData(COL_GROUP_UNIQUE_ID, Qt::DisplayRole,QString::fromStdString(meta.mMsgId.toStdString())); sitem->setData(COL_GROUP_AUTHOR_ID, Qt::DisplayRole, QString::fromStdString(meta.mAuthorId.toStdString())) ; sitem->setText(COL_GROUP_PUBLISHTS, QDateTime::fromTime_t(meta.mPublishTs).toString()); sitem->setData(COL_GROUP_PUBLISHTS, Qt::UserRole, qdatetime); } } } void GxsTransportStatistics::personDetails() { QTreeWidgetItem *item = treeWidget->currentItem(); std::string id = item->text(COL_PENDING_DESTINATION_ID).toStdString(); if (id.empty()) { return; } IdDetailsDialog *dialog = new IdDetailsDialog(RsGxsGroupId(id)); dialog->show(); } void GxsTransportStatistics::showAuthorInPeople() { QTreeWidgetItem *item = groupTreeWidget->currentItem(); std::string id = item->text(COL_GROUP_AUTHOR_ID).toStdString(); if (id.empty()) { return; } /* window will destroy itself! */ IdDialog *idDialog = dynamic_cast(MainWindow::getPage(MainWindow::People)); if (!idDialog) return ; MainWindow::showWindow(MainWindow::People); idDialog->navigate(RsGxsId(id)); } #ifdef TO_REMOVE void GxsTransportStatistics::loadGroupMeta(const std::vector& groupInfo) { mStateHelper->setLoading(GXSTRANS_GROUP_META, false); #ifdef DEBUG_GXSTRANS_STATS std::cerr << "GxsTransportStatisticsWidget::loadGroupMeta()"; std::cerr << std::endl; #endif mStateHelper->setActive(GXSTRANS_GROUP_META, true); std::set existing_groups ; for(auto vit = groupInfo.begin(); vit != groupInfo.end(); ++vit) { existing_groups.insert(vit->mGroupId) ; /* Add Widget, and request Pages */ #ifdef DEBUG_GXSTRANS_STATS std::cerr << "GxsTransportStatisticsWidget::loadGroupMeta() GroupId: " << vit->mGroupId << " Group: " << vit->mGroupName << std::endl; #endif loadGroupStats(vit->mGroupId) ; loadMsgMetas(vit->mGroupId) ; RsGxsTransGroupStatistics& s(mGroupStats[vit->mGroupId]); s.popularity = vit->mPop ; s.subscribed = IS_GROUP_SUBSCRIBED(vit->mSubscribeFlags) ; s.mGrpId = vit->mGroupId ; } // remove group stats for group that do not exist anymore for(std::map::iterator it(mGroupStats.begin());it!=mGroupStats.end();) if(existing_groups.find(it->first) == existing_groups.end()) it = mGroupStats.erase(it); else ++it; } void GxsTransportStatistics::loadGroupStats(const RsGxsGroupId& groupId) { #ifdef DEBUG_GXSTRANS_STATS std::cerr << "Loading group stats: " << stats.mGrpId << ", num msgs=" << stats.mNumMsgs << ", total size=" << stats.mTotalSizeOfMsgs << std::endl; #endif RsThread::async([this]() { // 1 - get message data from p3GxsForums #ifdef DEBUG_FORUMS std::cerr << "Retrieving post data for post " << mThreadId << std::endl; #endif GxsGroupStatistic stats; rsGxsTrans->getGroupStatistic(groupId,stats); RsQThreadUtils::postToObject( [stats,this]() { /* Here it goes any code you want to be executed on the Qt Gui * thread, for example to update the data model with new information * after a blocking call to RetroShare API complete */ dynamic_cast(mGroupStats[stats.mGrpId]) = stats ; mStateHelper->setLoading(GXSTRANS_GROUP_STAT, false); }, this ); }); } #endif void GxsTransportStatistics::loadGroups() { mStateHelper->setLoading(GXSTRANS_GROUP_META, true); RsThread::async([this]() { // 1 - get message data from p3GxsForums #ifdef DEBUG_FORUMS std::cerr << "Retrieving post data for post " << mThreadId << std::endl; #endif auto stats = std::make_unique< std::map >(); if(!rsGxsTrans->getGroupStatistics(*stats)) { RS_ERR("Cannot retrieve group statistics in GxsTransportStatistics"); return; } RsQThreadUtils::postToObject( [stats = std::move(stats), this]() { /* Here it goes any code you want to be executed on the Qt Gui * thread, for example to update the data model with new information * after a blocking call to RetroShare API complete */ // TODO: consider making mGroupStats an unique_ptr to avoid copying mGroupStats = *stats; updateContent(); mStateHelper->setLoading(GXSTRANS_GROUP_META, false); }, this ); }); }