Subversion Repositories SmartDukaan

Rev

Rev 20751 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
14220 amit.gupta 1
'''
2
Created on Jan 15, 2015
3
 
4
@author: amit
5
'''
6
from BeautifulSoup import BeautifulSoup
7
from bson.binary import Binary
8
from datetime import datetime, date, timedelta
9
from dtr import main
10
from dtr.config import PythonPropertyReader
11
from dtr.dao import AffiliateInfo, Order, SubOrder
12
from dtr.main import getBrowserObject, ScrapeException, getStore, ParseException, \
13
    Store as MStore, ungzipResponse, tprint
14
from pprint import pprint
15
from pymongo import MongoClient
16
import json
17
import pymongo
18
import re
19
import traceback
20
import urllib
21
import urllib2
16189 amit.gupta 22
from dtr.storage.Mongo import getDealRank
14220 amit.gupta 23
 
20778 amit.gupta 24
productsCashbackMap = {"1019749": 5, "1019753":5}
14222 amit.gupta 25
ORDERSTATUS  = {"PAYMENT_PENDING" : 0,
26
"PAYMENT_FAILED" : 1,
27
"COD_VERIFICATION_PENDING" : 2,
28
"SUBMITTED_FOR_PROCESSING" : 3,
29
"ACCEPTED" : 4,
30
"INVENTORY_LOW" : 5,
31
"REJECTED" : 6,
32
"BILLED" : 7,
33
"PAYMENT_FLAGGED" : 8,
34
"SHIPPED_FROM_WH" : 9,
35
"SHIPPED_TO_LOGST" : 10,
36
"PAYMENT_FLAGGED_DENIED" : 11,
37
"DELIVERY_SUCCESS" : 12,
38
"CANCEL_REQUEST_RECEIVED" : 13,
39
"CANCEL_REQUEST_CONFIRMED" : 14,
40
"CANCELLED_ON_CUSTOMER_REQUEST" : 15,
41
"SHIPPED_TO_DESTINATION_CITY" : 16,
42
"REACHED_DESTINATION_CITY" : 17,
43
"COD_VERIFICATION_FAILED" : 18,
44
"FAILED" : 19,
45
"RTO_IN_TRANSIT" : 20,
46
"RTO_RECEIVED_PRESTINE" : 21,
47
"DOA_PICKUP_REQUEST_RAISED" : 22,
48
"DOA_PICKUP_CONFIRMED" : 23,
49
"DOA_RETURN_IN_TRANSIT" : 24,
50
"DOA_RECEIVED_PRESTINE" : 25,
51
"DOA_CERT_INVALID" : 26,
52
"DOA_CERT_VALID" : 27,
53
"RTO_RESHIPPED" : 28,
54
"DOA_INVALID_RESHIPPED" : 29,
55
"DOA_VALID_RESHIPPED" : 30,
56
"RTO_REFUNDED" : 31,
57
"DOA_VALID_REFUNDED" : 32,
58
"DOA_INVALID_REFUNDED" : 33,
59
"CANCELLED_DUE_TO_LOW_INVENTORY" : 34,
60
"LOW_INV_PO_RAISED" : 35,
61
"LOW_INV_REVERSAL_IN_PROCESS" : 36,
62
"LOW_INV_NOT_AVAILABLE_AT_HOTSPOT" : 37,
63
"LOW_INV_PO_RAISED_TIMEOUT" : 38,
64
"LOW_INV_REVERSAL_TIMEOUT" : 39,
65
"FIRST_DELIVERY_ATTEMPT_MADE" : 40,
66
"CAPTURE_IN_PROCESS" : 41,
67
"DOA_REQUEST_RECEIVED" : 42,
68
"DOA_REQUEST_AUTHORIZED" : 43,
69
"DOA_PICKUP_DENIED" : 44,
70
"DOA_RECEIVED_DAMAGED" : 45,
71
"DOA_LOST_IN_TRANSIT" : 46,
72
"DOA_RESHIPPED_RCVD_DAMAGED" : 47,
73
"DOA_REFUNDED_RCVD_DAMAGED" : 48,
74
"DOA_RESHIPPED_LOST_IN_TRANSIT" : 49,
75
"DOA_REFUNDED_LOST_IN_TRANSIT" : 50,
76
"RTO_RECEIVED_DAMAGED" : 51,
77
"RTO_LOST_IN_TRANSIT" : 52,
78
"RTO_DAMAGED_RESHIPPED" : 53,
79
"RTO_DAMAGED_REFUNDED" : 54,
80
"RTO_LOST_IN_TRANSIT_RESHIPPED" : 55,
81
"RTO_LOST_IN_TRANSIT_REFUNDED" : 56,
82
"RTO_INVENTORY_REVERSED" : 57,
83
"RET_REQUEST_RECEIVED" : 58,
84
"RET_REQUEST_AUTHORIZED" : 59,
85
"RET_PICKUP_REQUEST_RAISED" : 60,
86
"RET_PICKUP_DENIED" : 61,
87
"RET_PICKUP_CONFIRMED" : 62,
88
"RET_RETURN_IN_TRANSIT" : 63,
89
"RET_RECEIVED_PRESTINE" : 64,
90
"RET_RECEIVED_DAMAGED" : 65,
91
"RET_LOST_IN_TRANSIT" : 66,
92
"RET_PRODUCT_USABLE" : 67,
93
"RET_PRODUCT_UNUSABLE" : 68,
94
"RET_PRODUCT_USABLE_RESHIPPED" : 69,
95
"RET_PRODUCT_USABLE_REFUNDED" : 70,
96
"RET_PRODUCT_UNUSABLE_RESHIPPED" : 71,
97
"RET_PRODUCT_UNUSABLE_REFUNDED" : 72,
98
"RET_RESHIPPED_RCVD_DAMAGED" : 73,
99
"RET_REFUNDED_RCVD_DAMAGED" : 74,
100
"RET_RESHIPPED_LOST_IN_TRANSIT" : 75,
101
"RET_REFUNDED_LOST_IN_TRANSIT" : 76,
102
"LOST_IN_TRANSIT" : 77,
103
"LOST_IN_TRANSIT_RESHIPPED" : 78,
104
"LOST_IN_TRANSIT_REFUNDED" : 79,
105
"DELIVERED_AT_STORE" : 80,
106
"RECEIVED_AT_STORE" : 81}
14220 amit.gupta 107
headers = { 
108
           'User-agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11',
109
            'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',      
110
            'Accept-Language' : 'en-US,en;q=0.8',                     
111
            'Accept-Charset' : 'ISO-8859-1,utf-8;q=0.7,*;q=0.3'
112
        }
113
SAHOLIC_ORDER_URL=PythonPropertyReader.getConfig('SAHOLIC_ORDER_URL')
14246 amit.gupta 114
SAHOLIC_ORDER_URL_TR=PythonPropertyReader.getConfig('SAHOLIC_ORDER_URL_TR')
14220 amit.gupta 115
class Store(MStore):
116
    '''
117
    This is to map order statuses of our system to order statuses of snapdeal.
118
    And our statuses will change accordingly.
119
 
120
    '''
121
    OrderStatusMap = {
122
                      MStore.ORDER_PLACED : [2,3,4,5,6,7,8,13],
123
                      MStore.ORDER_DELIVERED : [12],
19932 amit.gupta 124
                      MStore.ORDER_SHIPPED : [9,10,16,17,81,80, 40,20],
18998 amit.gupta 125
                      MStore.ORDER_CANCELLED : [1,6,11,14,15,18,19,31,34,28,53,54,55,56,20,21,77,79, 78]
14220 amit.gupta 126
                      }
127
 
128
 
129
    def parseOrderRawHtml(self, orderId, subTagId, userId, rawHtml, orderSuccessUrl):
14227 amit.gupta 130
        resp = {}
14237 amit.gupta 131
        paymentId = int(re.findall(r'=(.*)?',orderSuccessUrl)[0])
14220 amit.gupta 132
        orderRequest = urllib2.Request(SAHOLIC_ORDER_URL %(paymentId), headers=headers)
133
        try:
16189 amit.gupta 134
            self.userId = userId
14832 amit.gupta 135
            connection = urllib2.urlopen(orderRequest)
136
            response = connection.read()
137
            connection.close()
14220 amit.gupta 138
            response = json.loads(response)['response']
139
            payment = response['payment']
14246 amit.gupta 140
            subOrders = []
14220 amit.gupta 141
            orders = response['orders']
14228 amit.gupta 142
            items = response['itemsMap']
14220 amit.gupta 143
            merchantOrder = Order(orderId, userId, subTagId, self.store_id, orderSuccessUrl)
144
            merchantOrder.merchantOrderId = payment['merchantTxnId']
145
            merchantOrder.paidAmount = payment['amount']
14246 amit.gupta 146
 
14220 amit.gupta 147
            for o in orders:
14246 amit.gupta 148
                subOrders.append(self.createSubOrder(o, items))
14247 amit.gupta 149
            merchantOrder.subOrders = subOrders
14266 amit.gupta 150
            merchantOrder.placedOn = merchantOrder.subOrders[0].placedOn
14246 amit.gupta 151
            s = todict(merchantOrder)
152
            s['sUserId'] = orders[0]['customer_id']
18687 manish.sha 153
            if response.get('payment_option') is not None:
154
                s['payment_option'] = response ['payment_option']
14267 amit.gupta 155
            if self._saveToOrder(s):
156
                resp['result'] = 'ORDER_CREATED'
157
            else:
158
                #Order already created
14312 amit.gupta 159
                resp['result'] = 'ORDER_ALREADY_CREATED_IGNORED'
14220 amit.gupta 160
        except:
14227 amit.gupta 161
            traceback.print_exc()
14312 amit.gupta 162
            resp['result'] = 'ORDER_NOT_CREATED'
14227 amit.gupta 163
        return resp
14246 amit.gupta 164
 
165
    def createSubOrder(self, order, items):
166
            lineitem = order['lineitems'][0]
167
 
168
            item = items[str(lineitem['item_id'])]
169
            brand = lineitem.get('brand')
170
            modelNumber = lineitem.get('model_number')
171
            modelName = lineitem.get('model_name')
172
            color = lineitem.get('color')
173
            productTitle = brand + (" " + modelName if modelName else "") + (" "  + modelNumber if modelNumber is not None else "") + ("(" + color +")" if color else "")
18055 amit.gupta 174
            amountPaid = order['total_amount'] 
14246 amit.gupta 175
            subOrder = SubOrder(productTitle, None, datetime.strftime(datetime.fromtimestamp(order['created_timestamp']/1000),"%d %B %Y"), amountPaid)
18055 amit.gupta 176
            subOrder.amount = amountPaid - order['gvAmount']
14246 amit.gupta 177
            subOrder.merchantSubOrderId = str(order['id'])
178
            subOrder.orderDetailUrl = "http://m.saholic.com/order/" + subOrder.merchantSubOrderId 
17677 amit.gupta 179
            subOrder.quantity = int(lineitem['quantity'])
14246 amit.gupta 180
            subOrder.estimatedDeliveryDate = datetime.strftime(datetime.fromtimestamp(order['promised_delivery_time']/1000),"%d %B %Y")
18055 amit.gupta 181
            subOrder.imgUrl = item.get("imgUrl") 
14246 amit.gupta 182
            subOrder.productUrl = "http://m.saholic.com/" + item['url']
14301 amit.gupta 183
            subOrder.productCode = item['url'].split('-')[-1]
14246 amit.gupta 184
            subOrder.detailedStatus = order['statusDescription']
17677 amit.gupta 185
            (cashbackAmount, percentage) = self.getCashbackAmount(subOrder.productCode, amountPaid/subOrder.quantity)
20750 amit.gupta 186
            if cashbackAmount > 0 and productsCashbackMap.has_key(subOrder.productCode):
187
                quantity = productsCashbackMap.get(subOrder.productCode)
20751 amit.gupta 188
                if  subOrder.quantity < quantity:
20750 amit.gupta 189
                    cashbackAmount = 0 
16189 amit.gupta 190
            dealRank = getDealRank(subOrder.productCode, self.store_id, self.userId)
191
            subOrder.dealRank = dealRank.get('rank')
192
            subOrder.rankDesc = dealRank.get('description')
16283 amit.gupta 193
            subOrder.maxNlc = dealRank.get('maxNlc')
194
            subOrder.minNlc = dealRank.get('minNlc')
195
            subOrder.db = dealRank.get('dp')
196
            subOrder.itemStatus = dealRank.get('status')
14246 amit.gupta 197
            cashbackStatus = Store.CB_PENDING
198
            if cashbackAmount <= 0:
199
                cashbackStatus = Store.CB_NA
200
            subOrder.cashBackStatus = cashbackStatus
17677 amit.gupta 201
            subOrder.cashBackAmount = cashbackAmount*subOrder.quantity
14246 amit.gupta 202
            if percentage > 0:
203
                subOrder.cashBackPercentage = percentage
204
            return subOrder
14220 amit.gupta 205
    def _getStatusFromDetailedStatus(self, detailedStatus):
20172 aman.kumar 206
        print str(detailedStatus)
14220 amit.gupta 207
        for key, value in Store.OrderStatusMap.iteritems():
208
            if detailedStatus in value:
209
                return key
14349 amit.gupta 210
        print "Detailed Status need to be mapped", detailedStatus
16189 amit.gupta 211
        raise ParseException("_getStatusFromDetailedStatus", "Found new order status" + str(detailedStatus))
14220 amit.gupta 212
 
213
 
214
    def scrapeStoreOrders(self,):
18085 amit.gupta 215
        trs = self._getActiveOrders(collectionMap={'merchantOrderId':1, 'sUserId':1, 'userId':1})
14267 amit.gupta 216
        bulk = self.db.merchantOrder.initialize_ordered_bulk_op()
14246 amit.gupta 217
        for tr in trs:
18085 amit.gupta 218
            self.userId = tr['userId']
20172 aman.kumar 219
            orderRequest = urllib2.Request(SAHOLIC_ORDER_URL_TR %(tr['merchantOrderId'], tr['sUserId']), headers=headers)
14246 amit.gupta 220
            try:
221
                response = urllib2.urlopen(orderRequest).read()
19272 amit.gupta 222
                print "transaction_id----------", tr['merchantOrderId']
14246 amit.gupta 223
                response = json.loads(response)['response']
224
                items = response['itemsMap']
225
                orders = response['orders']
226
                closed = True
18254 amit.gupta 227
                splitSubOrdersMap={}
228
                ordersToClone = {}
14246 amit.gupta 229
                for order in orders:
19272 amit.gupta 230
                    #print "orderid---", order['id']
14269 amit.gupta 231
                    orderId = str(order['id'])
232
                    subOrder = self._isSubOrderActive(tr, orderId)
18254 amit.gupta 233
                    currentQty = int(order['lineitems'][0]['quantity'])
14246 amit.gupta 234
                    if subOrder:
235
                        if subOrder['closed']:
236
                            continue
237
                        else:
18254 amit.gupta 238
                            quantity = int(subOrder['quantity'])
14270 amit.gupta 239
                            findMap = {"orderId": tr['orderId'], "subOrders.merchantSubOrderId": orderId}
14246 amit.gupta 240
                            updateMap = {}
241
                            updateMap["subOrders.$.detailedStatus"] = order['statusDescription']
242
                            status = self._getStatusFromDetailedStatus(ORDERSTATUS[order['status']]) 
243
                            closedStatus = status in [Store.ORDER_DELIVERED, Store.ORDER_CANCELLED]
244
                            updateMap["subOrders.$.status"] = status
18254 amit.gupta 245
                            #Check if split
246
                            if quantity != currentQty:
247
                                updateMap["subOrders.$.quantity"] = currentQty
248
                                updateMap["subOrders.$.amount"] = int((subOrder['amount']*currentQty)/quantity)
249
                                updateMap["subOrders.$.cashBackAmount"] = int((subOrder['cashBackAmount']*currentQty)/quantity)
250
                                updateMap["subOrders.$.amountPaid"] = int((subOrder['amountPaid']*currentQty)/quantity)
251
                                splitSubOrdersMap[order['id']]=subOrder
14246 amit.gupta 252
                            if closedStatus:
253
                                #if status is closed then change the paybackStatus accordingly
254
                                updateMap["subOrders.$.closed"] = True
255
                                if status == Store.ORDER_DELIVERED:
256
                                    deliveredOn = datetime.strftime(datetime.fromtimestamp(order['delivery_timestamp']/1000),"%A %d %B %Y")
257
                                    updateMap['subOrders.$.deliveredOn'] = deliveredOn
258
                                    if subOrder.get("cashBackStatus") == Store.CB_PENDING:
259
                                        updateMap["subOrders.$.cashBackStatus"] = Store.CB_APPROVED
260
                                elif status == Store.ORDER_CANCELLED:
261
                                    if subOrder.get("cashBackStatus") == Store.CB_PENDING:
262
                                        updateMap["subOrders.$.cashBackStatus"] = Store.CB_CANCELLED
263
                            else:
264
                                expectedDelivery = datetime.strftime(datetime.fromtimestamp(order['promised_delivery_time']/1000),"%A %d %B %Y")
265
                                updateMap["subOrders.$.estimatedDeliveryDate"] = expectedDelivery
266
                                closed = False
267
                            bulk.find(findMap).update({'$set' : updateMap})
268
                    else:
18254 amit.gupta 269
                        if order['originalOrderId']:
270
                            ordersToClone[order['id']] = order
271
                        else:
272
                            subOrder = self.createSubOrder(order, items)
273
                            self.db.merchantOrder.update({"orderId":tr['orderId']},{'$push':{"subOrders":todict(subOrder)}})
274
                            print "Added new suborder with subOrder Id:", subOrder.merchantSubOrderId
275
                            closed = False
276
 
277
                for order in ordersToClone.values():
278
                    originalOrderId = self.getOriginalOrderId(order['id'], ordersToClone, splitSubOrdersMap)
279
                    subOrderToClone = splitSubOrdersMap[originalOrderId].copy()
19270 amit.gupta 280
                    subOrderToClone["merchantSubOrderId"] = str(order['id'])
18254 amit.gupta 281
                    currentQty = int(order['lineitems'][0]['quantity'])
282
                    quantity = int(subOrderToClone['quantity'])
283
                    subOrderToClone["detailedStatus"] = order['statusDescription']
284
                    status = self._getStatusFromDetailedStatus(ORDERSTATUS[order['status']]) 
285
                    closedStatus = status in [Store.ORDER_DELIVERED, Store.ORDER_CANCELLED]
286
                    print "status---", status, order['status']
287
                    subOrderToClone["status"] = status
288
                    subOrderToClone["quantity"] = currentQty
289
                    subOrderToClone["amount"] = int((subOrderToClone['amount']*currentQty)/quantity)
290
                    subOrderToClone["cashBackAmount"] = int((subOrderToClone['cashBackAmount']*currentQty)/quantity)
291
                    subOrderToClone["amountPaid"] = int((subOrderToClone['amountPaid']*currentQty)/quantity)
292
                    if closedStatus:
293
                        #if status is closed then change the paybackStatus accordingly
294
                        subOrderToClone["closed"] = True
295
                        if status == Store.ORDER_DELIVERED:
296
                            deliveredOn = datetime.strftime(datetime.fromtimestamp(order['delivery_timestamp']/1000),"%A %d %B %Y")
297
                            subOrderToClone['deliveredOn'] = deliveredOn
298
                            if subOrderToClone.get("cashBackStatus") == Store.CB_PENDING:
299
                                subOrderToClone["cashBackStatus"] = Store.CB_APPROVED
300
                        elif status == Store.ORDER_CANCELLED:
301
                            if subOrderToClone.get("cashBackStatus") == Store.CB_PENDING:
302
                                subOrderToClone["cashBackStatus"] = Store.CB_CANCELLED
303
                    else:
304
                        expectedDelivery = datetime.strftime(datetime.fromtimestamp(order['promised_delivery_time']/1000),"%A %d %B %Y")
305
                        subOrderToClone["estimatedDeliveryDate"] = expectedDelivery
14246 amit.gupta 306
                        closed = False
18254 amit.gupta 307
 
308
                    self.db.merchantOrder.update({"orderId":tr['orderId']},{'$push':{"subOrders":subOrderToClone}})
309
 
310
 
14283 amit.gupta 311
                bulk.find({"orderId":tr['orderId']}).update({'$set' : {'closed':closed}})
14246 amit.gupta 312
            except:
20172 aman.kumar 313
                print "something went wrong for request", orderRequest
14277 amit.gupta 314
                traceback.print_exc()
14870 amit.gupta 315
        try:
316
            bulk.execute()
317
        except:
20172 aman.kumar 318
            print "Could not execute bulk"
14870 amit.gupta 319
            traceback.print_exc()
14246 amit.gupta 320
 
321
 
322
 
14220 amit.gupta 323
 
18254 amit.gupta 324
    def getOriginalOrderId(self, orderId, ordersToClone, splitSubOrdersMap): 
325
        if splitSubOrdersMap.has_key(orderId):
326
            return orderId
327
        else:
328
            originalOrderId = ordersToClone[orderId]['originalOrderId']
329
            return self.getOriginalOrderId(originalOrderId, ordersToClone, splitSubOrdersMap) 
330
 
14220 amit.gupta 331
    def _saveToAffiliate(self, offers):
332
        if offers is None or len(offers)==0:
333
            print "no affiliate have been pushed"
334
            return
335
        collection = self.db.snapdealOrderAffiliateInfo
336
        try:
337
            collection.insert(offers,continue_on_error=True)
338
        except pymongo.errors.DuplicateKeyError as e:
339
            print e.details
340
 
341
 
342
    def covertToObj(self,offer):
343
        offerData = offer['Stat']
344
        offer1 = AffiliateInfo(offerData['affiliate_info1'], self.store_id, offerData['conversion_status'], offerData['ad_id'], 
345
                              offerData['datetime'], offerData['payout'], offer['Offer']['name'], offerData['ip'], offerData['conversion_sale_amount'])
346
 
347
        return offer1
348
def getPostData(token, page = 1, limit= 20, startDate=None, endDate=None):
349
    endDate=date.today() + timedelta(days=1)
350
    startDate=endDate - timedelta(days=31)
351
 
352
    parameters = (
353
        ("page",str(page)),
354
        ("limit",str(limit)),
355
        ("fields[]","Stat.offer_id"),
356
        ("fields[]","Stat.datetime"),
357
        ("fields[]","Offer.name"),
358
        ("fields[]","Stat.conversion_status"),
359
        ("fields[]","Stat.conversion_sale_amount"),
360
        ("fields[]","Stat.payout"),
361
        ("fields[]","Stat.ip"),
362
        ("fields[]","Stat.ad_id"),
363
        ("fields[]","Stat.affiliate_info1"),
364
        ("sort[Stat.datetime]","desc"),
365
        ("filters[Stat.date][conditional]","BETWEEN"),
366
        ("filters[Stat.date][values][]",startDate.strftime('%Y-%m-%d')),
367
        ("filters[Stat.date][values][]",endDate.strftime('%Y-%m-%d')),
368
        ("data_start",startDate.strftime('%Y-%m-%d')),
369
        ("data_end",endDate.strftime('%Y-%m-%d')),
370
        ("Method","getConversions"),
371
        ("NetworkId","jasper"),
372
        ("SessionToken",token),
373
    )
374
    #Encode the parameters
375
    return urllib.urlencode(parameters)
376
 
377
def main():
378
 
14301 amit.gupta 379
    store = getStore(4)
18254 amit.gupta 380
    store.scrapeStoreOrders()
14220 amit.gupta 381
    #store._isSubOrderActive(8, "5970688907")
382
    #store.scrapeAffiliate()
18254 amit.gupta 383
    #store.parseOrderRawHtml(112345, "subtagId", 1122323,  "html", 'http://www.saholic.com/pay-success?paymentId=1798123')
16967 amit.gupta 384
    #print store.getCashbackAmount('1011378', 500)
14220 amit.gupta 385
 
386
 
387
if __name__ == '__main__':
388
    main()
389
 
390
def todict(obj, classkey=None):
391
    if isinstance(obj, dict):
392
        data = {}
393
        for (k, v) in obj.items():
394
            data[k] = todict(v, classkey)
395
        return data
396
    elif hasattr(obj, "_ast"):
397
        return todict(obj._ast())
398
    elif hasattr(obj, "__iter__"):
399
        return [todict(v, classkey) for v in obj]
400
    elif hasattr(obj, "__dict__"):
401
        data = dict([(key, todict(value, classkey)) 
402
            for key, value in obj.__dict__.iteritems() 
403
            if not callable(value) and not key.startswith('_')])
404
        if classkey is not None and hasattr(obj, "__class__"):
405
            data[classkey] = obj.__class__.__name__
406
        return data
407
    else:
408
        return obj