From 2da4cac8ae38acb93e796aa53c612a148f24c738 Mon Sep 17 00:00:00 2001 From: Benjamin Bach Date: Mon, 7 Sep 2015 15:28:06 +0200 Subject: [PATCH 01/41] fix #4382 -- we are MIT licensed --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index f5d84fe32c..04c8824c9c 100644 --- a/setup.py +++ b/setup.py @@ -352,7 +352,7 @@ def install_distributions(distributions): author_email="info@learningequality.org", url="https://www.learningequality.org", description=DIST_DESCRIPTION, - license="GPLv3", + license="MIT", keywords=("khan academy", "offline", "education", "OER"), scripts=['bin/kalite'], packages=find_packages(exclude=["python-packages"]), @@ -361,7 +361,7 @@ def install_distributions(distributions): install_requires=DIST_REQUIREMENTS, classifiers=[ 'Development Status :: 4 - Beta', - 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', + 'License :: OSI Approved :: MIT License', 'Environment :: Web Environment', 'Framework :: Django', 'Intended Audience :: Developers', From 7073fb48c34defeb6a377040d1ca412c43d558d0 Mon Sep 17 00:00:00 2001 From: Richard Tibbles Date: Thu, 24 Sep 2015 10:05:46 -0700 Subject: [PATCH 02/41] Actually translate assessment item data. Conflicts: kalite/main/api_resources.py --- kalite/main/api_resources.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/kalite/main/api_resources.py b/kalite/main/api_resources.py index 5ab9022466..46cad312a1 100644 --- a/kalite/main/api_resources.py +++ b/kalite/main/api_resources.py @@ -9,7 +9,9 @@ from kalite.topic_tools.models import AssessmentItem from kalite.distributed.api_views import get_messages_for_api_calls -from kalite.topic_tools import get_exercise_data, get_content_data + +from kalite.topic_tools import get_exercise_data, get_content_data, get_assessment_item_data + from kalite.shared.api_auth.auth import UserObjectsOnlyAuthorization from kalite.facility.api_resources import FacilityUserResource @@ -178,13 +180,14 @@ def prepend_urls(self): name="api_dispatch_detail"), ] - def detail_uri_kwargs(self, bundle_or_obj): - kwargs = {} - if getattr(bundle_or_obj, 'obj', None): - kwargs['pk'] = bundle_or_obj.obj.id + + def obj_get(self, bundle, **kwargs): + id = kwargs.get("id", None) + assessment_item = get_assessment_item_data(bundle.request, id) + if assessment_item: + return AssessmentItem(**assessment_item) else: - kwargs['pk'] = bundle_or_obj.id - return kwargs + raise NotFound('AssessmentItem with id %s not found' % id) def obj_create(self, bundle, **kwargs): From b2e0feee029b3706c9a73796e7d13fc002dfb48b Mon Sep 17 00:00:00 2001 From: Richard Tibbles Date: Thu, 24 Sep 2015 10:25:27 -0700 Subject: [PATCH 03/41] Use empty list not string of empty list. --- kalite/topic_tools/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kalite/topic_tools/__init__.py b/kalite/topic_tools/__init__.py index 79e9819104..168ce4c6c8 100644 --- a/kalite/topic_tools/__init__.py +++ b/kalite/topic_tools/__init__.py @@ -218,7 +218,7 @@ def get_exercise_cache(force=False, language=None): elif exercise.get("uses_assessment_items", False): available = False items = [] - for item in exercise.get("all_assessment_items", "[]"): + for item in exercise.get("all_assessment_items", []): item = json.loads(item) if get_assessment_item_data(request=None, assessment_item_id=item.get("id")): items.append(item) From 4e82a5b26482c4c2919e73379bdea20139ca0bce Mon Sep 17 00:00:00 2001 From: Richard Tibbles Date: Thu, 24 Sep 2015 11:42:38 -0700 Subject: [PATCH 04/41] Add warning to langauge pack download completion to restart server. --- kalite/updates/management/commands/languagepackdownload.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kalite/updates/management/commands/languagepackdownload.py b/kalite/updates/management/commands/languagepackdownload.py index 4bc43a7c92..5f591f0978 100644 --- a/kalite/updates/management/commands/languagepackdownload.py +++ b/kalite/updates/management/commands/languagepackdownload.py @@ -97,7 +97,8 @@ def handle(self, *args, **options): self.next_stage(_("Invalidate caches")) caching.invalidate_all_caches() - self.complete(_("Finished processing language pack %(lang_name)s.") % {"lang_name": get_language_name(lang_code)}) + self.complete((_("Finished processing language pack %(lang_name)s.") % {"lang_name": get_language_name(lang_code)}) + + " Please restart the server to complete installation of the language pack.") except Exception as e: self.cancel(stage_status="error", notes=_("Error: %(error_msg)s") % {"error_msg": unicode(e)}) raise From 5ce63a6c6fe5b2965e8b21fa2e858984d7b4b050 Mon Sep 17 00:00:00 2001 From: Arno Angerer Date: Thu, 24 Sep 2015 14:14:45 -0700 Subject: [PATCH 05/41] Update install_all.rst Raspbian uses an old distribution of wget resulting in a certificate error upon attempting the download as documented in the installation guidelines. Including the "--no-check-certificate" parameter fixes this issue. --- docs/installguide/install_all.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installguide/install_all.rst b/docs/installguide/install_all.rst index 7518eb5604..53621a3f08 100644 --- a/docs/installguide/install_all.rst +++ b/docs/installguide/install_all.rst @@ -72,7 +72,7 @@ It can be installed by downloading the latest .deb on the Pi and installing it:: # Install dependencies sudo apt-get install python-m2crypto python-pkg-resources nginx python-psutil # Fetch the latest .deb - sudo wget https://learningequality.org/r/deb-pi-installer-0-14 + sudo wget https://learningequality.org/r/deb-pi-installer-0-14 --no-check-certificate # Install the .deb sudo dpkg -i ka-lite-raspberry-pi*.deb From c1b40458aa7754ba9b88ee53e57d874df223045b Mon Sep 17 00:00:00 2001 From: Aron Fyodor Asor Date: Thu, 24 Sep 2015 14:43:11 -0700 Subject: [PATCH 06/41] Generate a secret key file, and load SECRET_KEY from said file. Fixes #4213. --- kalite/settings/_utils.py | 17 +++++++++++++++++ kalite/settings/base.py | 18 +++++++++++++++--- 2 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 kalite/settings/_utils.py diff --git a/kalite/settings/_utils.py b/kalite/settings/_utils.py new file mode 100644 index 0000000000..3ccdc3f3d8 --- /dev/null +++ b/kalite/settings/_utils.py @@ -0,0 +1,17 @@ +import base64 +import sys +import uuid + + +def generate_secret_key(): + key = base64.b64encode(str(uuid.uuid4())) + return key + + +def cache_secret_key(key, key_file_path): + try: + with open(key_file_path, "w") as f: + f.write(key) + f.flush() + except Exception as e: + sys.stderr.write("Error writing secret key file. Error was: %s\n" % e) diff --git a/kalite/settings/base.py b/kalite/settings/base.py index 531bdc02fd..bc7673c3c6 100644 --- a/kalite/settings/base.py +++ b/kalite/settings/base.py @@ -314,8 +314,20 @@ USE_L10N = getattr(local_settings, "USE_L10N", False) # Make this unique, and don't share it with anybody. -SECRET_KEY = getattr(local_settings, "SECRET_KEY", - "8qq-!fa$92i=s1gjjitd&%s@4%ka9lj+=@n7a&fzjpwu%3kd#u") +SECRET_KEY_FILE = getattr(local_settings, + "SECRET_KEY_FILE", + os.path.join(USER_DATA_ROOT, "secretkey.txt")) + + +try: + with open(SECRET_KEY_FILE) as f: + SECRET_KEY = getattr(local_settings, "SECRET_KEY", f.read()) +except Exception as e: + sys.stderr.write("Error reading secret key file. Generating one now. Error was: %s\n" % e) + + from ._utils import generate_secret_key, cache_secret_key + SECRET_KEY = generate_secret_key() + cache_secret_key(SECRET_KEY, SECRET_KEY_FILE) LANGUAGE_COOKIE_NAME = "django_language" @@ -467,7 +479,7 @@ # Separate session caching from file caching. SESSION_ENGINE = getattr( local_settings, "SESSION_ENGINE", 'django.contrib.sessions.backends.signed_cookies' + ('')) - + # Expire session cookies whenever we close the browser. SESSION_EXPIRE_AT_BROWSER_CLOSE = True From 306de74188269f44907d1b130f3aa025fa96b04a Mon Sep 17 00:00:00 2001 From: Aron Fyodor Asor Date: Wed, 30 Sep 2015 14:42:55 -0700 Subject: [PATCH 07/41] bump version to 0.14.1. --- data/version.yml | 17 +++++++++++++++++ kalite/version.py | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/data/version.yml b/data/version.yml index 8e376b5962..0f8f0f312c 100644 --- a/data/version.yml +++ b/data/version.yml @@ -1,3 +1,20 @@ +0.14.1: + release_date: "2015/10/02" + git_commit: "0818d65" + new_features: + all: [] + students: + coaches: [] + admins: [] + bugs_fixed: + all: + - Fix assessment items not getting translated (#4482) + - Notify users that they need to restart their server after downloading a language pack (#4491) + - Generate a new secret key when the server starts (#4494) + students: [] + coaches: [] + admins: [] + 0.14.0: release_date: "2015/08/27" git_commit: "c453dab" diff --git a/kalite/version.py b/kalite/version.py index bb4464030c..75056850ec 100644 --- a/kalite/version.py +++ b/kalite/version.py @@ -7,7 +7,7 @@ # Must also be of the form N.N.N for internal use, where N is a non-negative integer MAJOR_VERSION = "0" MINOR_VERSION = "14" -PATCH_VERSION = "0" +PATCH_VERSION = "1" VERSION = "%s.%s.%s" % (MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION) SHORTVERSION = "%s.%s" % (MAJOR_VERSION, MINOR_VERSION) From 78552b6371250fe2209ca763eb4616fa1676bced Mon Sep 17 00:00:00 2001 From: benjaoming Date: Wed, 7 Oct 2015 03:03:02 +0200 Subject: [PATCH 08/41] Avoid unnecessary dependencies for old dbbackup version --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 242c43dda7..f09192108c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,5 +8,5 @@ slimit==0.8.1 six==1.9.0 django-appconf==1.0.1 python-dateutil==2.4.2 -django-dbbackup==2.0.4 +django-dbbackup==2.3.1 sqlitedict==1.3.0 From 3ed84eb7b6e3bdfe18481a94606407f2a5d407e0 Mon Sep 17 00:00:00 2001 From: benjaoming Date: Wed, 7 Oct 2015 03:13:59 +0200 Subject: [PATCH 09/41] Update requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f09192108c..01b9753a95 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,5 +8,5 @@ slimit==0.8.1 six==1.9.0 django-appconf==1.0.1 python-dateutil==2.4.2 -django-dbbackup==2.3.1 +django-dbbackup==2.0.5a sqlitedict==1.3.0 From 290b4fcce0a17a41b771db997a2b6c320a7f0f06 Mon Sep 17 00:00:00 2001 From: Michael Gallaspy Date: Wed, 7 Oct 2015 15:27:05 -0700 Subject: [PATCH 10/41] Change version comparison logic The central server sends a short version (e.g. 0.14), which the distributed server then compares to its long version (e.g. 0.14.1). The problem is that 0.14 compares as less than 0.14.1, so language packs are shown as unavailable. Instead, when comparing two mismatched-length versions, only consider the shortest version available -- thus 0.14 and 0.14.1 would compare equal. Then the languagepack versions will be compared, which is the actual intent. --- .../updates/static/js/updates/update_languages.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/kalite/updates/static/js/updates/update_languages.js b/kalite/updates/static/js/updates/update_languages.js index 51ac036852..3d5af88a6e 100644 --- a/kalite/updates/static/js/updates/update_languages.js +++ b/kalite/updates/static/js/updates/update_languages.js @@ -3,12 +3,18 @@ var installed_languages = []; var downloading = false; function version_comparison(v1, v2) { - // compare two version strings and return 1 if the first is higher than the second, - // -1 if the first is lower than the second, and 0 if they are equal + /* + compare two version strings and return 1 if the first is higher than the second, + -1 if the first is lower than the second, and 0 if they are equal. + + :params v1, v2: Version strings expected format is either "N.N.N" or "N.N", where N is a positive integer. + If both strings have the same format, they're compared using a lexical order. + If one string is shorter than the other, then the other is truncated and then compared using lexical order. + */ var v1parts = v1.split('.'), v2parts = v2.split('.'); - var maxLen = Math.max(v1parts.length, v2parts.length); + var minLen = Math.min(v1parts.length, v2parts.length); var part1, part2; - for(var i = 0; i < maxLen; i++) { + for(var i = 0; i < minLen; i++) { part1 = parseInt(v1parts[i], 10) || 0; part2 = parseInt(v2parts[i], 10) || 0; if (part1 > part2) return 1; From 1d47b8d157fe4468eee0fd49a7e491f4c1c606ba Mon Sep 17 00:00:00 2001 From: Aron Fyodor Asor Date: Wed, 7 Oct 2015 17:02:35 -0700 Subject: [PATCH 11/41] Increment version to 0.14.2. --- data/version.yml | 15 +++++++++++++++ kalite/version.py | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/data/version.yml b/data/version.yml index 0f8f0f312c..25c76e0ed5 100644 --- a/data/version.yml +++ b/data/version.yml @@ -1,3 +1,18 @@ +0.14.2: + release_date: "2015/10/02" + git_commit: "dceccca" + new_features: + all: [] + students: + coaches: [] + admins: [] + bugs_fixed: + all: + Properly compare language pack versions (#4587). + students: [] + coaches: [] + admins: [] + 0.14.1: release_date: "2015/10/02" git_commit: "0818d65" diff --git a/kalite/version.py b/kalite/version.py index 75056850ec..4b9243791f 100644 --- a/kalite/version.py +++ b/kalite/version.py @@ -7,7 +7,7 @@ # Must also be of the form N.N.N for internal use, where N is a non-negative integer MAJOR_VERSION = "0" MINOR_VERSION = "14" -PATCH_VERSION = "1" +PATCH_VERSION = "2" VERSION = "%s.%s.%s" % (MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION) SHORTVERSION = "%s.%s" % (MAJOR_VERSION, MINOR_VERSION) From a51c6af5131db9af7848d14724ea34b11b5778d6 Mon Sep 17 00:00:00 2001 From: Danny Jim Allado Date: Thu, 8 Oct 2015 23:33:42 +0800 Subject: [PATCH 12/41] Put note in release_notes and system_requirements --- docs/installguide/install_all.rst | 3 ++- docs/installguide/release_notes.rst | 3 +++ docs/installguide/system_requirements.rst | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/installguide/install_all.rst b/docs/installguide/install_all.rst index d25cb95fe9..71097ffb3f 100644 --- a/docs/installguide/install_all.rst +++ b/docs/installguide/install_all.rst @@ -10,7 +10,8 @@ You don't need to uninstall your old KA Lite installation first. Mac/OSX ======= -**Note:** The latest OSX version (EL Capitan) is not yet supported. +.. note:: + The latest OSX version (EL Capitan) is not yet supported. #. Download the KA Lite `OSX installer `_. #. After the download is complete, double click the .dmg file. diff --git a/docs/installguide/release_notes.rst b/docs/installguide/release_notes.rst index 4733affc84..75c04569ba 100644 --- a/docs/installguide/release_notes.rst +++ b/docs/installguide/release_notes.rst @@ -10,6 +10,9 @@ General Python 2.6 is no longer supported. It *may* still work, but we are no longer actively supporting it. Other known issues: +.. note:: + The latest OSX version (EL Capitan) is not yet supported. + * On OSX, you must restart the server after downloading videos in order for them to be marked as available. * On all platforms, you must restart the server after downloading a language pack in order to use it. * You can no longer configure your server using ``local_settings.py``. Instead, custom settings must appear in diff --git a/docs/installguide/system_requirements.rst b/docs/installguide/system_requirements.rst index 7e259cf23b..ed3b2b6b75 100644 --- a/docs/installguide/system_requirements.rst +++ b/docs/installguide/system_requirements.rst @@ -1,6 +1,9 @@ System requirements =================== +.. note:: + The latest OSX version (EL Capitan) is not yet supported. + Supported Browsers ------------------ KA Lite is currently *not* supported on Internet Explorer version 8 or lower. You must use IE9 or later. From e83b429644a1dc7e3f07e6106376749f45779b9f Mon Sep 17 00:00:00 2001 From: Richard Tibbles Date: Thu, 8 Oct 2015 20:21:23 -0700 Subject: [PATCH 13/41] Reinstate Khan i18n helpers, overwrite $._ to ensure translation. --- .../static/js/distributed/exercises/perseus-helpers.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kalite/distributed/static/js/distributed/exercises/perseus-helpers.js b/kalite/distributed/static/js/distributed/exercises/perseus-helpers.js index 6e586724b7..544d96c9be 100644 --- a/kalite/distributed/static/js/distributed/exercises/perseus-helpers.js +++ b/kalite/distributed/static/js/distributed/exercises/perseus-helpers.js @@ -10,6 +10,8 @@ require("../perseus/lib/mathquill/mathquill-basic.js"); require("../perseus/lib/mathquill/mathquill.css"); require("../perseus/lib/kas.js"); global.Jed = require("jed"); +require("../perseus/ke/local-only/i18n"); +global.$._ = gettext; require("qtip2"); var KhanUtil = window.KhanUtil || {}; From 2b71b960fc31b1e38dacc1d9fb93342ee3d8eb07 Mon Sep 17 00:00:00 2001 From: "Richard T. Amodia" Date: Fri, 9 Oct 2015 16:50:42 +0800 Subject: [PATCH 14/41] Update the documentation to upgrade on kalite OS X installer. --- docs/installguide/install_all.rst | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/docs/installguide/install_all.rst b/docs/installguide/install_all.rst index 71097ffb3f..215f80d675 100644 --- a/docs/installguide/install_all.rst +++ b/docs/installguide/install_all.rst @@ -10,17 +10,11 @@ You don't need to uninstall your old KA Lite installation first. Mac/OSX ======= -.. note:: - The latest OSX version (EL Capitan) is not yet supported. +.. warning:: The latest OSX version (EL Capitan) is not yet supported. #. Download the KA Lite `OSX installer `_. #. After the download is complete, double click the .dmg file. #. On the .dmg window, drag the ``KA-Lite Monitor`` app into the ``Applications`` folder. - - .. note:: - To upgrade an existing KA Lite installation, download the new installer and then drag it over to the "Applications" - folder to replace the existing "KA-Lite-Monitor" application. - Give confirmation to overwrite the existing app. Then proceed from this step as usual. #. Launch ``KA-Lite Monitor`` from your ``Applications`` folder. #. On first load, it will check your current environment and show the Preferences dialog. #. Input your preferred admin username and password, then click the Apply button in ``KA-Lite Preferences`` dialog. @@ -40,6 +34,15 @@ If the sidebar shows entries that are greyed-out, the child items of the entry m This will enable the greyed-out assessment items on the sidebar. +To upgrade an existing KA Lite installation. + +#. Download the KA Lite `OSX installer `_. +#. After the download is complete, double click the .dmg file. +#. On the .dmg window, drag the ``KA-Lite Monitor`` app into the ``Applications`` folder. +#. Give confirmation to overwrite the existing app. +#. Launch KA-Lite Monitor from your Applications folder. + + Linux ===== From ad1655d8f5e1dec2a65817e5e196a2a6f93e0400 Mon Sep 17 00:00:00 2001 From: "Richard T. Amodia" Date: Fri, 9 Oct 2015 20:14:22 +0800 Subject: [PATCH 15/41] Add step to install kalite OS X installer --- docs/installguide/install_all.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/installguide/install_all.rst b/docs/installguide/install_all.rst index 215f80d675..1611ab481d 100644 --- a/docs/installguide/install_all.rst +++ b/docs/installguide/install_all.rst @@ -14,6 +14,7 @@ Mac/OSX #. Download the KA Lite `OSX installer `_. #. After the download is complete, double click the .dmg file. +#. Click the agree button to accept the LECENSE agreement. #. On the .dmg window, drag the ``KA-Lite Monitor`` app into the ``Applications`` folder. #. Launch ``KA-Lite Monitor`` from your ``Applications`` folder. #. On first load, it will check your current environment and show the Preferences dialog. @@ -38,6 +39,7 @@ To upgrade an existing KA Lite installation. #. Download the KA Lite `OSX installer `_. #. After the download is complete, double click the .dmg file. +#. Click the agree button to accept the LECENSE agreement. #. On the .dmg window, drag the ``KA-Lite Monitor`` app into the ``Applications`` folder. #. Give confirmation to overwrite the existing app. #. Launch KA-Lite Monitor from your Applications folder. From e8f52eb941c48964a45f29a33290a287169c91a0 Mon Sep 17 00:00:00 2001 From: "Richard T. Amodia" Date: Fri, 9 Oct 2015 20:41:34 +0800 Subject: [PATCH 16/41] Update the release notes. --- docs/installguide/install_all.rst | 43 +++++++++++++++++------------ docs/installguide/release_notes.rst | 4 +-- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/docs/installguide/install_all.rst b/docs/installguide/install_all.rst index 1611ab481d..4dbdf0d568 100644 --- a/docs/installguide/install_all.rst +++ b/docs/installguide/install_all.rst @@ -7,42 +7,49 @@ Windows Upgrading KA Lite over an existing installation is easy -- just run the installer and follow the prompts! You don't need to uninstall your old KA Lite installation first. -Mac/OSX -======= +Mac OS X +======== + +.. warning:: The latest OS X version (EL Capitan) is not yet supported. -.. warning:: The latest OSX version (EL Capitan) is not yet supported. +Installation +____________ #. Download the KA Lite `OSX installer `_. #. After the download is complete, double click the .dmg file. -#. Click the agree button to accept the LECENSE agreement. +#. Click the ``Agree`` button to accept the LICENSE agreement. #. On the .dmg window, drag the ``KA-Lite Monitor`` app into the ``Applications`` folder. #. Launch ``KA-Lite Monitor`` from your ``Applications`` folder. #. On first load, it will check your current environment and show the Preferences dialog. -#. Input your preferred admin username and password, then click the Apply button in ``KA-Lite Preferences`` dialog. +#. Input your preferred administrator username and password, then click the ``Apply`` button in ``KA-Lite Preferences`` dialog. #. You will be prompted that initial setup will take a few minutes, click the ``OK`` button and wait for the notification that KA-Lite has been setup and can now be started. -#. Click on the KA-Lite logo icon on the Status Menu Bar and select the ``Start KA-Lite`` menu option. +#. Click on the KA-Lite logo icon on the menu bar and select the ``Start KA-Lite`` menu option. #. Wait for the notification that you can now click on ``Open in Browser`` menu option. -#. Click on the KA-Lite logo icon on the Status Menu Bar and select ``Open in Browser`` menu option - this should launch KA-Lite in your preferred web browser. +#. Click on the KA-Lite logo icon on the menu bar and select ``Open in Browser`` menu option - this should launch KA-Lite in your preferred web browser. #. Login using the administrator account you have specified during setup. -If the sidebar shows entries that are greyed-out, the child items of the entry may be videos that were not yet downloaded. If there are assessment items inside, then you need to extract the `assessment.zip` manually: - -#. Launch ``KA-Lite Monitor`` from your ``Applications`` folder. -#. Click on the app icon at the menu bar. -#. Click on ``Preferences`` in the menu option. -#. Go to Advanced tab, click on the ``Extract Assessment`` button, then confirm the action. -#. Restart the server, login to the web app, then check the sidebar contents. - -This will enable the greyed-out assessment items on the sidebar. +Upgrade +_______ To upgrade an existing KA Lite installation. #. Download the KA Lite `OSX installer `_. #. After the download is complete, double click the .dmg file. -#. Click the agree button to accept the LECENSE agreement. +#. Click the ``Agree`` button to accept the LICENSE agreement. #. On the .dmg window, drag the ``KA-Lite Monitor`` app into the ``Applications`` folder. #. Give confirmation to overwrite the existing app. -#. Launch KA-Lite Monitor from your Applications folder. +#. Launch ``KA-Lite Monitor`` from your ``Applications`` folder. + +.. tip:: + If the sidebar shows entries that are greyed-out, the child items of the entry may be videos that were not yet downloaded. If there are assessment items inside, then you need to extract the `assessment.zip` manually: + + #. Launch ``KA-Lite Monitor`` from your ``Applications`` folder. + #. Click on the app icon at the menu bar. + #. Click on ``Preferences`` in the menu option. + #. Go to ``Advanced`` tab, click on the ``Extract Assessment`` button, then confirm the action. + #. Restart the server, login to the web app, then check the sidebar contents. + + This will enable the greyed-out assessment items on the sidebar. Linux diff --git a/docs/installguide/release_notes.rst b/docs/installguide/release_notes.rst index 75c04569ba..d66cb6c359 100644 --- a/docs/installguide/release_notes.rst +++ b/docs/installguide/release_notes.rst @@ -10,9 +10,7 @@ General Python 2.6 is no longer supported. It *may* still work, but we are no longer actively supporting it. Other known issues: -.. note:: - The latest OSX version (EL Capitan) is not yet supported. - +* The latest OSX version (EL Capitan) is not yet supported. * On OSX, you must restart the server after downloading videos in order for them to be marked as available. * On all platforms, you must restart the server after downloading a language pack in order to use it. * You can no longer configure your server using ``local_settings.py``. Instead, custom settings must appear in From f268378826b942e735fae7c9c2fee2192bbac952 Mon Sep 17 00:00:00 2001 From: Benjamin Bach Date: Sat, 3 Oct 2015 00:23:05 +0200 Subject: [PATCH 17/41] 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 18/41] 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 e8444ab6d896323353f2cdcf6d4516430ae38e92 Mon Sep 17 00:00:00 2001 From: Richard Tibbles Date: Mon, 12 Oct 2015 10:58:54 -0700 Subject: [PATCH 19/41] Fixes doRequest reference. --- .../control_panel/static/js/control_panel/utils/force_sync.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kalite/control_panel/static/js/control_panel/utils/force_sync.js b/kalite/control_panel/static/js/control_panel/utils/force_sync.js index efda678164..2ee3a2da7d 100644 --- a/kalite/control_panel/static/js/control_panel/utils/force_sync.js +++ b/kalite/control_panel/static/js/control_panel/utils/force_sync.js @@ -1,7 +1,9 @@ +var api = require("utils/api"); + module.exports = function force_sync(zone_id, device_id) { // Simple function that calls the API endpoint to force a data sync, // then shows a message for success/failure - doRequest(window.Urls.api_force_sync()) + api.doRequest(window.Urls.api_force_sync()) .success(function() { var msg = gettext("Successfully launched data syncing job.") + " "; msg += sprintf(gettext("After syncing completes, visit the device management page to view results."), { From b2e9ad88a833f072dce1dd63bfa33f750585b41d Mon Sep 17 00:00:00 2001 From: Richard Tibbles Date: Mon, 12 Oct 2015 11:58:04 -0700 Subject: [PATCH 20/41] Don't try to parse empty responses. Bloody TastyPie. --- .../distributed/static/js/distributed/exercises/models.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kalite/distributed/static/js/distributed/exercises/models.js b/kalite/distributed/static/js/distributed/exercises/models.js index 7f8b6e2317..932e006112 100644 --- a/kalite/distributed/static/js/distributed/exercises/models.js +++ b/kalite/distributed/static/js/distributed/exercises/models.js @@ -221,8 +221,10 @@ var AttemptLogModel = Backbone.Model.extend({ }, parse: function(response) { - if (response.response_log) { - response.response_log = JSON.parse(response.response_log); + if (response) { + if (response.response_log) { + response.response_log = JSON.parse(response.response_log); + } } return response; }, From 903cf61ca23552fddce85f598f2cf0e430674dcf Mon Sep 17 00:00:00 2001 From: Richard Tibbles Date: Mon, 12 Oct 2015 11:59:40 -0700 Subject: [PATCH 21/41] Translate stuff in the guts of Khan Exercises i18n. --- kalite/distributed/static/js/distributed/base/jQuery.js | 2 -- .../static/js/distributed/exercises/perseus-helpers.js | 1 - .../static/js/distributed/perseus/ke/local-only/i18n.js | 2 ++ 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/kalite/distributed/static/js/distributed/base/jQuery.js b/kalite/distributed/static/js/distributed/base/jQuery.js index 593e2c02d7..e84453408e 100644 --- a/kalite/distributed/static/js/distributed/base/jQuery.js +++ b/kalite/distributed/static/js/distributed/base/jQuery.js @@ -4,8 +4,6 @@ var jQuery = require("jquery"); global.$ = global.jQuery = jQuery; -jQuery._ = global.gettext; - require("jquery-ui"); require("jquery-ui-touch-punch"); diff --git a/kalite/distributed/static/js/distributed/exercises/perseus-helpers.js b/kalite/distributed/static/js/distributed/exercises/perseus-helpers.js index 544d96c9be..f97f85bc55 100644 --- a/kalite/distributed/static/js/distributed/exercises/perseus-helpers.js +++ b/kalite/distributed/static/js/distributed/exercises/perseus-helpers.js @@ -11,7 +11,6 @@ require("../perseus/lib/mathquill/mathquill.css"); require("../perseus/lib/kas.js"); global.Jed = require("jed"); require("../perseus/ke/local-only/i18n"); -global.$._ = gettext; require("qtip2"); var KhanUtil = window.KhanUtil || {}; diff --git a/kalite/distributed/static/js/distributed/perseus/ke/local-only/i18n.js b/kalite/distributed/static/js/distributed/perseus/ke/local-only/i18n.js index 961125ed8e..0b7d861bbd 100644 --- a/kalite/distributed/static/js/distributed/perseus/ke/local-only/i18n.js +++ b/kalite/distributed/static/js/distributed/perseus/ke/local-only/i18n.js @@ -104,6 +104,8 @@ str = str.messages[0]; } + str = gettext(str); + return interpolateStringToArray(str, options).join(""); }; From c91c646a421ab2dca3bba742bcd02775503aaf5d Mon Sep 17 00:00:00 2001 From: Benjamin Bach Date: Mon, 12 Oct 2015 21:01:09 +0200 Subject: [PATCH 22/41] 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 23/41] 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 24/41] 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 25/41] 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 From aa5c0c1f0ac9eea687569a7a043e404bcf09060a Mon Sep 17 00:00:00 2001 From: Richard Tibbles Date: Mon, 12 Oct 2015 14:23:24 -0700 Subject: [PATCH 26/41] Adds additional requires for force_sync. --- .../control_panel/static/js/control_panel/utils/force_sync.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kalite/control_panel/static/js/control_panel/utils/force_sync.js b/kalite/control_panel/static/js/control_panel/utils/force_sync.js index 2ee3a2da7d..3cd2667549 100644 --- a/kalite/control_panel/static/js/control_panel/utils/force_sync.js +++ b/kalite/control_panel/static/js/control_panel/utils/force_sync.js @@ -1,4 +1,6 @@ var api = require("utils/api"); +var sprintf = require("sprintf-js").sprintf; +var messages = require("messages"); module.exports = function force_sync(zone_id, device_id) { // Simple function that calls the API endpoint to force a data sync, @@ -9,6 +11,6 @@ module.exports = function force_sync(zone_id, device_id) { msg += sprintf(gettext("After syncing completes, visit the device management page to view results."), { devman_url: Urls.device_management(zone_id, device_id) }); - show_message("success", msg); + messages.show_message("success", msg); }); }; \ No newline at end of file From 928d890a32802306cf39da04064d9c605c7f8ac1 Mon Sep 17 00:00:00 2001 From: Michael Gallaspy Date: Mon, 12 Oct 2015 15:27:07 -0700 Subject: [PATCH 27/41] Fix require stmt --- .../control_panel/static/js/control_panel/utils/force_sync.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kalite/control_panel/static/js/control_panel/utils/force_sync.js b/kalite/control_panel/static/js/control_panel/utils/force_sync.js index 3cd2667549..f735369481 100644 --- a/kalite/control_panel/static/js/control_panel/utils/force_sync.js +++ b/kalite/control_panel/static/js/control_panel/utils/force_sync.js @@ -1,6 +1,6 @@ var api = require("utils/api"); var sprintf = require("sprintf-js").sprintf; -var messages = require("messages"); +var messages = require("utils/messages"); module.exports = function force_sync(zone_id, device_id) { // Simple function that calls the API endpoint to force a data sync, From ae9d94abe009ada599d1a4a9bbc60d1d730b360c Mon Sep 17 00:00:00 2001 From: Benjamin Bach Date: Tue, 13 Oct 2015 18:43:51 +0200 Subject: [PATCH 28/41] automatically generate a manpage and put it in docs/ --- .gitignore | 1 + Makefile | 1 + requirements_dev.txt | 1 + 3 files changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 12e67119b0..e68a4576cf 100644 --- a/.gitignore +++ b/.gitignore @@ -59,6 +59,7 @@ static-updates # Documentation docs/_build docs/images/* +docs/kalite.1.gz # oh-my-zsh convention for automatically # switching on a venv diff --git a/Makefile b/Makefile index 66f7bbccde..0b020362df 100644 --- a/Makefile +++ b/Makefile @@ -73,6 +73,7 @@ docs: # sphinx-apidoc -o docs/ ka-lite-gtk $(MAKE) -C docs clean $(MAKE) -C docs html + cli2man bin/kalite -o docs/kalite.1.gz # open docs/_build/html/index.html assets: diff --git a/requirements_dev.txt b/requirements_dev.txt index 1a1851d6d4..8463558095 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -3,3 +3,4 @@ werkzeug # Not sure about version django-debug-toolbar # Version was not described in previously bundled files, used by kalite.testing app pep8 sphinx +cli2man From 64cadecdd49c3bb1328f25792beee12a6bff3b27 Mon Sep 17 00:00:00 2001 From: Benjamin Bach Date: Wed, 14 Oct 2015 00:02:59 +0200 Subject: [PATCH 29/41] Fix regressions introduced from merge with 0.14 master --- kalite/settings/base.py | 3 --- kalite/updates/management/commands/languagepackdownload.py | 3 +-- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/kalite/settings/base.py b/kalite/settings/base.py index 1afcd799d8..6520fab837 100644 --- a/kalite/settings/base.py +++ b/kalite/settings/base.py @@ -466,9 +466,6 @@ SESSION_COOKIE_AGE = 60 * 30 # 30 minutes SESSION_SAVE_EVERY_REQUEST = True -# Expire session cookies whenever we close the browser. -SESSION_EXPIRE_AT_BROWSER_CLOSE = True - # Use our custom message storage to avoid adding duplicate messages MESSAGE_STORAGE = 'fle_utils.django_utils.classes.NoDuplicateMessagesSessionStorage' diff --git a/kalite/updates/management/commands/languagepackdownload.py b/kalite/updates/management/commands/languagepackdownload.py index 3befb2b4c4..aed4d79ba1 100644 --- a/kalite/updates/management/commands/languagepackdownload.py +++ b/kalite/updates/management/commands/languagepackdownload.py @@ -101,8 +101,7 @@ def handle(self, *args, **options): self.next_stage(_("Invalidate caches")) caching.invalidate_all_caches() - self.complete((_("Finished processing language pack %(lang_name)s.") % {"lang_name": get_language_name(lang_code)}) + - " Please restart the server to complete installation of the language pack.") + self.complete(_("Finished processing language pack %(lang_name)s.") % {"lang_name": get_language_name(lang_code)}) except Exception as e: self.cancel(stage_status="error", notes=_("Error: %(error_msg)s") % {"error_msg": unicode(e)}) raise From 278687a35d9a2e9e28232d98c5823cbe18eecc42 Mon Sep 17 00:00:00 2001 From: Michael Gallaspy Date: Wed, 14 Oct 2015 16:22:08 -0700 Subject: [PATCH 30/41] Update install_all.rst Update link -- it slipped past us that it's for 0.14. --- docs/installguide/install_all.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installguide/install_all.rst b/docs/installguide/install_all.rst index 2654d16e96..77b49dfe92 100644 --- a/docs/installguide/install_all.rst +++ b/docs/installguide/install_all.rst @@ -106,7 +106,7 @@ It can be installed by downloading the latest .deb on the Pi and installing it:: # Install dependencies sudo apt-get install python-m2crypto python-pkg-resources nginx python-psutil # Fetch the latest .deb - sudo wget https://learningequality.org/r/deb-pi-installer-0-14 --no-check-certificate + sudo wget https://learningequality.org/r/deb-pi-installer-0-15 --no-check-certificate # Install the .deb sudo dpkg -i ka-lite-raspberry-pi*.deb From 129b271f19df4eb3733a86021952c221acf30df0 Mon Sep 17 00:00:00 2001 From: Benjamin Bach Date: Mon, 19 Oct 2015 15:43:08 +0200 Subject: [PATCH 31/41] Upgrade to latest dbbackup #4634 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 01b9753a95..d48c1ed469 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,5 +8,5 @@ slimit==0.8.1 six==1.9.0 django-appconf==1.0.1 python-dateutil==2.4.2 -django-dbbackup==2.0.5a +django-dbbackup==2.3.2 sqlitedict==1.3.0 From b6565551b7eeee6bd3c173d0e5a41d3db43148e1 Mon Sep 17 00:00:00 2001 From: Aron Fyodor Asor Date: Tue, 17 Nov 2015 09:27:55 -0800 Subject: [PATCH 32/41] propagate the request language in user progress. --- kalite/coachreports/api_resources.py | 10 +++++----- kalite/coachreports/models.py | 18 ++++++++++++++---- kalite/topic_tools/__init__.py | 2 +- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/kalite/coachreports/api_resources.py b/kalite/coachreports/api_resources.py index 0afb82b08c..5847c1f43c 100644 --- a/kalite/coachreports/api_resources.py +++ b/kalite/coachreports/api_resources.py @@ -9,7 +9,7 @@ class CoachReportBaseResource(Resource): """ - A base resource that houses shared code between the resources we actually use + A base resource that houses shared code between the resources we actually use in the API """ @@ -54,7 +54,7 @@ class Meta: def get_object_list(self, request): user_id = request.GET.get('user_id') - result = PlaylistProgress.user_progress(user_id=user_id) + result = PlaylistProgress.user_progress(user_id=user_id, language=request.language) return result class PlaylistProgressDetailResource(CoachReportBaseResource): @@ -72,8 +72,8 @@ class Meta: def get_object_list(self, request): user_id = request.GET.get("user_id") playlist_id = request.GET.get("playlist_id") - result = PlaylistProgressDetail.user_progress_detail(user_id=user_id, playlist_id=playlist_id) + language = request.language + result = PlaylistProgressDetail.user_progress_detail(user_id=user_id, playlist_id=playlist_id, language=language) if not result: - raise NotFound("User playlist progress details with user ID '%s' and playlist ID '%s' were not found." % (user_id, playlist_id)) + raise NotFound("User playlist progress details with user ID '%s' and playlist ID '%s' were not found." % (user_id, playlist_id)) return result - diff --git a/kalite/coachreports/models.py b/kalite/coachreports/models.py index 7aa86d8897..7059631028 100644 --- a/kalite/coachreports/models.py +++ b/kalite/coachreports/models.py @@ -1,8 +1,11 @@ """Classes used by the student progress tastypie API""" import json +from fle_utils.config.models import Settings +from django.conf import settings from django.core.urlresolvers import reverse, NoReverseMatch from django.core.exceptions import ObjectDoesNotExist +from django.utils.translation import ugettext as _ from kalite.facility.models import FacilityUser from kalite.main.models import ExerciseLog, VideoLog @@ -59,12 +62,16 @@ def __init__(self, **kwargs): setattr(self, k, v) @classmethod - def user_progress(cls, user_id): + def user_progress(cls, user_id, language=None): """ Return a list of PlaylistProgress objects associated with the user. """ + + if not language: + language = Settings.get("default_language") or settings.LANGUAGE_CODE + user = FacilityUser.objects.get(id=user_id) - all_playlists = get_leafed_topics() + all_playlists = get_leafed_topics(language=language) # Retrieve video, exercise, and quiz logs that appear in this playlist user_vid_logs, user_ex_logs = cls.get_user_logs(user) @@ -200,11 +207,14 @@ def create_empty_entry(cls, entity_id, kind, playlist): return entry @classmethod - def user_progress_detail(cls, user_id, playlist_id): + def user_progress_detail(cls, user_id, playlist_id, language=None): """ Return a list of video, exercise, and quiz log PlaylistProgressDetail objects associated with a specific user and playlist ID. """ + if not language: + language = settings.LANGUAGE_CODE + user = FacilityUser.objects.get(id=user_id) playlist = next((pl for pl in get_leafed_topics() if pl.get("id") == playlist_id), None) @@ -221,7 +231,7 @@ def user_progress_detail(cls, user_id, playlist_id): progress_details = list() for entity_id in playlist.get("children"): entry = {} - leaf_node = get_content_cache().get(entity_id, get_exercise_cache().get(entity_id, {})) + leaf_node = get_content_cache(language=language).get(entity_id) or get_exercise_cache(language=language).get(entity_id) or {} kind = leaf_node.get("kind") if kind == "Video": diff --git a/kalite/topic_tools/__init__.py b/kalite/topic_tools/__init__.py index 79b7d3732a..4800dc7d22 100644 --- a/kalite/topic_tools/__init__.py +++ b/kalite/topic_tools/__init__.py @@ -167,7 +167,7 @@ def get_node_cache(node_type=None, force=False, language=None): global NODE_CACHE if NODE_CACHE is None or force: - NODE_CACHE = generate_node_cache() + NODE_CACHE = generate_node_cache(language=language) if node_type is None: return NODE_CACHE else: From d0232c9027086ce4d013b43fe48c330220934cd9 Mon Sep 17 00:00:00 2001 From: Aron Fyodor Asor Date: Tue, 17 Nov 2015 12:18:47 -0800 Subject: [PATCH 33/41] set the right default language logic for user_progress_detail. --- kalite/coachreports/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kalite/coachreports/models.py b/kalite/coachreports/models.py index 7059631028..bff9461361 100644 --- a/kalite/coachreports/models.py +++ b/kalite/coachreports/models.py @@ -213,7 +213,7 @@ def user_progress_detail(cls, user_id, playlist_id, language=None): objects associated with a specific user and playlist ID. """ if not language: - language = settings.LANGUAGE_CODE + language = Settings.get("default_language") or settings.LANGUAGE_CODE user = FacilityUser.objects.get(id=user_id) playlist = next((pl for pl in get_leafed_topics() if pl.get("id") == playlist_id), None) From 2576612fcfe5279bda4f68e6fdbbabfbb272530b Mon Sep 17 00:00:00 2001 From: Aron Fyodor Asor Date: Wed, 18 Nov 2015 08:45:18 -0800 Subject: [PATCH 34/41] refactor user_progress_detail to not use create_empty_entry. --- kalite/coachreports/models.py | 88 ++++++----------------------------- 1 file changed, 14 insertions(+), 74 deletions(-) diff --git a/kalite/coachreports/models.py b/kalite/coachreports/models.py index bff9461361..8320858b23 100644 --- a/kalite/coachreports/models.py +++ b/kalite/coachreports/models.py @@ -183,29 +183,6 @@ def __init__(self, **kwargs): self.score = kwargs.get("score") self.path = kwargs.get("path") - @classmethod - def create_empty_entry(cls, entity_id, kind, playlist): - if kind != "Quiz": - if kind == "Video": - topic_node = get_content_cache().get(entity_id) - elif kind == "Exercise": - topic_node = get_exercise_cache().get(entity_id) - title = topic_node["title"] - path = topic_node["path"] - else: - title = playlist["title"] - path = "" - entry = { - "id": entity_id, - "kind": kind, - "status": "notstarted", - "score": 0, - "title": title, - "path": path, - } - - return entry - @classmethod def user_progress_detail(cls, user_id, playlist_id, language=None): """ @@ -234,6 +211,9 @@ def user_progress_detail(cls, user_id, playlist_id, language=None): leaf_node = get_content_cache(language=language).get(entity_id) or get_exercise_cache(language=language).get(entity_id) or {} kind = leaf_node.get("kind") + status = "notstarted" + score = 0 + if kind == "Video": vid_log = next((vid_log for vid_log in user_vid_logs if vid_log["video_id"] == entity_id), None) if vid_log: @@ -241,17 +221,8 @@ def user_progress_detail(cls, user_id, playlist_id, language=None): status = "complete" elif vid_log.get("total_seconds_watched"): status = "inprogress" - else: - status = "notstarted" - - entry = { - "id": entity_id, - "kind": kind, - "status": status, - "score": int(float(vid_log.get("points")) / float(750) * 100), - "title": leaf_node["title"], - "path": leaf_node["path"], - } + + score = int(float(vid_log.get("points")) / float(750) * 100) elif kind == "Exercise": ex_log = next((ex_log for ex_log in user_ex_logs if ex_log["exercise_id"] == entity_id), None) @@ -263,47 +234,16 @@ def user_progress_detail(cls, user_id, playlist_id, language=None): elif ex_log.get("attempts"): status = "inprogress" - entry = { - "id": entity_id, - "kind": kind, - "status": status, - "score": ex_log.get("streak_progress"), - "title": leaf_node["title"], - "path": leaf_node["path"], - } + score = ex_log.get('streak_progress') - # Oh Quizzes, we hardly knew ye! - # TODO (rtibbles): Sort out the status of Quizzes, and either reinstate them or remove them. - # Quizzes were introduced to provide a way of practicing multiple types of exercise at once - # However, there is currently no way to access them, and the manner for generating them (from the now deprecated Playlist models) is inaccessible - # elif kind == "Quiz": - # entity_id = playlist["id"] - # if quiz_log: - # if quiz_log.complete: - # if quiz_pct_score <= 59: - # status = "fail" - # elif quiz_pct_score <= 79: - # status = "borderline" - # else: - # status = "pass" - # elif quiz_log.attempts: - # status = "inprogress" - # else: - # status = "notstarted" - - # quiz_log_id = quiz_log.quiz - - # entry = { - # "id": quiz_log_id, - # "kind": "Quiz", - # "status": status, - # "score": quiz_pct_score, - # "title": playlist.get("title"), - # "path": "", - # } - - if not entry: - entry = cls.create_empty_entry(entity_id, kind, playlist) + entry = { + "id": entity_id, + "kind": kind, + "status": status, + "score": score, + "title": leaf_node["title"], + "path": leaf_node["path"], + } progress_details.append(cls(**entry)) From 1df82a56d62cf19203dbe6fb4bccba75095c7d7c Mon Sep 17 00:00:00 2001 From: Aron Fyodor Asor Date: Wed, 18 Nov 2015 08:54:16 -0800 Subject: [PATCH 35/41] add i18n tags. --- .../playlist-progress-container.handlebars | 4 ++-- .../playlist-progress-details.handlebars | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/kalite/coachreports/static/js/coachreports/student_progress/hbtemplates/playlist-progress-container.handlebars b/kalite/coachreports/static/js/coachreports/student_progress/hbtemplates/playlist-progress-container.handlebars index ecc39909c6..4c1f1010b6 100644 --- a/kalite/coachreports/static/js/coachreports/student_progress/hbtemplates/playlist-progress-container.handlebars +++ b/kalite/coachreports/static/js/coachreports/student_progress/hbtemplates/playlist-progress-container.handlebars @@ -24,13 +24,13 @@ {{/unless}} {{/if}} {{#if vid_pct_complete}} -
+
{{_ "Of those you have completed " }} {{vid_pct_complete}}%
{{/if}} {{#if vid_pct_started}} -
+
{{#ifcond vid_pct_started "<" 100}} {{_ "You are still working on " }} {{vid_pct_started}}% diff --git a/kalite/coachreports/static/js/coachreports/student_progress/hbtemplates/playlist-progress-details.handlebars b/kalite/coachreports/static/js/coachreports/student_progress/hbtemplates/playlist-progress-details.handlebars index d6f99a0919..b3390af245 100644 --- a/kalite/coachreports/static/js/coachreports/student_progress/hbtemplates/playlist-progress-details.handlebars +++ b/kalite/coachreports/static/js/coachreports/student_progress/hbtemplates/playlist-progress-details.handlebars @@ -15,10 +15,10 @@ {{#ifcond attributes.status "===" "struggling"}} {{_ " Struggling. " }} {{/ifcond}} - + {{#ifcond attributes.status "===" "notstarted"}} {{_ " Not started. " }} - {{/ifcond}} + {{/ifcond}}
{{!-- Score info for sr-user. --}} @@ -41,26 +41,26 @@ {{!-- Whole section is rendered invisible to screen readers --}}