![]() |
RTBKit
0.9
Open-source framework to create real-time ad bidding systems.
|
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