Subversion Repositories SmartDukaan

Rev

Rev 13725 | Rev 13760 | 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
 
88
 
13576 amit.gupta 89
    def parseOrderRawHtml(self, orderId, subTagId, userId, rawHtml, orderSuccessUrl):
13582 amit.gupta 90
        try:
91
            br = getBrowserObject()
92
            url = ORDER_TRACK_URL + re.findall('.*(\?.*?)$', orderSuccessUrl,re.IGNORECASE)[0]
93
            response = br.open(url)
13680 amit.gupta 94
            page = ungzipResponse(response)
13582 amit.gupta 95
            #page=page.decode("utf-8")
96
            soup = BeautifulSoup(page,convertEntities=BeautifulSoup.HTML_ENTITIES)
97
            #orderHead = soup.find(name, attrs, recursive, text)
98
            sections = soup.findAll("section")
99
 
100
            #print sections
101
 
102
            order = sections[1]
103
            orderTrs = order.findAll("tr")
104
 
105
            placedOn = str(orderTrs[0].findAll("td")[1].text)
106
 
107
            #Pop two section elements
108
            sections.pop(0) 
109
            sections.pop(0)
110
            subOrders = sections
111
 
112
 
113
            merchantSubOrders = []
114
 
115
            merchantOrder = Order(orderId, userId, subTagId, self.store_id, orderSuccessUrl)
116
            merchantOrder.merchantOderId = re.findall(r'\d+', str(soup.find("div", {"class":"deals_heading"})))[1]
117
            for orderTr in orderTrs:
118
                orderTrString = str(orderTr)
119
                if "Total Amount" in orderTrString:
120
                    merchantOrder.totalAmount = re.findall(r'\d+', orderTrString)[0]
121
                elif "Delivery Charges" in orderTrString:
122
                    merchantOrder.deliveryCharges = re.findall(r'\d+', orderTrString)[0]
123
                elif "Discount Applied" in orderTrString:
124
                    merchantOrder.discountApplied = re.findall(r'\d+', orderTrString)[0]
125
                elif "Paid Amount" in orderTrString:
126
                    merchantOrder.paidAmount = re.findall(r'\d+', orderTrString)[0]
127
 
128
            for subOrderElement in subOrders:
129
                productUrl = str(subOrderElement.find("a")['href'])
130
                subTable = subOrderElement.find("table", {"class":"lrPad"})
131
                subTrs = subTable.findAll("tr")
132
                unitPrice=None
133
                offerDiscount = None
134
                deliveryCharges = None
135
                amountPaid = None
136
                for subTr in subTrs:
137
                    subTrString = str(subTr)
138
                    if "Unit Price" in subTrString:
139
                        unitPrice = re.findall(r'\d+', subTrString)[0]
140
                    if "Quantity" in subTrString:
141
                        qty = re.findall(r'\d+', subTrString)[0]
142
                    elif "Offer Discount" in subTrString:
143
                        offerDiscount =   re.findall(r'\d+', subTrString)[0]
144
                    elif "Delivery Charges" in subTrString:
145
                        deliveryCharges =   re.findall(r'\d+', subTrString)[0]
146
                    elif "Subtotal" in subTrString:
13603 amit.gupta 147
                        if int(qty) > 0:
148
                            amountPaid =   str(int(re.findall(r'\d+', subTrString)[0])/int(qty))
149
                        else:
150
                            amountPaid =   "0"
13662 amit.gupta 151
                if self.CONF_CB_AMOUNT == MStore.CONF_CB_SELLING_PRICE or offerDiscount is None:
152
                    amount = int(unitPrice)
153
                else:
154
                    amount = int(unitPrice) - int(offerDiscount)
13582 amit.gupta 155
 
156
                divs = subOrderElement.findAll("div", {"class": "blk lrPad subordrs"})
157
                if len(divs)<=0:
158
                    raise ParseException("subOrder", "Could not Parse suborders for Snapdeal")
159
 
160
                for div in divs:
161
                    productTitle = str(subOrderElement.find("a").text)
162
                    productUrl = "http://m.snapdeal.com/" + productUrl 
163
                    subOrder = SubOrder(productTitle, productUrl, placedOn, amountPaid)
164
 
165
                    subOrder.amountPaid = amountPaid
166
                    subOrder.deliveryCharges = deliveryCharges
167
                    subOrder.offerDiscount = offerDiscount
168
                    subOrder.unitPrice = unitPrice
169
                    subOrder.productCode = re.findall(r'\d+$', productUrl)[0]
13721 amit.gupta 170
                    (cashbackAmount, percentage) = self.getCashbackAmount(subOrder.productCode, amount)
13662 amit.gupta 171
                    cashbackStatus = Store.CB_INIT
172
                    if cashbackAmount <= 0:
173
                        cashbackStatus = Store.CB_NA
13610 amit.gupta 174
                    subOrder.cashBackStatus = cashbackStatus
175
                    subOrder.cashBackAmount = cashbackAmount
13721 amit.gupta 176
                    if percentage > 0:
177
                        subOrder.cashBackPercentage = percentage
13569 amit.gupta 178
 
13610 amit.gupta 179
 
13582 amit.gupta 180
                    trackAnchor = div.find("a")   
181
                    if trackAnchor is not None:
182
                        subOrder.tracingkUrl = str(trackAnchor['href'])
183
 
184
                    divStr = str(div)
185
                    divStr = divStr.replace("\n","").replace("\t", "")
186
 
187
                    for line in divStr.split("<br />"):
188
                        if "Suborder ID" in line:
189
                            subOrder.merchantSubOrderId = re.findall(r'\d+', line)[0]   
190
                        elif "Status" in line:
191
                            subOrder.detailedStatus = re.findall('>(.*?)</span>', line, re.IGNORECASE)[0]
192
                        elif "Est. Shipping Date" in line:
193
                            subOrder.estimatedShippingDate = line.split(":")[1].strip()
194
                        elif "Est. Delivery Date" in line:
195
                            subOrder.estimatedDeliveryDate = line.split(":")[1].strip()
196
                        elif "Courier Name" in line:
197
                            subOrder.courierName = line.split(":")[1].strip()
198
                        elif "Tracking No" in line:
199
                            subOrder.trackingNumber = line.split(":")[1].strip()
200
 
201
                merchantSubOrders.append(subOrder)   
13569 amit.gupta 202
 
13582 amit.gupta 203
            merchantOrder.subOrders = merchantSubOrders
204
            #print merchantOrder
205
 
206
            self._saveToOrder(todict(merchantOrder))
207
            return True
208
        except:
209
            print "Error occurred"
13603 amit.gupta 210
            traceback.print_exc()
13569 amit.gupta 211
 
13582 amit.gupta 212
        return False
13569 amit.gupta 213
        #soup = BeautifulSoup(rawHtml,convertEntities=BeautifulSoup.HTML_ENTITIES)
214
        #soup.find(name, attrs, recursive, text)
13576 amit.gupta 215
 
216
    def _getStatusFromDetailedStatus(self, detailedStatus):
217
        for key, value in Store.OrderStatusMap.iteritems():
218
            if detailedStatus in value:
219
                return key
13662 amit.gupta 220
            print "Detailed Status need to be mapped"
13576 amit.gupta 221
        raise ParseException("_getStatusFromDetailedStatus", "Found new order status" + detailedStatus)
13569 amit.gupta 222
 
13610 amit.gupta 223
 
13569 amit.gupta 224
    def scrapeStoreOrders(self,):
13576 amit.gupta 225
        orders = self._getActiveOrders()
13730 amit.gupta 226
        print "Found orders", orders
13576 amit.gupta 227
        br = getBrowserObject()
228
        for order in orders:
229
            url = ORDER_TRACK_URL + re.findall('.*(\?.*?)$', order['orderSuccessUrl'],re.IGNORECASE)[0]
230
            response = br.open(url)
13680 amit.gupta 231
            page = ungzipResponse(response)
13576 amit.gupta 232
            #page=page.decode("utf-8")
233
            soup = BeautifulSoup(page,convertEntities=BeautifulSoup.HTML_ENTITIES)
234
            sections = soup.findAll("section")
235
            sections.pop(0)
236
            sections.pop(0)
237
 
238
            subOrders = sections
239
            bulk = self.db.merchantOrder.initialize_ordered_bulk_op()
240
            for subOrderElement in subOrders:
241
                closed = True
242
                divs = subOrderElement.findAll("div", {"class": "blk lrPad subordrs"})
243
                if len(divs)<=0:
244
                    raise ParseException("subOrder", "Could not Parse suborders for Snapdeal")
13721 amit.gupta 245
                subOrder = None
13576 amit.gupta 246
                for div in divs:
247
                    divStr = str(div)
248
                    divStr = divStr.replace("\n","").replace("\t", "")
249
                    updateMap = {}
250
                    for line in divStr.split("<br />"):
251
                        if "Suborder ID" in line:
13634 amit.gupta 252
                            merchantSubOrderId = re.findall(r'\d+', line)[0]
253
                            #break if suborder is inactive   
13610 amit.gupta 254
                            findMap = {"orderId": order['orderId'], "subOrders.merchantSubOrderId": merchantSubOrderId}
13721 amit.gupta 255
                            subOrder =  self._isSubOrderActive(order, merchantSubOrderId)
256
                            if subOrder is None:
13634 amit.gupta 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:
13721 amit.gupta 267
                                    if subOrder.get("cashBackStatus") == Store.CB_PENDING:
13634 amit.gupta 268
                                        updateMap["subOrders.$.cashBackStatus"] = Store.CB_APPROVED
269
                                elif closedStatus == Store.ORDER_CANCELLED:
13721 amit.gupta 270
                                    if subOrder.get("cashBackStatus") == Store.CB_PENDING:
13634 amit.gupta 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})
13721 amit.gupta 289
                bulk.find({'orderId': order['orderId']}).update({'$set':{'closed': closed}})
13576 amit.gupta 290
            result = bulk.execute()
291
            pprint(result)        
292
 
293
 
13569 amit.gupta 294
    def _saveToAffiliate(self, offers):
13725 amit.gupta 295
        if offers is None or len(offers)==0:
296
            print "no affiliate have been pushed"
297
            return
13576 amit.gupta 298
        collection = self.db.snapdealOrderAffiliateInfo
13569 amit.gupta 299
        try:
300
            collection.insert(offers,continue_on_error=True)
301
        except pymongo.errors.DuplicateKeyError as e:
302
            print e.details
303
 
304
 
305
    def _getAllOffers(self, br, token):
306
        allOffers = []
307
        nextPage = 1  
308
        while True:
309
            data = getPostData(token, nextPage)
310
            response = br.open(POST_URL, data)
13680 amit.gupta 311
            rmap = json.loads(ungzipResponse(response))
13569 amit.gupta 312
            if rmap is not None:
313
                rmap = rmap['response']
314
                if rmap is not None and len(rmap['errors'])==0:
315
                    allOffers += rmap['data']['data']
316
            nextPage += 1
317
            if rmap['data']['pageCount']<nextPage:
318
                break
319
 
320
        return allOffers
321
 
322
    def covertToObj(self,offer):
323
        offerData = offer['Stat']
324
        offer1 = AffiliateInfo(offerData['affiliate_info1'], self.store_id, offerData['conversion_status'], offerData['ad_id'], 
325
                              offerData['datetime'], offerData['payout'], offer['Offer']['name'], offerData['ip'], offerData['conversion_sale_amount'])
13680 amit.gupta 326
 
13569 amit.gupta 327
        return offer1
328
def getPostData(token, page = 1, limit= 20, startDate=None, endDate=None):
13680 amit.gupta 329
    endDate=date.today() + timedelta(days=1)
330
    startDate=endDate - timedelta(days=31)
13569 amit.gupta 331
 
332
    parameters = (
333
        ("page",str(page)),
334
        ("limit",str(limit)),
335
        ("fields[]","Stat.offer_id"),
336
        ("fields[]","Stat.datetime"),
337
        ("fields[]","Offer.name"),
338
        ("fields[]","Stat.conversion_status"),
339
        ("fields[]","Stat.conversion_sale_amount"),
340
        ("fields[]","Stat.payout"),
341
        ("fields[]","Stat.ip"),
342
        ("fields[]","Stat.ad_id"),
343
        ("fields[]","Stat.affiliate_info1"),
344
        ("sort[Stat.datetime]","desc"),
345
        ("filters[Stat.date][conditional]","BETWEEN"),
346
        ("filters[Stat.date][values][]",startDate.strftime('%Y-%m-%d')),
347
        ("filters[Stat.date][values][]",endDate.strftime('%Y-%m-%d')),
348
        ("data_start",startDate.strftime('%Y-%m-%d')),
349
        ("data_end",endDate.strftime('%Y-%m-%d')),
350
        ("Method","getConversions"),
351
        ("NetworkId","jasper"),
352
        ("SessionToken",token),
353
    )
354
    #Encode the parameters
355
    return urllib.urlencode(parameters)
356
 
357
def main():
13634 amit.gupta 358
 
13569 amit.gupta 359
    store = getStore(3)
13662 amit.gupta 360
    #store._isSubOrderActive(8, "5970688907")
361
    store.scrapeAffiliate()
13576 amit.gupta 362
    #store.parseOrderRawHtml(12345, "subtagId", 122323,  "html", 'https://m.snapdeal.com/purchaseMobileComplete?code=1f4166d13ea799b65aa9dea68b3e9e70&order=4509499363')
13569 amit.gupta 363
 
364
 
365
if __name__ == '__main__':
366
    main()
13576 amit.gupta 367
 
368
def todict(obj, classkey=None):
369
    if isinstance(obj, dict):
370
        data = {}
371
        for (k, v) in obj.items():
372
            data[k] = todict(v, classkey)
373
        return data
374
    elif hasattr(obj, "_ast"):
375
        return todict(obj._ast())
376
    elif hasattr(obj, "__iter__"):
377
        return [todict(v, classkey) for v in obj]
378
    elif hasattr(obj, "__dict__"):
379
        data = dict([(key, todict(value, classkey)) 
380
            for key, value in obj.__dict__.iteritems() 
381
            if not callable(value) and not key.startswith('_')])
382
        if classkey is not None and hasattr(obj, "__class__"):
383
            data[classkey] = obj.__class__.__name__
384
        return data
385
    else:
386
        return obj