Subversion Repositories SmartDukaan

Rev

Rev 17388 | Rev 18156 | 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
'''
14618 amit.gupta 6
from datetime import datetime, timedelta
15078 amit.gupta 7
from dtr.config import PythonPropertyReader
16394 amit.gupta 8
from dtr.storage.Mongo import getDealRank
14618 amit.gupta 9
from dtr.storage.Mysql import getOrdersAfterDate1, getOrdersAfterDate
13868 amit.gupta 10
from pprint import pprint
13569 amit.gupta 11
from pymongo.mongo_client import MongoClient
14413 amit.gupta 12
import base64
13569 amit.gupta 13
import importlib
13662 amit.gupta 14
import json
15
import math
13569 amit.gupta 16
import mechanize
14413 amit.gupta 17
import time
13631 amit.gupta 18
import traceback
13662 amit.gupta 19
import urllib
20
import urllib2
16736 amit.gupta 21
from dtr.utils import utils 
17013 manish.sha 22
sourceMap = {1:"amazon", 2:"flipkart", 3:"snapdeal", 4:"spice", 5:"shopclues", 6:"paytm", 7:"homeshop18"}
13662 amit.gupta 23
headers = { 
24
           'User-agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11',
25
            'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',      
26
            'Accept-Language' : 'en-US,en;q=0.8',                     
27
            'Accept-Charset' : 'ISO-8859-1,utf-8;q=0.7,*;q=0.3'
28
        }
14145 amit.gupta 29
CASHBACK_URL = PythonPropertyReader.getConfig('CASHBACK_URL')
30
USER_LOOKUP_URL = PythonPropertyReader.getConfig('USER_LOOKUP_URL')
31
WALLET_CREDIT_URL = PythonPropertyReader.getConfig('WALLET_CREDIT_URL')
13569 amit.gupta 32
 
33
def getStore(source_id):
34
    #module = sourceMap[source_id]
35
    store = Store(source_id)
13631 amit.gupta 36
    try:
37
        module = importlib.import_module("dtr.sources." + sourceMap[source_id])
38
        store = getattr(module, "Store")(source_id)
39
        return store
40
    except:
13781 amit.gupta 41
        traceback.print_exc()
13631 amit.gupta 42
        return None
13569 amit.gupta 43
 
44
class ScrapeException(Exception):
45
    """Exception raised for errors in the input.
46
 
47
    Attributes:
48
        expr -- input expression in which the error occurred
49
        msg  -- explanation of the error
50
    """
51
 
52
    def __init__(self, expr, msg):
53
        self.expr = expr
54
        self.msg = msg
55
 
56
class ParseException(Exception):
57
    """Exception raised for errors in the input.
58
 
59
    Attributes:
60
        expr -- input expression in which the error occurred
61
        msg  -- explanation of the error
62
    """
63
 
64
    def __init__(self, expr, msg):
65
        self.expr = expr
66
        self.msg = msg
67
 
13677 amit.gupta 68
client = MongoClient('mongodb://localhost:27017/') 
69
 
13569 amit.gupta 70
class Store(object):
71
 
72
    ORDER_PLACED = 'Order Placed'
73
    ORDER_DELIVERED = 'Delivered'
74
    ORDER_SHIPPED = 'Shipped' #Lets see if we can make use of it
75
    ORDER_CANCELLED = 'Cancelled'
76
 
13662 amit.gupta 77
    CB_INIT = 'Waiting Confirmation'
13610 amit.gupta 78
    CB_PENDING = 'Pending'
13955 amit.gupta 79
    CB_CREDIT_IN_PROCESS = 'Credit in process'
13610 amit.gupta 80
    CB_CREDITED = 'Credited to wallet'
81
    CB_NA = 'Not Applicable'
82
    CB_APPROVED = 'Approved'
14618 amit.gupta 83
    CB_REJECTED = 'Rejected'
84
    CB_ONHOLD = 'On hold'
13610 amit.gupta 85
    CB_CANCELLED = 'Cancelled'
13569 amit.gupta 86
 
13662 amit.gupta 87
    CONF_CB_SELLING_PRICE = 0
88
    CONF_CB_DISCOUNTED_PRICE = 1
13610 amit.gupta 89
 
13569 amit.gupta 90
    def __init__(self, store_id):
13662 amit.gupta 91
        self.db = client.Dtr
13569 amit.gupta 92
        self.store_id = store_id
13576 amit.gupta 93
        self.store_name = sourceMap[store_id]
13569 amit.gupta 94
 
13662 amit.gupta 95
    '''
96
    To Settle payback for respective stores.
97
    Also ensures that settlement happens only for approved orders
98
    '''
13569 amit.gupta 99
 
100
    def getName(self):
101
        raise NotImplementedError
102
 
103
    def scrapeAffiliate(self, startDate=None, endDate=None):
104
        raise NotImplementedError
105
 
16390 amit.gupta 106
    def getTrackingUrls(self, userId):
107
        raise NotImplementedError
108
 
109
 
13569 amit.gupta 110
    def saveToAffiliate(self, offers):
111
        raise NotImplementedError
112
 
16371 amit.gupta 113
    def getUpdateMap(self, subOrder, cashBackStatus):
14624 amit.gupta 114
        updateMap = {}
115
        closed = False
116
        for (key,value) in subOrder.iteritems():
117
            if key=="status":
118
                if value==Store.ORDER_CANCELLED:
119
                    closed = True
120
                    updateMap['subOrders.$.closed'] = True
16371 amit.gupta 121
                    if cashBackStatus == Store.CB_PENDING:
14624 amit.gupta 122
                        updateMap['subOrders.$.cashBackStatus'] = Store.CB_CANCELLED
123
                if value==Store.ORDER_DELIVERED:
124
                    closed = True
125
                    updateMap['subOrders.$.closed'] = True
16371 amit.gupta 126
                    if cashBackStatus == Store.CB_PENDING:
14624 amit.gupta 127
                        updateMap['subOrders.$.cashBackStatus'] = Store.CB_APPROVED
128
            updateMap['subOrders.$.' + key] = value
129
        subOrder['closed'] = closed
130
        return updateMap
131
 
14618 amit.gupta 132
    def saveOrder(self, merchantOrder):
133
        #merchantOrder = Order()
134
        #merchantOrder.status
135
        #mercha
136
        pass
137
 
138
 
13569 amit.gupta 139
    def scrapeStoreOrders(self,):
140
        raise NotImplementedError
13610 amit.gupta 141
 
14618 amit.gupta 142
    def updateSubOrder(self, subOrder):
143
        pass
144
        #if subOrder.get
145
 
13690 amit.gupta 146
    def _saveToOrder(self, order):
147
        collection = self.db.merchantOrder
148
        try:
17388 amit.gupta 149
            print "************************************************************************"
150
            print "order"
151
            print order
152
            print "************************************************************************"
13690 amit.gupta 153
            order = collection.insert(order)
14273 amit.gupta 154
            return True 
13690 amit.gupta 155
        except Exception as e:
14273 amit.gupta 156
            return False
14081 amit.gupta 157
 
158
    def _updateToOrder(self, order):
159
        collection = self.db.merchantOrder
160
        try:
14959 amit.gupta 161
            print "************************************************************************"
162
            print "order"
163
            print order
164
            print "************************************************************************"
14081 amit.gupta 165
            collection.update({"orderId":order['orderId']},{"$set":order}, upsert = True)
166
            #merchantOder 
167
        except Exception as e:
168
            traceback.print_exc()
13662 amit.gupta 169
 
170
    def getCashbackAmount(self, productCode, amount):
13995 amit.gupta 171
        alagvar = CASHBACK_URL % (productCode,self.store_id)
13662 amit.gupta 172
        filehandle = urllib2.Request(alagvar,headers=headers)
173
        x= urllib2.urlopen(filehandle)
17179 amit.gupta 174
        cashBack = json.loads(str(x.read()))
175
        if len(cashBack)==0:
13721 amit.gupta 176
            return (0,0)
13662 amit.gupta 177
        else:
17179 amit.gupta 178
            maxCashBack = cashBack.get('maxCashBack') 
179
            if maxCashBack:
180
                if cashBack.get('cash_back_type') ==1 and (float(cashBack.get('cash_back'))*amount)/100 > maxCashBack:
181
                    cashBack['cash_back'] = cashBack['maxCashBack']
182
                    return (cashBack['cash_back'], 0)
183
                elif cashBack.get('cash_back_type') ==2 and cashBack.get('cash_back') > cashBack.get('maxCashBack'):
184
                    cashBack['cash_back'] = cashBack['maxCashBack']
185
                    return (cashBack['cash_back'], 0)
186
                else:
187
                    pass
188
            if cashBack['cash_back_description'] == 'PERCENTAGE':
189
                return (math.floor((amount * cashBack['cash_back'])/100), cashBack['cash_back'])
13662 amit.gupta 190
            else:
17179 amit.gupta 191
                return (cashBack['cash_back'], 0)
13721 amit.gupta 192
 
13662 amit.gupta 193
 
13569 amit.gupta 194
    '''
195
    Parses the order for specific store
196
 
197
    order id, total amount, created on(now() if could not parse
198
    suborder id, title, quantity, unit price, expected delivery date,
199
    status (default would be Order placed)
200
 
201
    once products are identified, each suborder can then be updated
202
    with respective cashback.
203
 
204
    Possible fields to display for Not yet delivered orders are 
205
    Product/Quantity/Amount/Store/CashbackAmount/OrderDate/ExpectedDelivery/OrderStaus/DetailedStatus/CashbackStatus
206
    No need to show cancelled orders.
13610 amit.gupta 207
    CashbackStatus - NotApplicable/Pending/Approved/Cancelled/CreditedToWallet
13569 amit.gupta 208
    OrderStatus - Placed/Cancelled/Delivered
209
    '''
13576 amit.gupta 210
    def parseOrderRawHtml(self, orderId, subTagId, userId, rawHtml, orderSuccessUrl):
13569 amit.gupta 211
 
212
        pass
213
 
13721 amit.gupta 214
    def _updateOrdersPayBackStatus(self, searchMap, updateMap):
13781 amit.gupta 215
        searchMap['subOrders.missingAff'] = False
216
        updateMap['subOrders.$.missingAff'] = True
13927 amit.gupta 217
        self.db.merchantOrder.update(searchMap, { '$set': updateMap }, multi=True)
13721 amit.gupta 218
 
13781 amit.gupta 219
    def _getActiveOrders(self, searchMap={}, collectionMap={}):
13721 amit.gupta 220
        collection = self.db.merchantOrder
17234 amit.gupta 221
        searchMap = dict(searchMap.items()+ {"subOrders.closed": False, "storeId" : self.store_id}.items()) 
17395 amit.gupta 222
        #collectionMap =  dict(collectionMap.items() + {"orderSuccessUrl":1, "orderId":1,"subOrders":1, "placedOn":1, "orderTracking"}.items())
223
        stores = collection.find(searchMap)
13721 amit.gupta 224
        return [store for store in stores]
225
 
14081 amit.gupta 226
    def _getMissingOrders(self,searchMap={}):
227
        collection = self.db.merchantOrder
14948 amit.gupta 228
        searchMap = dict(searchMap.items()+ {"requireDetail":True, "storeId":self.store_id}.items()) 
14081 amit.gupta 229
        orders = collection.find(searchMap)
230
        return list(orders)
231
 
232
 
13721 amit.gupta 233
    def _isSubOrderActive(self,order, merchantSubOrderId):
234
        subOrders = order.get("subOrders")
14867 amit.gupta 235
        if subOrders is None:
236
            return None
13721 amit.gupta 237
        for subOrder in subOrders:
238
            if merchantSubOrderId == subOrder.get("merchantSubOrderId"):
239
                return subOrder
240
        return None
14663 amit.gupta 241
 
17395 amit.gupta 242
    def populateDerivedFields(self, order, update=False):
16371 amit.gupta 243
        closed=True
244
        for subOrder in order.subOrders:
245
            if subOrder.closed:
246
                continue
17395 amit.gupta 247
            if not update:
248
                amount = subOrder.amount if hasattr(subOrder, 'amount') else subOrder.amountPaid
249
                cashbackAmount, cashbackPercent = self.getCashbackAmount(subOrder.productCode, amount)
250
                cashbackStatus = Store.CB_PENDING
251
                if cashbackAmount <= 0:
252
                    cashbackStatus = Store.CB_NA
253
                subOrder.cashBackAmount = cashbackAmount
254
                subOrder.cashBackPercentage = cashbackPercent
255
                subOrder.cashBackStatus = cashbackStatus      
256
                dealRank = getDealRank(subOrder.productCode, self.store_id, order.userId)
257
                subOrder.dealRank = dealRank.get('rank')
258
                subOrder.rankDesc = dealRank.get('description')
259
                subOrder.maxNlc = dealRank.get('maxNlc')
260
                subOrder.minNlc = dealRank.get('minNlc')
261
                subOrder.db = dealRank.get('dp')
262
                subOrder.itemStatus = dealRank.get('status')
263
            subOrder.closed = subOrder.status in [Store.ORDER_CANCELLED, Store.ORDER_DELIVERED]
16371 amit.gupta 264
            closed = closed and subOrder.closed
17395 amit.gupta 265
            if subOrder.cashBackStatus!=Store.CB_NA:
266
                subOrder.cashBackStatus = Store.CB_PENDING
267
                if subOrder.status == self.ORDER_CANCELLED:
268
                    subOrder.cashBackStatus = Store.CB_CANCELLED 
269
                elif subOrder.status == self.ORDER_DELIVERED:
270
                    subOrder.cashBackStatus = Store.CB_APPROVED
16371 amit.gupta 271
        order.closed = closed
272
 
14618 amit.gupta 273
def settlePayBack(runtype='dry'):
14663 amit.gupta 274
    userAmountMap = {}
275
    searchMapList = []
14666 amit.gupta 276
    for mo in client.Dtr.merchantOrder.find({"subOrders.cashBackStatus":Store.CB_APPROVED}):
14663 amit.gupta 277
        userId = mo.get("userId")
14664 amit.gupta 278
        if   mo.get('subOrders') is not None:
14663 amit.gupta 279
            for so in mo['subOrders']:
14666 amit.gupta 280
                if so.get('cashBackStatus') == Store.CB_APPROVED:
14663 amit.gupta 281
                    searchMapList.append({"orderId":mo.get("orderId"), "subOrders.merchantSubOrderId":so.get("merchantSubOrderId")})
282
                    if not userAmountMap.has_key(userId):
283
                        userAmountMap[userId] = so.get('cashBackAmount')
284
                    else:
285
                        userAmountMap[userId] += so.get('cashBackAmount')
286
                    print "%s\t%s\t%s\t%s\t%s\t%s\t%s"%(userId, mo.get("orderId"), so.get("merchantSubOrderId"),so.get("productTitle") ,so.get("cashBackStatus"), so.get("cashBackAmount"), so.get("batchId"))
287
                else:  
288
                    print "%s\t%s\t%s\t%s\t%s\t%s\t%s"%(userId, mo.get("orderId"), so.get("merchantSubOrderId"),so.get("productTitle") ,so.get("cashBackStatus"), so.get("cashBackAmount"), so.get("batchId"))
289
    for searchMap in searchMapList:
290
        print "%s\t%s"%(searchMap.get('orderId'),searchMap.get('subOrders.merchantSubOrderId'))
14664 amit.gupta 291
    for key,val in userAmountMap.iteritems():
292
        print "%s\t%s"%(key,val)
14663 amit.gupta 293
    if (runtype=='live'):
14666 amit.gupta 294
        bulk = client.Dtr.merchantOrder.initialize_ordered_bulk_op()
14663 amit.gupta 295
        if len(searchMapList) == 0:
14618 amit.gupta 296
            return
14666 amit.gupta 297
        for searchMap in searchMapList:
298
            bulk.find(searchMap).update({'$set' : {'subOrders.$.cashBackStatus':Store.CB_CREDIT_IN_PROCESS}})
299
        bulk.execute()
14663 amit.gupta 300
 
13927 amit.gupta 301
        datetimeNow = datetime.now() 
302
        batchId = int(time.mktime(datetimeNow.timetuple()))
303
        if refundToWallet(batchId, userAmountMap):
14666 amit.gupta 304
            bulk = client.Dtr.merchantOrder.initialize_ordered_bulk_op()
305
            for searchMap in searchMapList:
306
                bulk.find(searchMap).update({'$set' : {'subOrders.$.cashBackStatus':Store.CB_CREDITED, "subOrders.$.batchId":batchId}})
307
            print bulk.execute()
17234 amit.gupta 308
            sum=0
309
            creditedSubOrders=0
310
            message = []
14666 amit.gupta 311
            for key, value in userAmountMap.iteritems():
17234 amit.gupta 312
                sum += value
313
                creditedSubOrders += 1
16736 amit.gupta 314
                client.Dtr.refund.insert({"userId": key, "batch":batchId, "userAmount":value, "timestamp":datetime.strftime(datetimeNow,"%Y-%m-%d %H:%M:%S"), "type":utils.CREDIT_TYPE_ORDER})
315
                client.Dtr.user.update({"userId":key}, {"$inc": { "credited": value, utils.CREDIT_TYPE_ORDER:value}}, upsert=True)
17234 amit.gupta 316
            message.append("<b>Batch Id - %d</b><br><b>Total Amount Credited - %d</b><br><b>Total SubOrders Credited- %d</b>"%(batchId,sum, creditedSubOrders))
317
            utils.sendmail(['amit.gupta@shop2020.in', 'rajneesh.arora@saholic.com'], "".join(message), 'Cashback for Order Credited Successfully')
13952 amit.gupta 318
            tprint("PayBack Settled")
14666 amit.gupta 319
        else:
320
            tprint("Error Occurred while running batch. Rolling Back")
321
            bulk = client.Dtr.merchantOrder.initialize_ordered_bulk_op()
322
            for searchMap in searchMapList:
323
                bulk.find(searchMap).update({'$set' : {'subOrders.$.cashBackStatus':Store.CB_APPROVED}})
324
            bulk.execute()
14663 amit.gupta 325
 
326
 
327
def settlePayBack1(runtype='dry'):
328
        approvedUserMap = {}
329
        pendingUserMap = {}
330
        creditedToWalletUserMap = {}
331
        #client.Dtr.merchantOrder.update({'subOrders.cashBackStatus':Store.CB_APPROVED},{'$set':{'subOrders.$.cashBackStatus':Store.CB_CREDIT_IN_PROCESS}}, multi=True)    
332
        for mo in client.Dtr.merchantOrder.find({"subOrders.cashBackStatus":Store.CB_CREDITED}):
333
            userId = mo.get("userId")
334
            for so in mo['subOrders']:
335
                print "%s\t%s\t%s\t%s\t%s\t%s\t%s"%(userId, mo.get("orderId"), so.get("merchantSubOrderId"),so.get("productTitle") ,so.get("cashBackStatus"), so.get("cashBackAmount"), so.get("batchId"))
336
 
337
        for refund in client.Dtr.refund.find():
338
            print "%s\t%s\t%s\t%s"%(refund.get("userId") ,refund.get("timestamp") ,refund.get("userAmount"), refund.get("batch"))
339
 
340
 
13927 amit.gupta 341
def refundToWallet(batchId, userAmountMap):
14413 amit.gupta 342
    base64string = base64.encodestring('%s:%s' % ("dtr", "dtr18Feb2015")).replace('\n', '')
13927 amit.gupta 343
    batchUpdateMap = {}
344
    try :
345
        saholicUserAmountMap = {}
346
        for key, value in userAmountMap.iteritems():
347
            userLookupRequest = urllib2.Request(USER_LOOKUP_URL %(key), headers=headers)
14413 amit.gupta 348
            userLookupRequest.add_header("Authorization", "Basic %s" % base64string)
13927 amit.gupta 349
            try:
350
                response = urllib2.urlopen(userLookupRequest).read()
13939 amit.gupta 351
                saholicUserId = json.loads(response)['account_key']
13927 amit.gupta 352
                saholicUserAmountMap[saholicUserId] = value
353
            except:
13934 amit.gupta 354
                tprint("Could not fetch saholic id for user : " + str(key))
13927 amit.gupta 355
                continue
13939 amit.gupta 356
        if len(saholicUserAmountMap) > 0:
357
            batchUpdateMap['userAmount'] = json.dumps(saholicUserAmountMap)
358
            batchUpdateMap['batchId'] = batchId
359
            request = urllib2.Request(WALLET_CREDIT_URL, headers=headers)
360
            data = urllib.urlencode(batchUpdateMap)
361
            response = urllib2.urlopen(request, data)
362
            return json.loads(response.read())['response']['credited']
363
        else:
364
            tprint("Nothing to Refund")
365
            return False
13927 amit.gupta 366
    except:
367
        traceback.print_exc()
368
        tprint("Could not batch refund")
369
        return False
13868 amit.gupta 370
 
13662 amit.gupta 371
def main():
14618 amit.gupta 372
#    client = MongoClient('mongodb://root:ecip$dtrMay2014@dtr:27017/')
373
#    s = Store(1)
374
#    orders = s.db.Dtr.find({"subOrders.imgUrl":{"$exists":1}}, {"merchantOrderId":1, "subOrders.productCode":1,"subOrders.imgUrl":1,"_id": 0})
375
#    db = client.Dtr
376
#    for order in orders:
377
#        for subOrder in order.get("subOrders"):
378
#            #db.merchantOrder.update({"merchantOrderId":order.getMerchantOrderId,"subOrders.imgUrl":{"$exists":0}, "subOrders.productCode":subOrder.get("productCode")}, {"$set":{"subOrders.$.imgUrl":subOrder.get("imgUrl")}})
379
#            db.merchantOrder.findOne()
14663 amit.gupta 380
    settlePayBack('dry')        
13569 amit.gupta 381
 
382
 
13868 amit.gupta 383
###
384
#Settlement process is suposed to be a batch and run weekly
385
#It is should be running on first hour of mondays. As cron should
386
# Maintain a batch id.
387
 
13677 amit.gupta 388
 
13569 amit.gupta 389
def getBrowserObject():
390
    import cookielib
391
    br = mechanize.Browser(factory=mechanize.RobustFactory())
392
    cj = cookielib.LWPCookieJar()
393
    br.set_cookiejar(cj)
394
    br.set_handle_equiv(True)
395
    br.set_handle_redirect(True)
396
    br.set_handle_referer(True)
397
    br.set_handle_robots(False)
398
    br.set_debug_http(False)
399
    br.set_debug_redirects(False)
400
    br.set_debug_responses(False)
401
 
402
    br.set_handle_refresh(mechanize._http.HTTPRefreshProcessor(), max_time=1)
403
 
404
    br.addheaders = [('User-agent','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11'),
405
                     ('Accept', 'text/html,application/xhtml+xml,application/json,application/xml;q=0.9,*/*;q=0.8'),
406
                     ('Accept-Encoding', 'gzip,deflate,sdch'),                  
407
                     ('Accept-Language', 'en-US,en;q=0.8'),                     
408
                     ('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.3')]
13677 amit.gupta 409
    return br
16371 amit.gupta 410
def todict(obj, classkey=None):
411
    if isinstance(obj, dict):
412
        data = {}
413
        for (k, v) in obj.items():
414
            data[k] = todict(v, classkey)
415
        return data
416
    elif hasattr(obj, "_ast"):
417
        return todict(obj._ast())
418
    elif hasattr(obj, "__iter__"):
419
        return [todict(v, classkey) for v in obj]
420
    elif hasattr(obj, "__dict__"):
421
        data = dict([(key, todict(value, classkey)) 
422
            for key, value in obj.__dict__.iteritems() 
423
            if not callable(value) and not key.startswith('_')])
424
        if classkey is not None and hasattr(obj, "__class__"):
425
            data[classkey] = obj.__class__.__name__
426
        return data
427
    else:
428
        return obj
13677 amit.gupta 429
 
13690 amit.gupta 430
def ungzipResponse(r):
431
    headers = r.info()
17388 amit.gupta 432
    if headers.get('Content-Encoding')=='gzip':
13690 amit.gupta 433
        import gzip
434
        gz = gzip.GzipFile(fileobj=r, mode='rb')
435
        html = gz.read()
436
        gz.close()
17388 amit.gupta 437
    else:
438
        html = r.read() 
439
 
440
    return html
13724 amit.gupta 441
 
442
def tprint(*msg):
13927 amit.gupta 443
    print datetime.now(), "-", msg
13939 amit.gupta 444
 
445
if __name__ == '__main__':
14848 amit.gupta 446
    main()
447