Rev 21332 | Blame | Compare with Previous | Last modification | View Log | RSS feed
'''Created on Jan 15, 2015@author: amit'''from datetime import datetime, timedeltafrom dtr.config import PythonPropertyReaderfrom dtr.storage.Mongo import getDealRank, userLookUpForSaholicIdfrom dtr.storage.Mysql import getOrdersAfterDate1, getOrdersAfterDatefrom pprint import pprintfrom pymongo.mongo_client import MongoClientimport base64import importlibimport jsonimport mathimport mechanizeimport timeimport tracebackimport urllibimport urllib2from dtr.utils import utilssourceMap = {1:"amazon", 2:"flipkart", 3:"snapdeal", 4:"spice", 5:"shopclues", 6:"paytm", 7:"homeshop18"}headers = {'User-agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11','Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8','Accept-Language' : 'en-US,en;q=0.8','Accept-Charset' : 'ISO-8859-1,utf-8;q=0.7,*;q=0.3'}CASHBACK_URL = PythonPropertyReader.getConfig('CASHBACK_URL')USER_LOOKUP_URL = PythonPropertyReader.getConfig('USER_LOOKUP_URL')WALLET_CREDIT_URL = PythonPropertyReader.getConfig('WALLET_CREDIT_URL')def getStore(source_id):#module = sourceMap[source_id]store = Store(source_id)try:module = importlib.import_module("dtr.sources." + sourceMap[source_id])store = getattr(module, "Store")(source_id)return storeexcept:traceback.print_exc()return Noneclass ScrapeException(Exception):"""Exception raised for errors in the input.Attributes:expr -- input expression in which the error occurredmsg -- explanation of the error"""def __init__(self, expr, msg):self.expr = exprself.msg = msgclass ParseException(Exception):"""Exception raised for errors in the input.Attributes:expr -- input expression in which the error occurredmsg -- explanation of the error"""def __init__(self, expr, msg):self.expr = exprself.msg = msgclient = MongoClient('mongodb://localhost:27017/')class Store(object):ORDER_PLACED = 'Order Placed'ORDER_DELIVERED = 'Delivered'ORDER_SHIPPED = 'Shipped' #Lets see if we can make use of itORDER_CANCELLED = 'Cancelled'CB_INIT = 'Waiting Confirmation'CB_PENDING = 'Pending'CB_CREDIT_IN_PROCESS = 'Credit in process'CB_CREDITED = 'Credited to wallet'CB_NA = 'Not Applicable'CB_APPROVED = 'Approved'CB_REJECTED = 'Rejected'CB_ONHOLD = 'On hold'CB_CANCELLED = 'Cancelled'CONF_CB_SELLING_PRICE = 0CONF_CB_DISCOUNTED_PRICE = 1def __init__(self, store_id):self.db = client.Dtrself.catalogdb = client.Catalogself.store_id = store_idself.store_name = sourceMap[store_id]'''To Settle payback for respective stores.Also ensures that settlement happens only for approved orders'''def getName(self):raise NotImplementedErrordef scrapeAffiliate(self, startDate=None, endDate=None):raise NotImplementedErrordef getTrackingUrls(self, userId):raise NotImplementedErrordef saveToAffiliate(self, offers):raise NotImplementedErrordef getUpdateMap(self, subOrder, cashBackStatus):updateMap = {}closed = Falsefor (key,value) in subOrder.iteritems():if key=="status":if value==Store.ORDER_CANCELLED:closed = TrueupdateMap['subOrders.$.closed'] = Trueif cashBackStatus == Store.CB_PENDING:updateMap['subOrders.$.cashBackStatus'] = Store.CB_CANCELLEDif value==Store.ORDER_DELIVERED:closed = TrueupdateMap['subOrders.$.closed'] = Trueif cashBackStatus == Store.CB_PENDING:updateMap['subOrders.$.cashBackStatus'] = Store.CB_APPROVEDupdateMap['subOrders.$.' + key] = valuesubOrder['closed'] = closedreturn updateMapdef saveOrder(self, merchantOrder):#merchantOrder = Order()#merchantOrder.status#merchapassdef scrapeStoreOrders(self,):raise NotImplementedErrordef updateSubOrder(self, subOrder):pass#if subOrder.getdef _saveToOrder(self, order):collection = self.db.merchantOrdertry:print "************************************************************************"print "order"print orderprint "************************************************************************"order = collection.insert(order)return Trueexcept Exception as e:return Falsedef _updateToOrder(self, order):collection = self.db.merchantOrdertry:print "************************************************************************"print "order"print orderprint "************************************************************************"collection.update({"orderId":order['orderId']},{"$set":order}, upsert = True)#merchantOderexcept Exception as e:traceback.print_exc()def getCashbackAmount(self, productCode, amount):alagvar = CASHBACK_URL % (productCode,self.store_id)filehandle = urllib2.Request(alagvar,headers=headers)x= urllib2.urlopen(filehandle)cashBack = json.loads(str(x.read()))if len(cashBack)==0:return (0,0)else:maxCashBack = cashBack.get('maxCashBack')if maxCashBack:if cashBack.get('cash_back_type') ==1 and (float(cashBack.get('cash_back'))*amount)/100 > maxCashBack:cashBack['cash_back'] = cashBack['maxCashBack']return (cashBack['cash_back'], 0)elif cashBack.get('cash_back_type') ==2 and cashBack.get('cash_back') > cashBack.get('maxCashBack'):cashBack['cash_back'] = cashBack['maxCashBack']return (cashBack['cash_back'], 0)else:passif cashBack['cash_back_type'] == 1:return (math.floor((amount * cashBack['cash_back'])/100), cashBack['cash_back'])else:return (cashBack['cash_back'], 0)'''Parses the order for specific storeorder id, total amount, created on(now() if could not parsesuborder id, title, quantity, unit price, expected delivery date,status (default would be Order placed)once products are identified, each suborder can then be updatedwith respective cashback.Possible fields to display for Not yet delivered orders areProduct/Quantity/Amount/Store/CashbackAmount/OrderDate/ExpectedDelivery/OrderStaus/DetailedStatus/CashbackStatusNo need to show cancelled orders.CashbackStatus - NotApplicable/Pending/Approved/Cancelled/CreditedToWalletOrderStatus - Placed/Cancelled/Delivered'''def parseOrderRawHtml(self, orderId, subTagId, userId, rawHtml, orderSuccessUrl):passdef _updateOrdersPayBackStatus(self, searchMap, updateMap):searchMap['subOrders.missingAff'] = FalseupdateMap['subOrders.$.missingAff'] = Trueself.db.merchantOrder.update(searchMap, { '$set': updateMap }, multi=True)def _getActiveOrders(self, searchMap={}, collectionMap={}):collection = self.db.merchantOrdersearchMap = dict(searchMap.items()+ {"subOrders.closed": False, "storeId" : self.store_id}.items())#collectionMap = dict(collectionMap.items() + {"orderSuccessUrl":1, "orderId":1,"subOrders":1, "placedOn":1, "orderTracking"}.items())stores = collection.find(searchMap)return [store for store in stores]def _getMissingOrders(self,searchMap={}):collection = self.db.merchantOrdersearchMap = dict(searchMap.items()+ {"requireDetail":True, "storeId":self.store_id}.items())orders = collection.find(searchMap)return list(orders)def _isSubOrderActive(self,order, merchantSubOrderId):subOrders = order.get("subOrders")if subOrders is None:return Nonefor subOrder in subOrders:if merchantSubOrderId == subOrder.get("merchantSubOrderId"):return subOrderreturn Nonedef _isSubOrderActive1(self,order, merchantSubOrderId):subOrders = order.subOrdersif subOrders is None:return Nonefor subOrder in subOrders:if merchantSubOrderId == subOrder.merchantSubOrderId:return subOrderreturn Nonedef populateDerivedFields(self, order, update=False):closed=Truefor subOrder in order.subOrders:if subOrder.closed:continueif not subOrder.status:subOrder.status = self._getStatusFromDetailedStatus(subOrder.detailedStatus)if not update:amount = subOrder.amount if hasattr(subOrder, 'amount') else subOrder.amountPaidcashbackAmount, cashbackPercent = self.getCashbackAmount(subOrder.productCode, amount/subOrder.quantity)cashbackStatus = Store.CB_PENDINGif cashbackAmount <= 0:cashbackStatus = Store.CB_NAsubOrder.cashBackAmount = cashbackAmount*subOrder.quantitysubOrder.cashBackPercentage = cashbackPercentsubOrder.cashBackStatus = cashbackStatusdealRank = getDealRank(subOrder.productCode, self.store_id, order.userId)subOrder.dealRank = dealRank.get('rank')subOrder.rankDesc = dealRank.get('description')subOrder.maxNlc = dealRank.get('maxNlc')subOrder.minNlc = dealRank.get('minNlc')subOrder.db = dealRank.get('dp')subOrder.itemStatus = dealRank.get('status')subOrder.closed = subOrder.status in [Store.ORDER_CANCELLED, Store.ORDER_DELIVERED]closed = closed and subOrder.closedif subOrder.cashBackStatus!=Store.CB_NA:subOrder.cashBackStatus = Store.CB_PENDINGif subOrder.status == self.ORDER_CANCELLED:subOrder.cashBackStatus = Store.CB_CANCELLEDelif subOrder.status == self.ORDER_DELIVERED:subOrder.cashBackStatus = Store.CB_APPROVEDorder.closed = closeddef settlePayBack(runtype='dry'):userAmountMap = {}searchMapList = []for mo in client.Dtr.merchantOrder.find({"subOrders.cashBackStatus":Store.CB_APPROVED}):userId = mo.get("userId")if mo.get('subOrders') is not None:for so in mo['subOrders']:if so.get('cashBackStatus') == Store.CB_APPROVED:searchMapList.append({"orderId":mo.get("orderId"), "subOrders.merchantSubOrderId":so.get("merchantSubOrderId")})if not userAmountMap.has_key(userId):userAmountMap[userId] = so.get('cashBackAmount')else:userAmountMap[userId] += so.get('cashBackAmount')print "%s\t%s\t%s\t%s\t%s\t%s\t%s"%(userId, mo.get("orderId"), so.get("merchantSubOrderId"),so.get("productTitle") ,so.get("cashBackStatus"), so.get("cashBackAmount"), so.get("batchId"))else:print "%s\t%s\t%s\t%s\t%s\t%s\t%s"%(userId, mo.get("orderId"), so.get("merchantSubOrderId"),so.get("productTitle") ,so.get("cashBackStatus"), so.get("cashBackAmount"), so.get("batchId"))for searchMap in searchMapList:print "%s\t%s"%(searchMap.get('orderId'),searchMap.get('subOrders.merchantSubOrderId'))for key,val in userAmountMap.iteritems():print "%s\t%s"%(key,val)if (runtype=='live'):bulk = client.Dtr.merchantOrder.initialize_ordered_bulk_op()if len(searchMapList) == 0:returnfor searchMap in searchMapList:bulk.find(searchMap).update({'$set' : {'subOrders.$.cashBackStatus':Store.CB_CREDIT_IN_PROCESS}})bulk.execute()datetimeNow = datetime.now()batchId = int(time.mktime(datetimeNow.timetuple()))if refundToWallet(batchId, userAmountMap):bulk = client.Dtr.merchantOrder.initialize_ordered_bulk_op()for searchMap in searchMapList:bulk.find(searchMap).update({'$set' : {'subOrders.$.cashBackStatus':Store.CB_CREDITED, "subOrders.$.batchId":batchId}})print bulk.execute()sum=0creditedSubOrders=0message = []for key, value in userAmountMap.iteritems():sum += valuecreditedSubOrders += 1client.Dtr.refund.insert({"userId": key, "batch":batchId, "userAmount":value, "timestamp":datetime.strftime(datetimeNow,"%Y-%m-%d %H:%M:%S"), "type":utils.CREDIT_TYPE_ORDER})client.Dtr.user.update({"userId":key}, {"$inc": { "credited": value, utils.CREDIT_TYPE_ORDER:value}}, upsert=True)message.append("<b>Batch Id - %d</b><br><b>Total Amount Credited - %d</b><br><b>Total SubOrders Credited- %d</b>"%(batchId,sum, creditedSubOrders))utils.sendmail(['amit.gupta@shop2020.in', 'rajneesh.arora@saholic.com','khushal.bhatia@saholic.com'], "".join(message), 'Cashback for Order Credited Successfully')tprint("PayBack Settled")else:tprint("Error Occurred while running batch. Rolling Back")bulk = client.Dtr.merchantOrder.initialize_ordered_bulk_op()for searchMap in searchMapList:bulk.find(searchMap).update({'$set' : {'subOrders.$.cashBackStatus':Store.CB_APPROVED}})bulk.execute()def settlePayBack1(runtype='dry'):approvedUserMap = {}pendingUserMap = {}creditedToWalletUserMap = {}#client.Dtr.merchantOrder.update({'subOrders.cashBackStatus':Store.CB_APPROVED},{'$set':{'subOrders.$.cashBackStatus':Store.CB_CREDIT_IN_PROCESS}}, multi=True)for mo in client.Dtr.merchantOrder.find({"subOrders.cashBackStatus":Store.CB_CREDITED}):userId = mo.get("userId")for so in mo['subOrders']:print "%s\t%s\t%s\t%s\t%s\t%s\t%s"%(userId, mo.get("orderId"), so.get("merchantSubOrderId"),so.get("productTitle") ,so.get("cashBackStatus"), so.get("cashBackAmount"), so.get("batchId"))for refund in client.Dtr.refund.find():print "%s\t%s\t%s\t%s"%(refund.get("userId") ,refund.get("timestamp") ,refund.get("userAmount"), refund.get("batch"))def refundToWallet(batchId, userAmountMap):#base64string = base64.encodestring('%s:%s' % ("dtr", "dtr18Feb2015")).replace('\n', '')batchUpdateMap = {}try :saholicUserAmountMap = {}for key, value in userAmountMap.iteritems():#userLookupRequest = urllib2.Request(USER_LOOKUP_URL %(key), headers=headers)#userLookupRequest.add_header("Authorization", "Basic %s" % base64string)try:#response = urllib2.urlopen(userLookupRequest).read()#saholicUserId = json.loads(response)['account_key']saholicUserId = userLookUpForSaholicId(key)if saholicUserId is not 'User Not present in profitmandi':saholicUserAmountMap[saholicUserId] = valueelse:raise Exception('User Not present in dtr')except:traceback.print_exc()tprint("Could not fetch saholic id for user : " + str(key))continueif len(saholicUserAmountMap) > 0:batchUpdateMap['userAmount'] = json.dumps(saholicUserAmountMap)batchUpdateMap['batchId'] = batchIdrequest = urllib2.Request(WALLET_CREDIT_URL, headers=headers)data = urllib.urlencode(batchUpdateMap)response = urllib2.urlopen(request, data)return json.loads(response.read())['response']['credited']else:tprint("Nothing to Refund")return Falseexcept:traceback.print_exc()tprint("Could not batch refund")return Falsedef main():# client = MongoClient('mongodb://root:ecip$dtrMay2014@dtr:27017/')# s = Store(1)# orders = s.db.Dtr.find({"subOrders.imgUrl":{"$exists":1}}, {"merchantOrderId":1, "subOrders.productCode":1,"subOrders.imgUrl":1,"_id": 0})# db = client.Dtr# for order in orders:# for subOrder in order.get("subOrders"):# #db.merchantOrder.update({"merchantOrderId":order.getMerchantOrderId,"subOrders.imgUrl":{"$exists":0}, "subOrders.productCode":subOrder.get("productCode")}, {"$set":{"subOrders.$.imgUrl":subOrder.get("imgUrl")}})# db.merchantOrder.findOne()settlePayBack('dry')####Settlement process is suposed to be a batch and run weekly#It is should be running on first hour of mondays. As cron should# Maintain a batch id.agentDefault = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11'def getBrowserObject(agent = agentDefault):import cookielibbr = mechanize.Browser(factory=mechanize.RobustFactory())cj = cookielib.LWPCookieJar()br.set_cookiejar(cj)br.set_handle_equiv(True)br.set_handle_redirect(True)br.set_handle_referer(True)br.set_handle_robots(False)br.set_debug_http(False)br.set_debug_redirects(False)br.set_debug_responses(False)br.set_handle_refresh(mechanize._http.HTTPRefreshProcessor(), max_time=1)br.addheaders = [('User-Agent', agent),('Accept', 'text/html,application/xhtml+xml,application/json,application/xml;q=0.9,*/*;q=0.8'),('Accept-Encoding', 'gzip,deflate,sdch'),('Accept-Language', 'en-US,en;q=0.8'),('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.3')]return brdef todict(obj, classkey=None):if isinstance(obj, dict):data = {}for (k, v) in obj.items():data[k] = todict(v, classkey)return dataelif hasattr(obj, "_ast"):return todict(obj._ast())elif hasattr(obj, "__iter__"):return [todict(v, classkey) for v in obj]elif hasattr(obj, "__dict__"):data = dict([(key, todict(value, classkey))for key, value in obj.__dict__.iteritems()if not callable(value) and not key.startswith('_')])if classkey is not None and hasattr(obj, "__class__"):data[classkey] = obj.__class__.__name__return dataelse:return objdef manualCredit(runtype='dry'):#userAmountMap = {19964:8,7144:9,20274:12,25978:24,29914:24,28026:48,6409:48,2482:49,21852:52,2261:56,8109:57,23372:57,5772:82,29467:82,19612:82,7447:125,15898:126,11701:136,29483:138,20001:162,7070:164,11628:166,5222:250,14074:363,17378:369,7764:684,30153:996}userAmountMap={}for key,val in userAmountMap.iteritems():print "%s\t%s"%(key,val)if (runtype=='live'):datetimeNow = datetime.now()batchId = int(time.mktime(datetimeNow.timetuple()))if refundToWallet(batchId, userAmountMap):sum=0creditedSubOrders=0message = []for key, value in userAmountMap.iteritems():sum += valuecreditedSubOrders += 1client.Dtr.refund.insert({"userId": key, "batch":batchId, "userAmount":value, "timestamp":datetime.strftime(datetimeNow,"%Y-%m-%d %H:%M:%S"), "type":utils.CREDIT_TYPE_ORDER})client.Dtr.user.update({"userId":key}, {"$inc": { "credited": value, utils.CREDIT_TYPE_ORDER:value}}, upsert=True)message.append("<b>Batch Id - %d</b><br><b>Total Amount Credited - %d</b><br><b>Total SubOrders Credited- %d</b>"%(batchId,sum, creditedSubOrders))utils.sendmail(['amit.gupta@shop2020.in', 'rajneesh.arora@saholic.com','khushal.bhatia@saholic.com'], "".join(message), 'Cashback for Order Credited Successfully')tprint("PayBack Settled")else:tprint("Error Occurred while running batch. Rolling Back")def ungzipResponse(r):headers = r.info()if headers.get('Content-Encoding')=='gzip':import gzipgz = gzip.GzipFile(fileobj=r, mode='rb')html = gz.read()gz.close()else:html = r.read()return htmldef tprint(*msg):print datetime.now(), "-", msgif __name__ == '__main__':main()