/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "statistics/statistics_data_deserialize.h" #include "base/debug_log.h" #include "data/data_statistics_chart.h" #include "statistics/statistics_types.h" #include #include #include #include namespace Statistic { Data::StatisticalChart StatisticalChartFromJSON(const QByteArray &json) { auto error = QJsonParseError{ 0, QJsonParseError::NoError }; const auto document = QJsonDocument::fromJson(json, &error); if (error.error != QJsonParseError::NoError || !document.isObject()) { LOG(("API Error: Bad stats graph json received.")); return {}; } const auto root = document.object(); const auto columns = root.value(u"columns"_q).toArray(); if (columns.empty()) { LOG(("API Error: Empty columns list from stats graph received.")); return {}; } const auto hiddenLinesRaw = root.value(u"hidden"_q).toArray(); const auto hiddenLines = ranges::views::all( hiddenLinesRaw ) | ranges::views::transform([](const auto &q) { return q.toString(); }) | ranges::to_vector; auto result = Data::StatisticalChart(); auto columnIdCount = 0; for (const auto &column : columns) { const auto array = column.toArray(); if (array.empty()) { LOG(("API Error: Empty column from stats graph received.")); return {}; } const auto columnId = array.first().toString(); if (columnId == u"x"_q) { const auto length = array.size() - 1; result.x.resize(length); for (auto i = 0; i < length; i++) { result.x[i] = array.at(i + 1).toDouble(); } } else { auto line = Data::StatisticalChart::Line(); const auto length = array.size() - 1; line.id = (++columnIdCount); line.idString = columnId; line.isHiddenOnStart = ranges::contains(hiddenLines, columnId); line.y.resize(length); for (auto i = 0; i < length; i++) { const auto value = ChartValue(base::SafeRound( array.at(i + 1).toDouble())); line.y[i] = value; if (value > line.maxValue) { line.maxValue = value; } if (value < line.minValue) { line.minValue = value; } } result.lines.push_back(std::move(line)); } if (result.x.size() > 1) { result.timeStep = result.x[1] - result.x[0]; } else { constexpr auto kOneDay = 3600 * 24 * 1000; result.timeStep = kOneDay; } result.measure(); } if (result.maxValue == result.minValue) { if (result.minValue) { result.minValue = 0; } else { result.maxValue = 1; } } { const auto subchart = root.value(u"subchart"_q).toObject(); const auto subchartShowIt = subchart.constFind(u"show"_q); if (subchartShowIt != subchart.constEnd()) { if (subchartShowIt->isBool()) { result.isFooterHidden = !(subchartShowIt->toBool()); } } const auto defaultZoomIt = subchart.constFind(u"defaultZoom"_q); auto min = int(0); auto max = int(result.x.size() - 1); if (defaultZoomIt != subchart.constEnd()) { if (const auto array = defaultZoomIt->toArray(); !array.empty()) { const auto minValue = array.first().toDouble(); const auto maxValue = array.last().toDouble(); for (auto i = 0; i < result.x.size(); i++) { if (result.x[i] == minValue) { min = i; } if (result.x[i] == maxValue) { max = i; } } } } result.defaultZoomXIndex.min = std::min(min, max); result.defaultZoomXIndex.max = std::max(min, max); } { const auto percentageShowIt = root.constFind(u"percentage"_q); if (percentageShowIt != root.constEnd()) { if (percentageShowIt->isBool()) { result.hasPercentages = (percentageShowIt->toBool()); } } } { const auto tooltipFormatIt = root.constFind(u"xTooltipFormatter"_q); if (tooltipFormatIt != root.constEnd()) { const auto tooltipFormat = tooltipFormatIt->toString(); result.weekFormat = tooltipFormat.contains(u"'week'"_q); } } const auto colors = root.value(u"colors"_q).toObject(); const auto names = root.value(u"names"_q).toObject(); for (auto &line : result.lines) { const auto colorIt = colors.constFind(line.idString); if (colorIt != colors.constEnd() && (*colorIt).isString()) { static const auto RegExp = QRegularExpression(u"(.*)(#.*)"_q); const auto match = RegExp.match( colorIt->toString()); if (match.hasMatch()) { line.colorKey = match.captured(1); line.color = QColor(match.captured(2)); } } const auto nameIt = names.constFind(line.idString); if (nameIt != names.constEnd() && (*nameIt).isString()) { line.name = nameIt->toString().replace('-', QChar(8212)); } } return result; } } // namespace Statistic