add celery to push message(async)
diff --git a/book/Util/conf.py b/book/Util/conf.py
index c2a0843..59f63aa 100644
--- a/book/Util/conf.py
+++ b/book/Util/conf.py
@@ -1,5 +1,8 @@
 # please put your app_key and master_secret here
 #app_key = u'65ad21e7cb76e1b60bcc237f'
 #master_secret = u'f9e78a98c0d996e527fea4aa'
+'''
+we get app_key and master_secret from Jpush Server
+'''
 app_key = u'77f80ebef5571a87bc17f616'
 master_secret = u'c2de5c19b0a146646ea42bda'
diff --git a/book/Util/tasks.py b/book/Util/tasks.py
new file mode 100644
index 0000000..56a01f2
--- /dev/null
+++ b/book/Util/tasks.py
@@ -0,0 +1,147 @@
+#coding = utf-8
+'''
+This function of this file:
+1. modify the alias or tags of device
+2. push message by registerId or alise or tags
+3. adopt celery Using an asyncchronous manner to push message by share_task
+    for the detailed info: please read the link below:
+        http://www.jianshu.com/p/1840035cb510 
+        http://shangliuyan.github.io/2015/07/04/celery%E6%9C%89%E4%BB%80%E4%B9%88%E9%9A%BE%E7%90%86%E8%A7%A3%E7%9A%84/
+'''
+
+from __future__ import absolute_import
+from .conf import app_key, master_secret
+from celery import shared_task
+from jpush import common
+import jpush as jpush
+#regid = "18071adc030bb6dd31d"
+
+
+def jpushCreateDevice():
+    _jpush = jpush.JPush(app_key, master_secret)
+    device = _jpush.create_device()
+    _jpush.set_logging("DEBUG")
+    
+    return device
+
+@shared_task
+def add(x, y):
+    return x+y
+
+@shared_task
+def jpushDeviceSetAlias(regid, alias):
+    device = jpushCreateDevice()
+    entity = jpush.device_alias(alias)
+    result = device.set_devicemobile(regid, entity)
+    return result.status_code
+
+@shared_task
+def jpushDeviceClearAlias(regid, alias):
+    device = jpushCreateDevice()
+    entity = jpush.device_alias("")
+    result = device.set_deviceinfo(regid, entity)
+    return result.status_code
+
+@shared_task
+def jpushDeviceSetTag(regid, tags):
+    device = jpushCreateDevice()
+    entity = jpush.device_tag(jpush.add(tags))
+    result = device.set_devicemobile(regid, entity)
+    return result.status_code
+
+@shared_task
+def jpushDeviceSetTag(regid, tags):
+    device = jpushCreateDevice()
+    entity = jpush.device_tag("")
+    result = device.set_deviceinfo(regid, entity)
+    return result.status_code
+
+@shared_task
+def jpushDeviceSetAliasAndTag(regid, tags, alias):
+    result1 = jpushDeviceSetAlias(regid, alias)
+    result2 = jpushDeviceSetTag(regid, tags)
+
+    return result1 and result2
+
+''' push operation '''
+
+@shared_task
+def jpushCreateClient():
+    _jpush = jpush.JPush(app_key, master_secret)
+    push = _jpush.create_push()
+    push.platform = jpush.all_
+
+    return push
+
+@shared_task
+def jushPushMessageToJiGuang(push):
+    try:
+        response=push.send()
+    except common.Unauthorized:
+        raise common.Unauthorized("Unauthorized")
+    except common.APIConnectionException:
+        raise common.APIConnectionException("conn")
+    except common.JPushFailure:
+        print ("JPushFailure")
+    except:
+        print ("Exception")
+
+    return response
+
+@shared_task
+def jpushMessageWithRegId(regid, msg, action):
+    push = jpushCreateClient()
+    push.audience = jpush.audience(
+            jpush.registration_id(regid)
+        )
+    push.notification = jpush.notification(android=jpush.android(alert=action, extras=msg))
+    print msg, push.notification
+    resp = jushPushMessageToJiGuang(push)
+    return resp
+
+@shared_task
+def jpushMessageWithTags(tags, msg, action):
+    push = jpushCreateClient()
+    push.audience = jpush.audience(
+            jpush.tag(tags)
+        )
+    push.notification = jpush.notification(android=jpush.android(alert=action, extras=msg))
+    resp = jushPushMessageToJiGuang(push)
+    return resp
+
+
+@shared_task
+def jpushMessageWithAlias(alias, msg, action):
+    push = jpushCreateClient()
+    push.audience = jpush.audience(
+            jpush.alias(alias)
+        )
+    push.notification = jpush.notification(android=jpush.android(alert=action, extras=msg))
+    resp = jushPushMessageToJiGuang(push)
+    return resp
+
+@shared_task
+def jpushMessageWithAliasTag(alias, tags, msg, action):
+    print "xxx"
+    push = jpushCreateClient()
+    push.audience = jpush.audience(
+            jpush.alias(alias),
+            jpush.tag(tags)
+            )
+    push.notification = jpush.notification(android=jpush.android(alert=action, extras=msg))
+    resp = jushPushMessageToJiGuang(push)
+    return resp
+
+@shared_task
+def jpushMessageAllUser(action, msg):
+    push = jpushCreateClient()
+    push.audience = jpush.all_
+    push.notification = jpush.notification(android=jpush.android(alert=action, extras=msg))
+    resp = jushPushMessageToJiGuang(push)
+    return resp
+
+
+if __name__ == '__main__':
+#jpushDeviceSetAlias(regid, "xxx")
+    print jpushMessageAllUser('hello', {"112":"www"})
+    print jpushMessageWithRegId("1507bfd3f7cf3e96363", {}, "hello")
diff --git a/book/Util/upJiGuang.py b/book/Util/upJiGuang.py
index 4bcf81e..58ed11a 100644
--- a/book/Util/upJiGuang.py
+++ b/book/Util/upJiGuang.py
@@ -1,4 +1,7 @@
 #coding = utf-8
+'''
+ block to push message to Jpush server without celery
+'''
 from conf import app_key, master_secret
 import jpush as jpush
 from jpush import common
diff --git a/book/Util/util.py b/book/Util/util.py
index ed72bce..a6354b4 100644
--- a/book/Util/util.py
+++ b/book/Util/util.py
@@ -1,5 +1,8 @@
 #coding = utf-8
-
+'''
+the function of this file:
+1. accomplish the common function
+'''
 def getCacheKey(cacheTag, key_prefix, version):
     return "".join([key_prefix, cacheTag, str(version)])
 
diff --git a/book/urls.py b/book/urls.py
index 011e159..23b5c33 100755
--- a/book/urls.py
+++ b/book/urls.py
@@ -1,4 +1,4 @@
-
+'''
 from django.conf.urls import patterns, url
 from book import views,vbook
 from django.views.decorators.cache import cache_page
@@ -32,3 +32,37 @@
         url(r'^getMessage/$',views.getEventComment, name = 'getEventComment'),
 
 )
+'''
+
+from django.conf.urls import url
+from book import views,vbook
+from django.views.decorators.cache import cache_page
+from django.contrib import admin
+
+urlpatterns = [
+#       user and shop
+        url(r'^regist/$',views.regist,name = 'regist'),
+        url(r'^login/$',views.login,name = 'login'),
+        url(r'^postRegisterID/$',views.postRegisterID,name = 'postRegisterID'),
+        url(r'^createShop/$',views.createShop, name = 'createShop'),
+        url(r'^modifyShop/$',views.modifyShop, name = 'modifyShop'),
+        url(r'^manageShop/$',views.manageShop, name = 'manageShop'),
+        url(r'^browseShop/$',views.browseShop, name = 'browseShop'),
+#       book
+        url(r'^addBook/$',vbook.addBook, name = 'addBook'),
+		url(r'^getBook/$',vbook.getBook, name = 'getBook'),
+        url(r'^removeBook/$',vbook.removeBook, name = 'removeBook'),
+        url(r'^getBorrowBook/$',vbook.reqBorrowBook, name = 'borrowBook'),
+        url(r'^postBorrowAction/$',vbook.respBorrowAction, name = 'respBorrowBook'),
+        url(r'^returnBook/$',vbook.returnBook, name = 'returnBook'),
+        url(r'^getCurrentBorrowBook/$',vbook.getCurrentBorrowBook, name = 'currentBorrowBook'),
+        url(r'^getHistoryBorrowBook/$',vbook.getHistoryBorrowBook, name = 'HistoryBorrowBook'),
+#        url(r'^modifyBook/$',views.modifyBook, name = 'modifyBook'),
+#        url(r'^delBook/$',views.delBook, name = 'delBook'),
+#       search 
+		url(r'^searchBook/$',vbook.searchBook, name = 'searchBook'),
+		url(r'^searchArea/$',views.searchArea, name = 'searchArea'),
+#       event
+        url(r'^checkMessage/$',views.checkEventCnt, name = 'checkEventCnt'),
+        url(r'^getMessage/$',views.getEventComment, name = 'getEventComment'),
+]
diff --git a/book/vbook.py b/book/vbook.py
index aca6a7d..08d1ce3 100644
--- a/book/vbook.py
+++ b/book/vbook.py
@@ -4,20 +4,30 @@
 from django.forms.models import model_to_dict
 
 from book.models import *
-from book.Util.upJiGuang import *
+#from book.Util.upJiGuang import *
+from book.Util.tasks import *
 from book.Util.util import *
 import simplejson
 import random
 import datetime
 import logging
 import sys
-#logger = logging.getLogger(__name__)
-logger = logging.getLogger("mysite")
+logger = logging.getLogger(__name__)
+#logger = logging.getLogger("mysite")
 
-
+'''
+function: get current time
+return: Time
+Notice: none
+'''
 def getCurrentTime():
     return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
 
+'''
+function: record footprint of borrowing book
+return: none
+Notice: please sync the status of APP
+'''
 def recordToHistory(user, book_dict):
     if book_dict['action'] == 'borrow':
         if UserCurrentBorrow.checkIsExistThisBook(user.id, book_dict['owner'], book_dict['shop'], book_dict['book'], "borrowing"):
@@ -55,7 +65,11 @@
         logger.info("no this action")
 
 ######################## B O O K #############################	
-		
+'''
+function: add book to database
+return: states
+Notice:invalid cache of shop
+'''
 def addBook(request):
     state = "fail" 
     dict = {}
@@ -84,8 +98,8 @@
                     imageurl = request.POST.get('imageurl', '')
                     extlink = request.POST.get('extlink', '')
                     book = Book.createBookRow(bookname, author, publisher, detail, shop.id, 1, isbn, "", imageurl, extlink)
-                    shop.Belong_Shop.add(book)
-                    shop.changeTag += 1
+# shop.Belong_Shop.add(book)
+                    shop.changeTag += 1 # to invalid cache
                     shop.save()
                     state = 'success'
 
@@ -94,7 +108,11 @@
     logger.info(dict)
     return HttpResponse(json)
 
-
+'''
+function: get book
+return: state and the detailed info about book
+notice: none
+'''
 def getBook(request):
     state = "fail"
     dict = {}
@@ -122,6 +140,11 @@
     logger.info(dict)
     return HttpResponse(json)
 
+'''
+function: delete book from database
+return: state
+notice: invalid cache
+'''
 def removeBook(request):
     state = "fail" 
     dict = {}
@@ -150,7 +173,11 @@
     logger.info(dict)
     return HttpResponse(json)
 
-
+'''
+function: request to borrow book 
+return: state
+Notice: jpush 
+'''
 def reqBorrowBook(request):
     state = "fail" 
     dict = {}
@@ -189,11 +216,11 @@
                 alert =  borrower.name + u'  向你借书: <<'.encode('utf-8').decode('utf-8') + bookname + ">>"
                 logger.info(bookname)
                 logger.info(alert)
-                jpushMessageWithRegId(owner.regid, msg, alert);
+                jpushMessageWithRegId.delay(owner.regid, msg, alert); # push message
                     #userEvent = UserEvent.objects.create(user_id=owner.id, owner=owner.name, borrower=borrower.name, \
                     #        book=bookname, time=curtime, shop=shopname, action="borrow")
 #recordToHistory(borrower, model_to_dict(userEvent))
-                
+                 
                 recordToHistory(borrower, {'owner':owner.name,'shop':shopname, 'book':bookname, 'action':action, 'time':curtime})
                 state = 'success'
             
@@ -202,6 +229,11 @@
     logger.info(dict)
     return HttpResponse(json)
 
+'''
+function:  response about borrowing request -- accept or refuse
+return: state
+notice: jpush
+'''
 def respBorrowAction(request):
     state = "fail"
     dict = {}
@@ -239,7 +271,7 @@
                 msg['count'] = 1
 #alert =  owner.name + u'  同意借书请求'.encode('utf-8').decode('utf-8')
                 alert =  owner.name + u'  同意借书: <<'.encode('utf-8').decode('utf-8') + bookname + ">>"
-                jpushMessageWithRegId(borrower.regid, msg, alert);
+                jpushMessageWithRegId.delay(borrower.regid, msg, alert);
                     #userEvent = UserEvent.objects.create(user_id=borrower.id, owner=owner.name, borrower=fromname, book=bookname, time=curtime, shop=shopname, action=action)
 #recordToHistory(borrower, model_to_dict(userEvent))
                 recordToHistory(borrower, {'owner':owner.name,'shop':shopname, 'book':bookname, 'action':action, 'time':curtime})
@@ -257,6 +289,12 @@
     logger.info(dict)
     return HttpResponse(json)
 
+''' 
+function: return book by owner
+return: status
+notice: not communicate with borrower
+        invalid cache
+'''
 def returnBook(request):
     state = "fail"
     dict = {}
@@ -299,6 +337,11 @@
     logger.info(dict)
     return HttpResponse(json)
 
+''' 
+function: search book by name
+return:booklist 
+notice: none
+'''
 def searchBook(request):
     state = None
     dict = {}
@@ -328,6 +371,11 @@
     logger.info(dict)
     return HttpResponse(json)
 
+'''
+function: get history of borrowing book
+return: booklist that were borrowing
+Notice: none
+'''
 def getCurrentBorrowBook(request):
     state = "fail"
     dict = {}
@@ -353,6 +401,11 @@
     logger.info(dict)
     return HttpResponse(json)
 
+'''
+function: get history of borrowed book
+return: booklist that were borrowed
+Notice: none
+'''
 def getHistoryBorrowBook(request):
     state = "fail"
     dict = {}
diff --git a/book/views.py b/book/views.py
index d02baf4..6051826 100755
--- a/book/views.py
+++ b/book/views.py
@@ -4,8 +4,11 @@
 from django.forms.models import model_to_dict
 from django.core.cache import cache
 from book.models import *
-from book.Util.upJiGuang import *
+#from book.Util.upJiGuang import *
+from book.Util.tasks import *
+#from book.Util.pushTask import *
 from book.Util.util import *
+from bookStore import celery_app
 import simplejson
 import random
 import datetime
@@ -14,29 +17,36 @@
 
 # Get an instance of a logger
 #logger = logging.getLogger(__name__)
+'''
+set log output to console or file
+modify the bookStore/setting.py 
+'''
 logger = logging.getLogger("mysite")
 
 # Create your views here.
+'''
+function: regist new User with username and password
+return: state
+Notice: not support have the same username in the database"
+'''
 def regist(request):
     state = "fail"
     dict = {}
     
+    add.delay(4,4)
     logger.info(request.POST)
     logger.info(os.getpid())
     if request.method == 'POST':
         username = request.POST.get('username', '')		
         password = request.POST.get('password', '')
-        #regid = request.POST.get('regid', '')
-        #regid = "18071adc030bb6dd31d"
 
         if User.checkIsExistWithName(username):
             state = 'user_exist'
         else:
             User.createUserRow(username, password, username, "", "", username)
-#            jpushResult = jpushDeviceSetAlias(regid, username)
+#           jpushResult = jpushDeviceSetAlias(regid, username)
     	    dict['token'] = username 
-#            print "==========? " +  str(jpushResult)
-#            dict['jpushResult'] = jpushResult
+#           dict['jpushResult'] = jpushResult
             state = 'success'
 
     dict['result'] = state
@@ -44,6 +54,12 @@
     logger.info(dict)
     return HttpResponse(json)
 
+'''
+function: get regid from APP when user connect APP server
+          regid - APP can get it from Jpush server
+return: state
+Notice: none
+'''
 def postRegisterID(request):
     state = "fail"
     dict = {}
@@ -67,7 +83,11 @@
     logger.info(dict)
     return HttpResponse(json)
 
-
+'''
+function: establish the connection between APP and Server throught Token
+return: token and state
+Notice: Token will randomly generate when user log in server 
+'''
 def login(request):
     state = "fail"
     dict = {}
@@ -82,12 +102,16 @@
         if user is None:
             state = 'not_exist_or_password_error'        
         else:
+            ''' 
+                Get shoplist when user log in server
+                Now only support one shop, so return the first shop of shoplist
+            '''
             shops = User.getUserShops(user)
             if shops is not None and len(shops):
                 fstShop = shops[0]
                 dict['shopname'] = fstShop.name
 
-            token = User.setUserToken(user)
+            token = User.setUserToken(user) # regenerate the new token
             dict['token'] = token 
             state = 'success'
 
@@ -98,7 +122,11 @@
 
        		
 ######################## S H O P #############################
-
+'''
+function:  create a new shop, and shop can't belong the shoplist of user
+return: state
+Notice: the shoplist can't have the same shop name
+'''
 def createShop(request):
     state = "fail"
     dict = {}
@@ -127,7 +155,7 @@
                 addr = request.POST.get('shopaddr', '')
                 comment = request.POST.get('shopcomment', '')
                 shop = Shop.createShopRow(shopname, addr, comment, user.id, user.name, changeTag=0)
-                user.Belong_user.add(shop)
+#user.Belong_user.add(shop)
                 state = 'success'					        
 
     dict['result'] = state
@@ -135,6 +163,13 @@
     logger.info(dict)
     return HttpResponse(json)
 
+'''
+function: get the detailed information about shop
+return: state and booklist
+Notice: when cache valid, we will get result from cache, or we will read database and recache shop
+        when the status of book changes or add book or remove, we need invalid cache.
+'''
+
 def manageShop(request):
     state = "fail"
     dict = {}
@@ -152,10 +187,17 @@
                 if shop is None:
                     state = 'this shop not in '+user.name
                 else:
+                    '''
+                    when between cache and database have difference, we should change changeTag to invalid cache - key
+                    It's not good solution.
+                    Because the value of originl cache have existed.
+                    '''
                     shopVersion = shop.changeTag
                     cacheTag = request.get_full_path()
                     cacheKey = getCacheKey(cacheTag, key_prefix='shop', version=shopVersion)
                     cacheValue = cache.get(cacheKey, version=shopVersion)
+#cacheValue.state = 'xxxxxxxxxxxxxxx'
+#                    print cacheValue
 #cacheValue = 0
                     if cacheValue:
                         json=simplejson.dumps(cacheValue)
@@ -180,6 +222,11 @@
     logger.info(dict)
     return HttpResponse(json)
 
+'''
+function:   browse shop by username
+return: the booklist of this shop
+Notice: none
+'''
 def browseShop(request):
     state = "fail"
     dict = {}
@@ -211,7 +258,11 @@
     logger.info(dict)
     return HttpResponse(json)
 
-
+'''
+function:   modify shopname by old shopname
+return: state and new shopname
+Notice: none
+'''
 def modifyShop(request):
     state = "fail" 
     dict = {}
@@ -240,7 +291,11 @@
     logger.info(dict)
     return HttpResponse(json)
 
-
+'''
+function: get all area from contains the key string
+return: all area matched
+Notice: none
+'''
 def searchArea(request):
     state = "fail"
     dict = {}
@@ -264,6 +319,9 @@
     logger.info(dict)
     return HttpResponse(json)
 
+'''
+function: deleted
+'''
 def checkEventCnt(request):
     state = None
     dict = {}
@@ -285,6 +343,9 @@
     logger.info(dict)
     return HttpResponse(json)
 
+'''
+function: deleted
+'''
 def getEventComment(request):
     state = None
     dict = {}
diff --git a/bookStore/__init__.py b/bookStore/__init__.py
index e69de29..b21edbb 100755
--- a/bookStore/__init__.py
+++ b/bookStore/__init__.py
@@ -0,0 +1,6 @@
+from __future__ import absolute_import
+
+# This will make sure the app is always imported when
+# Django starts so that shared_task will use this app.
+from .celery import app as celery_app  # noqa
+__all__ = ['celery_app']
diff --git a/bookStore/celery.py b/bookStore/celery.py
new file mode 100644
index 0000000..bc32cb0
--- /dev/null
+++ b/bookStore/celery.py
@@ -0,0 +1,45 @@
+from __future__ import absolute_import
+
+import os
+
+from celery import Celery
+
+# set the default Django settings module for the 'celery' program.
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'bookStore.settings')
+
+from django.conf import settings  # noqa
+
+app = Celery('bookStore',
+    broker = 'redis://localhost',
+    backend = 'redis://localhost',
+    )
+
+
+# Using a string here means the worker will not have to
+# pickle the object when using Windows.
+app.config_from_object('django.conf:settings')
+app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
+
+
+@app.task(bind=True)
+def debug_task(self):
+    print('Request: {0!r}'.format(self.request))
+
+'''
+from __future__ import absolute_import
+
+from celery import Celery
+
+app = Celery('proj',
+    broker = 'redis://localhost',
+    backend = 'redis://localhost',
+    include=['proj.tasks'])
+
+# Optional configuration, see the application user guide.
+app.conf.update(
+        CELERY_TASK_RESULT_EXPIRES=3600,
+        )
+
+if __name__ == '__main__':
+    app.start()
+'''
diff --git a/bookStore/settings.py b/bookStore/settings.py
index ae1f92e..c998558 100755
--- a/bookStore/settings.py
+++ b/bookStore/settings.py
@@ -39,6 +39,7 @@
 #'online',
 #   'management',
     'book',
+    'book.Util',
 )
 
 MIDDLEWARE_CLASSES = (
@@ -195,7 +196,7 @@
     'disable_existing_loggers': False,
     'formatters': {
         'verbose': {
-            'format' : "[%(asctime)s] [%(name)s:%(funcName)s:%(lineno)s] [%(levelname)s]: %(message)s",
+            'format' : "[%(asctime)s.%(msecs)03d] [%(name)s:%(funcName)s:%(lineno)s] [%(levelname)s]: %(message)s",
             'datefmt' : "%d/%b/%Y %H:%M:%S"
         },
         'simple': {
@@ -216,9 +217,9 @@
         },
         'console': {
             'level': 'INFO',
-            'filters': ['require_debug_false'],
+#            'filters': ['require_debug_false'],
             'class': 'logging.StreamHandler',
-            'formatter': 'simple'
+            'formatter': 'verbose'
         },
         'mail_admins': {
             'level': 'ERROR',
@@ -238,7 +239,7 @@
             'propagate': False,
         },
         'mysite': {
-            'handlers': ['file'],
+            'handlers': ['console'],
             'level': 'INFO',
         },
     }
diff --git a/bookStore/urls.py b/bookStore/urls.py
index 1bebfcd..0efb97c 100755
--- a/bookStore/urls.py
+++ b/bookStore/urls.py
@@ -1,3 +1,4 @@
+'''
 from django.conf.urls import patterns, include, url
 
 from django.contrib import admin
@@ -11,3 +12,13 @@
     url(r'^book/', include('book.urls')),
     url(r'^admin/', include(admin.site.urls)),
 )
+'''
+
+
+from django.conf.urls import url,include
+from django.contrib import admin
+
+urlpatterns = [
+    url(r'^book/', include('book.urls')),
+    url(r'^admin/', include(admin.site.urls)),
+]