class TestReviewController(MnemosyneTest): def setup(self): global expected_scheduled_count expected_scheduled_count = None self.initialise_data_dir() path = os.path.join(os.getcwd(), "..", "mnemosyne", "libmnemosyne", "renderers") if path not in sys.path: sys.path.append(path) self.mnemosyne = Mnemosyne(upload_science_logs=False, interested_in_old_reps=True, asynchronous_database=True) self.mnemosyne.components.insert( 0, ("mnemosyne.libmnemosyne.gui_translators.gettext_gui_translator", "GetTextGuiTranslator")) self.mnemosyne.components.append(\ ("mnemosyne.libmnemosyne.ui_components.main_widget", "MainWidget")) self.mnemosyne.gui_for_component["ScheduledForgottenNew"] = \ [("test_review_controller", "MyReviewWidget")] self.mnemosyne.components.append(\ ("mnemosyne.libmnemosyne.ui_components.dialogs", "EditCardDialog")) self.mnemosyne.initialise(os.path.abspath("dot_test"), automatic_upgrades=False) self.review_controller().reset() def test_1(self): card_1 = None self.review_controller().reset() for i in range(10): fact_data = {"f": "question" + str(i), "b": "answer" + str(i)} if i % 2: card_type = self.card_type_with_id("1") else: card_type = self.card_type_with_id("2") card = self.controller().create_new_cards(fact_data, card_type, grade=4, tag_names=[ "default" + str(i) ])[0] if i == 0: card_1 = card card.next_rep -= 1000 * 24 * 60 * 60 self.database().update_card(card) self.review_controller().set_render_chain("default") self.review_controller().show_new_question() self.review_controller().reset_but_try_to_keep_current_card() self.review_controller().show_answer() assert self.review_controller().card == card_1 assert self.review_controller().counters() == (1, 0, 15) self.review_controller().grade_answer(0) assert self.review_controller().counters() == (0, 1, 15) self.review_controller().grade_answer(2) assert self.review_controller().counters() == (0, 0, 15) self.review_controller().next_rep_string(0) self.review_controller().next_rep_string(1) self.review_controller().next_rep_string(2) def test_2(self): card_1 = None self.review_controller().reset() for i in range(10): fact_data = {"f": "question" + str(i), "b": "answer" + str(i)} if i % 2: card_type = self.card_type_with_id("1") else: card_type = self.card_type_with_id("2") card = self.controller().create_new_cards(fact_data, card_type, grade=4, tag_names=[ "default" + str(i) ])[0] if i == 0: card_1 = card card.next_rep -= 1000 * 24 * 60 * 60 self.database().update_card(card) self.review_controller().show_new_question() assert self.review_controller().card == card_1 self.review_controller().reload_counters() assert self.review_controller().counters() == (1, 0, 15) self.review_controller().grade_answer(0) self.review_controller().reload_counters() assert self.review_controller().counters() == (0, 1, 15) self.review_controller().grade_answer(2) self.review_controller().reload_counters() assert self.review_controller().counters() == (0, 0, 15) self.mnemosyne.review_widget().set_grade_enabled(1, True) def test_reset_but_try_to_keep_current_card_turned_inactive(self): card_type = self.card_type_with_id("1") fact_data = {"f": "1", "b": "b"} card = self.controller().create_new_cards(fact_data, card_type, grade=-1, tag_names=["forbidden"])[0] self.review_controller().show_new_question() assert self.review_controller().card == card c = DefaultCriterion(self.mnemosyne.component_manager) c.deactivated_card_type_fact_view_ids = set() c._tag_ids_active = set( [self.database().get_or_create_tag_with_name("active")._id]) c._tag_ids_forbidden = set( [self.database().get_or_create_tag_with_name("forbidden")._id]) self.database().set_current_criterion(c) assert self.database().active_count() == 0 assert self.review_controller().card == card self.review_controller().reset_but_try_to_keep_current_card() assert self.review_controller().card is None def test_last_card(self): card_type = self.card_type_with_id("1") for data in ["1", "2", "3"]: fact_data = {"f": data, "b": data} self.controller().create_new_cards(fact_data, card_type, grade=-1, tag_names=[]) self.review_controller().show_new_question() self.review_controller().show_answer() for i in range(5): self.review_controller().grade_answer(0) self.review_controller().show_answer() for i in range(2): self.review_controller().grade_answer(2) self.review_controller().show_answer() for i in range(6): self.review_controller().grade_answer(0) self.review_controller().show_answer() def test_counters(self): global expected_scheduled_count card_type = self.card_type_with_id("1") fact_data = {"f": '1', "b": '1'} card = self.controller().create_new_cards(fact_data, card_type, grade=5, tag_names=[])[0] card.next_rep = 0 self.database().update_card(card) expected_scheduled_count = 1 self.review_controller().show_new_question() assert self.review_controller().scheduled_count == 1 assert self.review_controller().counters()[0] == 1 self.review_controller().show_answer() expected_scheduled_count = 0 self.review_controller().grade_answer(0) assert self.review_controller().scheduled_count == 0 assert self.review_controller().counters()[0] == 0 def test_counters_prefetch(self): global expected_scheduled_count card_type = self.card_type_with_id("1") for data in ['1', '2', '3', '4']: fact_data = {"f": data, "b": data} card = self.controller().create_new_cards(fact_data, card_type, grade=5, tag_names=[])[0] card.next_rep = 0 self.database().update_card(card) expected_scheduled_count = 4 self.review_controller().show_new_question() assert self.review_controller().scheduled_count == 4 assert self.review_controller().counters()[0] == 4 self.review_controller().show_answer() expected_scheduled_count = 3 self.review_controller().grade_answer(3) assert self.review_controller().scheduled_count == 3 assert self.review_controller().counters()[0] == 3
class WebServer(Component): def __init__(self, port, data_dir, config_dir, filename, **kwds): if "client_on_same_machine_as_server" in kwds: self.client_on_same_machine_as_server = \ kwds["client_on_same_machine_as_server"] del kwds["client_on_same_machine_as_server"] else: self.client_on_same_machine_as_server = False super().__init__(**kwds) self.wsgi_server = None self.port = port self.data_dir = data_dir self.config_dir = config_dir self.filename = filename # When restarting the server, make sure we discard info from the # browser resending the form from the previous session. self.is_just_started = True self.is_mnemosyne_loaded = False self.is_shutting_down = False def activate(self): Component.activate(self) # Late import to speed up application startup. from cheroot import wsgi self.wsgi_server = wsgi.Server(\ ("0.0.0.0", self.port), self.wsgi_app, server_name="localhost", numthreads=1, timeout=5) # We need to set the timeout relatively low, otherwise it will take # too long for the server to process a 'stop' request. def serve_until_stopped(self): try: self.wsgi_server.start() # Sets self.wsgi_server.ready except KeyboardInterrupt: self.wsgi_server.stop() self.unload_mnemosyne() def stop(self): if self.wsgi_server: self.wsgi_server.stop() self.unload_mnemosyne() def load_mnemosyne(self): self.mnemosyne = Mnemosyne(upload_science_logs=True, interested_in_old_reps=True) self.mnemosyne.components.insert( 0, (("mnemosyne.libmnemosyne.gui_translators.gettext_gui_translator", "GetTextGuiTranslator"))) self.mnemosyne.components.append(\ ("mnemosyne.libmnemosyne.ui_components.main_widget", "MainWidget")) self.mnemosyne.components.append(\ ("mnemosyne.web_server.web_server_render_chain", "WebServerRenderChain")) self.mnemosyne.gui_for_component["ScheduledForgottenNew"] = [\ ("mnemosyne.web_server.review_wdgt", "ReviewWdgt")] self.mnemosyne.gui_for_component["NewOnly"] = [\ ("mnemosyne.web_server.review_wdgt", "ReviewWdgt")] self.mnemosyne.gui_for_component["CramAll"] = [\ ("mnemosyne.web_server.review_wdgt", "ReviewWdgt")] self.mnemosyne.gui_for_component["CramRecent"] = [\ ("mnemosyne.web_server.review_wdgt", "ReviewWdgt")] self.mnemosyne.initialise(self.data_dir, config_dir=self.config_dir, filename=self.filename, automatic_upgrades=False) self.save_after_n_reps = self.mnemosyne.config()["save_after_n_reps"] self.mnemosyne.config()["save_after_n_reps"] = 1 self.mnemosyne.config()["study_mode"] = "ScheduledForgottenNew" self.mnemosyne.config()["QA_split"] = "fixed" self.mnemosyne.review_widget().set_client_on_same_machine_as_server(\ self.client_on_same_machine_as_server) self.mnemosyne.controller().reset_study_mode() self.is_mnemosyne_loaded = True self.release_database_after_timeout = \ ReleaseDatabaseAfterTimeout(self.port) self.release_database_after_timeout.start() def unload_mnemosyne(self): if not self.is_mnemosyne_loaded: return self.mnemosyne.config()["save_after_n_reps"] = self.save_after_n_reps self.mnemosyne.finalise() self.is_mnemosyne_loaded = False def wsgi_app(self, environ, start_response): filename = environ["PATH_INFO"] if filename == "/status": response_headers = [("Content-type", "text/html")] start_response("200 OK", response_headers) return [b"200 OK"] # Sometimes, even after the user has clicked 'exit' in the page, # a browser sends a request for e.g. an audio file. if self.is_shutting_down and filename != "/release_database": response_headers = [("Content-type", "text/html")] start_response("503 Service Unavailable", response_headers) return [b"Server stopped"] # Load database if needed. if not self.is_mnemosyne_loaded and filename != "/release_database": self.load_mnemosyne() self.release_database_after_timeout.ping() # All our request return to the root page, so if the path is '/', # return the html of the review widget. if filename == "/": # Process clicked buttons in the form. form = cgi.FieldStorage(fp=environ["wsgi.input"], environ=environ) if "show_answer" in form and not self.is_just_started: self.mnemosyne.review_widget().show_answer() page = self.mnemosyne.review_widget().to_html() elif "grade" in form and not self.is_just_started: grade = int(form["grade"].value) self.mnemosyne.review_widget().grade_answer(grade) page = self.mnemosyne.review_widget().to_html() elif "star" in form: self.mnemosyne.controller().star_current_card() page = self.mnemosyne.review_widget().to_html() elif "exit" in form: self.unload_mnemosyne() page = "Server stopped" self.wsgi_server.stop() self.stop_server_after_timeout = \ StopServerAfterTimeout(self.wsgi_server) self.stop_server_after_timeout.start() self.is_shutting_down = True else: page = self.mnemosyne.review_widget().to_html() if self.is_just_started: self.is_just_started = False # Serve the web page. response_headers = [("Content-type", "text/html")] start_response("200 OK", response_headers) return [page] elif filename == "/release_database": self.unload_mnemosyne() response_headers = [("Content-type", "text/html")] start_response("200 OK", response_headers) return [b"200 OK"] # We need to serve a media file. else: # Late import to speed up application startup. from webob import Request from webob.static import FileApp full_path = self.mnemosyne.database().media_dir() for word in filename.split("/"): full_path = os.path.join(full_path, word) request = Request(environ) if os.path.exists(full_path): etag = "%s-%s-%s" % (os.path.getmtime(full_path), os.path.getsize(full_path), hash(full_path)) else: etag = "none" app = FileApp(full_path, etag=etag) return app(request)(environ, start_response)
class WebServer(Component): def __init__(self, component_manager, port, data_dir, config_dir, filename, is_server_local=False): Component.__init__(self, component_manager) self.port = port self.data_dir = data_dir self.config_dir = config_dir self.filename = filename self.is_server_local = is_server_local # When restarting the server, make sure we discard info from the # browser resending the form from the previous session. self.is_just_started = True self.is_mnemosyne_loaded = False self.is_shutting_down = False self.wsgi_server = wsgiserver.CherryPyWSGIServer(\ ("0.0.0.0", port), self.wsgi_app, server_name="localhost", numthreads=1, timeout=5) # We need to set the timeout relatively low, otherwise it will take # too long for the server to process a 'stop' request. def serve_until_stopped(self): try: self.wsgi_server.start() # Sets self.wsgi_server.ready except KeyboardInterrupt: self.wsgi_server.stop() self.unload_mnemosyne() def stop(self): self.wsgi_server.stop() self.unload_mnemosyne() def load_mnemosyne(self): self.mnemosyne = Mnemosyne(upload_science_logs=True, interested_in_old_reps=True) self.mnemosyne.components.insert(0, ( ("mnemosyne.libmnemosyne.translators.gettext_translator", "GetTextTranslator"))) self.mnemosyne.components.append(\ ("mnemosyne.libmnemosyne.ui_components.main_widget", "MainWidget")) self.mnemosyne.components.append(\ ("mnemosyne.web_server.review_wdgt", "ReviewWdgt")) self.mnemosyne.components.append(\ ("mnemosyne.web_server.web_server_render_chain", "WebServerRenderChain")) self.mnemosyne.initialise(self.data_dir, config_dir=self.config_dir, filename=self.filename, automatic_upgrades=False) self.mnemosyne.review_controller().set_render_chain("web_server") self.save_after_n_reps = self.mnemosyne.config()["save_after_n_reps"] self.mnemosyne.config()["save_after_n_reps"] = 1 self.mnemosyne.start_review() self.mnemosyne.review_widget().set_is_server_local(\ self.is_server_local) self.is_mnemosyne_loaded = True self.release_database_after_timeout = \ ReleaseDatabaseAfterTimeout(self.port) self.release_database_after_timeout.start() def unload_mnemosyne(self): if not self.is_mnemosyne_loaded: return self.mnemosyne.config()["save_after_n_reps"] = self.save_after_n_reps self.mnemosyne.finalise() self.is_mnemosyne_loaded = False def wsgi_app(self, environ, start_response): filename = environ["PATH_INFO"].decode("utf-8") if filename == "/status": response_headers = [("Content-type", "text/html")] start_response("200 OK", response_headers) return ["200 OK"] # Sometimes, even after the user has clicked 'exit' in the page, # a browser sends a request for e.g. an audio file. if self.is_shutting_down and filename != "/release_database": response_headers = [("Content-type", "text/html")] start_response("503 Service Unavailable", response_headers) return ["Server stopped"] # Load database if needed. if not self.is_mnemosyne_loaded and filename != "/release_database": self.load_mnemosyne() self.release_database_after_timeout.ping() # All our request return to the root page, so if the path is '/', # return the html of the review widget. if filename == "/": # Process clicked buttons in the form. form = cgi.FieldStorage(fp=environ["wsgi.input"], environ=environ) if "show_answer" in form and not self.is_just_started: self.mnemosyne.review_widget().show_answer() page = self.mnemosyne.review_widget().to_html() elif "grade" in form and not self.is_just_started: grade = int(form["grade"].value) self.mnemosyne.review_widget().grade_answer(grade) page = self.mnemosyne.review_widget().to_html() elif "star" in form: self.mnemosyne.controller().star_current_card() page = self.mnemosyne.review_widget().to_html() elif "exit" in form: self.unload_mnemosyne() page = "Server stopped" self.wsgi_server.stop() self.stop_server_after_timeout = \ StopServerAfterTimeout(self.wsgi_server) self.stop_server_after_timeout.start() self.is_shutting_down = True else: page = self.mnemosyne.review_widget().to_html() if self.is_just_started: self.is_just_started = False # Serve the web page. response_headers = [("Content-type", "text/html")] start_response("200 OK", response_headers) return [page] elif filename == "/release_database": self.unload_mnemosyne() response_headers = [("Content-type", "text/html")] start_response("200 OK", response_headers) return ["200 OK"] # We need to serve a media file. else: full_path = self.mnemosyne.database().media_dir() for word in filename.split("/"): full_path = os.path.join(full_path, word) request = Request(environ) # Check if file exists, but work around Android not reporting # the correct filesystem encoding. try: exists = os.path.exists(full_path) except (UnicodeEncodeError, UnicodeDecodeError): _ENCODING = sys.getfilesystemencoding() or \ locale.getdefaultlocale()[1] or "utf-8" full_path = full_path.encode(_ENCODING) if os.path.exists(full_path): etag = "%s-%s-%s" % (os.path.getmtime(full_path), os.path.getsize(full_path), hash(full_path)) else: etag = "none" app = FileApp(full_path, etag=etag) return app(request)(environ, start_response)
class TestReviewController(MnemosyneTest): def setup(self): global expected_scheduled_count expected_scheduled_count = None self.initialise_data_dir() self.mnemosyne = Mnemosyne(upload_science_logs=False, interested_in_old_reps=True, asynchronous_database=True) self.mnemosyne.components.insert( 0, ("mnemosyne.libmnemosyne.translators.gettext_translator", "GetTextTranslator") ) self.mnemosyne.components.append(("mnemosyne.libmnemosyne.ui_components.main_widget", "MainWidget")) self.mnemosyne.components.append(("test_review_controller", "MyReviewWidget")) self.mnemosyne.components.append(("mnemosyne.libmnemosyne.ui_components.dialogs", "EditCardDialog")) self.mnemosyne.initialise(os.path.abspath("dot_test"), automatic_upgrades=False) self.review_controller().reset() def test_1(self): card_1 = None self.review_controller().reset() for i in range(10): fact_data = {"f": "question" + str(i), "b": "answer" + str(i)} if i % 2: card_type = self.card_type_with_id("1") else: card_type = self.card_type_with_id("2") card = self.controller().create_new_cards(fact_data, card_type, grade=4, tag_names=["default" + str(i)])[0] if i == 0: card_1 = card card.next_rep -= 1000 * 24 * 60 * 60 self.database().update_card(card) self.review_controller().set_render_chain("default") self.review_controller().show_new_question() self.review_controller().reset_but_try_to_keep_current_card() self.review_controller().show_answer() assert self.review_controller().card == card_1 assert self.review_controller().counters() == (1, 0, 15) self.review_controller().grade_answer(0) assert self.review_controller().counters() == (0, 1, 15) self.review_controller().grade_answer(2) assert self.review_controller().counters() == (0, 0, 15) self.review_controller().next_rep_string(0) self.review_controller().next_rep_string(1) self.review_controller().next_rep_string(2) def test_2(self): card_1 = None self.review_controller().reset() for i in range(10): fact_data = {"f": "question" + str(i), "b": "answer" + str(i)} if i % 2: card_type = self.card_type_with_id("1") else: card_type = self.card_type_with_id("2") card = self.controller().create_new_cards(fact_data, card_type, grade=4, tag_names=["default" + str(i)])[0] if i == 0: card_1 = card card.next_rep -= 1000 * 24 * 60 * 60 self.database().update_card(card) self.review_controller().show_new_question() assert self.review_controller().card == card_1 self.review_controller().reload_counters() assert self.review_controller().counters() == (1, 0, 15) self.review_controller().grade_answer(0) self.review_controller().reload_counters() assert self.review_controller().counters() == (0, 1, 15) self.review_controller().grade_answer(2) self.review_controller().reload_counters() assert self.review_controller().counters() == (0, 0, 15) self.mnemosyne.review_widget().set_grade_enabled(1, True) def test_reset_but_try_to_keep_current_card_turned_inactive(self): card_type = self.card_type_with_id("1") fact_data = {"f": "1", "b": "b"} card = self.controller().create_new_cards(fact_data, card_type, grade=-1, tag_names=["forbidden"])[0] self.review_controller().show_new_question() assert self.review_controller().card == card c = DefaultCriterion(self.mnemosyne.component_manager) c.deactivated_card_type_fact_view_ids = set() c._tag_ids_active = set([self.database().get_or_create_tag_with_name("active")._id]) c._tag_ids_forbidden = set([self.database().get_or_create_tag_with_name("forbidden")._id]) self.database().set_current_criterion(c) assert self.database().active_count() == 0 assert self.review_controller().card == card self.review_controller().reset_but_try_to_keep_current_card() assert self.review_controller().card is None def test_last_card(self): card_type = self.card_type_with_id("1") for data in ["1", "2", "3"]: fact_data = {"f": data, "b": data} self.controller().create_new_cards(fact_data, card_type, grade=-1, tag_names=[]) self.review_controller().show_new_question() self.review_controller().show_answer() for i in range(5): self.review_controller().grade_answer(0) self.review_controller().show_answer() for i in range(2): self.review_controller().grade_answer(2) self.review_controller().show_answer() for i in range(6): self.review_controller().grade_answer(0) self.review_controller().show_answer() def test_counters(self): global expected_scheduled_count card_type = self.card_type_with_id("1") fact_data = {"f": "1", "b": "1"} card = self.controller().create_new_cards(fact_data, card_type, grade=5, tag_names=[])[0] card.next_rep = 0 self.database().update_card(card) expected_scheduled_count = 1 self.review_controller().show_new_question() assert self.review_controller().scheduled_count == 1 assert self.review_controller().counters()[0] == 1 self.review_controller().show_answer() expected_scheduled_count = 0 self.review_controller().grade_answer(0) assert self.review_controller().scheduled_count == 0 assert self.review_controller().counters()[0] == 0 def test_counters_prefetch(self): global expected_scheduled_count card_type = self.card_type_with_id("1") for data in ["1", "2", "3", "4"]: fact_data = {"f": data, "b": data} card = self.controller().create_new_cards(fact_data, card_type, grade=5, tag_names=[])[0] card.next_rep = 0 self.database().update_card(card) expected_scheduled_count = 4 self.review_controller().show_new_question() assert self.review_controller().scheduled_count == 4 assert self.review_controller().counters()[0] == 4 self.review_controller().show_answer() expected_scheduled_count = 3 self.review_controller().grade_answer(3) assert self.review_controller().scheduled_count == 3 assert self.review_controller().counters()[0] == 3
class WebServer(Component): def __init__(self, component_manager, port, data_dir, config_dir, filename, is_server_local=False): Component.__init__(self, component_manager) self.port = port self.data_dir = data_dir self.config_dir = config_dir self.filename = filename self.is_server_local = is_server_local # When restarting the server, make sure we discard info from the # browser resending the form from the previous session. self.is_just_started = True self.is_mnemosyne_loaded = False self.is_shutting_down = False self.wsgi_server = wsgiserver.CherryPyWSGIServer(\ ("0.0.0.0", port), self.wsgi_app, server_name="localhost", numthreads=1, timeout=5) # We need to set the timeout relatively low, otherwise it will take # too long for the server to process a 'stop' request. def serve_until_stopped(self): try: self.wsgi_server.start() # Sets self.wsgi_server.ready except KeyboardInterrupt: self.wsgi_server.stop() self.unload_mnemosyne() def stop(self): self.wsgi_server.stop() self.unload_mnemosyne() def load_mnemosyne(self): self.mnemosyne = Mnemosyne(upload_science_logs=True, interested_in_old_reps=True) self.mnemosyne.components.insert( 0, (("mnemosyne.libmnemosyne.translators.gettext_translator", "GetTextTranslator"))) self.mnemosyne.components.append(\ ("mnemosyne.libmnemosyne.ui_components.main_widget", "MainWidget")) self.mnemosyne.components.append(\ ("mnemosyne.web_server.review_wdgt", "ReviewWdgt")) self.mnemosyne.components.append(\ ("mnemosyne.web_server.web_server_render_chain", "WebServerRenderChain")) self.mnemosyne.initialise(self.data_dir, config_dir=self.config_dir, filename=self.filename, automatic_upgrades=False) self.mnemosyne.review_controller().set_render_chain("web_server") self.save_after_n_reps = self.mnemosyne.config()["save_after_n_reps"] self.mnemosyne.config()["save_after_n_reps"] = 1 self.mnemosyne.start_review() self.mnemosyne.review_widget().set_is_server_local(\ self.is_server_local) self.is_mnemosyne_loaded = True self.release_database_after_timeout = \ ReleaseDatabaseAfterTimeout(self.port) self.release_database_after_timeout.start() def unload_mnemosyne(self): if not self.is_mnemosyne_loaded: return self.mnemosyne.config()["save_after_n_reps"] = self.save_after_n_reps self.mnemosyne.finalise() self.is_mnemosyne_loaded = False def wsgi_app(self, environ, start_response): filename = environ["PATH_INFO"].decode("utf-8") if filename == "/status": response_headers = [("Content-type", "text/html")] start_response("200 OK", response_headers) return ["200 OK"] # Sometimes, even after the user has clicked 'exit' in the page, # a browser sends a request for e.g. an audio file. if self.is_shutting_down and filename != "/release_database": response_headers = [("Content-type", "text/html")] start_response("503 Service Unavailable", response_headers) return ["Server stopped"] # Load database if needed. if not self.is_mnemosyne_loaded and filename != "/release_database": self.load_mnemosyne() self.release_database_after_timeout.ping() # All our request return to the root page, so if the path is '/', # return the html of the review widget. if filename == "/": # Process clicked buttons in the form. form = cgi.FieldStorage(fp=environ["wsgi.input"], environ=environ) if "show_answer" in form and not self.is_just_started: self.mnemosyne.review_widget().show_answer() page = self.mnemosyne.review_widget().to_html() elif "grade" in form and not self.is_just_started: grade = int(form["grade"].value) self.mnemosyne.review_widget().grade_answer(grade) page = self.mnemosyne.review_widget().to_html() elif "star" in form: self.mnemosyne.controller().star_current_card() page = self.mnemosyne.review_widget().to_html() elif "exit" in form: self.unload_mnemosyne() page = "Server stopped" self.wsgi_server.stop() self.stop_server_after_timeout = \ StopServerAfterTimeout(self.wsgi_server) self.stop_server_after_timeout.start() self.is_shutting_down = True else: page = self.mnemosyne.review_widget().to_html() if self.is_just_started: self.is_just_started = False # Serve the web page. response_headers = [("Content-type", "text/html")] start_response("200 OK", response_headers) return [page] elif filename == "/release_database": self.unload_mnemosyne() response_headers = [("Content-type", "text/html")] start_response("200 OK", response_headers) return ["200 OK"] # We need to serve a media file. else: full_path = self.mnemosyne.database().media_dir() for word in filename.split("/"): full_path = os.path.join(full_path, word) request = Request(environ) # Check if file exists, but work around Android not reporting # the correct filesystem encoding. try: exists = os.path.exists(full_path) except (UnicodeEncodeError, UnicodeDecodeError): _ENCODING = sys.getfilesystemencoding() or \ locale.getdefaultlocale()[1] or "utf-8" full_path = full_path.encode(_ENCODING) if os.path.exists(full_path): etag = "%s-%s-%s" % (os.path.getmtime(full_path), os.path.getsize(full_path), hash(full_path)) else: etag = "none" app = FileApp(full_path, etag=etag) return app(request)(environ, start_response)