long long TransfersUser(acl::UserID uid, ::stats::Timeframe timeframe, const std::string& section, ::stats::Direction direction) { mongo::BSONObjBuilder match; match.append("direction", util::EnumToString(direction)); if (!section.empty()) match.append("section", section); else { mongo::BSONArrayBuilder sections; for (const auto& kv : cfg::Get().Sections()) sections.append(kv.first); match.appendElements(BSON("section" << BSON("$nin" << sections.arr()))); } match.appendElements(Serialize(timeframe)); mongo::BSONObj cmd = BSON("aggregate" << "transfers" << "pipeline" << BSON_ARRAY( BSON("$match" << match.obj()) << BSON("$group" << BSON("_id" << (uid == -1 ? "" : "$uid") << "total" << BSON("$sum" << "$kbytes") )))); mongo::BSONObj result; NoErrorConnection conn; if (conn.RunCommand(cmd, result)) { try { auto elems = result["result"].Array(); if (!elems.empty()) { return elems[0]["total"].Long(); } } catch (const mongo::DBException& e) { LogException("Unserialize transfers total", e, result); } } return 0; }
long long XfertimeCorrection(acl::UserID uid, long long kBytes, time_t modTime, ::stats::Direction direction) { long long xfertime = -1; util::Time t(modTime); auto cmd = BSON("aggregate" << "transfers" << "pipeline" << BSON_ARRAY( BSON("$match" << BSON("year" << t.Year() << "month" << t.Month() << "week" << t.Week() << "day" << t.Day() << "direction" << util::EnumToString(direction))) << BSON("$group" << BSON("_id" << uid << "total kbytes" << BSON("$sum" << "$kbytes") << "total xfertime" << BSON("$sum" << "$xfertime") )))); NoErrorConnection conn; mongo::BSONObj result; if (conn.RunCommand(cmd, result)) { auto elems = result["result"].Array(); if (!elems.empty()) { try { long long totalXfertime = elems[0]["total xfertime"].Long(); if (totalXfertime > 0) xfertime = std::ceil(static_cast<double>(totalXfertime) / elems[0]["total kbytes"].Long() * kBytes); else xfertime = 0; } catch (const mongo::DBException& e) { LogException("Unserialize upload decr avg speed", e, result); } } } return xfertime; }
bool User::DecrCredits(const std::string& section, long long kBytes, bool force) { if (!kBytes) return true; mongo::BSONObjBuilder elemQuery; elemQuery.append("section", section); if (!force) elemQuery.append("value", BSON("$gte" << kBytes)); auto query = BSON("uid" << user.id << "credits" << BSON("$elemMatch" << elemQuery.obj())); auto update = BSON("$inc" << BSON("credits.$.value" << -kBytes)); auto cmd = BSON("findandmodify" << "users" << "query" << query << "update" << update); NoErrorConnection conn; mongo::BSONObj result; bool ret = conn.RunCommand(cmd, result); return force || (ret && result["value"].type() != mongo::jstNULL); }
void User::IncrCredits(const std::string& section, long long kBytes) { auto doIncrement = [section, kBytes](acl::UserID uid) { NoErrorConnection conn; auto updateExisting = [&]() -> bool { auto query = BSON("uid" << uid << "credits" << BSON("$elemMatch" << BSON("section" << section))); auto update = BSON("$inc" << BSON("credits.$.value" << kBytes)); auto cmd = BSON("findandmodify" << "users" << "query" << query << "update" << update); mongo::BSONObj result; return conn.RunCommand(cmd, result) && result["value"].type() != mongo::jstNULL; }; auto doInsert = [&]() -> bool { auto query = QUERY("uid" << uid << "credits" << BSON("$not" << BSON("$elemMatch" << BSON("section" << section)))); auto update = BSON("$push" << BSON("credits" << BSON("section" << section << "value" << kBytes))); return conn.Update("users", query, update, false) > 0; }; if (updateExisting()) return; if (doInsert()) return; if (updateExisting()) return; logs::Database("Unable to increment credits for UID %1%%2%", uid, !section.empty() ? " in section " + section : std::string("")); }; asyncTasks.Assign(std::async(std::launch::async, doIncrement, user.id)); }
std::vector< ::stats::Stat> RetrieveUsers(const std::string& section, ::stats::Timeframe timeframe, ::stats::Direction direction, boost::optional< ::stats::SortField> sortField = boost::none, boost::optional<acl::UserID> uid = boost::none) { static const char* sortFields[] = { "total kbytes", "total files", "avg speed" }; mongo::BSONObjBuilder match; match.append("direction", util::EnumToString(direction)); match.appendElements(Serialize(timeframe)); if (!section.empty()) match.append("section", section); else { mongo::BSONArrayBuilder sections; for (const auto& kv : cfg::Get().Sections()) sections.append(kv.first); match.appendElements(BSON("section" << BSON("$in" << sections.arr()))); } if (uid) match.append("uid", *uid); mongo::BSONArrayBuilder ops; ops.append(BSON("$match" << match.obj())); ops.append(BSON("$group" << BSON("_id" << "$uid" << "total kbytes" << BSON("$sum" << "$kbytes") << "total files" << BSON("$sum" << "$files") << "total xfertime" << BSON("$sum" << "$xfertime")))); ops.append(BSON("$project" << BSON("total kbytes" << 1 << "total files" << 1 << "total xfertime" << 1 << "avg speed" << BSON("$divide" << BSON_ARRAY("$total kbytes" << "$total xfertime"))))); if (sortField) { ops.append(BSON("$sort" << BSON(sortFields[static_cast<unsigned>(*sortField)] << -1))); } auto cmd = BSON("aggregate" << "transfers" << "pipeline" << ops.arr()); std::vector< ::stats::Stat> users; mongo::BSONObj result; NoErrorConnection conn; if (conn.RunCommand(cmd, result)) { for (const auto& elem : result["result"].Array()) { users.emplace_back(Unserialize(elem.Obj())); } } return users; }