RTBKit  0.9
Open-source framework to create real-time ad bidding systems.
common/auction_events.cc
00001 /* post_auction_loop.h                                             -*- C++ -*-
00002    Jeremy Barnes, 31 May 2012
00003    Copyright (c) 2012 Datacratic.  All rights reserved.
00004 
00005    *AuctionEvent and related classes
00006 */
00007 
00008 
00009 
00010 #include <ostream>
00011 #include <string>
00012 
00013 #include "jml/utils/pair_utils.h"
00014 
00015 #include "auction_events.h"
00016 
00017 using namespace std;
00018 using namespace ML;
00019 using namespace RTBKIT;
00020 
00021 
00022 /*****************************************************************************/
00023 /* SUBMITTED AUCTION EVENT                                                   */
00024 /*****************************************************************************/
00025 
00026 void
00027 SubmittedAuctionEvent::
00028 serialize(ML::DB::Store_Writer & store) const
00029 {
00030     store << (unsigned char)0
00031           << auctionId << adSpotId << lossTimeout << augmentations
00032           << bidRequestStr << bidResponse << bidRequestStrFormat;
00033 }
00034 
00035 void
00036 SubmittedAuctionEvent::
00037 reconstitute(ML::DB::Store_Reader & store)
00038 {
00039     unsigned char version;
00040     store >> version;
00041     if (version != 0)
00042         throw ML::Exception("unknown SubmittedAuctionEvent type");
00043 
00044     store >> auctionId >> adSpotId >> lossTimeout >> augmentations
00045           >> bidRequestStr >> bidResponse >> bidRequestStrFormat;
00046 
00047     bidRequest.reset(BidRequest::parse(bidRequestStrFormat, bidRequestStr));
00048 }
00049 
00050 
00051 /*****************************************************************************/
00052 /* POST AUCTION EVENT TYPE                                                   */
00053 /*****************************************************************************/
00054 
00055 const char *
00056 RTBKIT::
00057 print(PostAuctionEventType type)
00058 {
00059     switch (type) {
00060     case PAE_INVALID: return "INVALID";
00061     case PAE_WIN: return "WIN";
00062     case PAE_LOSS: return "LOSS";
00063     case PAE_CAMPAIGN_EVENT: return "EVENT";
00064     default:
00065         return "UNKNOWN";
00066     }
00067 }
00068 
00069 namespace RTBKIT {
00070 COMPACT_PERSISTENT_ENUM_IMPL(PostAuctionEventType);
00071 }
00072 
00073 /*****************************************************************************/
00074 /* POST AUCTION EVENT                                                        */
00075 /*****************************************************************************/
00076 
00077 PostAuctionEvent::
00078 PostAuctionEvent()
00079     : type(PAE_INVALID)
00080 {
00081 }
00082 
00083 
00084 PostAuctionEvent::
00085 PostAuctionEvent(Json::Value const & json)
00086     : type(PAE_INVALID)
00087 {
00088     for (auto it = json.begin(), end = json.end(); it != end; ++it) {
00089         if (it.memberName() == "type")
00090             type = (PostAuctionEventType) it->asInt();
00091         else if (it.memberName() == "label")
00092             label = it->asString();
00093         else if (it.memberName() == "auctionId")
00094             auctionId.parse(it->asString());
00095         else if (it.memberName() == "adSpotId")
00096             adSpotId.parse(it->asString());
00097         else if (it.memberName() == "timestamp")
00098             timestamp = Date::fromSecondsSinceEpoch(it->asDouble());
00099         else if (it.memberName() == "account")
00100             account = AccountKey::fromJson(*it);
00101         else if (it.memberName() == "winPrice")
00102             winPrice = Amount::fromJson(*it);
00103         else if (it.memberName() == "uids")
00104             uids = UserIds::createFromJson(*it);
00105         else if (it.memberName() == "channels")
00106             channels = SegmentList::createFromJson(*it);
00107         else if (it.memberName() == "bidTimestamp")
00108             bidTimestamp = Date::fromSecondsSinceEpoch(it->asDouble());
00109         else throw ML::Exception("unknown location field " + it.memberName());
00110     }
00111 }
00112 
00113 
00114 Json::Value
00115 PostAuctionEvent::
00116 toJson() const
00117 {
00118     Json::Value result;
00119     result["type"] = (int) type;
00120     result["auctionId"] = auctionId.toString();
00121     result["adSpotId"] = adSpotId.toString();
00122     result["timestamp"] = timestamp.secondsSinceEpoch();
00123     result["account"] = account.toJson();
00124     result["winPrice"] = winPrice.toJson();
00125     result["uids"] = uids.toJson();
00126     result["channels"] = channels.toJson();
00127     result["bidTimestamp"] = bidTimestamp.secondsSinceEpoch();
00128     return result;
00129 }
00130 
00131 
00132 void
00133 PostAuctionEvent::
00134 serialize(ML::DB::Store_Writer & store) const
00135 {
00136     unsigned char version = 2;
00137     store << version << type;
00138     if (type == PAE_CAMPAIGN_EVENT) {
00139         store << label;
00140     }
00141     store << auctionId << adSpotId << timestamp
00142           << metadata << account << winPrice
00143           << uids << channels << bidTimestamp;
00144 }
00145 
00146 void
00147 PostAuctionEvent::
00148 reconstitute(ML::DB::Store_Reader & store)
00149 {
00150     unsigned char version;
00151     store >> version;
00152     if (version > 2)
00153         throw ML::Exception("reconstituting unknown version of "
00154                             "PostAuctionEvent");
00155     if (version <= 1) {
00156         string campaign, strategy;
00157         store >> type >> auctionId >> adSpotId >> timestamp
00158               >> metadata >> campaign >> strategy;
00159         account = { campaign, strategy };
00160     }
00161     else {
00162         store >> type;
00163         if (type == PAE_CAMPAIGN_EVENT) {
00164             store >> label;
00165         }
00166         store >> auctionId >> adSpotId >> timestamp
00167               >> metadata >> account;
00168     }
00169     if (version == 0) {
00170         int winCpmInMillis;
00171         store >> winCpmInMillis;
00172         winPrice = MicroUSD(winCpmInMillis);
00173     }
00174     else store >> winPrice;
00175 
00176     store >> uids >> channels >> bidTimestamp;
00177 }
00178 
00179 std::string
00180 PostAuctionEvent::
00181 print() const
00182 {
00183     std::string result = RTBKIT::print(type);
00184 
00185     auto addVal = [&] (const std::string & val)
00186         {
00187             result += '\t' + val;
00188         };
00189 
00190     if (auctionId) {
00191         addVal(auctionId.toString());
00192         addVal(adSpotId.toString());
00193     }
00194     addVal(timestamp.print(6));
00195     if (metadata.isNonNull())
00196         addVal(metadata.toString());
00197     if (!account.empty())
00198         addVal(account.toString());
00199     if (type == PAE_WIN)
00200         addVal(winPrice.toString());
00201     if (!uids.empty())
00202         addVal(uids.toString());
00203     if (!channels.empty())
00204         addVal(channels.toString());
00205     if (bidTimestamp != Date())
00206         addVal(bidTimestamp.print(6));
00207 
00208     return result;
00209 }
00210 
00211 std::ostream &
00212 RTBKIT::
00213 operator << (std::ostream & stream, const PostAuctionEvent & event)
00214 {
00215     return stream << event.print();
00216 }
00217 
00218 DB::Store_Writer &
00219 RTBKIT::
00220 operator << (DB::Store_Writer & store, shared_ptr<PostAuctionEvent> event)
00221 {
00222     event->serialize(store);
00223     return store;
00224 }
00225 
00226 DB::Store_Reader &
00227 RTBKIT::
00228 operator >> (DB::Store_Reader & store, shared_ptr<PostAuctionEvent> & event)
00229 {
00230     event.reset(new PostAuctionEvent());
00231     event->reconstitute(store);
00232     return store;
00233 }
00234 
00235 
00236 /******************************************************************************/
00237 /* CAMPAIGN EVENTS                                                            */
00238 /******************************************************************************/
00239 
00240 CampaignEvent
00241 CampaignEvent::
00242 fromJson(const Json::Value & jsonValue)
00243 {
00244     double timeSeconds(jsonValue["time"].asDouble());
00245 
00246     CampaignEvent event(
00247             jsonValue["label"].asString(),
00248             Date::fromSecondsSinceEpoch(timeSeconds),
00249             jsonValue["meta"]);
00250 
00251     return event;
00252 }
00253 
00254 Json::Value
00255 CampaignEvent::
00256 toJson() const
00257 {
00258     Json::Value json(Json::ValueType::objectValue);
00259 
00260     json["label"] = label_;
00261     json["timestamp"] = time_.secondsSinceEpoch();
00262     json["meta"] = meta_.toJson();
00263 
00264     return json;
00265 }
00266 
00267 void
00268 CampaignEvent::
00269 serialize(DB::Store_Writer & writer) const
00270 {
00271     writer << label_ << time_.secondsSinceEpoch();
00272     meta_.serialize(writer);
00273 }
00274 
00275 void
00276 CampaignEvent::
00277 reconstitute(DB::Store_Reader & store)
00278 {
00279     double timeSeconds;
00280     string metaStr;
00281 
00282     store >> label_ >> timeSeconds;
00283     time_ = Date::fromSecondsSinceEpoch(timeSeconds);
00284     meta_.reconstitute(store);
00285 }
00286 
00287 Json::Value
00288 CampaignEvents::
00289 toJson() const
00290 {
00291     Json::Value json(Json::ValueType::arrayValue);
00292 
00293     for (const CampaignEvent & history: *this) {
00294         json.append(history.toJson());
00295     }
00296 
00297     return json;
00298 }
00299 
00300 CampaignEvents
00301 CampaignEvents::
00302 fromJson(const Json::Value& json)
00303 {
00304     CampaignEvents events;
00305     ExcCheck(json.isArray(), "invalid format for a campaign events object");
00306 
00307     for (size_t i = 0; i < json.size(); ++i)
00308         events.push_back(CampaignEvent::fromJson(json[i]));
00309 
00310     return events;
00311 }
00312 
00313 bool
00314 CampaignEvents::
00315 hasEvent(const std::string & label) const
00316 {
00317     for (const CampaignEvent & history: *this) {
00318         if (history.label_ == label) {
00319             return true;
00320         }
00321     }
00322     return false;
00323 }
00324 
00325 void
00326 CampaignEvents::
00327 setEvent(const std::string & label,
00328                  Date eventTime,
00329                  const JsonHolder & eventMeta)
00330 {
00331     if (hasEvent(label))
00332         throw ML::Exception("already has event '" + label + "'");
00333     emplace_back(label, eventTime, eventMeta);
00334 }
00335 
00336 
00337 /******************************************************************************/
00338 /* DELIVERY EVENTS                                                            */
00339 /******************************************************************************/
00340 
00341 
00342 DeliveryEvent::Bid
00343 DeliveryEvent::Bid::
00344 fromJson(const Json::Value& json)
00345 {
00346     Bid bid;
00347     if (!json) return bid;
00348 
00349     bid.present = true;
00350 
00351     const auto& members = json.getMemberNames();
00352 
00353     for (const auto& m : members) {
00354         const Json::Value& member = json[m];
00355         bool invalid = false;
00356 
00357         switch(m[0]) {
00358         case 'a':
00359             if (m == "agent") bid.agent = member.asString();
00360             else if (m == "account") bid.account = AccountKey::fromJson(member);
00361             else invalid = true;
00362             break;
00363 
00364         case 'b':
00365             if (m == "bidData") bid.bids = Bids::fromJson(member.asString());
00366             else invalid = true;
00367             break;
00368 
00369         case 'c':
00370             if (m == "creativeId") bid.creativeId = member.asInt();
00371             else if (m == "creativeName") bid.creativeName = member.asString();
00372             else invalid = true;
00373             break;
00374 
00375         case 'l':
00376             if (m == "localStatus") {
00377                 string status = member.asString();
00378                 if (status == "PENDING") bid.localStatus = Auction::PENDING;
00379                 else if (status == "WIN") bid.localStatus = Auction::WIN;
00380                 else if (status == "LOSS") bid.localStatus = Auction::LOSS;
00381                 else if (status == "TOOLATE") bid.localStatus = Auction::TOOLATE;
00382                 else if (status == "INVALID") bid.localStatus = Auction::INVALID;
00383                 else throw Exception("invalid localStatus value: " + status);
00384             }
00385             else invalid = true;
00386             break;
00387 
00388         case 'm':
00389             if (m == "meta") bid.meta = member.asString();
00390             else invalid = true;
00391             break;
00392 
00393         case 'p':
00394             if (m == "price") bid.price = Auction::Price::fromJson(member);
00395             else invalid = true;
00396             break;
00397 
00398         case 't':
00399             if (m == "timestamp")
00400                 bid.time = Date::fromSecondsSinceEpoch(member.asDouble());
00401             else if (m == "test") bid.test = member.asBool();
00402             else if (m == "tagId") bid.test = member.asInt();
00403             else invalid = true;
00404             break;
00405 
00406         default:
00407             invalid = true;
00408             break;
00409         }
00410 
00411         ExcCheck(!invalid, "Unknown member: " + m);
00412     }
00413 
00414     return bid;
00415 }
00416 
00417 Json::Value
00418 DeliveryEvent::Bid::
00419 toJson() const
00420 {
00421     Json::Value json;
00422     if (!present) return json;
00423 
00424     json["timestamp"] = time.secondsSinceEpoch();
00425 
00426     json["price"] = price.toJson();
00427     json["test"] = test;
00428     json["tagId"] = tagId;
00429     json["bidData"] = bids.toJson();
00430 
00431     json["agent"] = agent;
00432     json["account"] = account.toJson();
00433     json["meta"] = meta;
00434 
00435     json["creativeId"] = creativeId;
00436     json["creativeName"] = creativeName;
00437 
00438     json["localStatus"] = Auction::Response::print(localStatus);
00439 
00440     return json;
00441 }
00442 
00443 
00444 
00445 DeliveryEvent::Win
00446 DeliveryEvent::Win::
00447 fromJson(const Json::Value& json)
00448 {
00449     Win win;
00450     if (!json) return win;
00451     win.present = true;
00452 
00453     const auto& members = json.getMemberNames();
00454 
00455     for (const auto& m : members) {
00456         const Json::Value& member = json[m];
00457         bool invalid = false;
00458 
00459         switch(m[0]) {
00460         case 'm':
00461             if (m == "meta") win.meta = member.asString();
00462             else invalid = true;
00463             break;
00464 
00465         case 't':
00466             if (m == "timestamp")
00467                 win.time = Date::fromSecondsSinceEpoch(member.asDouble());
00468             else invalid = true;
00469             break;
00470 
00471         case 'r':
00472             if (m == "reportedStatus")
00473                 win.reportedStatus = bidStatusFromString(member.asString());
00474             else invalid = true;
00475             break;
00476 
00477         case 'w':
00478             if (m == "winPrice") win.price = Amount::fromJson(member);
00479             else invalid = true;
00480             break;
00481 
00482         default:
00483             invalid = true;
00484             break;
00485         }
00486 
00487         ExcCheck(!invalid, "Unknown member: " + m);
00488     }
00489 
00490     return win;
00491 }
00492 
00493 Json::Value
00494 DeliveryEvent::Win::
00495 toJson() const
00496 {
00497     Json::Value json;
00498     if (!present) return json;
00499 
00500     json["timestamp"] = time.secondsSinceEpoch();
00501     json["reportedStatus"] = (reportedStatus == BS_WIN ? "WIN" : "LOSS");
00502     json["winPrice"] = price.toJson();
00503     json["meta"] = meta;
00504 
00505     return json;
00506 }
00507 
00508 Json::Value
00509 DeliveryEvent::
00510 impressionToJson() const
00511 {
00512     Json::Value json;
00513 
00514     for (const CampaignEvent& ev : campaignEvents) {
00515         if (ev.label_ != "IMPRESSION") continue;
00516         json = ev.toJson();
00517         break;
00518     }
00519 
00520     return json;
00521 }
00522 
00523 Json::Value
00524 DeliveryEvent::
00525 clickToJson() const
00526 {
00527     Json::Value json;
00528 
00529     for (const CampaignEvent& ev : campaignEvents) {
00530         if (ev.label_ != "CLICK") continue;
00531         json = ev.toJson();
00532         break;
00533     }
00534 
00535     return json;
00536 }
00537 
00538 DeliveryEvent::Visit
00539 DeliveryEvent::Visit::
00540 fromJson(const Json::Value& json)
00541 {
00542     Visit visit;
00543     if (!json) return visit;
00544 
00545     const auto& members = json.getMemberNames();
00546 
00547     for (const auto& m : members) {
00548         const Json::Value& member = json[m];
00549         bool invalid = false;
00550 
00551         switch(m[0]) {
00552 
00553         case 'c':
00554             if (m == "channels")
00555                 visit.channels = SegmentList::createFromJson(member);
00556             else invalid = true;
00557             break;
00558 
00559         case 'm':
00560             if (m == "meta") visit.meta = member.asString();
00561             else invalid = true;
00562             break;
00563 
00564         case 't':
00565             if (m == "timestamp")
00566                 visit.time = Date::fromSecondsSinceEpoch(member.asDouble());
00567             else invalid = true;
00568             break;
00569 
00570         default:
00571             invalid = true;
00572             break;
00573         }
00574 
00575         ExcCheck(!invalid, "Unknown member: " + m);
00576     }
00577 
00578     return visit;
00579 }
00580 
00581 Json::Value
00582 DeliveryEvent::Visit::
00583 toJson() const
00584 {
00585     Json::Value json;
00586 
00587     json["timestamp"] = time.secondsSinceEpoch();
00588     json["channels"] = channels.toJson();
00589     json["meta"] = meta;
00590 
00591     return json;
00592 }
00593 
00594 
00595 Json::Value
00596 DeliveryEvent::
00597 visitsToJson() const
00598 {
00599     Json::Value json;
00600 
00601     for (const auto& visit : visits)
00602         json.append(visit.toJson());
00603 
00604     return json;
00605 }
00606 
00607 
00608 
00609 DeliveryEvent
00610 DeliveryEvent::
00611 parse(const std::vector<std::string>& msg)
00612 {
00613     DeliveryEvent ev;
00614     ExcCheckGreaterEqual(msg.size(), 12, "Invalid message size");
00615 
00616     using boost::lexical_cast;
00617 
00618     ev.event = msg[0];
00619     ev.timestamp = Date::fromSecondsSinceEpoch(lexical_cast<double>(msg[1]));
00620     ev.auctionId = Id(msg[2]);
00621     ev.spotId = Id(msg[3]);
00622     ev.spotIndex = lexical_cast<int>(msg[4]);
00623 
00624     string bidRequestSource = msg[5];
00625     ev.bidRequest.reset(BidRequest::parse(bidRequestSource, msg[6]));
00626 
00627     ev.augmentations = msg[7];
00628 
00629     auto jsonParse = [] (const string& str)
00630         {
00631             if (str.empty()) return Json::Value();
00632             return Json::parse(str);
00633         };
00634 
00635     ev.bid = Bid::fromJson(jsonParse(msg[8]));
00636     ev.win = Win::fromJson(jsonParse(msg[9]));
00637     ev.campaignEvents = CampaignEvents::fromJson(jsonParse(msg[10]));
00638 
00639     const Json::Value& visits = jsonParse(msg[11]);
00640     for (size_t i = 0; i < visits.size(); ++i)
00641         ev.visits.push_back(Visit::fromJson(visits[i]));
00642 
00643     return ev;
00644 }
00645 
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator