Subversion Repositories SmartDukaan

Rev

Rev 13662 | Rev 13690 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
13569 amit.gupta 1
'''
2
Created on Jan 15, 2015
3
 
4
@author: amit
5
'''
13576 amit.gupta 6
from BeautifulSoup import BeautifulSoup
13569 amit.gupta 7
from bson.binary import Binary
13680 amit.gupta 8
from datetime import datetime, date, timedelta
13569 amit.gupta 9
from dtr import main
13576 amit.gupta 10
from dtr.dao import AffiliateInfo, Order, SubOrder
13680 amit.gupta 11
from dtr.main import getBrowserObject, ScrapeException, getStore, ParseException, Store as MStore,\
12
    ungzipResponse
13662 amit.gupta 13
from pprint import pprint
13569 amit.gupta 14
from pymongo import MongoClient
15
import json
16
import pymongo
17
import re
13662 amit.gupta 18
import traceback
13569 amit.gupta 19
import urllib
13603 amit.gupta 20
 
13569 amit.gupta 21
USERNAME='saholic1@gmail.com'
22
PASSWORD='spice@2020'
23
AFFILIATE_URL='http://affiliate.snapdeal.com'
24
POST_URL='https://api-p03.hasoffers.com/v3/Affiliate_Report.json'
25
ORDER_TRACK_URL='https://m.snapdeal.com/orderSummary'
26
CONFIG_URL='http://affiliate.snapdeal.com/publisher/js/config.php'
27
 
13662 amit.gupta 28
class Store(MStore):
13569 amit.gupta 29
 
30
    '''
31
    This is to map order statuses of our system to order statuses of snapdeal.
32
    And our statuses will change accordingly.
33
 
34
    '''
35
    OrderStatusMap = {
13662 amit.gupta 36
                      MStore.ORDER_PLACED : ['In Progress','N/A'],
37
                      MStore.ORDER_DELIVERED : ['Delivered'],
38
                      MStore.ORDER_SHIPPED : ['In Transit'],
39
                      MStore.ORDER_CANCELLED : ['Closed For Vendor Reallocation', 'Cancelled']
13569 amit.gupta 40
 
41
                      }
13662 amit.gupta 42
 
43
    CONF_CB_AMOUNT = MStore.CONF_CB_DISCOUNTED_PRICE
13569 amit.gupta 44
    def __init__(self,store_id):
45
        super(Store, self).__init__(store_id)
46
 
47
    def getName(self):
48
        return "snapdeal"
49
 
50
    def scrapeAffiliate(self, startDate=None, endDate=None):
51
        br = getBrowserObject()
52
        br.open(AFFILIATE_URL)
53
        br.select_form(nr=0)
54
        br.form['data[User][password]'] = PASSWORD 
55
        br.form['data[User][email]'] = USERNAME
56
        br.submit()
57
        response = br.open(CONFIG_URL)
58
 
13680 amit.gupta 59
        token =  re.findall('"session_token":"(.*?)"', ungzipResponse(response), re.IGNORECASE)[0]
13569 amit.gupta 60
 
61
        allOffers = self._getAllOffers(br, token)
62
 
13662 amit.gupta 63
        allPyOffers = []
64
        maxSaleDate = self._getLastSaleDate()
65
        newMaxSaleDate = maxSaleDate
66
        for offer in allOffers:
13680 amit.gupta 67
            pyOffer = self.covertToObj(offer).__dict__
68
            allPyOffers.append(pyOffer)
69
            saleDate = datetime.strptime(pyOffer['saleDate'],"%Y-%m-%d %H:%M:%S")
13662 amit.gupta 70
            if maxSaleDate < saleDate:
13680 amit.gupta 71
                self._updateOrdersPayBackStatus(pyOffer['subTagId'],pyOffer['saleDate'])
13662 amit.gupta 72
                if newMaxSaleDate < saleDate:
73
                    newMaxSaleDate = saleDate
74
 
75
        self._setLastSaleDate(newMaxSaleDate)
13569 amit.gupta 76
        self._saveToAffiliate(allPyOffers)
77
 
13662 amit.gupta 78
    def _setLastSaleDate(self, saleDate):
13680 amit.gupta 79
        self.db.lastSaleDtate.update({'storeId':self.store_id}, {'$set':{'saleDate':saleDate}})
13569 amit.gupta 80
 
13662 amit.gupta 81
    def _updateOrdersPayBackStatus(self, subTagId, saleDate):
82
        self.db.merchantOrder.update({"subTagId":subTagId, "placedOn":saleDate, "subOrders.cashbackStatus":MStore.CB_INIT},
83
                                              { '$set': { "subOrders.$.cashbackStatus" : MStore.CB_PENDING } })
84
 
85
 
86
 
87
    def _getLastSaleDate(self,):
88
        lastDaySaleObj = self.db.lastDaySale.find_one({"storeId":self.store_id})
89
        if lastDaySaleObj is None:
90
            return datetime.min
91
 
92
 
13576 amit.gupta 93
    def parseOrderRawHtml(self, orderId, subTagId, userId, rawHtml, orderSuccessUrl):
13582 amit.gupta 94
        try:
95
            br = getBrowserObject()
96
            url = ORDER_TRACK_URL + re.findall('.*(\?.*?)$', orderSuccessUrl,re.IGNORECASE)[0]
97
            response = br.open(url)
13680 amit.gupta 98
            page = ungzipResponse(response)
13582 amit.gupta 99
            #page=page.decode("utf-8")
100
            soup = BeautifulSoup(page,convertEntities=BeautifulSoup.HTML_ENTITIES)
101
            #orderHead = soup.find(name, attrs, recursive, text)
102
            sections = soup.findAll("section")
103
 
104
            #print sections
105
 
106
            order = sections[1]
107
            orderTrs = order.findAll("tr")
108
 
109
            placedOn = str(orderTrs[0].findAll("td")[1].text)
110
 
111
            #Pop two section elements
112
            sections.pop(0) 
113
            sections.pop(0)
114
            subOrders = sections
115
 
116
 
117
            merchantSubOrders = []
118
 
119
            merchantOrder = Order(orderId, userId, subTagId, self.store_id, orderSuccessUrl)
120
            merchantOrder.merchantOderId = re.findall(r'\d+', str(soup.find("div", {"class":"deals_heading"})))[1]
121
            for orderTr in orderTrs:
122
                orderTrString = str(orderTr)
123
                if "Total Amount" in orderTrString:
124
                    merchantOrder.totalAmount = re.findall(r'\d+', orderTrString)[0]
125
                elif "Delivery Charges" in orderTrString:
126
                    merchantOrder.deliveryCharges = re.findall(r'\d+', orderTrString)[0]
127
                elif "Discount Applied" in orderTrString:
128
                    merchantOrder.discountApplied = re.findall(r'\d+', orderTrString)[0]
129
                elif "Paid Amount" in orderTrString:
130
                    merchantOrder.paidAmount = re.findall(r'\d+', orderTrString)[0]
131
 
132
            for subOrderElement in subOrders:
133
                productUrl = str(subOrderElement.find("a")['href'])
134
                subTable = subOrderElement.find("table", {"class":"lrPad"})
135
                subTrs = subTable.findAll("tr")
136
                unitPrice=None
137
                offerDiscount = None
138
                deliveryCharges = None
139
                amountPaid = None
140
                for subTr in subTrs:
141
                    subTrString = str(subTr)
142
                    if "Unit Price" in subTrString:
143
                        unitPrice = re.findall(r'\d+', subTrString)[0]
144
                    if "Quantity" in subTrString:
145
                        qty = re.findall(r'\d+', subTrString)[0]
146
                    elif "Offer Discount" in subTrString:
147
                        offerDiscount =   re.findall(r'\d+', subTrString)[0]
148
                    elif "Delivery Charges" in subTrString:
149
                        deliveryCharges =   re.findall(r'\d+', subTrString)[0]
150
                    elif "Subtotal" in subTrString:
13603 amit.gupta 151
                        if int(qty) > 0:
152
                            amountPaid =   str(int(re.findall(r'\d+', subTrString)[0])/int(qty))
153
                        else:
154
                            amountPaid =   "0"
13662 amit.gupta 155
                if self.CONF_CB_AMOUNT == MStore.CONF_CB_SELLING_PRICE or offerDiscount is None:
156
                    amount = int(unitPrice)
157
                else:
158
                    amount = int(unitPrice) - int(offerDiscount)
13582 amit.gupta 159
 
160
                divs = subOrderElement.findAll("div", {"class": "blk lrPad subordrs"})
161
                if len(divs)<=0:
162
                    raise ParseException("subOrder", "Could not Parse suborders for Snapdeal")
163
 
164
                for div in divs:
165
                    productTitle = str(subOrderElement.find("a").text)
166
                    productUrl = "http://m.snapdeal.com/" + productUrl 
167
                    subOrder = SubOrder(productTitle, productUrl, placedOn, amountPaid)
168
 
169
                    subOrder.amountPaid = amountPaid
170
                    subOrder.deliveryCharges = deliveryCharges
171
                    subOrder.offerDiscount = offerDiscount
172
                    subOrder.unitPrice = unitPrice
173
                    subOrder.productCode = re.findall(r'\d+$', productUrl)[0]
13662 amit.gupta 174
                    cashbackAmount = self.getCashbackAmount(subOrder.productCode, amount)
175
                    cashbackStatus = Store.CB_INIT
176
                    if cashbackAmount <= 0:
177
                        cashbackStatus = Store.CB_NA
13610 amit.gupta 178
                    subOrder.cashBackStatus = cashbackStatus
179
                    subOrder.cashBackAmount = cashbackAmount
13569 amit.gupta 180
 
13610 amit.gupta 181
 
13582 amit.gupta 182
                    trackAnchor = div.find("a")   
183
                    if trackAnchor is not None:
184
                        subOrder.tracingkUrl = str(trackAnchor['href'])
185
 
186
                    divStr = str(div)
187
                    divStr = divStr.replace("\n","").replace("\t", "")
188
 
189
                    for line in divStr.split("<br />"):
190
                        if "Suborder ID" in line:
191
                            subOrder.merchantSubOrderId = re.findall(r'\d+', line)[0]   
192
                        elif "Status" in line:
193
                            subOrder.detailedStatus = re.findall('>(.*?)</span>', line, re.IGNORECASE)[0]
194
                        elif "Est. Shipping Date" in line:
195
                            subOrder.estimatedShippingDate = line.split(":")[1].strip()
196
                        elif "Est. Delivery Date" in line:
197
                            subOrder.estimatedDeliveryDate = line.split(":")[1].strip()
198
                        elif "Courier Name" in line:
199
                            subOrder.courierName = line.split(":")[1].strip()
200
                        elif "Tracking No" in line:
201
                            subOrder.trackingNumber = line.split(":")[1].strip()
202
 
203
                merchantSubOrders.append(subOrder)   
13569 amit.gupta 204
 
13582 amit.gupta 205
            merchantOrder.subOrders = merchantSubOrders
206
            #print merchantOrder
207
 
208
            self._saveToOrder(todict(merchantOrder))
209
            return True
210
        except:
211
            print "Error occurred"
13603 amit.gupta 212
            traceback.print_exc()
13569 amit.gupta 213
 
13582 amit.gupta 214
        return False
13569 amit.gupta 215
        #soup = BeautifulSoup(rawHtml,convertEntities=BeautifulSoup.HTML_ENTITIES)
216
        #soup.find(name, attrs, recursive, text)
13576 amit.gupta 217
 
218
    def _getStatusFromDetailedStatus(self, detailedStatus):
219
        for key, value in Store.OrderStatusMap.iteritems():
220
            if detailedStatus in value:
221
                return key
13662 amit.gupta 222
            print "Detailed Status need to be mapped"
13576 amit.gupta 223
        raise ParseException("_getStatusFromDetailedStatus", "Found new order status" + detailedStatus)
13569 amit.gupta 224
 
13610 amit.gupta 225
 
13569 amit.gupta 226
    def scrapeStoreOrders(self,):
13576 amit.gupta 227
        orders = self._getActiveOrders()
228
        br = getBrowserObject()
229
        for order in orders:
230
            url = ORDER_TRACK_URL + re.findall('.*(\?.*?)$', order['orderSuccessUrl'],re.IGNORECASE)[0]
231
            response = br.open(url)
13680 amit.gupta 232
            page = ungzipResponse(response)
13576 amit.gupta 233
            #page=page.decode("utf-8")
234
            soup = BeautifulSoup(page,convertEntities=BeautifulSoup.HTML_ENTITIES)
235
            sections = soup.findAll("section")
236
            sections.pop(0)
237
            sections.pop(0)
238
 
239
            subOrders = sections
240
            bulk = self.db.merchantOrder.initialize_ordered_bulk_op()
241
            for subOrderElement in subOrders:
242
                closed = True
243
                divs = subOrderElement.findAll("div", {"class": "blk lrPad subordrs"})
244
                if len(divs)<=0:
245
                    raise ParseException("subOrder", "Could not Parse suborders for Snapdeal")
246
 
247
                for div in divs:
248
                    divStr = str(div)
249
                    divStr = divStr.replace("\n","").replace("\t", "")
250
                    updateMap = {}
251
                    for line in divStr.split("<br />"):
252
                        if "Suborder ID" in line:
13634 amit.gupta 253
                            merchantSubOrderId = re.findall(r'\d+', line)[0]
254
                            #break if suborder is inactive   
13610 amit.gupta 255
                            findMap = {"orderId": order['orderId'], "subOrders.merchantSubOrderId": merchantSubOrderId}
13634 amit.gupta 256
                            if not self._isSubOrderActive(order, merchantSubOrderId):
257
                                break
13576 amit.gupta 258
                        elif "Status" in line:
259
                            detailedStatus = re.findall('>(.*?)</span>', line, re.IGNORECASE)[0]
260
                            detailedStatus = re.findall('>(.*?)</span>', line, re.IGNORECASE)[0]
261
                            updateMap["subOrders.$.detailedStatus"] = detailedStatus
262
                            status = self._getStatusFromDetailedStatus(detailedStatus) 
13634 amit.gupta 263
                            closedStatus = status in [Store.ORDER_DELIVERED, Store.ORDER_CANCELLED]
264
                            if closedStatus:
265
                                #if status is closed then change the paybackStatus accordingly
266
                                if closedStatus == Store.ORDER_DELIVERED:
267
                                    if order.get("cashBackStatus") == Store.CB_PENDING:
268
                                        updateMap["subOrders.$.cashBackStatus"] = Store.CB_APPROVED
269
                                elif closedStatus == Store.ORDER_CANCELLED:
270
                                    if order.get("cashBackStatus") == Store.CB_PENDING:
271
                                        updateMap["subOrders.$.cashBackStatus"] = Store.CB_CANCELLED
272
 
273
                            else:
274
                                closed = False
13576 amit.gupta 275
                        elif "Est. Shipping Date" in line:
276
                            estimatedShippingDate = line.split(":")[1].strip()
277
                            updateMap["subOrders.$.estimatedShippingDate"] = estimatedShippingDate
278
                        elif "Est. Delivery Date" in line:
279
                            estimatedDeliveryDate = line.split(":")[1].strip()
280
                            updateMap["subOrders.$.estimatedDeliveryDate"] = estimatedDeliveryDate
281
                        elif "Courier Name" in line:
282
                            courierName = line.split(":")[1].strip()
283
                            updateMap["subOrders.$.courierName"] = courierName
284
                        elif "Tracking No" in line:
285
                            trackingNumber = line.split(":")[1].strip()
286
                            updateMap["subOrders.$.trackingNumber"] = trackingNumber
287
 
288
                    bulk.find(findMap).update({'$set' : updateMap})
13610 amit.gupta 289
                    bulk.find({'orderId': order['orderId']}).update({'$set':{'closed': closed}})
13576 amit.gupta 290
            result = bulk.execute()
291
            pprint(result)        
292
 
293
 
13634 amit.gupta 294
    def _isSubOrderActive(self,order, merchantSubOrderId):
295
        subOrders = order.get("subOrders")
296
        for subOrder in subOrders:
297
            if merchantSubOrderId == subOrder.get("merchantSubOrderId"):
298
                return True
299
        return False
300
 
13576 amit.gupta 301
 
302
 
13569 amit.gupta 303
    def _saveToAffiliate(self, offers):
13576 amit.gupta 304
        collection = self.db.snapdealOrderAffiliateInfo
13569 amit.gupta 305
        try:
306
            collection.insert(offers,continue_on_error=True)
307
        except pymongo.errors.DuplicateKeyError as e:
308
            print e.details
309
 
13576 amit.gupta 310
    def _saveToOrder(self, order):
311
        collection = self.db.merchantOrder
312
        try:
313
            order = collection.insert(order)
314
            #merchantOder 
315
        except Exception as e:
13603 amit.gupta 316
            traceback.print_exc()
13569 amit.gupta 317
 
318
    def _getAllOffers(self, br, token):
319
        allOffers = []
320
        nextPage = 1  
321
        while True:
322
            data = getPostData(token, nextPage)
323
            response = br.open(POST_URL, data)
13680 amit.gupta 324
            rmap = json.loads(ungzipResponse(response))
13569 amit.gupta 325
            if rmap is not None:
326
                rmap = rmap['response']
327
                if rmap is not None and len(rmap['errors'])==0:
328
                    allOffers += rmap['data']['data']
329
            nextPage += 1
330
            if rmap['data']['pageCount']<nextPage:
331
                break
332
 
333
        return allOffers
334
 
335
    def covertToObj(self,offer):
336
        offerData = offer['Stat']
337
        offer1 = AffiliateInfo(offerData['affiliate_info1'], self.store_id, offerData['conversion_status'], offerData['ad_id'], 
338
                              offerData['datetime'], offerData['payout'], offer['Offer']['name'], offerData['ip'], offerData['conversion_sale_amount'])
13680 amit.gupta 339
 
13569 amit.gupta 340
        return offer1
341
def getPostData(token, page = 1, limit= 20, startDate=None, endDate=None):
13680 amit.gupta 342
    endDate=date.today() + timedelta(days=1)
343
    startDate=endDate - timedelta(days=31)
13569 amit.gupta 344
 
345
    parameters = (
346
        ("page",str(page)),
347
        ("limit",str(limit)),
348
        ("fields[]","Stat.offer_id"),
349
        ("fields[]","Stat.datetime"),
350
        ("fields[]","Offer.name"),
351
        ("fields[]","Stat.conversion_status"),
352
        ("fields[]","Stat.conversion_sale_amount"),
353
        ("fields[]","Stat.payout"),
354
        ("fields[]","Stat.ip"),
355
        ("fields[]","Stat.ad_id"),
356
        ("fields[]","Stat.affiliate_info1"),
357
        ("sort[Stat.datetime]","desc"),
358
        ("filters[Stat.date][conditional]","BETWEEN"),
359
        ("filters[Stat.date][values][]",startDate.strftime('%Y-%m-%d')),
360
        ("filters[Stat.date][values][]",endDate.strftime('%Y-%m-%d')),
361
        ("data_start",startDate.strftime('%Y-%m-%d')),
362
        ("data_end",endDate.strftime('%Y-%m-%d')),
363
        ("Method","getConversions"),
364
        ("NetworkId","jasper"),
365
        ("SessionToken",token),
366
    )
367
    #Encode the parameters
368
    return urllib.urlencode(parameters)
369
 
370
def main():
13634 amit.gupta 371
 
13569 amit.gupta 372
    store = getStore(3)
13662 amit.gupta 373
    #store._isSubOrderActive(8, "5970688907")
374
    store.scrapeAffiliate()
13576 amit.gupta 375
    #store.parseOrderRawHtml(12345, "subtagId", 122323,  "html", 'https://m.snapdeal.com/purchaseMobileComplete?code=1f4166d13ea799b65aa9dea68b3e9e70&order=4509499363')
13569 amit.gupta 376
 
377
 
378
if __name__ == '__main__':
379
    main()
13576 amit.gupta 380
 
381
def todict(obj, classkey=None):
382
    if isinstance(obj, dict):
383
        data = {}
384
        for (k, v) in obj.items():
385
            data[k] = todict(v, classkey)
386
        return data
387
    elif hasattr(obj, "_ast"):
388
        return todict(obj._ast())
389
    elif hasattr(obj, "__iter__"):
390
        return [todict(v, classkey) for v in obj]
391
    elif hasattr(obj, "__dict__"):
392
        data = dict([(key, todict(value, classkey)) 
393
            for key, value in obj.__dict__.iteritems() 
394
            if not callable(value) and not key.startswith('_')])
395
        if classkey is not None and hasattr(obj, "__class__"):
396
            data[classkey] = obj.__class__.__name__
397
        return data
398
    else:
399
        return obj