Subversion Repositories SmartDukaan

Rev

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