Subversion Repositories SmartDukaan

Rev

Rev 14348 | Rev 14398 | 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
 
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:
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()
13809 amit.gupta 202
            subOrders.append(subOrder)
203
        return subOrders
13760 amit.gupta 204
 
13576 amit.gupta 205
    def parseOrderRawHtml(self, orderId, subTagId, userId, rawHtml, orderSuccessUrl):
13760 amit.gupta 206
                    #print merchantOrder
13796 amit.gupta 207
        resp = {}
13582 amit.gupta 208
        try:
209
            br = getBrowserObject()
210
            url = ORDER_TRACK_URL + re.findall('.*(\?.*?)$', orderSuccessUrl,re.IGNORECASE)[0]
13791 amit.gupta 211
            page = br.open(url)
212
            page = ungzipResponse(page)
14145 amit.gupta 213
            merchantOrder = self._parse(orderId, subTagId, userId, page, orderSuccessUrl)
14312 amit.gupta 214
            merchantOrder.orderTrackingUrl = url
215
 
216
            if self._saveToOrder(todict(merchantOrder)):
217
                resp['result'] = 'ORDER_CREATED'
218
            else:
219
                resp['result'] = 'ORDER_ALREADY_CREATED_IGNORED'
220
 
13796 amit.gupta 221
            return resp
13582 amit.gupta 222
        except:
223
            print "Error occurred"
13603 amit.gupta 224
            traceback.print_exc()
14312 amit.gupta 225
            resp['result'] = 'ORDER_NOT_CREATED'
13796 amit.gupta 226
            return resp
13781 amit.gupta 227
 
13569 amit.gupta 228
 
229
        #soup = BeautifulSoup(rawHtml,convertEntities=BeautifulSoup.HTML_ENTITIES)
230
        #soup.find(name, attrs, recursive, text)
13576 amit.gupta 231
 
232
    def _getStatusFromDetailedStatus(self, detailedStatus):
233
        for key, value in Store.OrderStatusMap.iteritems():
14288 amit.gupta 234
            print key, value
13576 amit.gupta 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()
13730 amit.gupta 244
        print "Found orders", orders
13576 amit.gupta 245
        br = getBrowserObject()
246
        for order in orders:
247
            url = ORDER_TRACK_URL + re.findall('.*(\?.*?)$', order['orderSuccessUrl'],re.IGNORECASE)[0]
248
            response = br.open(url)
13680 amit.gupta 249
            page = ungzipResponse(response)
13576 amit.gupta 250
            #page=page.decode("utf-8")
251
            soup = BeautifulSoup(page,convertEntities=BeautifulSoup.HTML_ENTITIES)
252
            sections = soup.findAll("section")
13760 amit.gupta 253
            orderEl = sections[1]
254
            orderTrs = orderEl.findAll("tr")
255
 
256
            placedOn = str(orderTrs[0].findAll("td")[1].text)
13576 amit.gupta 257
            sections.pop(0)
258
            sections.pop(0)
259
 
260
            subOrders = sections
261
            bulk = self.db.merchantOrder.initialize_ordered_bulk_op()
262
            for subOrderElement in subOrders:
263
                closed = True
264
                divs = subOrderElement.findAll("div", {"class": "blk lrPad subordrs"})
265
                if len(divs)<=0:
266
                    raise ParseException("subOrder", "Could not Parse suborders for Snapdeal")
13721 amit.gupta 267
                subOrder = None
13760 amit.gupta 268
                breakFlag = False
13576 amit.gupta 269
                for div in divs:
270
                    divStr = str(div)
271
                    divStr = divStr.replace("\n","").replace("\t", "")
272
                    updateMap = {}
273
                    for line in divStr.split("<br />"):
274
                        if "Suborder ID" in line:
13634 amit.gupta 275
                            merchantSubOrderId = re.findall(r'\d+', line)[0]
276
                            #break if suborder is inactive   
13721 amit.gupta 277
                            subOrder =  self._isSubOrderActive(order, merchantSubOrderId)
278
                            if subOrder is None:
13809 amit.gupta 279
                                subOrders = self.parseSubOrder(subOrderElement, placedOn)
14239 amit.gupta 280
                                self.db.merchantOrder.update({"orderId":order['orderId']},{'$push':{"subOrders":{"$each":todict(subOrders)}}})
14241 amit.gupta 281
                                print "Added new suborders to Order id - " + order['orderId']
13760 amit.gupta 282
                                closed = False
14240 amit.gupta 283
                                breakFlag = True
284
                                break
13760 amit.gupta 285
                            elif subOrder['closed']:
286
                                breakFlag = True
13634 amit.gupta 287
                                break
13760 amit.gupta 288
                            else: 
289
                                findMap = {"orderId": order['orderId'], "subOrders.merchantSubOrderId": merchantSubOrderId}
13576 amit.gupta 290
                        elif "Status" in line:
291
                            detailedStatus = re.findall('>(.*?)</span>', line, re.IGNORECASE)[0]
292
                            updateMap["subOrders.$.detailedStatus"] = detailedStatus
293
                            status = self._getStatusFromDetailedStatus(detailedStatus) 
13634 amit.gupta 294
                            closedStatus = status in [Store.ORDER_DELIVERED, Store.ORDER_CANCELLED]
13760 amit.gupta 295
                            updateMap["subOrders.$.status"] = status
296
                            if detailedStatus == 'Closed For Vendor Reallocation':
13770 amit.gupta 297
                                #if it is more than 6hours mark closed.
13760 amit.gupta 298
                                closeAt = subOrder.get("closeAt") 
299
                                if closeAt is None:
300
                                    closeAt = datetime.now() + timedelta(hours=6)
13770 amit.gupta 301
                                    updateMap["subOrders.$.closeAt"] = datetime.strftime(closeAt,"%Y-%m-%d %H:%M:%S")
13760 amit.gupta 302
                                else:
13770 amit.gupta 303
                                    closeAt = datetime.strptime(closeAt,"%Y-%m-%d %H:%M:%S")
13760 amit.gupta 304
                                    if datetime.now() > closeAt:
305
                                        closedStatus = True
306
 
307
 
13634 amit.gupta 308
                            if closedStatus:
309
                                #if status is closed then change the paybackStatus accordingly
13760 amit.gupta 310
                                updateMap["subOrders.$.closed"] = True
311
                                if status == Store.ORDER_DELIVERED:
13721 amit.gupta 312
                                    if subOrder.get("cashBackStatus") == Store.CB_PENDING:
13634 amit.gupta 313
                                        updateMap["subOrders.$.cashBackStatus"] = Store.CB_APPROVED
13760 amit.gupta 314
                                elif status == Store.ORDER_CANCELLED:
13721 amit.gupta 315
                                    if subOrder.get("cashBackStatus") == Store.CB_PENDING:
13634 amit.gupta 316
                                        updateMap["subOrders.$.cashBackStatus"] = Store.CB_CANCELLED
317
 
318
                            else:
319
                                closed = False
13576 amit.gupta 320
                        elif "Est. Shipping Date" in line:
321
                            estimatedShippingDate = line.split(":")[1].strip()
322
                            updateMap["subOrders.$.estimatedShippingDate"] = estimatedShippingDate
323
                        elif "Est. Delivery Date" in line:
324
                            estimatedDeliveryDate = line.split(":")[1].strip()
325
                            updateMap["subOrders.$.estimatedDeliveryDate"] = estimatedDeliveryDate
326
                        elif "Courier Name" in line:
327
                            courierName = line.split(":")[1].strip()
328
                            updateMap["subOrders.$.courierName"] = courierName
329
                        elif "Tracking No" in line:
330
                            trackingNumber = line.split(":")[1].strip()
331
                            updateMap["subOrders.$.trackingNumber"] = trackingNumber
13760 amit.gupta 332
 
333
                    if breakFlag:
334
                        break
13576 amit.gupta 335
 
336
                    bulk.find(findMap).update({'$set' : updateMap})
13721 amit.gupta 337
                bulk.find({'orderId': order['orderId']}).update({'$set':{'closed': closed}})
13576 amit.gupta 338
            result = bulk.execute()
339
            pprint(result)        
340
 
341
 
13569 amit.gupta 342
    def _saveToAffiliate(self, offers):
13725 amit.gupta 343
        if offers is None or len(offers)==0:
344
            print "no affiliate have been pushed"
345
            return
13576 amit.gupta 346
        collection = self.db.snapdealOrderAffiliateInfo
13569 amit.gupta 347
        try:
348
            collection.insert(offers,continue_on_error=True)
349
        except pymongo.errors.DuplicateKeyError as e:
350
            print e.details
351
 
352
 
353
    def _getAllOffers(self, br, token):
354
        allOffers = []
355
        nextPage = 1  
356
        while True:
357
            data = getPostData(token, nextPage)
358
            response = br.open(POST_URL, data)
13680 amit.gupta 359
            rmap = json.loads(ungzipResponse(response))
13569 amit.gupta 360
            if rmap is not None:
361
                rmap = rmap['response']
362
                if rmap is not None and len(rmap['errors'])==0:
363
                    allOffers += rmap['data']['data']
364
            nextPage += 1
365
            if rmap['data']['pageCount']<nextPage:
366
                break
367
 
368
        return allOffers
369
 
370
    def covertToObj(self,offer):
371
        offerData = offer['Stat']
372
        offer1 = AffiliateInfo(offerData['affiliate_info1'], self.store_id, offerData['conversion_status'], offerData['ad_id'], 
373
                              offerData['datetime'], offerData['payout'], offer['Offer']['name'], offerData['ip'], offerData['conversion_sale_amount'])
13680 amit.gupta 374
 
13569 amit.gupta 375
        return offer1
376
def getPostData(token, page = 1, limit= 20, startDate=None, endDate=None):
13680 amit.gupta 377
    endDate=date.today() + timedelta(days=1)
378
    startDate=endDate - timedelta(days=31)
13569 amit.gupta 379
 
380
    parameters = (
381
        ("page",str(page)),
382
        ("limit",str(limit)),
383
        ("fields[]","Stat.offer_id"),
384
        ("fields[]","Stat.datetime"),
385
        ("fields[]","Offer.name"),
386
        ("fields[]","Stat.conversion_status"),
387
        ("fields[]","Stat.conversion_sale_amount"),
388
        ("fields[]","Stat.payout"),
389
        ("fields[]","Stat.ip"),
390
        ("fields[]","Stat.ad_id"),
391
        ("fields[]","Stat.affiliate_info1"),
392
        ("sort[Stat.datetime]","desc"),
393
        ("filters[Stat.date][conditional]","BETWEEN"),
394
        ("filters[Stat.date][values][]",startDate.strftime('%Y-%m-%d')),
395
        ("filters[Stat.date][values][]",endDate.strftime('%Y-%m-%d')),
396
        ("data_start",startDate.strftime('%Y-%m-%d')),
397
        ("data_end",endDate.strftime('%Y-%m-%d')),
398
        ("Method","getConversions"),
399
        ("NetworkId","jasper"),
400
        ("SessionToken",token),
401
    )
402
    #Encode the parameters
403
    return urllib.urlencode(parameters)
404
 
405
def main():
14239 amit.gupta 406
    print todict([1,2,"3"])
407
    #store = getStore(3)
408
    #store.scrapeStoreOrders()
13662 amit.gupta 409
    #store._isSubOrderActive(8, "5970688907")
13760 amit.gupta 410
    #store.scrapeAffiliate()
13576 amit.gupta 411
    #store.parseOrderRawHtml(12345, "subtagId", 122323,  "html", 'https://m.snapdeal.com/purchaseMobileComplete?code=1f4166d13ea799b65aa9dea68b3e9e70&order=4509499363')
13569 amit.gupta 412
 
413
 
13576 amit.gupta 414
 
415
def todict(obj, classkey=None):
416
    if isinstance(obj, dict):
417
        data = {}
418
        for (k, v) in obj.items():
419
            data[k] = todict(v, classkey)
420
        return data
421
    elif hasattr(obj, "_ast"):
422
        return todict(obj._ast())
423
    elif hasattr(obj, "__iter__"):
424
        return [todict(v, classkey) for v in obj]
425
    elif hasattr(obj, "__dict__"):
426
        data = dict([(key, todict(value, classkey)) 
427
            for key, value in obj.__dict__.iteritems() 
428
            if not callable(value) and not key.startswith('_')])
429
        if classkey is not None and hasattr(obj, "__class__"):
430
            data[classkey] = obj.__class__.__name__
431
        return data
432
    else:
433
        return obj
14239 amit.gupta 434
 
435
if __name__ == '__main__':
436
    main()