Subversion Repositories SmartDukaan

Rev

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