Subversion Repositories SmartDukaan

Rev

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