diff options
-rw-r--r-- | src/css/manage_problem.less | 13 | ||||
-rw-r--r-- | src/css/style.less | 4 | ||||
-rw-r--r-- | src/html/index.html | 2 | ||||
-rw-r--r-- | src/html/manage_problem.html | 58 | ||||
-rw-r--r-- | src/html/manage_square.html | 2 | ||||
-rw-r--r-- | src/js/manage.js | 196 | ||||
-rw-r--r-- | src/js/mod.js | 35 | ||||
-rw-r--r-- | src/js/square.js | 8 | ||||
-rwxr-xr-x | src/py/backend_server.py | 7 | ||||
-rwxr-xr-x | src/py/imc/blobclient.py | 233 | ||||
-rwxr-xr-x | src/py/imc/blobhandle.py | 150 | ||||
-rwxr-xr-x | src/py/imc/blobserver.py | 263 | ||||
-rw-r--r-- | src/py/square.py | 45 |
13 files changed, 953 insertions, 63 deletions
diff --git a/src/css/manage_problem.less b/src/css/manage_problem.less new file mode 100644 index 0000000..ce7c5a4 --- /dev/null +++ b/src/css/manage_problem.less @@ -0,0 +1,13 @@ +@import 'color.less'; +@import 'mixin.less'; + +#index_page{ + div.oper{ + text-align:center; + } + table.list{ + tr.item{ + + } + } +} diff --git a/src/css/style.less b/src/css/style.less index 7d6ef90..c03cec6 100644 --- a/src/css/style.less +++ b/src/css/style.less @@ -7,6 +7,10 @@ src:url('/DejaVuSansMono.woff'); } +div.tiny_modal{ + width:270px; + margin-left:-135px; +} div.small_modal{ width:570px; margin-left:-285px; diff --git a/src/html/index.html b/src/html/index.html index 902411b..27efe74 100644 --- a/src/html/index.html +++ b/src/html/index.html @@ -26,6 +26,7 @@ <script src="/toj/js/index.js" type="text/javascript"></script> <script src="/toj/js/user.js" type="text/javascript"></script> <script src="/toj/js/notice.js" type="text/javascript"></script> +<script src="/toj/js/mod.js" type="text/javascript"></script> <script src="/toj/js/home.js" type="text/javascript"></script> <script src="/toj/js/square.js" type="text/javascript"></script> <script src="/toj/js/mail.js" type="text/javascript"></script> @@ -42,6 +43,7 @@ $(document).ready(function(){ com.conn_callback.add(function(){ user.ready(); notice.ready(); + mod.ready(); index.ready(); home.ready(); square.ready(); diff --git a/src/html/manage_problem.html b/src/html/manage_problem.html new file mode 100644 index 0000000..abad7a3 --- /dev/null +++ b/src/html/manage_problem.html @@ -0,0 +1,58 @@ +<link href="/toj/css/manage_problem.css" rel="stylesheet"> + +<div class="modal hide fade tiny_modal create"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> + <h3>建立題目</h3> + </div> + <div class="modal-body container-fluid"> + <div class="row-fluid"> + <div class="span13"> + <label>題目名稱</label> + <input name="title" type="text"> + <label>題目模組</label> + <select name="pmod"></select> + </div> + </div> + </div> + <div class="modal-footer"> + <button class="btn btn-primary submit">確定</button> + <button class="btn cancel">取消</button> + </div> +</div> +<div class="modal hide fade tiny_modal set"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> + <h3>題目設定</h3> + </div> + <div class="modal-body container-fluid"> + <div class="row-fluid"> + <div class="span13"> + <label>題目名稱</label> + <input name="title" type="text"> + </div> + </div> + </div> + <div class="modal-footer"> + <button class="btn btn-primary submit">確定</button> + <button class="btn cancel">取消</button> + </div> +</div> + +<div class="row"> + <div class="span2 offset1 oper"> + <button class="btn create">建立題目</button> + </div> + <div class="span6"> + <table class="table list"> + <thead> + <tr> + <th class="span1">#</th> + <th class="span3">題目名稱</th> + <th class="span2"></th> + </tr> + </thead> + <tbody></tbody> + </table> + </div> +</div> diff --git a/src/html/manage_square.html b/src/html/manage_square.html index 1f83dbc..900e280 100644 --- a/src/html/manage_square.html +++ b/src/html/manage_square.html @@ -17,6 +17,8 @@ <img class="img-polaroid hide logo"></img> </div> <div class="span7"> + <label>方塊模組</label> + <select name="sqmod"></select> <label>公開狀態</label> <select name="hidden"> <option value=0>顯示</option> diff --git a/src/js/manage.js b/src/js/manage.js index cab6bcb..9767a38 100644 --- a/src/js/manage.js +++ b/src/js/manage.js @@ -5,10 +5,12 @@ var manage = new function(){ var j_index_page; that.ready = function(){ - var j_tabnav_square; - var manage_node = new vus.node('manage'); var square_node = new vus.node('square'); + var problem_node = new vus.node('problem'); + + var j_tabnav_square; + var j_tabnav_problem; j_index_page = $('#index_page'); @@ -18,6 +20,7 @@ var manage = new function(){ index.clear_tabnav(); j_tabnav_square = index.add_tabnav('方塊','/toj/manage/square/'); + j_tabnav_problem = index.add_tabnav('題目','/toj/manage/problem/'); com.call_backend('core/user/','list_auth',function(result){ console.log(result); @@ -94,6 +97,9 @@ var manage = new function(){ return j_item; } function _update(){ + var cate_defer = $.Deferred(); + var sqmod_defer = $.Deferred(); + com.call_backend('core/square/','list_category',function(result){ var i; var data = result.data; @@ -116,67 +122,95 @@ var manage = new function(){ create_tagbox_cate.set_words(catelist); set_tagbox_cate.set_words(catelist); - com.call_backend('core/square/','list_square',function(result){ - var i; - var data = result.data; - var items; - var j_item; - var sqo; + cate_defer.resolve(); + } + }); + com.call_backend('core/square/','list_sqmod',function(result){ + var i; + var data = result.data; + var j_sqmod; + var j_option; - if(com.is_callerr(result)){ - index.add_alert('','警告','管理發生錯誤'); - }else{ - items = j_list.find('tr.item'); - - for(i = 0;i < Math.min(items.length,data.length);i++){ - sqo = data[i]; - - if(sqo.start_time != null){ - sqo.start_time = new Date(sqo.start_time); - } - if(sqo.end_time != null){ - sqo.end_time = new Date(sqo.end_time); - } - - _item_set($(items[i]),sqo.sqid, - sqo.title, - sqo.start_time, - sqo.end_time, - sqo.cateid, - sqo.intro, - sqo.logo, - sqo.hidden); - } - for(;i < data.length;i++){ - sqo = data[i]; - - j_item = _item_create(sqo.sqid, - sqo.title, - sqo.start_time, - sqo.end_time, - sqo.cateid, - sqo.intro, - sqo.logo, - sqo.hidden); - j_list.append(j_item); + if(com.is_callerr(result)){ + index.add_alert('','警告','管理發生錯誤'); + }else{ + j_sqmod = j_create.find('[name="sqmod"]'); + console.log(j_sqmod.length); + j_sqmod.empty(); + for(i = 0;i < data.length;i++){ + j_option = $('<option></option>'); + j_option.attr('value',data[i].sqmodid); + j_option.text(data[i].sqmodname); + + j_sqmod.append(j_option); + } + + sqmod_defer.resolve(); + } + }); + + $.when(cate_defer,sqmod_defer).done(function(cate){ + com.call_backend('core/square/','list_square',function(result){ + var i; + var data = result.data; + var items; + var j_item; + var sqo; + + if(com.is_callerr(result)){ + index.add_alert('','警告','管理發生錯誤'); + }else{ + items = j_list.find('tr.item'); + + for(i = 0;i < Math.min(items.length,data.length);i++){ + sqo = data[i]; + + if(sqo.start_time != null){ + sqo.start_time = new Date(sqo.start_time); } - for(;i < items.length;i++){ - $(items[i]).remove(); + if(sqo.end_time != null){ + sqo.end_time = new Date(sqo.end_time); } + + _item_set($(items[i]),sqo.sqid, + sqo.title, + sqo.start_time, + sqo.end_time, + sqo.cateid, + sqo.intro, + sqo.logo, + sqo.hidden); } - }); - } - }); + for(;i < data.length;i++){ + sqo = data[i]; + + j_item = _item_create(sqo.sqid, + sqo.title, + sqo.start_time, + sqo.end_time, + sqo.cateid, + sqo.intro, + sqo.logo, + sqo.hidden); + j_list.append(j_item); + } + for(;i < items.length;i++){ + $(items[i]).remove(); + } + } + }); + }); } if(direct == 'in'){ com.loadpage('/toj/html/manage_square.html').done(function(){ var j_catebox; - j_list = j_index_page.find('table.list'); + j_tabnav_square.active(); + j_create = j_index_page.find('div.create'); j_set = j_index_page.find('div.set'); - j_tabnav_square.active(); + j_list = j_index_page.find('table.list > tbody'); j_catebox = j_create.find('div.catebox'); create_tagbox_cate = j_catebox.tagbox({'words':[],'restrict':true,'duplicate':false}); @@ -198,6 +232,7 @@ var manage = new function(){ var title = j_create.find('[name="title"]').val(); var intro = j_create.find('[name="intro"]').val(); var logo = j_create.find('[name="logo"]').val(); + var sqmodid = parseInt(j_create.find('[name="sqmod"]').val()); var hidden = j_create.find('[name="hidden"]').val(); var tags = create_tagbox_cate.get_tag(); var cateid_list; @@ -244,7 +279,7 @@ var manage = new function(){ j_create.modal('hide'); _update(); } - },title,hidden,1,intro,logo,cateid_list); + },title,hidden,sqmodid,intro,logo,cateid_list); }); j_create.find('button.cancel').on('click',function(e){ @@ -372,5 +407,62 @@ var manage = new function(){ return 'cont'; }; manage_node.child_set(square_node); + + problem_node.url_chg = function(direct,url_upart,url_dpart,param){ + var j_create; + var j_list; + var set_data; + + function _item_set(j_item,proid,title,pmodid){ + j_item.find('td.proid').text(proid); + j_item.find('td.title').text(title); + + j_item.find('button.set').on('click',function(e){ + set_data = { + 'proid':proid, + 'title':title, + 'pmodid':pmodid + }; + }); + j_item.find('button.del').on('click',function(e){ + + }); + } + function _item_create(proid,title,pmodid){ + var j_item = $('<tr><td class="proid"></td><td class="title"></td><td class="oper"><div class="btn-group"><button class="btn btn-small set"><i class="icon-cog"></i></button><button class="btn btn-small del"><i class="icon-trash"></i></button></div></td></tr>') + + _item_set(j_item,proid,title,pmodid); + + return j_item; + } + function _update(){ + + } + + if(direct == 'in'){ + j_tabnav_problem.active(); + + com.loadpage('/toj/html/manage_problem.html').done(function(){ + j_create = j_index_page.find('div.create'); + j_list = j_index_page.find('table.list > tbody'); + + j_index_page.find('button.create').on('click',function(e){ + j_create.modal('show'); + }); + + j_create.on('show',function(e){ + + }); + j_create.on('hide',function(e){ + j_create.find('input').val(''); + }); + }); + }else{ + + } + + return 'cont'; + }; + manage_node.child_set(problem_node); }; }; diff --git a/src/js/mod.js b/src/js/mod.js index b8cae5f..e626ff9 100644 --- a/src/js/mod.js +++ b/src/js/mod.js @@ -1,17 +1,48 @@ var mod = new function(){ var that = this; + that.curr_sqmod = null; + that.ready = function(){ var sq_node = new vus.node('sq'); sq_node.url_chg = function(direct,url_upart,url_dpart,param){ + var sqid; + if(direct == 'in'){ + index.set_menu('方塊'); + index.clear_tabnav(); + + sqid = url_dpart[0]; + sq_node.child_delayset(sqid); + + com.call_backend('core/square/','get_square_info',function(result){ + var data = result.data; + var sqmodname; + + if(com.is_callerr(result)){ + index.add_alert('','警告','開啓方塊發生錯誤'); + }else{ + index.set_title(data.title); + + sqmodname = escape(data.sqmodname); + $.getScript('/toj/sqmod/' + sqmodname + '/js/' + sqmodname + '.js',function(script,status,xhr){ + var sqid_node; + + curr_sqmod = sqmodname; + + sqid_node = new vus.node(sqid); + eval(sqmodname + '(sqid_node);'); + sq_node.child_set(sqid_node); + }); + } + },parseInt(sqid)); }else if(direct == 'out'){ - + eval(curr_sqmod + '.unload()'); } - return 'stop'; + return 'cont'; }; com.vus_root.child_set(sq_node); }; diff --git a/src/js/square.js b/src/js/square.js index b6ee4fa..9b5f684 100644 --- a/src/js/square.js +++ b/src/js/square.js @@ -30,7 +30,7 @@ var square = new function(){ j_oper = j_box.find('div.oper'); j_oper.empty(); if(active == null){ - j_oper.append('<button class="btn btn-primary join" data-loading-text="處理中">加入</button><button class="btn">開啓</button>'); + j_oper.append('<button class="btn btn-primary join" data-loading-text="處理中">加入</button><a class="btn open">開啓</a>'); j_oper.find('button.join').on('click',function(e){ $(this).button('loading'); @@ -52,9 +52,9 @@ var square = new function(){ }); }else{ if(active== true){ - j_oper.append('<button class="btn btn-success quit" data-loading-text="處理中">退出</button><button class="btn">開啓</button>'); + j_oper.append('<button class="btn btn-success quit" data-loading-text="處理中">退出</button><a class="btn open">開啓</a>'); }else{ - j_oper.append('<button class="btn btn-warning quit" data-loading-text="處理中">取消申請</button><button class="btn">開啓</button>'); + j_oper.append('<button class="btn btn-warning quit" data-loading-text="處理中">取消申請</button><a class="btn open">開啓</a>'); } j_oper.find('button.quit').on('click',function(e){ @@ -75,6 +75,8 @@ var square = new function(){ },id); }); } + + j_oper.find('a.open').attr('href','/toj/sq/' + id + '/'); } function box_update(id,logo,title,start_time,end_time,intro,active){ var i; diff --git a/src/py/backend_server.py b/src/py/backend_server.py index e0faf38..5a58445 100755 --- a/src/py/backend_server.py +++ b/src/py/backend_server.py @@ -146,7 +146,7 @@ class BackendWorker(tornado.tcpserver.TCPServer): @imc.async.caller def _init_blobclient(self): - '''blobclient = BlobClient(Proxy.instance, + blobclient = BlobClient(Proxy.instance, TOJAuth.instance, self._idendesc, self._link, @@ -160,8 +160,7 @@ class BackendWorker(tornado.tcpserver.TCPServer): TOJBlobHandle.CREATE) print(handle._fileno) handle.write(bytes('Hello Data','utf-8'),0) - handle.commit(False);''' - pass + handle.commit(False); def _conn_link(self,link): def __handle_pend(conn): @@ -381,7 +380,7 @@ if __name__ == '__main__': worker_list = [] worker_list.append(Process(target = start_backend_worker,args = (81, ))) - worker_list.append(Process(target = start_backend_worker,args = (82, ))) + #worker_list.append(Process(target = start_backend_worker,args = (82, ))) #worker_list.append(Process(target = start_backend_worker,args = (181, ))) #worker_list.append(Process(target = start_backend_worker,args = (182, ))) #worker_list.append(Process(target = start_backend_worker,args = (183, ))) diff --git a/src/py/imc/blobclient.py b/src/py/imc/blobclient.py new file mode 100755 index 0000000..8d54ffe --- /dev/null +++ b/src/py/imc/blobclient.py @@ -0,0 +1,233 @@ +#! /usr/bin/env python + +import os + +from imc.auth import Auth +import imc.async +from imc.proxy import Proxy + +from collections import Counter + +class BlobClient: + def __init__(self, proxy, auth, idendesc, link, serverlink, + location, cachetable, BlobHandle): + self._proxy = proxy + self._auth = auth + self._idendesc = idendesc + self._link = link + self._server = serverlink + self._is_connected = False + self._location = location + self._cachetable = cachetable + self.BlobHandle = BlobHandle + self._opencounts = Counter() + self._containers = dict() + self._deltags = set() + self.connect_server() + self._proxy.register_call('blobclient/', 'get_update', + self.get_update) + + def __del__(self): + self._proxy.unregister_call('blobclient/', 'get_update') + + def _server_call(self, func, *args): + server = self._server + 'blobserver/' + with Auth.change_current_iden(self._idendesc): + for i in range(5): + sta, ret = self._proxy.call(server, func, 10000, + self._link, *args) + if sta or (not sta and ret == 'Enoexist'): + break + return (sta, ret) + + def _client_call(self, otherclient, func, *args): + otherclient += 'blobclient/' + with TOJAuth.change_current_iden(self._idendesc): + for i in range(5): + sta, ret = self._proxy.call(otherclient, func, 10000, + self._link, *args) + if sta or (not sta and ret == 'Enoexist'): + break + return (sta, ret) + + def connect_server(self, serverlink=None): + if serverlink: + self._server = serverlink + sta, ret = self._server_call('connect_client', + self._cachetable.get_blob_list()) + if sta: + if ret: + self._is_connected = True + else: + pass + else: + pass + if self._is_connected: + # TODO: + pass + + + def open_container(self, container, method): + sta, ret = self._server_call('open_container', container, method) + if not sta: + # TODO: + # pend operation when client can't imc call server + return None + if ret: + self._containers[container] = method + return True + else: + return False + + def close_container(self, container): + sta, ret = self._server_call('close_container', container) + if not sta: + # TODO: + # pend operation when client can't imc call server + return None + if ret: + del self._containers[container] + return True + else: + return False + + # TODO: + # periodically call this function to clean old data and do something else + # ex: send pending operation + def sync(self): + for blobname_rev in self._deltags: + self.del_real_blob(blobname_rev) + # for container in self._containers: + # if self._containers[container] == 'ALWAYS': + # for blob in self._cachetable.get_blob_list(container): + # self.update(blob) + + @imc.async.caller + def get_update(self, blobname, info, filekey=None): + if info is None: + self.del_blob(blobname) + elif filekey is not None: + rev = info['rev'] + if self.recv_blob(filekey, blobname, rev).wait() == 'Success': + self.update_blob(blobname, info) + sta, ret = self._server_call('recv_update_result', + blobname, "Success", rev) + if not sta: + # TODO: + pass + else: + self.update_blob(blobname, info) + return self._link + + def update(self, blobname): + cacherev = self._cachetable.get_blob_info(blobname, 'rev') + if cacherev == None: + cacherev = 0 + + sta, ret = self._server_call('check_blob', blobname, cacherev) + + if not sta: + # TODO: + # pend operation when client can't imc call server + return None + if ret is None: + return True + elif not ret: + self._cachetable.del_blob(blobname) + if cacherev: + self.del_real_blob(''.join([blobname, '_', str(cacherev)])) + return None + else: + info = ret['info'] + rev = info['rev'] + for i in range(4): + rst = self.recv_blob(ret['filekey'], blobname, rev).wait() + sta, ret = self._server_call('recv_update_result', blobname, + rst, rev, True) + + if not sta: + pass + # TODO: + if 'Success' == ret: + self.update_blob(blobname, info) + return True + + return False + + def commit(self, commit_info, force_flag, blobhandle): + filekey = None + if not commit_info['deltag'] and commit_info['written']: + result = self.send_blob(blobhandle._tmpfile) + filekey = result.filekey + sta, ret = self._server_call('recv_commit', commit_info, + force_flag, filekey) + if not sta: + # TODO: + # pend operation when client can't imc call server + return False + # TODO: + # if commit success , copy tmpfile to location + + # TODO: + # opencounts ? + def send_blob(self, blobpath, otherclient=None): + if otherclient is None: + return self._proxy.sendfile(self._server, blobpath) + else: + return self._proxy.sendfile(otherclient, blobpath) + + def recv_blob(self, filekey, blobname, rev): + blobpath = os.path.join(self._location, blobname + '_' + str(rev)) + return self._proxy.recvfile(filekey, blobpath) + + def update_blob(self, blobname, info): + rev = self._cachetable.get_blob_info(blobname, 'rev') + blobname_rev = ''.join([blobname, '_', str(rev)]) + self.del_real_blob(blobname_rev) + self._cachetable.update_blob(blobname, info) + + def del_blob(self, blobname): + rev = self._cachetable.get_blob_info(blobname, 'rev') + blobname_rev = ''.join([blobname, '_', str(rev)]) + self._cachetable.del_blob(blobname) + self.del_real_blob(blobname_rev) + + def del_real_blob(self, blobname_rev): + if self._opencounts[blobname_rev] == 0: + path = os.path.join(self._location, blobname_rev) + self.BlobHandle.del_blob(path) + else: + self._deltags.add(blobname_rev) + + def open(self, container, blobname, flag): + if container not in self._containers: + raise Exception("this container isn't open") + blob = container + '_' + blobname + is_existent = self.update(blob) + if is_existent is None: + if (not flag & self.BlobHandle.WRITE or + not flag & self.BlobHandle.CREATE): + raise Exception("the blob doesn't exist, so you must " + "add a create flag") + elif not is_existent: + pass + try: + info = self._cachetable.get_blob_info(blob) + if info is None: + info = {'container': container, + 'rev': 0, + 'metadata': '', + 'size': None, + 'commit_time': None} + handle = self.BlobHandle(blob, info, flag, self) + except: + raise + else: + blob += '_' + str(handle.get_rev()) + self._opencounts[blob] += 1 + return handle + + def close(self, blobhandle): + blob = ''.join([blobhandle._name, '_', + str(blobhandle.get_rev())]) + self._opencounts[blob] -= 1 diff --git a/src/py/imc/blobhandle.py b/src/py/imc/blobhandle.py new file mode 100755 index 0000000..b957162 --- /dev/null +++ b/src/py/imc/blobhandle.py @@ -0,0 +1,150 @@ +#! /usr/bin/env python + +from abc import abstractmethod + +class BlobHandle: + READ = 0x1 + WRITE = 0x2 + CREATE = 0x4 + DELETE = 0x8 + WRITEMETA = 0x10 + def __init__(self, name, info, flag, blobclient): + self._name = name + self._info = info + self._flag = flag + self._blobclient = blobclient + self._location = self._blobclient._location + self._is_closed = False + self._deltag = False + self._written = False + self._createtag = False + self._need_commit = False + self._tmpfile = None + self._blobpath = ''.join([self._location, self._name, + '_', str(self.get_rev())]) + if flag & BlobHandle.CREATE: + if not flag & BlobHandle.WRITE: + raise Exception("invalid flag") + else: + self._need_commit = True + self._createtag = True + self._written = True + if flag & BlobHandle.WRITE: + self._tmpfile = self.gen_tmp() + + def __del__(self): + self._del_tmp() + self._blobclient.close(self) + + def create(self): + self._create(self.location + self._name) + + def read(self, length, offset): + if self._is_closed: + raise Exception("This Blob is closed") + if not self._flag & BlobHandle.READ: + raise Exception("Permission Denied") + return self._read(length, offset) + + def write(self, data, offset): + if self._is_closed: + raise Exception("This Blob is closed") + if not self._flag & BlobHandle.WRITE: + raise Exception("Permission Denied") + self._need_commit = True + written_bytes = self._write(data, offset) + self._written = bool(written_bytes) + self._info['size'] = self._get_size() + return written_bytes + + def rename(self, newname): + if self._is_closed: + raise Exception("This Blob is closed") + if not self._flag & BlobHandle.DELETE: + raise Exception("Permission Denied") + self._need_commit = True + + def delete(self, deltag=True): + if self._is_closed: + raise Exception("This Blob is closed") + if not self._flag & BlobHandle.DELETE: + raise Exception("Permission Denied") + self._need_commit = True + self._deltag = deltag + + def close(self): + self._is_closed = True + if self._flag != BlobHandle.READ: + self._blobclient.close(self) + + def get_metadata(self): + if self._is_closed: + raise Exception("This Blob is closed") + return self._info['metadata'] + + def set_metadata(self, metadata): + if self._is_closed: + raise Exception("This Blob is closed") + if not self._flag & BlobHandle.WRITEMETA: + raise Exception("Permission Deniedd") + self._info['metadata'] = metadata + self._need_commit = True + + def get_rev(self): + if self._is_closed: + raise Exception("This Blob is closed") + return self._info['rev'] + + def get_container(self): + if self._is_closed: + raise Exception("This Blob is closed") + return self._info['container'] + + def get_size(self): + if self._is_closed: + raise Exception("This Blob is closed") + return self._info['size'] + + def commit(self, flag): + if self._is_closed: + raise Exception("This Blob is closed") + if not self._need_commit: + return False + commit_info = dict() + commit_info['blobname'] = self._name + if self._deltag: + commit_info['deltag'] = True + else: + commit_info['deltag'] = False + commit_info['createtag'] = self._createtag + commit_info['info'] = self._info + commit_info['written'] = self._written + return self._blobclient.commit(commit_info, flag, self) + + @abstractmethod + def gen_tmp(self): + # return tmp file path + pass + + @abstractmethod + def del_tmp(self): + pass + + @abstractmethod + def _read(self, length, offset): + pass + + @abstractmethod + def _write(self, data, offset): + pass + + @abstractmethod + def _get_size(self): + pass + + @staticmethod + @abstractmethod + def del_blob(blobpath): + pass + + diff --git a/src/py/imc/blobserver.py b/src/py/imc/blobserver.py new file mode 100755 index 0000000..73bc143 --- /dev/null +++ b/src/py/imc/blobserver.py @@ -0,0 +1,263 @@ +#! /usr/bin/env python + +import os + +from imc.auth import Auth +import imc.async +from imc.proxy import Proxy + +from collections import Counter + +class BlobServer: + def __init__(self, proxy, auth, idendesc, link, + location, blobtable, BlobHandle): + + self._proxy = proxy + self._auth = auth + self._idendesc = idendesc + self._link = link + self._location = location + self._blobtable = blobtable + self.BlobHandle = BlobHandle + self._clients = {} + self._containers = dict.fromkeys(self._blobtable.get_container_list(), + dict()) + self._proxy.register_call('blobserver/', 'connect_client', + self.connect_client) + self._proxy.register_call('blobserver/', 'open_container', + self.open_container) + self._proxy.register_call('blobserver/', 'close_container', + self.close_container) + self._proxy.register_call('blobserver/', 'check_blob', + self.check_blob) + self._proxy.register_call('blobserver/', 'recv_update_result', + self.recv_update_result) + self._proxy.register_call('blobserver/', 'recv_commit', + self.recv_commit) + + def __del__(self): + self._proxy.unregister_call('blobserver/', 'connect_client') + self._proxy.unregister_call('blobserver/', 'open_container') + self._proxy.unregister_call('blobserver/', 'close_container') + self._proxy.unregister_call('blobserver/', 'check_blob') + self._proxy.unregister_call('blobserver/', 'recv_update_result') + self._proxy.unregister_call('blobserver/', 'recv_commit') + + def _client_call(self, client, func, timeout=10000, *args): + client += 'blobclient/' + with Auth.change_current_iden(self._idendesc): + for i in range(5): + sta, ret = self._proxy.call(client, func, timeout, *args) + if sta or (not sta and ret == 'Enoexist'): + break + return (sta, ret) + + def _client_call_async(self, client, func, callback, + timeout=10000, *args, **kwargs): + client += 'blobclient/' + with Auth.change_current_iden(self._idendesc): + for i in range(5): + sta, ret = self._proxy.call_async(client, func, timeout, + callback, *args) + if sta or (not sta and ret == 'Enoexist'): + break + return (sta, ret) + + @imc.async.caller + def connect_client(self, client, cache_list): + if client not in self._clients: + self._clients.update({client: cache_list}) + else: + self._clients[client] = cache_list + + def disconnect_client(self, client): + try: + self._clients.pop[client] + except ValueError: + raise Exception("this client doesn't exist") + + def create_container(self, container): + self._blobtable.create_container(container) + self._containers[container] = dict() + + def del_container(self, container): + try: + self._blobtable.del_container(container) + del self._containers[container] + except: + raise + + @imc.async.caller + def open_container(self, client, container, method): + try: + self._containers[container][client] = method + except KeyError: + return False + else: + return True + + @imc.async.caller + def close_container(self, client, container): + try: + self._containers[container].pop(client) + except KeyError: + raise + + def update_blob(self, blobname, info): + self._blobtable.update_blob(blobname, info) + + def del_blob(self, blobname): + rev = self._blobtable.get_blob_info(blobname, 'rev') + blobname_rev = ''.join([blobname, '_', str(rev)]) + self._blobtable.del_blob(blobname) + self.del_real_blob(blobname_rev) + + def del_real_blob(self, blobname_rev): + blobpath = self._location + blobname_rev + self.BlobHandle.del_blob(blobpath) + + def send_blob(self, client, blobname): + rev = str(self._blobtable.get_blob_info(blobname, 'rev')) + blobpath = os.path.join(self._location, blobname + '_' + rev) + return self._proxy.sendfile(client, blobpath) + + def recv_blob(self, filekey, blobname, rev): + blobpath = os.path.join(self._location, blobname + '_' + str(rev)) + ret = self._proxy.recvfile(filekey, blobpath) + + return ret + + @imc.async.caller + def check_blob(self, client, blobname, cacherev): + rev = self._blobtable.get_blob_info(blobname, 'rev') + if rev is None: + return False + elif cacherev < rev: + result = self.send_blob(client, blobname) + response = {'filekey': result.filekey, + 'info': self._blobtable.get_blob_info(blobname)} + return response + else: + return None + + @imc.async.caller + def recv_update_result(self, client, blobname, result, + cacherev, retry=False): + if client not in self._clients: + return None + else: + if result == 'Success': + self._clients[client].append({blobname: cacherev}) + return 'Success' + elif retry: + result = self.send_blob(client, blobname) + response = {'filekey': result.filekey, + 'info': self._blobtable.get_blob_info(blobname)} + return response + else: + return 'Finish' + + def send_update(self, clients, blobname, info, written): + result_table = dict.fromkeys(clients) + def recv_result(result): + nonlocal result_table + nonlocal blobname + nonlocal info + sta, client = result + # TODO: + # limit retry + if not sta: + self._client_call_async(client, 'get_update', + recv_result, + blobname, info, + result_table[client].filekey) + else: + if result_table[client] is None: + result_table.pop(client) + elif result_table[client].wait() != 'Success': + result_table[client] = self.send_blob(client, blobname) + self._client_call_async(client, 'get_update', + recv_result, + blobname, info, + result_table[client].filekey) + else: + result_table.pop(client) + + for client in clients: + if written: + result_table[client] = self.send_blob(client, blobname) + else: + result_table[client] = None + sta, ret = self._client_call(client, 'get_update', + recv_result, + blobname, info, + result_table[client].filekey) + if not sta: + # TODO: + pass + + @imc.async.caller + def recv_commit(self, client, commit_info, force_flag, filekey=None): + blobname = commit_info['blobname'] + info = commit_info['info'] + rev = self._blobtable.get_blob_info(blobname, 'rev') + if rev is None: + if commit_info['createtag']: + rev = 0 + else: + return False + elif info['rev'] < rev and not force_flag: + return False + + if commit_info['deltag']: + self.del_blob(blobname) + clients = set() + for needed_client, method in ( + self._containers[info['container']].items() + ): + if method == "ACTIVE": + clients.add(needed_client) + clients.discard(client) + self.send_update(clients, blobname, None, False) + result = True + else: + info['rev'] = rev + 1 + if commit_info['written']: + status = self.recv_blob(filekey, blobname, rev + 1) + result = status.wait() + if rev: + self.del_real_blob(''.join([blobname, '_', str(rev)])) + else: + result = True + if result: + self.update_blob(blobname, info) + clients = set() + for needed_client, method in ( + self._containers[info['container']].items()): + if method == "ACTIVE": + clients.add(needed_client) + clients.discard(client) + self.send_update(clients, blobname, + info, commit_info['written']) + + return True + else: + return False + + + +################### Testing Code ####################### +''' +if __name__ == '__main__': + global blob_serv + + blob_serv = BlobServer() + blob_serv.listen(5730) + + #http_serv = tornado.httpserver.HTTPServer(tornado.web.Application([ + # ('/conn',WebConnHandler), + #])) + #http_serv.listen(83) + + tornado.ioloop.IOLoop.instance().start() +''' diff --git a/src/py/square.py b/src/py/square.py index e00c12b..bf16621 100644 --- a/src/py/square.py +++ b/src/py/square.py @@ -49,6 +49,10 @@ class SquareMg: 'core/square/', 'delete_square', self.delete_square) Proxy.instance.register_call( 'core/square/', 'set_square', self.imc_set_square) + Proxy.instance.register_call( + 'core/square/', 'get_square_info', self.get_square_info) + Proxy.instance.register_call( + 'core/square/', 'list_sqmod', self.list_sqmod) def unload(self): Proxy.instance.unregister_call( @@ -65,6 +69,10 @@ class SquareMg: 'core/square/', 'delete_square') Proxy.instance.unregister_call( 'core/square/', 'set_square') + Proxy.instance.unregister_call( + 'core/square/', 'get_square_info') + Proxy.instance.unregister_call( + 'core/square/', 'list_sqmod') @TOJAuth.check_access(_accessid, TOJAuth.ACCESS_EXECUTE) def load_square(self, sqid): @@ -292,11 +300,11 @@ class SquareMg: uid = mod.UserMg.get_current_uid() - ret = self._list_square_category(cateid, uid) + ret = self._list_square(cateid, uid) return ret - def _list_square_category(self, cateid, uid): + def _list_square(self, cateid, uid): cur = self.db.cursor() sqlsel = ('SELECT "SQUARE"."sqid", "title", "start_time", "end_time", ' '"hidden", "sqmodid", "intro", "logo", "cateid"') @@ -356,6 +364,20 @@ class SquareMg: return ret @imc.async.caller + def get_square_info(self, sqid): + if( + type(sqid) != int + ): + return 'Eparameter' + + ret = self.get_square_info_by_sqid(sqid) + + if ret == None: + return 'Esqid' + + return ret + + @imc.async.caller def create_group(): pass @@ -438,6 +460,23 @@ class SquareMg: return cate_list @imc.async.caller + def list_sqmod(self): + cur = self.db.cursor() + sqlstr = ('SELECT "sqmodid", "sqmodname", "info" FROM "SQMOD" ' + 'ORDER BY "sqmodid" ASC;') + cur.execute(sqlstr) + + sqmod_list = [] + for data in cur: + obj = {} + obj['sqmodid'] = data[0] + obj['sqmodname'] = data[1] + obj['info'] = data[2] + sqmod_list.append(obj) + + return sqmod_list + + @imc.async.caller def join_square(self, sqid): if( type(sqid) != int @@ -540,6 +579,8 @@ class SquareMg: if 0 in ret['cateid']: ret['cateid'] = [] + ret['sqmodname'] = self.get_sqmodname_by_sqmodid(ret['sqmodid']) + return ret def does_sqid_exist(self, sqid): |