From f268378826b942e735fae7c9c2fee2192bbac952 Mon Sep 17 00:00:00 2001 From: Benjamin Bach Date: Sat, 3 Oct 2015 00:23:05 +0200 Subject: [PATCH 1/6] Include docs in sdist and add Makefile for common build, dev, test --- .gitignore | 10 +- MANIFEST.in.dist | 3 + Makefile | 96 +++++++++++++++++++ circle.yml | 11 ++- .../features/steps/content_rating.py | 11 +-- .../static/js/distributed/rating/views.js | 16 ++-- .../commands/languagepackdownload.py | 23 +++-- kalite/updates/videos.py | 3 - kalitectl.py | 3 - requirements_dev.txt | 3 +- requirements_test.txt | 4 + setup.cfg | 10 +- setup.py | 52 ++++++---- 13 files changed, 186 insertions(+), 59 deletions(-) create mode 100644 Makefile diff --git a/.gitignore b/.gitignore index d9afb0c99d..12e67119b0 100644 --- a/.gitignore +++ b/.gitignore @@ -24,9 +24,6 @@ dist-packages-temp/ dist-packages-downloads/ dist-packages/ -# Generated by kalite manage setup -secretkey.txt - # User cache data generated at runtime cache/ @@ -48,8 +45,15 @@ static-updates # Python compiling *.pyc *.pyo + +# Backup files *~ + +# Sublime editor *.sublime-* + +# Ignore all .zip files!? No comment?? Needs fixing, I guess it's to avoid +# committing local assessment items. *.zip # Documentation diff --git a/MANIFEST.in.dist b/MANIFEST.in.dist index c87008e666..811c9b900c 100644 --- a/MANIFEST.in.dist +++ b/MANIFEST.in.dist @@ -27,3 +27,6 @@ recursive-include data * # Necessary because it's a data directory so they # do not get automatically excluded recursive-exclude python-packages *pyc + +# Docs +recursive-include docs/_build/html * diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..b1ed1b1e35 --- /dev/null +++ b/Makefile @@ -0,0 +1,96 @@ +.PHONY: clean-pyc clean-build docs clean + +help: + @echo "clean - remove all build, test, coverage and Python artifacts" + @echo "clean-build - remove build artifacts" + @echo "clean-pyc - remove Python file artifacts" + @echo "clean-test - remove test and coverage artifacts" + @echo "lint - check style with pep8" + @echo "test - run tests the default Python" + @echo "test-bdd - run BDD tests only" + @echo "test-nobdd - run non-BDD tests only" + @echo "assets - build all JS/CSS assets" + @echo "coverage - check code coverage quickly with the default Python" + @echo "docs - generate Sphinx HTML documentation, including API docs" + @echo "release - package and upload a release" + @echo "dist - package locally" + @echo "install - install the package to the active Python's site-packages" + +clean: clean-build clean-pyc clean-test + +clean-build: + rm -fr build/ + rm -fr dist/ + rm -fr .eggs/ + rm -fr dist-packages/ + rm -fr dist-packages-temp/ + find . -name '*.egg-info' -exec rm -fr {} + + find . -name '*.egg' -exec rm -f {} + + +clean-pyc: + find . -name '*.pyc' -exec rm -f {} + + find . -name '*.pyo' -exec rm -f {} + + find . -name '*~' -exec rm -f {} + + find . -name '__pycache__' -exec rm -fr {} + + +clean-test: + rm -fr .tox/ + rm -f .coverage + rm -fr htmlcov/ + +lint: + pep8 kalite + jshint kalite/*/static/js/*/ + +test: + bin/kalite manage test --bdd-only + +test-bdd: + bin/kalite manage test --bdd-only + +test-nobdd: + bin/kalite manage test --no-bdd + +test-all: + @echo "Not supported yet" + # tox + +coverage: + coverage run --source kalite kalitectl.py test + coverage report -m + +coverage-bdd: + coverage run --source kalite kalitectl.py test --bdd-only + coverage report -m + +coverage-nobdd: + coverage run --source kalite kalitectl.py test --no-bdd + coverage report -m + +docs: + # rm -f docs/ka-lite.rst + # rm -f docs/modules.rst + # sphinx-apidoc -o docs/ ka-lite-gtk + $(MAKE) -C docs clean + $(MAKE) -C docs html + # open docs/_build/html/index.html + +assets: clean + # Necessary because NPM may have wrong versions in the cache + npm cache clean + npm install --production + node build.js + bin/kalite manage compileymltojson + +release: clean docs assets + python setup.py sdist --formats=gztar,zip upload --sign + python setup.py sdist --formats=gztar,zip upload --sign --static + ls -l dist + +dist: clean docs assets + python setup.py sdist + python setup.py sdist --static + ls -l dist + +install: clean + python setup.py install diff --git a/circle.yml b/circle.yml index 09bdbebfa5..aeb73234ec 100644 --- a/circle.yml +++ b/circle.yml @@ -9,22 +9,25 @@ dependencies: override: - pip install -r requirements_test.txt - pip install -e . - - npm install - - npm install -g jshint + # This cannot be done because pip on Circle doesn't understand our sdist + # - make sdist + # - pip install dist/ka-lite-"$(python setup.py --version)".tar.gz post: - if [[ ! -e sc-latest-linux/bin/sc ]]; then wget https://saucelabs.com/downloads/sc-latest-linux.tar.gz && tar -xzf sc-latest-linux.tar.gz && mv sc-*-linux sc-latest-linux; fi test: override: - - node build.js + - make assets - kalite start --settings=kalite.project.settings.disk_based_cache --traceback -v2 - kalite status - kalite stop --traceback -v2 - cd sc-*-linux && ./bin/sc -u $SAUCE_USERNAME -k $SAUCE_ACCESS_KEY --tunnel-identifier $CIRCLE_BUILD_NUM-$CIRCLE_NODE_INDEX --readyfile ~/sauce_is_ready > sc_output.txt 2>&1: background: true - while [ ! -e ~/sauce_is_ready ]; do sleep 1; done - - case $CIRCLE_NODE_INDEX in 0) kalite manage test --bdd-only ;; 1) kalite manage test --no-bdd;; esac: + - case $CIRCLE_NODE_INDEX in 0) make test-bdd ;; 1) make test-nobdd;; esac: parallel: true + # TODO: replace below with "make link" when we're pep8 + - npm install -g jshint - jshint kalite/*/static/js/*/ post: - killall --wait sc # wait for Sauce Connect to close the tunnel diff --git a/kalite/distributed/features/steps/content_rating.py b/kalite/distributed/features/steps/content_rating.py index 16e4a62b03..44c0236b3c 100644 --- a/kalite/distributed/features/steps/content_rating.py +++ b/kalite/distributed/features/steps/content_rating.py @@ -11,8 +11,6 @@ from securesync.models import Device from selenium.webdriver.support.select import Select -from selenium.webdriver.support.ui import WebDriverWait - RATING_CONTAINER_ID = "rating-container" TEXT_CONTAINER_ID = "text-container" @@ -169,17 +167,16 @@ def rate_id(context, id_, val=3): :return: nothing """ - def rate_element(driver): + def get_rating_el(driver): try: container = find_id_with_wait(context, id_) els = container.find_elements_by_class_name(STAR_RATING_OPTION_CLASS) - rating_el = [el for el in els if int(el.get_attribute("data-val")) == val].pop() - rating_el.click() - return True + return [el for el in els if int(el.get_attribute("data-val")) == val].pop() except (NoSuchElementException, StaleElementReferenceException): return False - WebDriverWait(context.browser, 10).until(rate_element) + rating_el = WebDriverWait(context.browser, 10).until(get_rating_el) + rating_el.click() def enter_text_feedback(context, text_feedback): diff --git a/kalite/distributed/static/js/distributed/rating/views.js b/kalite/distributed/static/js/distributed/rating/views.js index 4021ed25ad..1c4dbe0adf 100644 --- a/kalite/distributed/static/js/distributed/rating/views.js +++ b/kalite/distributed/static/js/distributed/rating/views.js @@ -56,20 +56,18 @@ module.exports = BaseView.extend({ Then once the user has filled out the rating completely, call renderAll to allow review/editing. */ this.$el.html(this.template()); - this.$(".rating-delete").hide(); this.star_view_quality = this.add_subview(StarView, {title: gettext("Quality"), el: this.$("#star-container-quality"), model: this.model, rating_attr: "quality", label_values: this.quality_label_values}); var self = this; - var callback = function() { - self.star_view_difficulty = self.add_subview(StarView, {title: gettext("Difficulty"), el: self.$("#star-container-difficulty"), model: self.model, rating_attr: "difficulty", label_values: this.difficulty_label_values}); - self.$(".rating-delete").show(); - }; + // If the "quality" is already set, display "difficulty" immediately. Otherwise wait. - if (!this.model.get("quality") || parseInt(this.model.get("quality")) === 0) { - this.listenToOnce(this.model, "change:quality", callback); + if (parseInt(this.model.get("quality")) === 0) { + this.listenToOnce(this.model, "change:quality", function(){ + self.star_view_difficulty = self.add_subview(StarView, {title: gettext("Difficulty"), el: self.$("#star-container-difficulty"), model: self.model, rating_attr: "difficulty", label_values: this.difficulty_label_values}); + }); } else { - callback(); + self.star_view_difficulty = self.add_subview(StarView, {title: gettext("Difficulty"), el: self.$("#star-container-difficulty"), model: self.model, rating_attr: "difficulty", label_values: this.difficulty_label_values}); } this.listenToOnce(this.model, "change:difficulty", this.renderAll); @@ -167,7 +165,7 @@ var StarView = BaseView.extend({ var val = $(target).attr("data-val"); this.model.set(this.rating_attr, val); this.model.debounced_save(); - }, 100, true), + }, 500, true), mouse_enter_callback: function(ev) { // The target event could be either the .star-rating-option or a child element, so whatever the case get the diff --git a/kalite/updates/management/commands/languagepackdownload.py b/kalite/updates/management/commands/languagepackdownload.py index aed4d79ba1..e2f89eb723 100644 --- a/kalite/updates/management/commands/languagepackdownload.py +++ b/kalite/updates/management/commands/languagepackdownload.py @@ -107,7 +107,7 @@ def handle(self, *args, **options): raise def cb(self, percent): - self.update_stage(stage_percent=percent / 100.) + self.update_stage(stage_percent=percent/100.) def get_language_pack(lang_code, software_version, callback): """Download language pack for specified language""" @@ -133,14 +133,13 @@ def unpack_language(lang_code, zip_filepath=None, zip_fp=None, zip_data=None): logging.info("Unpacking new translations") ensure_dir(get_po_filepath(lang_code=lang_code)) - # # Unpack into temp dir + ## Unpack into temp dir try: z = zipfile.ZipFile(zip_fp or (zip_data and StringIO(zip_data)) or open(zip_filepath, "rb")) except zipfile.BadZipfile as e: - # Need to add more information on the errror message. + # Need to add more information on the error message. # See http://stackoverflow.com/questions/6062576/adding-information-to-a-python-exception raise type(e), type(e)(e.message + _("Language pack corrupted. Please try downloading the language pack again in a few minutes.")) - z.extractall(os.path.join(settings.USER_WRITABLE_LOCALE_DIR, lang_code)) def move_dubbed_video_map(lang_code): lang_pack_location = os.path.join(settings.USER_WRITABLE_LOCALE_DIR, lang_code) @@ -160,11 +159,17 @@ def move_dubbed_video_map(lang_code): logging.error("Error removing dubbed video directory (%s): %s" % (dubbed_video_dir, e)) def move_video_sizes_file(lang_code): - """ - This is no longer needed. See: - https://github.com/learningequality/ka-lite/issues/4538#issuecomment-144560505 - """ - return + lang_pack_location = os.path.join(settings.USER_WRITABLE_LOCALE_DIR, lang_code) + filename = os.path.basename(REMOTE_VIDEO_SIZE_FILEPATH) + src_path = os.path.join(lang_pack_location, filename) + dest_path = REMOTE_VIDEO_SIZE_FILEPATH + + # replace the old remote_video_size json + if not os.path.exists(src_path): + logging.error("Could not find videos sizes file (%s)" % src_path) + else: + logging.debug('Moving %s to %s' % (src_path, dest_path)) + shutil.move(src_path, dest_path) def move_exercises(lang_code): lang_pack_location = os.path.join(settings.USER_WRITABLE_LOCALE_DIR, lang_code) diff --git a/kalite/updates/videos.py b/kalite/updates/videos.py index b9240d9691..b336b4543b 100644 --- a/kalite/updates/videos.py +++ b/kalite/updates/videos.py @@ -6,9 +6,6 @@ from fle_utils import videos # keep access to all functions from fle_utils.general import softload_json - -# I will have nightmares from this :) -# Fixing all this in 0.16 from fle_utils.videos import * # get all into the current namespace, override some. from kalite.topic_tools import settings as topic_tools_settings diff --git a/kalitectl.py b/kalitectl.py index 98904f9d9e..2038fac8c6 100644 --- a/kalitectl.py +++ b/kalitectl.py @@ -569,8 +569,6 @@ def start(debug=False, watch=False, daemonize=True, args=[], skip_job_scheduler= except KeyboardInterrupt: # Handled in cherrypy by waiting for all threads to join pass - except SystemExit: - print("KA Lite caught system exit signal, quitting.") print("FINISHED serving HTTP") @@ -581,7 +579,6 @@ def start(debug=False, watch=False, daemonize=True, args=[], skip_job_scheduler= from fle_utils.chronograph.management.commands import cronserver_blocking cronserver_blocking.shutdown = True cron_thread.join() - print("Job scheduler terminated.") def stop(args=[], sys_exit=True): diff --git a/requirements_dev.txt b/requirements_dev.txt index e90b466b01..1a1851d6d4 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -1,4 +1,5 @@ -r requirements_test.txt werkzeug # Not sure about version django-debug-toolbar # Version was not described in previously bundled files, used by kalite.testing app -pyyaml==3.11 +pep8 +sphinx diff --git a/requirements_test.txt b/requirements_test.txt index d24bc668ec..79cc3fe319 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -8,3 +8,7 @@ hachoir-core==1.3.3 hachoir-parser==1.3.4 hachoir-metadata==1.3.3 sauceclient==0.2.1 + +# Used for building assets, which is necessary for testing +pyyaml==3.11 + diff --git a/setup.cfg b/setup.cfg index 396c126565..c0049d6ef0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,8 +6,16 @@ [metadata] description-file = README.md +# This is the one we use officially +[pep8] +ignore = E226,E302,E41,E402 +max-line-length = 160 +exclude = kalite/*/migrations/* + +# Using flake8 is also supported.. [flake8] # E124: closing bracket does not match indentation # E702: semicolon at end of line; ignored because we have `from django.conf import settings; logging = settings.LOG` lines and others ignore = E702,E124 -max-line-length = 130 +max-line-length = 160 + diff --git a/setup.py b/setup.py index 04c8824c9c..399ff18d3b 100644 --- a/setup.py +++ b/setup.py @@ -123,17 +123,26 @@ def get_installed_packages(): # site-packages directory. -def gen_data_files(*dirs): +def gen_data_files(*dirs, **kwargs): """ We can only link files, not directories. Therefore, we use an approach that scans all files to pass them to the data_files kwarg for setup(). Thanks: http://stackoverflow.com/a/7288382/405682 """ results = [] - - filter_illegal_extensions = lambda f: os.path.splitext(f)[1] != ".pyc" - + + optional = kwargs.pop('optional', False) + + def filter_illegal_extensions(f): + return os.path.splitext(f)[1] != ".pyc" + for src_dir in dirs: + if not os.path.isdir(src_dir): + if optional: + continue + else: + raise RuntimeError("{dir:s} does not exist, cannot continue".format(dir=src_dir)) + for root, dirs, files in os.walk(src_dir): results.append( ( @@ -162,6 +171,11 @@ def gen_data_files(*dirs): gen_data_files('static-libraries') ) +data_files += map( + lambda x: (os.path.join(kalite.ROOT_DATA_PATH, x[0]), x[1]), + gen_data_files('docs/_build/html', optional=True) +) + # For now, just disguise the kalitectl.py script here as it's only to be accessed # with the bin/kalite proxy. data_files += [( @@ -228,37 +242,37 @@ def run(self): # If it's a static build, we invoke pip to bundle dependencies in python-packages # This would be the case for commands "bdist" and "sdist" if STATIC_BUILD: - + manifest_content = file(os.path.join(where_am_i, 'MANIFEST.in.dist'), 'r').read() manifest_content += "\n" + "recursive-include dist-packages *\nrecursive-exclude dist-packages *pyc" file(os.path.join(where_am_i, 'MANIFEST.in'), "w").write(manifest_content) - + sys.stderr.write( "This is a static build... invoking pip to put static dependencies in " "dist-packages/\n" ) - + STATIC_DIST_PACKAGES_DOWNLOAD_CACHE = os.path.join(where_am_i, 'dist-packages-downloads') STATIC_DIST_PACKAGES_TEMP = os.path.join(where_am_i, 'dist-packages-temp') - + # Create directory where dynamically created dependencies are put if not os.path.exists(STATIC_DIST_PACKAGES_DOWNLOAD_CACHE): os.mkdir(STATIC_DIST_PACKAGES_DOWNLOAD_CACHE) - + # Should remove the temporary directory always if os.path.exists(STATIC_DIST_PACKAGES_TEMP): print("Removing previous temporary sources for pip {}".format(STATIC_DIST_PACKAGES_TEMP)) shutil.rmtree(STATIC_DIST_PACKAGES_TEMP) - + # Install from pip - + # Code modified from this example: # http://threebean.org/blog/2011/06/06/installing-from-pip-inside-python-or-a-simple-pip-api/ import pip.commands.install - + # Ensure we get output from pip enable_log_to_stdout('pip.commands.install') - + def install_distributions(distributions): command = pip.commands.install.InstallCommand() opts, ___ = command.parser.parse_args([]) @@ -273,17 +287,17 @@ def install_distributions(distributions): command.run(opts, distributions) # requirement_set.source_dir = STATIC_DIST_PACKAGES_TEMP # requirement_set.install(opts) - + # Install requirements into dist-packages if DIST_BUILDING_COMMAND: install_distributions(STATIC_REQUIREMENTS) - + # Empty the requirements.txt file # It's not a build command with --static or it's not a build command at all else: - + # If the dist-packages directory is non-empty if os.listdir(STATIC_DIST_PACKAGES): # If we are building something or running from the source @@ -302,7 +316,7 @@ def install_distributions(distributions): # everything in the requirements.txt file DIST_REQUIREMENTS = [] DIST_NAME = 'ka-lite-static' - + if "ka-lite" in get_installed_packages(): raise RuntimeError( "Already installed ka-lite so cannot install ka-lite-static. " @@ -314,7 +328,7 @@ def install_distributions(distributions): "...or other possible installation mechanisms you may have " "been using." ) - + # No dist-packages/ and not building, so must be installing the dynamic # version elif not DIST_BUILDING_COMMAND: @@ -335,7 +349,7 @@ def install_distributions(distributions): manifest_content = file(os.path.join(where_am_i, 'MANIFEST.in.dist'), 'r').read() manifest_content += "\n" + "recursive-include dist-packages *" file(os.path.join(where_am_i, 'MANIFEST.in'), "w").write(manifest_content) - + # All files from dist-packages are included if the directory exists if os.listdir(STATIC_DIST_PACKAGES): From 03b6df07999cde07651c02832b45aa0a17c1c601 Mon Sep 17 00:00:00 2001 From: Benjamin Bach Date: Sun, 11 Oct 2015 17:58:28 +0200 Subject: [PATCH 2/6] do not clean before making assets, bad for automated testing scenario --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b1ed1b1e35..66f7bbccde 100644 --- a/Makefile +++ b/Makefile @@ -75,7 +75,7 @@ docs: $(MAKE) -C docs html # open docs/_build/html/index.html -assets: clean +assets: # Necessary because NPM may have wrong versions in the cache npm cache clean npm install --production From c91c646a421ab2dca3bba742bcd02775503aaf5d Mon Sep 17 00:00:00 2001 From: Benjamin Bach Date: Mon, 12 Oct 2015 21:01:09 +0200 Subject: [PATCH 3/6] Say 0.16 about the fix for cmd line options --- kalitectl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kalitectl.py b/kalitectl.py index 2038fac8c6..871c054124 100644 --- a/kalitectl.py +++ b/kalitectl.py @@ -97,7 +97,7 @@ # Match all patterns of "--option value" and fail if they exist __validate_cmd_options = re.compile(r"--?[^\s]+\s+(?:(?!--|-[\w]))") if __validate_cmd_options.search(" ".join(sys.argv[1:])): - sys.stderr.write("Please only use --option=value or -x123 patterns. No spaces allowed between option and value. The option parser gets confused if you do otherwise.\n\nWill be fixed for next version 0.15") + sys.stderr.write("Please only use --option=value or -x123 patterns. No spaces allowed between option and value. The option parser gets confused if you do otherwise.\n\nWill be fixed for next version 0.16") sys.exit(1) from threading import Thread From f7ce3733a03b3cc3bccde5c56f86bd13d7a96d42 Mon Sep 17 00:00:00 2001 From: Benjamin Bach Date: Mon, 12 Oct 2015 21:18:37 +0200 Subject: [PATCH 4/6] Remove regressions from bad rebase --- .../features/steps/content_rating.py | 11 +++++---- .../static/js/distributed/rating/views.js | 16 +++++++------ .../commands/languagepackdownload.py | 23 ++++++++----------- kalitectl.py | 3 +++ 4 files changed, 28 insertions(+), 25 deletions(-) diff --git a/kalite/distributed/features/steps/content_rating.py b/kalite/distributed/features/steps/content_rating.py index 44c0236b3c..16e4a62b03 100644 --- a/kalite/distributed/features/steps/content_rating.py +++ b/kalite/distributed/features/steps/content_rating.py @@ -11,6 +11,8 @@ from securesync.models import Device from selenium.webdriver.support.select import Select +from selenium.webdriver.support.ui import WebDriverWait + RATING_CONTAINER_ID = "rating-container" TEXT_CONTAINER_ID = "text-container" @@ -167,16 +169,17 @@ def rate_id(context, id_, val=3): :return: nothing """ - def get_rating_el(driver): + def rate_element(driver): try: container = find_id_with_wait(context, id_) els = container.find_elements_by_class_name(STAR_RATING_OPTION_CLASS) - return [el for el in els if int(el.get_attribute("data-val")) == val].pop() + rating_el = [el for el in els if int(el.get_attribute("data-val")) == val].pop() + rating_el.click() + return True except (NoSuchElementException, StaleElementReferenceException): return False - rating_el = WebDriverWait(context.browser, 10).until(get_rating_el) - rating_el.click() + WebDriverWait(context.browser, 10).until(rate_element) def enter_text_feedback(context, text_feedback): diff --git a/kalite/distributed/static/js/distributed/rating/views.js b/kalite/distributed/static/js/distributed/rating/views.js index 1c4dbe0adf..4021ed25ad 100644 --- a/kalite/distributed/static/js/distributed/rating/views.js +++ b/kalite/distributed/static/js/distributed/rating/views.js @@ -56,18 +56,20 @@ module.exports = BaseView.extend({ Then once the user has filled out the rating completely, call renderAll to allow review/editing. */ this.$el.html(this.template()); + this.$(".rating-delete").hide(); this.star_view_quality = this.add_subview(StarView, {title: gettext("Quality"), el: this.$("#star-container-quality"), model: this.model, rating_attr: "quality", label_values: this.quality_label_values}); var self = this; - + var callback = function() { + self.star_view_difficulty = self.add_subview(StarView, {title: gettext("Difficulty"), el: self.$("#star-container-difficulty"), model: self.model, rating_attr: "difficulty", label_values: this.difficulty_label_values}); + self.$(".rating-delete").show(); + }; // If the "quality" is already set, display "difficulty" immediately. Otherwise wait. - if (parseInt(this.model.get("quality")) === 0) { - this.listenToOnce(this.model, "change:quality", function(){ - self.star_view_difficulty = self.add_subview(StarView, {title: gettext("Difficulty"), el: self.$("#star-container-difficulty"), model: self.model, rating_attr: "difficulty", label_values: this.difficulty_label_values}); - }); + if (!this.model.get("quality") || parseInt(this.model.get("quality")) === 0) { + this.listenToOnce(this.model, "change:quality", callback); } else { - self.star_view_difficulty = self.add_subview(StarView, {title: gettext("Difficulty"), el: self.$("#star-container-difficulty"), model: self.model, rating_attr: "difficulty", label_values: this.difficulty_label_values}); + callback(); } this.listenToOnce(this.model, "change:difficulty", this.renderAll); @@ -165,7 +167,7 @@ var StarView = BaseView.extend({ var val = $(target).attr("data-val"); this.model.set(this.rating_attr, val); this.model.debounced_save(); - }, 500, true), + }, 100, true), mouse_enter_callback: function(ev) { // The target event could be either the .star-rating-option or a child element, so whatever the case get the diff --git a/kalite/updates/management/commands/languagepackdownload.py b/kalite/updates/management/commands/languagepackdownload.py index e2f89eb723..aed4d79ba1 100644 --- a/kalite/updates/management/commands/languagepackdownload.py +++ b/kalite/updates/management/commands/languagepackdownload.py @@ -107,7 +107,7 @@ def handle(self, *args, **options): raise def cb(self, percent): - self.update_stage(stage_percent=percent/100.) + self.update_stage(stage_percent=percent / 100.) def get_language_pack(lang_code, software_version, callback): """Download language pack for specified language""" @@ -133,13 +133,14 @@ def unpack_language(lang_code, zip_filepath=None, zip_fp=None, zip_data=None): logging.info("Unpacking new translations") ensure_dir(get_po_filepath(lang_code=lang_code)) - ## Unpack into temp dir + # # Unpack into temp dir try: z = zipfile.ZipFile(zip_fp or (zip_data and StringIO(zip_data)) or open(zip_filepath, "rb")) except zipfile.BadZipfile as e: - # Need to add more information on the error message. + # Need to add more information on the errror message. # See http://stackoverflow.com/questions/6062576/adding-information-to-a-python-exception raise type(e), type(e)(e.message + _("Language pack corrupted. Please try downloading the language pack again in a few minutes.")) + z.extractall(os.path.join(settings.USER_WRITABLE_LOCALE_DIR, lang_code)) def move_dubbed_video_map(lang_code): lang_pack_location = os.path.join(settings.USER_WRITABLE_LOCALE_DIR, lang_code) @@ -159,17 +160,11 @@ def move_dubbed_video_map(lang_code): logging.error("Error removing dubbed video directory (%s): %s" % (dubbed_video_dir, e)) def move_video_sizes_file(lang_code): - lang_pack_location = os.path.join(settings.USER_WRITABLE_LOCALE_DIR, lang_code) - filename = os.path.basename(REMOTE_VIDEO_SIZE_FILEPATH) - src_path = os.path.join(lang_pack_location, filename) - dest_path = REMOTE_VIDEO_SIZE_FILEPATH - - # replace the old remote_video_size json - if not os.path.exists(src_path): - logging.error("Could not find videos sizes file (%s)" % src_path) - else: - logging.debug('Moving %s to %s' % (src_path, dest_path)) - shutil.move(src_path, dest_path) + """ + This is no longer needed. See: + https://github.com/learningequality/ka-lite/issues/4538#issuecomment-144560505 + """ + return def move_exercises(lang_code): lang_pack_location = os.path.join(settings.USER_WRITABLE_LOCALE_DIR, lang_code) diff --git a/kalitectl.py b/kalitectl.py index 871c054124..d9a98262d8 100644 --- a/kalitectl.py +++ b/kalitectl.py @@ -569,6 +569,8 @@ def start(debug=False, watch=False, daemonize=True, args=[], skip_job_scheduler= except KeyboardInterrupt: # Handled in cherrypy by waiting for all threads to join pass + except SystemExit: + print("KA Lite caught system exit signal, quitting.") print("FINISHED serving HTTP") @@ -579,6 +581,7 @@ def start(debug=False, watch=False, daemonize=True, args=[], skip_job_scheduler= from fle_utils.chronograph.management.commands import cronserver_blocking cronserver_blocking.shutdown = True cron_thread.join() + print("Job scheduler terminated.") def stop(args=[], sys_exit=True): From f01c301123797a9f83a49ec0c7fdb4ed94383333 Mon Sep 17 00:00:00 2001 From: Benjamin Bach Date: Mon, 12 Oct 2015 22:50:59 +0200 Subject: [PATCH 5/6] fix platform dependent pattern --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 399ff18d3b..18ebfccd7a 100644 --- a/setup.py +++ b/setup.py @@ -173,7 +173,7 @@ def filter_illegal_extensions(f): data_files += map( lambda x: (os.path.join(kalite.ROOT_DATA_PATH, x[0]), x[1]), - gen_data_files('docs/_build/html', optional=True) + gen_data_files(os.path.join('docs', '_build', 'html'), optional=True) ) # For now, just disguise the kalitectl.py script here as it's only to be accessed From 435d35ad62c43b9abc734b2cf0ff2eaf220ee6c9 Mon Sep 17 00:00:00 2001 From: Benjamin Bach Date: Mon, 12 Oct 2015 22:58:57 +0200 Subject: [PATCH 6/6] use @MCGallaspy's wording --- kalitectl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kalitectl.py b/kalitectl.py index d9a98262d8..932f077ce4 100644 --- a/kalitectl.py +++ b/kalitectl.py @@ -97,7 +97,7 @@ # Match all patterns of "--option value" and fail if they exist __validate_cmd_options = re.compile(r"--?[^\s]+\s+(?:(?!--|-[\w]))") if __validate_cmd_options.search(" ".join(sys.argv[1:])): - sys.stderr.write("Please only use --option=value or -x123 patterns. No spaces allowed between option and value. The option parser gets confused if you do otherwise.\n\nWill be fixed for next version 0.16") + sys.stderr.write("Please only use --option=value or -x123 patterns. No spaces allowed between option and value. The option parser gets confused if you do otherwise.\n\nWill be fixed in a future release.") sys.exit(1) from threading import Thread