/* * libretroshare/src/services p3ranking.cc * * 3P/PQI network interface for RetroShare. * * Copyright 2007-2008 by Robert Fernie. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License Version 2 as published by the Free Software Foundation. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. * * Please report all bugs and problems to "retroshare@lunamutt.com". * */ #include "services/p3ranking.h" #include "pqi/pqibin.h" #include "pqi/p3authmgr.h" const uint32_t RANK_MAX_FWD_OFFSET = (60 * 60 * 24 * 2); /* 2 Days */ std::string generateRandomLinkId(); /***** * TODO * (1) Streaming. * (2) Ranking. * */ #define RANK_DEBUG 1 p3Ranking::p3Ranking(uint16_t subtype, CacheTransfer *cft, std::string sourcedir, std::string storedir, uint32_t storePeriod) :CacheSource(subtype, true, sourcedir), CacheStore(subtype, true, cft, storedir), mStorePeriod(storePeriod) { mOwnId = getAuthMgr()->OwnId(); mViewPeriod = 60 * 60 * 24 * 30; /* one Month */ mSortType = RS_RANK_ALG; createDummyData(); return; } bool p3Ranking::loadLocalCache(const CacheData &data) { return true; } int p3Ranking::loadCache(const CacheData &data) { return 1; } void p3Ranking::loadRankFile(std::string filename, std::string src) { #if 0 /* create the serialiser to load info */ pqistreamer *streamer = createStreamer(filename, src, BIN_FLAGS_READABLE); time_t now = time(NULL); time_t min = now - mStorePeriod; time_t max = now + RANK_MAX_FWD_OFFSET; RsItem *item; RsRankMsg *newMsg; while(NULL != (item = streamer->GetItem())) { newMsg = (RsRankMsg *) item; /* check timestamp */ if ((newMsg->timestamp < min) || (newMsg->timestamp > max)) { /* if outside range -> remove */ delete newMsg; } else { addRankMsg(newMsg); } } #endif } void p3Ranking::publishMsgs() { #if 0 /* create a serialiser */ std::string file; pqistreamer *stream = createStreamer(file, mOwnId, BIN_FLAGS_NO_DELETE | BIN_FLAGS_HASH_DATA); /* iterate through list */ std::map::iterator it; for(it = mData.begin(); it != mData.end(); it++) { if (it->second.ownTag) { /* write to serialiser */ RsItem *item = it->second.comments[mOwnId]; if (item) stream->SendItem(item); } } CacheData data; data.pid = mOwnId; data.cid = CacheId(CacheSource::getCacheType(), 0); data.name = file; refreshCache(data); #endif } void p3Ranking::addRankMsg(RsRankMsg *msg) { /* find msg */ std::string id = msg->PeerId(); std::string rid = msg->rid; std::map::iterator it; it = mData.find(rid); if (it == mData.end()) { /* add a new one */ RankGroup grp; grp.rid = rid; grp.ownTag = false; /******** LINK SPECIFIC ****/ grp.link = msg->link; grp.title = msg->title; mData[rid] = grp; it = mData.find(rid); } /* check for old comment */ std::map::iterator cit; cit = (it->second).comments.find(id); if ((it->second).comments.end() != cit) { (it->second).comments.erase(cit); } (it->second).comments[id] = msg; if (id == mOwnId) { it->second.ownTag = true; mRepublish = true; } reSortGroup(it->second); } /***************** Sorting ****************/ bool p3Ranking::setSortPeriod(uint32_t period) { bool reSort = (mViewPeriod != period); mViewPeriod = period; if (reSort) { sortAllMsgs(); } return true; } bool p3Ranking::setSortMethod(uint32_t type) { bool reSort = (mSortType != type); mSortType = type; if (reSort) { sortAllMsgs(); } return true; } bool p3Ranking::clearPeerFilter() { bool reSort = (mPeerFilter.size() > 0); mPeerFilter.clear(); if (reSort) { sortAllMsgs(); } return true; } bool p3Ranking::setPeerFilter(std::list peers) { mPeerFilter = peers; sortAllMsgs(); return true; } float p3Ranking::locked_calcRank(RankGroup &grp) { /* Ranking Calculations ..... */ time_t now = time(NULL); time_t minTime = now-mViewPeriod; bool doFilter = (mPeerFilter.size() > 0); bool doScore = (mSortType & RS_RANK_SCORE); bool doTime = (mSortType & RS_RANK_TIME); uint32_t count = 0; float algScore = 0; #ifdef RANK_DEBUG std::string normlink(grp.link.begin(), grp.link.end()); std::cerr << "p3Ranking::locked_calcRank() for: " << normlink; std::cerr << std::endl; std::cerr << "Period: " << mViewPeriod; std::cerr << " doFilter: " << doFilter; std::cerr << " doScore: " << doScore; std::cerr << " doTime: " << doTime; std::cerr << std::endl; #endif std::map::iterator it; for(it = grp.comments.begin(); it != grp.comments.end(); it++) { #ifdef RANK_DEBUG std::cerr << "Comment by:" << it->first << " age: " << now - it->second->timestamp; std::cerr << std::endl; #endif if (doFilter) { if (mPeerFilter.end() == std::find(mPeerFilter.begin(), mPeerFilter.end(), it->first)) { continue; /* skip it */ #ifdef RANK_DEBUG std::cerr << "\tFiltered Out"; std::cerr << std::endl; #endif } } /* if Scoring is involved... drop old ones */ if ((doScore) && (it->second->timestamp < minTime)) { #ifdef RANK_DEBUG std::cerr << "\tToo Old"; std::cerr << std::endl; #endif continue; } time_t deltaT; if (it->second->timestamp > now) { deltaT = it->second->timestamp - now; } else { deltaT = now - it->second->timestamp; } float timeScore = ((float) mViewPeriod - deltaT) / (mViewPeriod + 0.01); #ifdef RANK_DEBUG std::cerr << "\tTimeScore: " << timeScore; std::cerr << std::endl; #endif /* algScore is sum of (filtered) timeScores */ /* timeScore is average of (all) timeScores */ /* popScore is just count of valid scores */ algScore += timeScore; count++; } #ifdef RANK_DEBUG std::cerr << "p3Ranking::locked_calcRank() algScore: " << algScore; std::cerr << " Count: " << count; std::cerr << std::endl; #endif if ((count < 0) || (algScore < 0)) { #ifdef RANK_DEBUG std::cerr << "Final score: 0"; std::cerr << std::endl; #endif return 0; } if ((doScore) && (doTime)) { #ifdef RANK_DEBUG std::cerr << "Final (alg) score:" << algScore; std::cerr << std::endl; #endif return algScore; } else if (doScore) { #ifdef RANK_DEBUG std::cerr << "Final (pop) score:" << count; std::cerr << std::endl; #endif return count; } else if (doTime) { #ifdef RANK_DEBUG std::cerr << "Final (time) score:" << algScore / count; std::cerr << std::endl; #endif return algScore / count; } return 0; } void p3Ranking::reSortGroup(RankGroup &grp) { std::string rid = grp.rid; float rank = grp.rank; /* remove from existings rankings */ std::multimap::iterator rit; rit = mRankings.lower_bound(grp.rank); for(; (rit != mRankings.end()) && (rit->first == grp.rank); rit++) { if (rit->second == rid) { mRankings.erase(rit); break; } } /* add it back in */ grp.rank = locked_calcRank(grp); mRankings.insert( std::pair(grp.rank, rid)); } void p3Ranking::sortAllMsgs() { /* iterate through list and re-score each one */ std::map::iterator it; mRankings.clear(); for(it = mData.begin(); it != mData.end(); it++) { (it->second).rank = locked_calcRank(it->second); if (it->second.rank > 0) { mRankings.insert( std::pair (it->second.rank, it->first)); } } } /******** ACCESS *************/ /* get Ids */ uint32_t p3Ranking::getRankingsCount() { return mRankings.size(); } float p3Ranking::getMaxRank() { if (mRankings.size() == 0) return 0; return mRankings.rbegin()->first; } bool p3Ranking::getRankings(uint32_t first, uint32_t count, std::list &rids) { uint32_t i = 0; std::multimap::reverse_iterator rit; for(rit = mRankings.rbegin(); (i < first) && (rit != mRankings.rend()); rit++); i = 0; for(; (i < count) && (rit != mRankings.rend()); rit++) { rids.push_back(rit->second); } return true; } bool p3Ranking::getRankDetails(std::string rid, RsRankDetails &details) { /* get the details. */ std::map::iterator it; it = mData.find(rid); if (mData.end() == it) { return false; } details.rid = it->first; details.link = (it->second).link; details.title = (it->second).title; details.rank = (it->second).rank; details.ownTag = (it->second).ownTag; std::map::iterator cit; for(cit = (it->second).comments.begin(); cit != (it->second).comments.end(); cit++) { RsRankComment comm; comm.id = (cit->second)->PeerId(); comm.timestamp = (cit->second)->timestamp; comm.comment = (cit->second)->comment; details.comments.push_back(comm); } return true; } void p3Ranking::tick() { if (mRepublish) { publishMsgs(); mRepublish = false; } } /***** NEW CONTENT *****/ std::string p3Ranking::newRankMsg(std::wstring link, std::wstring title, std::wstring comment) { /* generate an id */ std::string rid = generateRandomLinkId(); RsRankMsg *msg = new RsRankMsg(); time_t now = time(NULL); msg->PeerId(mOwnId); msg->rid = rid; msg->title = title; msg->timestamp = now; msg->link = link; msg->comment = comment; addRankMsg(msg); return rid; } bool p3Ranking::updateComment(std::string rid, std::wstring comment) { return true; } pqistreamer *createStreamer(std::string file, std::string src, uint32_t bioflags) { #if 0 RsSerialiser *rsSerialiser = new RsSerialiser(); RsSerialType *serialType = new RsRankSerial(); /* TODO */ rsSerialiser->addSerialType(serialType); BinInterface *bio = BinFileInterface(file.c_str(), bioflags); pqistreamer *streamer = new pqistreamer(rsSerialiser, src, bio, 0); return streamer; #endif return NULL; } std::string generateRandomLinkId() { std::ostringstream out; out << std::hex; /* 4 bytes per random number: 4 x 4 = 16 bytes */ for(int i = 0; i < 4; i++) { uint32_t rint = random(); out << rint; } return out.str(); } void p3Ranking::createDummyData() { RsRankMsg *msg = new RsRankMsg(); time_t now = time(NULL); msg->PeerId(mOwnId); msg->rid = "0001"; msg->title = L"Original Awesome Site!"; msg->timestamp = now - 60 * 60 * 24 * 15; msg->link = L"http://www.retroshare.org"; msg->comment = L"Retroshares Website"; addRankMsg(msg); msg = new RsRankMsg(); msg->PeerId(mOwnId); msg->rid = "0002"; msg->title = L"Awesome Site!"; msg->timestamp = now - 123; msg->link = L"http://www.lunamutt.org"; msg->comment = L"Lunamutt's Website"; addRankMsg(msg); msg = new RsRankMsg(); msg->PeerId("ALTID"); msg->rid = "0002"; msg->title = L"Awesome Site!"; msg->timestamp = now - 60 * 60 * 24 * 29; msg->link = L"http://www.lunamutt.org"; msg->comment = L"Lunamutt's Website (TWO) How Long can this comment be!\n"; msg->comment += L"What happens to the second line?\n"; msg->comment += L"And a 3rd!"; addRankMsg(msg); msg = new RsRankMsg(); msg->PeerId("ALTID2"); msg->rid = "0002"; msg->title = L"Awesome Site!"; msg->timestamp = now - 60 * 60 * 7; msg->link = L"http://www.lunamutt.org"; msg->comment += L"A Short Comment"; addRankMsg(msg); /***** Third one ****/ msg = new RsRankMsg(); msg->PeerId(mOwnId); msg->rid = "0003"; msg->title = L"Weird Site!"; msg->timestamp = now - 60 * 60; msg->link = L"http://www.lunamutt.com"; msg->comment = L""; addRankMsg(msg); msg = new RsRankMsg(); msg->PeerId("ALTID"); msg->rid = "0003"; msg->title = L"Weird Site!"; msg->timestamp = now - 60 * 60 * 24 * 2; msg->link = L"http://www.lunamutt.com"; msg->comment = L""; addRankMsg(msg); }