class Handler: """Bộ điều khiển truy vấn và xác thực qua API. Các API key được quản lý qua công cụ api_manager trong gói admin kèm trong mã nguồn. Theo mặc định, API key chỉ có duy nhất 1 và không giới hạn lượt truy cập và độ trễ giữa mỗi lần truy câp. """ def __init__(self): self.m = URIMapper() self.error = Error() def _parse_request(self, path): try: info = path.split(':') request = info[1][:-1].replace("'&", '"&') # Normalize params = {} for keyword in request.split('"&'): pair = keyword.split("=") params[pair[0]] = urllib.unquote(pair[1][1:]) return params except: return 400 # Bad request def request_handler(self, environ, start_response): try: # check method if environ['REQUEST_METHOD'] != 'GET': info = '<error status_code="405" \ description="Phương thức này không được hệ thống hỗ trợ"/>' response_headers = [('Content-type', 'text/xml'), ('Content-Length', str(len(info)))] start_response(status, response_headers) return [info] #-- get uri -- path = environ['PATH_INFO'].lstrip('/') path = urllib.unquote(path) # some tweak :-) if path == 'favicon.ico': response_headers = [('Content-type', 'text/xml'), ('Content-Length', 0)] start_response(status, response_headers) return [''] if path.startswith('about'): info = """<about author="AloneRoad" full_name="Phạm Tuấn Anh" email="*****@*****.**" mobile_phone="+84 167 345 0 799" message="Cảm ơn vì đã quan tâm ⍣ "/>""" response_headers = [('Content-type', 'text/xml'), ('Content-Length', str(len(info)))] start_response(status, response_headers) return [info] #------ for documents------ elif path == '': # return api developer's guide doc = open(os.path.join(os.path.dirname(__file__), 'doc/build/html/index.html')).read() response_headers = [('Content-type', 'text/html'), ('Content-Length', str(len(doc)))] start_response(status, response_headers) return [doc] elif path in ['_static/nature.css', '_static/pygments.css', '_static/basic.css']: doc = open(os.path.join(os.path.dirname(__file__), 'doc/build/html/', path)).read() response_headers = [('Content-type', 'text/css'), ('Content-Length', str(len(doc)))] start_response(status, response_headers) return [doc] elif path in ['_static/jquery.js', '_static/doctools.js', '_static/jquery.js']: doc = open(os.path.join(os.path.dirname(__file__), 'doc/build/html/', path)).read() response_headers = [('Content-type', 'text/javascript'), ('Content-Length', str(len(doc)))] start_response(status, response_headers) return [doc] #----- end developer's guide ----- #----- get remote ip ----- ip_request = environ['REMOTE_ADDR'] ip_key = 'api::address="%s"?last_request' % ip_request _time = datetime.now() last_request = API_DB.get(ip_key) # get info if last_request: last_request = datetime.strptime(last_request, '%Y-%m-%d %H:%M:%S.%f') else: last_request = _time API_DB.set(ip_key, str(_time)) # set new info #----- Parse request ----- _params = self._parse_request(path) if _params == 400: xml = """<error status_code="400" description="Yêu cầu không hợp lệ"/>""" xml = __description__ % xml xml = pretty_xml(xml) response_headers = [('Content-type', 'text/xml'), ('Content-Length', str(len(xml)))] start_response(status, response_headers) return [xml] else: try: key = 'api::api_key="%s"?last_call' % (_params['api_key']) except KeyError: xml = self.error.authen_error("Thiếu api_key") xml = __description__ % xml xml = pretty_xml(xml) response_headers = [('Content-type', 'text/xml'), ('Content-Length', str(len(xml)))] start_response(status, response_headers) return [xml] API_DB.set(key, _time) total_key = 'api::api_key="%s"?total_request_per_day' % (_params['api_key']) total = API_DB.get(total_key) if total is None: xml = """<error status_code="401" description="api_key không hợp lệ"/>""" xml = __description__ % xml xml = pretty_xml(xml) response_headers = [('Content-type', 'text/xml'), ('Content-Length', str(len(xml)))] start_response(status, response_headers) return [xml] delay_key = 'api::api_key="%s"?delay' % (_params['api_key']) limit = API_DB.get(delay_key) total_call_key = 'api::api_key="%s"?total_call' % (_params['api_key']) total_call = API_DB.get(total_call_key) delta = _time - last_request if delta.days != 0: API_DB.set(total_call_key, 0) # Check sum requests if int(total_call) >= int(total) and int(total) != 0: message = """ api_key này chỉ được phép sử dụng %s lần một ngày """ % total xml = """ <error status_code="403" description="%s"/> """ % message xml = __description__ % xml xml = pretty_xml(xml) response_headers = [('Content-type', 'text/xml'), ('Content-Length', str(len(xml)))] start_response(status, response_headers) return [xml] # Limit time per request elif (delta.seconds * 1e6 + delta.microseconds) < limit: message = """ Khoảng thời gian tối thiểu giữa 2 lần request phải lớn hơn %s mili-giây """ % (limit / 1000) xml = """ <error status_code="403" description="%s"/> """ % message xml = __description__ % xml xml = pretty_xml(xml) response_headers = [('Content-type', 'text/xml'), ('Content-Length', str(len(xml)))] start_response(status, response_headers) return [xml] else: # accept request API_DB.set(total_call_key, int(total_call) + 1) # incr total call environ['request'] = self._parse_request(path) xml = self.m.execute(environ) xml = __description__ % xml xml = pretty_xml(xml) response_headers = [('Content-type', 'text/xml'), ('Content-Length', str(len(xml)))] start_response(status, response_headers) return [xml] # Debug off | (on when KeyboardInterrupt) except KeyboardInterrupt: # pylint: disable-msg=W0702 message = """ Hệ thống gặp một lỗi chưa rõ nguyên nhân. Vui lòng báo lại cho bộ phận quản trị nếu gặp thông báo này """ xml = """ <error status_code="500" description="%s"/> """ % message xml = __description__ % xml response_headers = [('Content-type', 'text/xml'), ('Content-Length', str(len(xml)))] start_response(status, response_headers) return [xml]
class Course: def __init__(self): self.cache = KeyValueDatabase() client = connect(cassandra_hosts) self.u = ColumnFamily(client, cassandra_keyspace, 'Users', super=True) self.error = Error() def refresh(self): self.cache.flush() def _levels(self, lang='en'): key = 'levels_%s' % (lang) results = self.cache.get(key) if results: return results try: parent_folder = unicode(os.path.join(data_folder, lang)) _levels = os.listdir(parent_folder) if _levels == []: return self.error.not_found("Chưa có dữ liệu") results = [] # get only folder names for level in _levels: if os.path.isdir(os.path.join(parent_folder, level)): results.append(level) results = sorted(results) self.cache.set(key, results) return results except OSError: return 404 def _lessons(self, level, lang='en'): key = 'lessons_%s_%s' % (level, lang) results = self.cache.get(key) if results: return results try: parent_folder = unicode(os.path.join(data_folder, lang)) parent_folder = os.path.join(parent_folder, level) _lessons = os.listdir(parent_folder) if _lessons == []: return self.error.not_found("Chưa có dữ liệu") results = [] for lesson in _lessons: if os.path.isdir(os.path.join(parent_folder, lesson)): results.append(lesson) results = sorted(results) self.cache.set(key, results) return results except OSError: return 404 def _listen(self, level, lesson, lang='en'): key = 'listen_%s_%s_%s' % (level, lesson, lang) results = self.cache.get(key) if results: return results try: parent_folder = unicode(os.path.join(data_folder, lang, level, lesson)) audio = os.listdir(parent_folder) if audio == []: return self.error.not_found("Chưa có dữ liệu") results = [] for i in audio: title = None description = None if i.endswith('.inf'): # add '\t' to detect end string when replace i = i + '\t' amr = os.path.join(parent_folder, i.replace('.inf\t', '.amr')) if not os.path.exists(amr): # check amr file exist return 404 i = i.replace('\t', '') # restore i # file inf chứa 2 biến là title và description cmd = open(os.path.join(parent_folder, i)) exec(cmd) # thực hiện chuỗi như là 1 lệnh info = {'title': title, 'description': description, 'audio_file': amr.replace(data_folder, '')} results.append(info) results = sorted(results) self.cache.set(key, results) return results except OSError: return 404 def levels(self, environ): try: lang = environ['request']['lang'] if lang not in ('en', 'vi'): return self.error.param_error("Hệ thống chỉ hỗ trợ 2 ngôn ngữ là en và vi") session_id = environ['request']['session_id'] except KeyError: return self.error.authen_error() try: self.u.get('session_id', super_column=session_id) except (NotFoundException, InvalidRequestException): return self.error.authen_error() results = self._levels(lang) if results == 404: return self.error.not_found("Cấu hình hệ thống sai") xml = ['<level name="%s"/>' % result for result in results] return '\n\t'.join(xml) def lessons(self, environ): try: level = environ['request']['level'] lang = environ['request']['lang'] if lang not in ('en', 'vi'): return self.error.param_error("Hệ thống chỉ hỗ trợ 2 ngôn ngữ là en và vi") session_id = environ['request']['session_id'] except KeyError: return self.error.authen_error() try: self.u.get('session_id', super_column=session_id) except (NotFoundException, InvalidRequestException): return self.error.authen_error() results = self._lessons(level, lang) if results == 404: return self.error.not_found("Cấu hình hệ thống sai") xml = ['<lesson name="%s"/>' % result for result in results] return '\n\t'.join(xml) def listen(self, environ): try: level = environ['request']['level'] lang = environ['request']['lang'] if lang not in ('en', 'vi'): return self.error.param_error("Hệ thống chỉ hỗ trợ 2 ngôn ngữ là en và vi") lesson = environ['request']['lesson'] session_id = environ['request']['session_id'] except KeyError: return self.error.authen_error() try: self.u.get('session_id', super_column=session_id) except (NotFoundException, InvalidRequestException): return self.error.authen_error() results = self._listen(level, lesson, lang) if results == 404: return self.error.not_found("Cấu hình hệ thống sai") xml = ['<audio title="%s" description="%s" link="%s"/>' \ % (x['title'], x['description'], audio_url_prefix + x['audio_file']) \ for x in results] return '\n\t'.join(xml)