RTBKit
0.9
Open-source framework to create real-time ad bidding systems.
|
00001 00009 #include "bids.h" 00010 00011 #include "jml/utils/exc_check.h" 00012 #include "jml/utils/json_parsing.h" 00013 00014 using namespace std; 00015 using namespace ML; 00016 00017 namespace RTBKIT { 00018 00019 00020 /******************************************************************************/ 00021 /* BID STATUS */ 00022 /******************************************************************************/ 00023 00024 const char* bidStatusToChar(BidStatus status) 00025 { 00026 switch (status) { 00027 case BS_WIN: return "WIN"; break; 00028 case BS_LOSS: return "LOSS"; break; 00029 case BS_TOOLATE: return "TOOLATE"; break; 00030 case BS_INVALID: return "INVALID"; break; 00031 case BS_LOSTBID: return "LOSTBID"; break; 00032 case BS_DROPPEDBID: return "DROPPEDBID"; break; 00033 case BS_NOBUDGET: return "NOBUDGET"; break; 00034 default: 00035 throw ML::Exception("unknown bid status"); 00036 } 00037 } 00038 00039 BidStatus bidStatusFromString(const std::string& str) 00040 { 00041 switch (str[0]) { 00042 case 'D': 00043 if (str == "DROPPEDBID") return BS_DROPPEDBID; 00044 break; 00045 00046 case 'I': 00047 if (str == "INVALID") return BS_INVALID; 00048 break; 00049 00050 case 'N': 00051 if (str == "NOBUDGET") return BS_NOBUDGET; 00052 break; 00053 00054 case 'L': 00055 if (str == "LOSS") return BS_LOSS; 00056 if (str == "LOSTBID") return BS_LOSTBID; 00057 break; 00058 00059 case 'T': 00060 if (str == "TOOLATE") return BS_TOOLATE; 00061 break; 00062 00063 case 'W': 00064 if (str == "WIN") return BS_WIN; 00065 break; 00066 }; 00067 00068 throw ML::Exception("unknown bid status"); 00069 } 00070 00071 00072 /******************************************************************************/ 00073 /* BID */ 00074 /******************************************************************************/ 00075 00076 void 00077 Bid:: 00078 bid(int creativeIndex, Amount price, double priority) 00079 { 00080 if (this->price > price) return; 00081 if (this->price == price && this->priority > priority) return; 00082 00083 ExcCheckGreaterEqual(creativeIndex, 0, "Invalid creative index"); 00084 00085 auto it = find( 00086 availableCreatives.begin(), 00087 availableCreatives.end(), 00088 creativeIndex); 00089 ExcCheck(it != availableCreatives.end(), 00090 "Creative index is not available for bidding: " + creativeIndex); 00091 00092 this->creativeIndex = creativeIndex; 00093 this->price = price; 00094 this->priority = priority; 00095 } 00096 00097 00098 Json::Value 00099 Bid:: 00100 toJson() const 00101 { 00102 if (isNullBid()) return Json::Value(); 00103 00104 Json::Value json(Json::objectValue); 00105 00106 json["creative"] = creativeIndex; 00107 json["price"] = price.toString(); 00108 json["priority"] = priority; 00109 if (!account.empty()) json["account"] = account.toString(); 00110 00111 return json; 00112 } 00113 00114 Bid 00115 Bid:: 00116 fromJson(ML::Parse_Context& context) 00117 { 00118 Bid bid; 00119 00120 if (context.match_literal("null") || context.match_literal("{}")) 00121 return bid; // null bid 00122 00123 auto onBidField = [&] ( 00124 const std::string& fieldName, ML::Parse_Context& context) 00125 { 00126 ExcCheck(!fieldName.empty(), "invalid empty field name"); 00127 00128 if (fieldName[0] == 'a' && fieldName == "account") 00129 bid.account = AccountKey(expectJsonStringAscii(context)); 00130 00131 else if (fieldName[0] == 'c' && fieldName == "creative") 00132 bid.creativeIndex = context.expect_int(); 00133 00134 else if (fieldName[0] == 'p' && fieldName == "price") 00135 bid.price = Amount::parse(expectJsonStringAscii(context)); 00136 00137 else if ((fieldName[0] == 'p' && fieldName == "priority") 00138 || (fieldName[0] == 's' && fieldName == "surplus")) 00139 { 00140 bid.priority = context.expect_double(); 00141 } 00142 00143 else throw ML::Exception("unknown bid field " + fieldName); 00144 }; 00145 00146 expectJsonObject(context, onBidField); 00147 00148 return bid; 00149 } 00150 00151 00152 /******************************************************************************/ 00153 /* BIDS */ 00154 /******************************************************************************/ 00155 00156 Json::Value 00157 Bids:: 00158 toJson() const 00159 { 00160 Json::Value json(Json::objectValue); 00161 00162 auto& bids = json["bids"]; 00163 for (const Bid& bid : *this) 00164 bids.append(bid.toJson()); 00165 00166 if (!dataSources.empty()) { 00167 auto& sources = json["sources"]; 00168 for (const string& dataSource : dataSources) 00169 sources.append(dataSource); 00170 } 00171 00172 return json; 00173 } 00174 00175 Bids 00176 Bids:: 00177 fromJson(const std::string& raw) 00178 { 00179 Bids result; 00180 00181 auto onDataSourceEntry = [&] (int, ML::Parse_Context& context) 00182 { 00183 result.emplace_back(Bid::fromJson(context)); 00184 }; 00185 00186 auto onBidEntry = [&] (int, ML::Parse_Context& context) 00187 { 00188 result.dataSources.insert(expectJsonStringAscii(context)); 00189 }; 00190 00191 auto onBidsEntry = [&] (const string& fieldName, ML::Parse_Context& context) 00192 { 00193 ExcCheck(!fieldName.empty(), "invalid empty field name"); 00194 00195 if (fieldName[0] == 'b' && fieldName == "bids") 00196 expectJsonArray(context, onDataSourceEntry); 00197 00198 else if (fieldName[0] == 's' && fieldName == "sources") 00199 expectJsonArray(context, onBidEntry); 00200 }; 00201 00202 ML::Parse_Context context(raw, raw.c_str(), raw.c_str() + raw.length()); 00203 expectJsonObject(context, onBidsEntry); 00204 00205 return result; 00206 } 00207 00208 00209 /******************************************************************************/ 00210 /* BID RESULT */ 00211 /******************************************************************************/ 00212 00213 BidResult 00214 BidResult:: 00215 parse(const std::vector<std::string>& msg) 00216 { 00217 BidResult result; 00218 ExcCheckGreaterEqual(msg.size(), 6, "Invalid bid result message size"); 00219 00220 result.result = bidStatusFromString(msg[0]); 00221 result.timestamp = boost::lexical_cast<double>(msg[1]); 00222 00223 result.confidence = msg[2]; 00224 result.auctionId = Id(msg[3]); 00225 result.spotNum = boost::lexical_cast<int>(msg[4]); 00226 result.secondPrice = Amount::parse(msg[5]); 00227 00228 // Lightweight messages stop here 00229 if (msg.size() <= 6) return result; 00230 ExcCheckGreaterEqual(msg.size(), 11, "Invalid long bid result message size"); 00231 00232 string bidRequestSource = msg[6]; 00233 result.request.reset(BidRequest::parse(bidRequestSource, msg[7])); 00234 00235 auto jsonParse = [] (const std::string& str) 00236 { 00237 if (str.empty()) return Json::Value(); 00238 return Json::parse(str); 00239 }; 00240 00241 result.ourBid = Bids::fromJson(msg[8]); 00242 result.metadata = jsonParse(msg[9]); 00243 result.augmentations = jsonParse(msg[10]); 00244 00245 return result; 00246 } 00247 00248 00249 00250 } // namepsace RTBKIT