Subversion Repositories SmartDukaan

Rev

Rev 14444 | Rev 14447 | 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)
14023 amit.gupta 99
        merchantOrder.merchantOrderId = re.findall(r'\d+', str(soup.find("div", {"class":"deals_heading"})))[1]
13760 amit.gupta 100
        for orderTr in orderTrs:
101
            orderTrString = str(orderTr)
102
            if "Total Amount" in orderTrString:
103
                merchantOrder.totalAmount = re.findall(r'\d+', orderTrString)[0]
104
            elif "Delivery Charges" in orderTrString:
105
                merchantOrder.deliveryCharges = re.findall(r'\d+', orderTrString)[0]
106
            elif "Discount Applied" in orderTrString:
107
                merchantOrder.discountApplied = re.findall(r'\d+', orderTrString)[0]
108
            elif "Paid Amount" in orderTrString:
109
                merchantOrder.paidAmount = re.findall(r'\d+', orderTrString)[0]
110
 
111
        for subOrderElement in subOrders:
13809 amit.gupta 112
            subOrders = self.parseSubOrder(subOrderElement, placedOn)                           
113
            merchantSubOrders.extend(subOrders)   
13760 amit.gupta 114
 
115
        merchantOrder.subOrders = merchantSubOrders
116
        return merchantOrder
117
 
118
    def parseSubOrder(self, subOrderElement, placedOn):
13809 amit.gupta 119
        subOrders = []
13760 amit.gupta 120
        productUrl = str(subOrderElement.find("a")['href'])
121
        subTable = subOrderElement.find("table", {"class":"lrPad"})
122
        subTrs = subTable.findAll("tr")
123
        unitPrice=None
124
        offerDiscount = None
125
        deliveryCharges = None
126
        amountPaid = None
127
        for subTr in subTrs:
128
            subTrString = str(subTr)
129
            if "Unit Price" in subTrString:
130
                unitPrice = re.findall(r'\d+', subTrString)[0]
131
            if "Quantity" in subTrString:
132
                qty = re.findall(r'\d+', subTrString)[0]
133
            elif "Offer Discount" in subTrString:
134
                offerDiscount =   re.findall(r'\d+', subTrString)[0]
135
            elif "Delivery Charges" in subTrString:
136
                deliveryCharges =   re.findall(r'\d+', subTrString)[0]
137
            elif "Subtotal" in subTrString:
138
                if int(qty) > 0:
139
                    amountPaid =   str(int(re.findall(r'\d+', subTrString)[0])/int(qty))
140
                else:
141
                    amountPaid =   "0"
142
        if self.CONF_CB_AMOUNT == MStore.CONF_CB_SELLING_PRICE or offerDiscount is None:
143
            amount = int(unitPrice)
144
        else:
145
            amount = int(unitPrice) - int(offerDiscount)
146
 
147
        divs = subOrderElement.findAll("div", {"class": "blk lrPad subordrs"})
148
        if len(divs)<=0:
149
            raise ParseException("subOrder", "Could not Parse suborders for Snapdeal")
150
 
151
        for div in divs:
152
            productTitle = str(subOrderElement.find("a").text)
153
            productUrl = "http://m.snapdeal.com/" + productUrl 
154
            subOrder = SubOrder(productTitle, productUrl, placedOn, amountPaid)
155
 
156
            subOrder.amountPaid = amountPaid
157
            subOrder.deliveryCharges = deliveryCharges
158
            subOrder.offerDiscount = offerDiscount
13809 amit.gupta 159
            subOrder.unitPrice = int(unitPrice)
13760 amit.gupta 160
            subOrder.productCode = re.findall(r'\d+$', productUrl)[0]
14415 amit.gupta 161
            subOrder.imgUrl = Mongo.getImgSrc(subOrder.productCode, self.store_id).get('thumbnail')
13760 amit.gupta 162
            (cashbackAmount, percentage) = self.getCashbackAmount(subOrder.productCode, amount)
13770 amit.gupta 163
            cashbackStatus = Store.CB_PENDING
13760 amit.gupta 164
            if cashbackAmount <= 0:
165
                cashbackStatus = Store.CB_NA
166
            subOrder.cashBackStatus = cashbackStatus
167
            subOrder.cashBackAmount = cashbackAmount
168
            if percentage > 0:
169
                subOrder.cashBackPercentage = percentage
170
 
171
 
172
            trackAnchor = div.find("a")   
173
            if trackAnchor is not None:
174
                subOrder.tracingkUrl = str(trackAnchor['href'])
175
 
176
            divStr = str(div)
177
            divStr = divStr.replace("\n","").replace("\t", "")
178
 
179
            for line in divStr.split("<br />"):
180
                if "Suborder ID" in line:
181
                    subOrder.merchantSubOrderId = re.findall(r'\d+', line)[0]   
182
                elif "Status" in line:
14402 amit.gupta 183
                    print line
13760 amit.gupta 184
                    subOrder.detailedStatus = re.findall('>(.*?)</span>', line, re.IGNORECASE)[0]
185
                elif "Est. Shipping Date" in line:
186
                    subOrder.estimatedShippingDate = line.split(":")[1].strip()
187
                elif "Est. Delivery Date" in line:
188
                    subOrder.estimatedDeliveryDate = line.split(":")[1].strip()
189
                elif "Courier Name" in line:
190
                    subOrder.courierName = line.split(":")[1].strip()
191
                elif "Tracking No" in line:
192
                    subOrder.trackingNumber = line.split(":")[1].strip()
13809 amit.gupta 193
            subOrders.append(subOrder)
194
        return subOrders
13760 amit.gupta 195
 
13576 amit.gupta 196
    def parseOrderRawHtml(self, orderId, subTagId, userId, rawHtml, orderSuccessUrl):
13760 amit.gupta 197
                    #print merchantOrder
13796 amit.gupta 198
        resp = {}
13582 amit.gupta 199
        try:
200
            br = getBrowserObject()
201
            url = ORDER_TRACK_URL + re.findall('.*(\?.*?)$', orderSuccessUrl,re.IGNORECASE)[0]
13791 amit.gupta 202
            page = br.open(url)
203
            page = ungzipResponse(page)
14145 amit.gupta 204
            merchantOrder = self._parse(orderId, subTagId, userId, page, orderSuccessUrl)
14312 amit.gupta 205
            merchantOrder.orderTrackingUrl = url
206
 
207
            if self._saveToOrder(todict(merchantOrder)):
208
                resp['result'] = 'ORDER_CREATED'
209
            else:
210
                resp['result'] = 'ORDER_ALREADY_CREATED_IGNORED'
211
 
13796 amit.gupta 212
            return resp
13582 amit.gupta 213
        except:
214
            print "Error occurred"
13603 amit.gupta 215
            traceback.print_exc()
14312 amit.gupta 216
            resp['result'] = 'ORDER_NOT_CREATED'
13796 amit.gupta 217
            return resp
13781 amit.gupta 218
 
13569 amit.gupta 219
 
220
        #soup = BeautifulSoup(rawHtml,convertEntities=BeautifulSoup.HTML_ENTITIES)
221
        #soup.find(name, attrs, recursive, text)
13576 amit.gupta 222
 
223
    def _getStatusFromDetailedStatus(self, detailedStatus):
224
        for key, value in Store.OrderStatusMap.iteritems():
225
            if detailedStatus in value:
226
                return key
14289 amit.gupta 227
        print "Detailed Status need to be mapped", detailedStatus
13576 amit.gupta 228
        raise ParseException("_getStatusFromDetailedStatus", "Found new order status" + detailedStatus)
13569 amit.gupta 229
 
13610 amit.gupta 230
 
13569 amit.gupta 231
    def scrapeStoreOrders(self,):
13760 amit.gupta 232
        #collectionMap = {'palcedOn':1}
13576 amit.gupta 233
        orders = self._getActiveOrders()
234
        br = getBrowserObject()
235
        for order in orders:
14399 amit.gupta 236
            print "Found order ", order
14398 amit.gupta 237
            try:
238
                url = ORDER_TRACK_URL + re.findall('.*(\?.*?)$', order['orderSuccessUrl'],re.IGNORECASE)[0]
239
                response = br.open(url)
240
                page = ungzipResponse(response)
241
                #page=page.decode("utf-8")
242
                soup = BeautifulSoup(page,convertEntities=BeautifulSoup.HTML_ENTITIES)
243
                sections = soup.findAll("section")
244
                orderEl = sections[1]
245
                orderTrs = orderEl.findAll("tr")
246
 
247
                placedOn = str(orderTrs[0].findAll("td")[1].text)
248
                sections.pop(0)
249
                sections.pop(0)
250
 
251
                subOrders = sections
252
                bulk = self.db.merchantOrder.initialize_ordered_bulk_op()
253
                for subOrderElement in subOrders:
254
                    closed = True
255
                    divs = subOrderElement.findAll("div", {"class": "blk lrPad subordrs"})
256
                    if len(divs)<=0:
257
                        raise ParseException("subOrder", "Could not Parse suborders for Snapdeal")
258
                    subOrder = None
259
                    breakFlag = False
260
                    for div in divs:
261
                        divStr = str(div)
262
                        divStr = divStr.replace("\n","").replace("\t", "")
263
                        updateMap = {}
264
                        for line in divStr.split("<br />"):
265
                            if "Suborder ID" in line:
266
                                merchantSubOrderId = re.findall(r'\d+', line)[0]
267
                                #break if suborder is inactive   
268
                                subOrder =  self._isSubOrderActive(order, merchantSubOrderId)
269
                                if subOrder is None:
270
                                    subOrders = self.parseSubOrder(subOrderElement, placedOn)
271
                                    self.db.merchantOrder.update({"orderId":order['orderId']},{'$push':{"subOrders":{"$each":todict(subOrders)}}})
272
                                    print "Added new suborders to Order id - " + order['orderId']
273
                                    closed = False
274
                                    breakFlag = True
275
                                    break
276
                                elif subOrder['closed']:
277
                                    breakFlag = True
278
                                    break
279
                                else: 
280
                                    findMap = {"orderId": order['orderId'], "subOrders.merchantSubOrderId": merchantSubOrderId}
14406 amit.gupta 281
                            elif "Status :" in line:
14398 amit.gupta 282
                                detailedStatus = re.findall('>(.*?)</span>', line, re.IGNORECASE)[0]
283
                                updateMap["subOrders.$.detailedStatus"] = detailedStatus
284
                                status = self._getStatusFromDetailedStatus(detailedStatus) 
285
                                closedStatus = status in [Store.ORDER_DELIVERED, Store.ORDER_CANCELLED]
286
                                updateMap["subOrders.$.status"] = status
287
                                if detailedStatus == 'Closed For Vendor Reallocation':
288
                                    #if it is more than 6hours mark closed.
289
                                    closeAt = subOrder.get("closeAt") 
290
                                    if closeAt is None:
291
                                        closeAt = datetime.now() + timedelta(hours=6)
292
                                        updateMap["subOrders.$.closeAt"] = datetime.strftime(closeAt,"%Y-%m-%d %H:%M:%S")
293
                                    else:
294
                                        closeAt = datetime.strptime(closeAt,"%Y-%m-%d %H:%M:%S")
295
                                        if datetime.now() > closeAt:
296
                                            closedStatus = True
297
 
298
 
299
                                if closedStatus:
300
                                    #if status is closed then change the paybackStatus accordingly
301
                                    updateMap["subOrders.$.closed"] = True
302
                                    if status == Store.ORDER_DELIVERED:
303
                                        if subOrder.get("cashBackStatus") == Store.CB_PENDING:
304
                                            updateMap["subOrders.$.cashBackStatus"] = Store.CB_APPROVED
305
                                    elif status == Store.ORDER_CANCELLED:
306
                                        if subOrder.get("cashBackStatus") == Store.CB_PENDING:
307
                                            updateMap["subOrders.$.cashBackStatus"] = Store.CB_CANCELLED
308
 
13760 amit.gupta 309
                                else:
14398 amit.gupta 310
                                    closed = False
311
                            elif "Est. Shipping Date" in line:
312
                                estimatedShippingDate = line.split(":")[1].strip()
313
                                updateMap["subOrders.$.estimatedShippingDate"] = estimatedShippingDate
314
                            elif "Est. Delivery Date" in line:
315
                                estimatedDeliveryDate = line.split(":")[1].strip()
316
                                updateMap["subOrders.$.estimatedDeliveryDate"] = estimatedDeliveryDate
317
                            elif "Courier Name" in line:
318
                                courierName = line.split(":")[1].strip()
319
                                updateMap["subOrders.$.courierName"] = courierName
320
                            elif "Tracking No" in line:
321
                                trackingNumber = line.split(":")[1].strip()
322
                                updateMap["subOrders.$.trackingNumber"] = trackingNumber
323
 
324
                        if breakFlag:
325
                            break
326
 
327
                        bulk.find(findMap).update({'$set' : updateMap})
328
                    bulk.find({'orderId': order['orderId']}).update({'$set':{'closed': closed}})
329
                result = bulk.execute()
330
                pprint(result)
331
            except:
332
                tprint("Could not update " + str(order['orderId']))
333
                traceback.print_exc()                
13576 amit.gupta 334
 
335
 
13569 amit.gupta 336
    def _saveToAffiliate(self, offers):
13576 amit.gupta 337
        collection = self.db.snapdealOrderAffiliateInfo
14443 amit.gupta 338
        mcollection = self.db.merchantOrder
339
        for offer in offers:
340
            offer = self.covertToObj(offer)
341
            collection.update({"adId":offer.adId, "saleAmount":offer.saleAmount, "payOut":offer.payOut},{"$set":todict(offer)}, upsert=True)
14446 amit.gupta 342
            mcollection.update({"subTagId":offer.subTagId, "storeId":self.store_id, "subOrders.missingAff":True}, {"$set":{"subOrders.$.missingAff":False}})
13569 amit.gupta 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'])
14443 amit.gupta 366
        offer1.saleTime = int(time.mktime(datetime.strptime(offer1.saleDate, "%Y-%m-%d %H:%M:%S").timetuple()))
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():
14239 amit.gupta 398
    print todict([1,2,"3"])
14402 amit.gupta 399
    store = getStore(3)
14443 amit.gupta 400
    #store.scrapeStoreOrders()
13662 amit.gupta 401
    #store._isSubOrderActive(8, "5970688907")
14443 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
 
13576 amit.gupta 406
 
407
def todict(obj, classkey=None):
408
    if isinstance(obj, dict):
409
        data = {}
410
        for (k, v) in obj.items():
411
            data[k] = todict(v, classkey)
412
        return data
413
    elif hasattr(obj, "_ast"):
414
        return todict(obj._ast())
415
    elif hasattr(obj, "__iter__"):
416
        return [todict(v, classkey) for v in obj]
417
    elif hasattr(obj, "__dict__"):
418
        data = dict([(key, todict(value, classkey)) 
419
            for key, value in obj.__dict__.iteritems() 
420
            if not callable(value) and not key.startswith('_')])
421
        if classkey is not None and hasattr(obj, "__class__"):
422
            data[classkey] = obj.__class__.__name__
423
        return data
424
    else:
425
        return obj
14239 amit.gupta 426
 
427
if __name__ == '__main__':
428
    main()