Subversion Repositories SmartDukaan

Rev

Rev 17046 | Rev 17051 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
17013 manish.sha 1
'''
2
Created on Jan 15, 2015
3
 
4
@author: Manish 
5
'''
6
from bs4 import BeautifulSoup
7
from bson.binary import Binary
8
from datetime import datetime, date, timedelta
9
from dtr import main
10
from dtr.dao import AffiliateInfo, Order, SubOrder, HomeShopAffiliateInfo
11
from dtr.main import getBrowserObject, ScrapeException, getStore, ParseException, \
12
    Store as MStore, ungzipResponse, tprint
13
from dtr.storage import Mongo
14
from dtr.storage.Mongo import getImgSrc
15
from dtr.utils.utils import fetchResponseUsingProxy, PROXY_MESH_GENERAL
16
from pprint import pprint
17
from pymongo import MongoClient
18
import json
19
import pymongo
20
import re
21
import time
22
import traceback
23
import urllib
24
import urllib2
25
from urlparse import urlparse, parse_qs
26
import xml.etree.ElementTree as ET
27
from dtr.storage import MemCache
28
from dtr.storage.Mongo import getDealRank
29
 
30
 
31
AFFLIATE_TRASACTIONS_URL = "https://admin.optimisemedia.com/v2/reports/affiliate/leads/leadsummaryexport.aspx?Contact=796881&Country=26&Agency=95&Merchant=331902&Status=-1&Year=%d&Month=%d&Day=%d&EndYear=%d&EndMonth=%d&EndDay=%d&DateType=0&Sort=CompletionDate&Login=1347562DA5E3EFF6FB1561765C47C782&Format=XML&RestrictURL=0"
32
ORDER_TRACK_URL='https://m.homeshop18.com/order/orderDetail.mobi?orderId=%d'
33
HS_ORDER_TRACK_URL='http://www.homeshop18.com/track-your-order.html'
34
BASE_URL= 'http://www.shopclues.com'
35
BASE_MURL= 'http://m.shopclues.com'
36
BASE_PRODUCT_URL= 'http://m.homeshop18.com/product.mobi?productId=%d'
37
BASE_IMG_URL='http://stat.homeshop18.com/homeshop18'
38
 
39
 
40
#http://m.homeshop18.com/checkout/paySuccess.mobi?orderComplete=true
41
 
42
class Store(MStore):
43
    '''
44
    This is to map order statuses of our system to order statuses of snapdeal.
45
    And our statuses will change accordingly.
46
 
47
    '''
48
    OrderStatusMap = {
49
                      MStore.ORDER_PLACED : ['payment successful', 'new order - cod confirmation pending', 'processing', 'quality check','on schedule', 'processing - pickup initiated', 'processing - ready to dispatch','processing - procurement delay from merchant','processing - slight procurment delay from merchant','cod order confirmed by customer'],
50
                      MStore.ORDER_DELIVERED : ['delivered', 'complete'],
51
                      MStore.ORDER_SHIPPED : ['in transit', 'dispatched','shipped','order handed to courier','order handed over to courier'],
52
                      MStore.ORDER_CANCELLED : ['payment failed', 'canceled', 'payment declined', 'order on hold - cancellation requested by customer', 'courier returned', 'canceled on customer request', 'canceled by customer','order canceled by customer','canceled - address not shippable','return complete','undelivered - returning to origin']
53
                      }
54
    OrderStatusConfirmationMap= {
55
                                 "P" : "Payment Successful",
56
                                 "D" : "Order Declined",
57
                                 "O" : "New Order - COD confirmation Pending"
58
                                 }
59
 
60
    OrderStatusStringMap = {
61
                            MStore.ORDER_PLACED : ['expect the order to reach', 'received your payment'],
62
                            MStore.ORDER_DELIVERED : ['has been delivered'],
63
                            MStore.ORDER_SHIPPED : ['has been shipped', 'has been dispatched'], 
64
                            MStore.ORDER_CANCELLED : ['has been cancelled', 'has been rejected','is returned back to us']
65
                            }
66
 
67
    CONF_CB_AMOUNT = MStore.CONF_CB_DISCOUNTED_PRICE
68
 
69
 
70
    def __init__(self,store_id):
71
        super(Store, self).__init__(store_id)
72
 
73
    def convertToObj(self,offer):
74
        orderRef = offer['MerchantRef']
75
        if len(orderRef)>15:
76
            orderRef = orderRef[0:len(orderRef)-10]
77
        offer1 = HomeShopAffiliateInfo(offer['UID'], offer['TransactionTime'], offer['TransactionId'], orderRef,  orderRef, offer['Merchant'], offer['PID'], offer['Product'], float(str(offer['SR'])), float(str(offer['TransactionValue'])), offer['UKey'], offer['ClickTime'], offer['Status'])
78
        return offer1
79
 
80
    def _saveToAffiliate(self, offers):
81
        collection = self.db.homeshopOrderAffiliateInfo
82
        mcollection = self.db.merchantOrder
83
        for offerObj in offers:
84
            offer = self.convertToObj(offerObj)
85
            collection.update({"transactionId":offer.transactionId, "subTagId":offer.subTagId, "payOut":offer.payOut},{"$set":todict(offer)}, upsert=True)
86
            mcollection.update({"subTagId":offer.subTagId, "storeId":self.store_id, "subOrders.missingAff":True}, {"$set":{"subOrders.$.missingAff":False}})
87
 
88
    def scrapeAffiliate(self, startDate=datetime.today() - timedelta(days=10), endDate=datetime.today()):
89
        uri = AFFLIATE_TRASACTIONS_URL%(startDate.year,startDate.month,startDate.day,endDate.year,endDate.month,endDate.day)
90
        root = ET.parse(urllib2.urlopen(uri)).getroot()
91
        if len(root)> 0 and len(root[0])> 0:
92
            offers = []
93
            for child in root[0][0]:
94
                offers.append(child.attrib)
95
            self._saveToAffiliate(offers)
96
 
97
    def _setLastSaleDate(self, saleDate):
98
        self.db.lastSaleDtate.update({'storeId':self.store_id}, {'$set':{'saleDate':saleDate}})
99
 
100
    def getName(self):
101
        return "homeshop18"  
102
 
103
 
104
    def _getLastSaleDate(self,):
105
        lastDaySaleObj = self.db.lastDaySale.find_one({"storeId":self.store_id})
106
        if lastDaySaleObj is None:
107
            return datetime.min
108
 
109
    def _getStatusFromDetailedStatus(self, detailedStatus):
110
        for key, value in Store.OrderStatusMap.iteritems():
111
            if detailedStatus.lower() in value:
112
                return key
113
        print "Detailed Status need to be mapped", detailedStatus, self.store_id
114
        return None
115
 
116
    def updateCashbackInSubOrders(self, subOrders):
117
        for subOrder in subOrders:
118
            cashbackStatus = Store.CB_NA
119
            cashbackAmount = 0
120
            percentage = 0
121
            amount = subOrder.amountPaid
122
            if amount > 0:
123
                (cashbackAmount, percentage) = self.getCashbackAmount(subOrder.productCode, amount)
124
                if cashbackAmount > 0:
125
                    cashbackStatus = Store.CB_PENDING
126
            subOrder.cashBackStatus = cashbackStatus
127
            subOrder.cashBackAmount = cashbackAmount
128
            subOrder.cashBackPercentage = percentage
129
        return subOrders
130
 
131
    def _parseUsingOrderJson(self, orderId, subTagId, userId, rawHtmlSoup, orderSuccessUrl):
132
        orderObj = None
133
 
134
        scripts = rawHtmlSoup.find_all('script')
135
        for script in scripts:
136
            if 'var order =' in script.text:
137
                requiredObjList = script.text.strip().split('\n')
138
                for val in requiredObjList:
139
                    if "$.parseJSON('" in val:
140
                        print val.split("$.parseJSON('")[1].split("');")[0]
141
                        orderObj = json.loads(val.split("$.parseJSON('")[1].split("');")[0])
142
                        print orderObj
143
                        break
144
                break
145
 
146
        if orderObj is not None:
147
            merchantOrder = Order(orderId, userId, subTagId, self.store_id, orderSuccessUrl)
148
            merchantOrder.placedOn = orderObj['orderDate']
149
            merchantOrder.merchantOrderId = str(long(orderObj['orderId']))
150
            merchantOrder.paidAmount = long(orderObj['pricing']['orderNetPrice'])
151
            merchantOrder.totalAmount = long(orderObj['pricing']['orderGrossPrice'])
152
            merchantOrder.discountApplied = long(orderObj['pricing']['discountCouponRedemptionAmount'])+long(orderObj['pricing']['giftCouponRedemptionAmount'])
153
            merchantOrder.deliveryCharges = long(orderObj['totalShipmentCharges'])
154
            subOrders= []
155
            for subOrderObj in orderObj['subOrders']:
17045 manish.sha 156
                subOrder = SubOrder(subOrderObj['cartItem']['cartItemTitle'], BASE_PRODUCT_URL%(long(subOrderObj['cartItem']['productId'])), orderObj['orderDate'], long(subOrderObj['pricing']['payablePrice']))
17046 manish.sha 157
                subOrder.estimatedDeliveryDate = subOrderObj['shipment']['expectedDeliveryDate']
17013 manish.sha 158
                subOrder.merchantSubOrderId = str(subOrderObj['subOrderId'])
17047 manish.sha 159
                if rawHtmlSoup.body.find("div", {'class':'sub-order-status'}) is not None:
160
                    subOrder.detailedStatus = rawHtmlSoup.body.find("div", {'class':'sub-order-status'}).text
161
                else:
162
                    subOrder.detailedStatus = 'Order Placed'
17013 manish.sha 163
                subOrder.imgUrl = BASE_IMG_URL+subOrderObj['cartItem']['lineItemImageUrl']
17045 manish.sha 164
                subOrder.offerDiscount = long(subOrderObj['cartItem']['price'])*long(subOrderObj['cartItem']['itemQuantity'])-long(subOrderObj['pricing']['payablePrice'])
17013 manish.sha 165
                subOrder.unitPrice = long(subOrderObj['cartItem']['price'])
166
                subOrder.productCode = str(long(subOrderObj['cartItem']['productId']))
17046 manish.sha 167
                subOrder.amountPaid = long(subOrderObj['pricing']['payablePrice'])
17013 manish.sha 168
                subOrder.quantity = long(subOrderObj['cartItem']['itemQuantity'])
169
                subOrder.tracingkUrl = ORDER_TRACK_URL%(long(orderObj['orderId']))
170
                dealRank = getDealRank(subOrder.productCode, self.store_id, merchantOrder.userId)
171
                subOrder.dealRank = dealRank.get('rank')
172
                subOrder.rankDesc = dealRank.get('description')
173
                subOrder.maxNlc = dealRank.get('maxNlc')
174
                subOrder.minNlc = dealRank.get('minNlc')
175
                subOrder.db = dealRank.get('dp')
176
                subOrder.itemStatus = dealRank.get('status')
177
            merchantOrder.subOrders = self.updateCashbackInSubOrders(subOrders)
178
            return merchantOrder
179
 
180
 
181
    def parseOrderRawHtml(self, orderId, subTagId, userId, rawHtml, orderSuccessUrl):
182
        resp = {}
183
        try:
184
            rawHtmlSoup = BeautifulSoup(rawHtml)
185
            merchantOrder = self._parseUsingOrderJson(orderId, subTagId, userId, rawHtmlSoup, orderSuccessUrl)
186
            merchantOrder.orderTrackingUrl = ORDER_TRACK_URL%(long(merchantOrder.merchantOrderId))
187
            if self._saveToOrder(todict(merchantOrder)):
188
                resp['result'] = 'ORDER_CREATED'
189
            else:
190
                resp['result'] = 'ORDER_ALREADY_CREATED_IGNORED'
191
 
192
            return resp
193
        except:
194
            print "Error occurred"
195
            traceback.print_exc()
196
            resp['result'] = 'ORDER_NOT_CREATED'
197
            return resp
198
 
199
    def scrapeStoreOrders(self,):
200
        #collectionMap = {'palcedOn':1}
201
        searchMap = {}
202
        collectionMap = {"orderTrackingUrl":1,"merchantOrderId":1}
203
        orders = self._getActiveOrders(searchMap,collectionMap)
204
        for order in orders:
205
            print "Order", self.store_name, order['orderId'], order['orderTrackingUrl'], order['merchantOrderId']
206
            br1 = track(HS_ORDER_TRACK_URL, order['merchantOrderId'])
207
            ungzipResponseBr(br1.response(), br1)
208
            trackPageSoup = BeautifulSoup(br1.response().read())
209
            subOrderTable = trackPageSoup.body.find("table", {'class':'lower-table'})
210
            subOrders = subOrderTable.findAll('tr', recursive=False)
211
            firstRow = subOrders.pop(0)
212
 
213
            closed = True
214
            for row in subOrders:
215
                cols = row.find_all('td')
216
                subOrderId = cols[0].text.strip()
217
                subOrderStatus = cols[1].text.strip()
218
                subbulk = self.db.merchantOrder.initialize_ordered_bulk_op()
219
                print 'Sub Order Id', str(subOrderId)
220
                subOrder =  self._isSubOrderActive(order, str(subOrderId))
221
                if subOrder is None:
222
                    print 'No HS Sub Order Found for SubOrder Id:- '+ str(subOrderId)
223
                elif subOrder['closed']:
224
                    continue
225
                else:
226
                    findMap = {"orderId": order['orderId'], "subOrders.merchantSubOrderId": str(subOrderId)}
227
                    updateMap = {}
228
                    updateMap["subOrders.$.detailedStatus"] = subOrderStatus
229
                    status = self._getStatusFromDetailedStatus(subOrderStatus) 
230
                    closedStatus = status in [Store.ORDER_DELIVERED, Store.ORDER_CANCELLED]
231
                    if status is not None:
232
                        updateMap["subOrders.$.status"] = status
233
                    if closedStatus:
234
                        #if status is closed then change the paybackStatus accordingly
235
                        print 'Order Closed'
236
                        updateMap["subOrders.$.closed"] = True
237
                        if status == Store.ORDER_DELIVERED:
238
                            if subOrder.get("cashBackStatus") == Store.CB_PENDING:
239
                                updateMap["subOrders.$.cashBackStatus"] = Store.CB_APPROVED
240
                        elif status == Store.ORDER_CANCELLED:
241
                            if subOrder.get("cashBackStatus") == Store.CB_PENDING:
242
                                updateMap["subOrders.$.cashBackStatus"] = Store.CB_CANCELLED
243
                    else:
244
                        closed = False
245
                        print 'Order not Closed'
246
 
247
                    subbulk.find(findMap).update({'$set' : updateMap})
248
                    subresult = subbulk.execute()
249
                    tprint(subresult)
250
 
251
 
252
 
253
def track(url, orderId):
254
    br = getBrowserObject()
255
    br.set_proxies({"http": PROXY_MESH_GENERAL})
256
    br.open(url)
257
    response = br.open(url)
258
    ungzipResponseBr(response, br)
259
    soup = BeautifulSoup(br.response())
260
    csrf = soup.find('input', {'name': '_csrf'}).get('value')
261
    print csrf
262
    #html = response.read()
263
    #print html
264
    br.select_form(name='trackForm')
265
    br.form['orderId'] = str(orderId)
266
    response = br.submit()
267
    print "********************"
268
    print "Attempting to Login"
269
    print "********************"
270
    #ungzipResponse(response, br)
271
    return br
272
 
273
def ungzipResponseBr(r,b):
274
    headers = r.info()
275
    if headers['Content-Encoding']=='gzip':
276
        import gzip
277
        print "********************"
278
        print "Deflating gzip response"
279
        print "********************"
280
        gz = gzip.GzipFile(fileobj=r, mode='rb')
281
        html = gz.read()
282
        gz.close()
283
        headers["Content-type"] = "text/html; charset=utf-8"
284
        r.set_data( html )
285
        b.set_response(r)
286
 
287
 
288
def to_py_date(java_timestamp):
289
    date = datetime.fromtimestamp(java_timestamp)
290
    return date       
291
 
292
def todict(obj, classkey=None):
293
    if isinstance(obj, dict):
294
        data = {}
295
        for (k, v) in obj.items():
296
            data[k] = todict(v, classkey)
297
        return data
298
    elif hasattr(obj, "_ast"):
299
        return todict(obj._ast())
300
    elif hasattr(obj, "__iter__"):
301
        return [todict(v, classkey) for v in obj]
302
    elif hasattr(obj, "__dict__"):
303
        data = dict([(key, todict(value, classkey)) 
304
            for key, value in obj.__dict__.iteritems() 
305
            if not callable(value) and not key.startswith('_')])
306
        if classkey is not None and hasattr(obj, "__class__"):
307
            data[classkey] = obj.__class__.__name__
308
        return data
309
    else:
310
        return obj