From e7ec79ec40a47482bd5a62718e372d469ecb9b19 Mon Sep 17 00:00:00 2001 From: Benjamin Bach Date: Mon, 20 Jul 2015 23:45:51 +0200 Subject: [PATCH 01/13] Have one single KEY_PREFIX --- docs/usermanual/userman_admin.rst | 7 +++++++ kalite/settings/__init__.py | 3 --- kalite/settings/base.py | 13 +++++++------ 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/docs/usermanual/userman_admin.rst b/docs/usermanual/userman_admin.rst index 15500c429b..6670e68d80 100644 --- a/docs/usermanual/userman_admin.rst +++ b/docs/usermanual/userman_admin.rst @@ -699,6 +699,13 @@ User restrictions Disables user sign ups. +..note:: KA Lite uses caching of web pages, if you change ``LOCKDOWN`` or + ``DISABLE_SELF_ADMIN``, you need to flush the cache. To do that, run + the following management command:: + + kalite manage cache clearweb + + Online Synchronization ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/kalite/settings/__init__.py b/kalite/settings/__init__.py index 718962ab6d..3d1e648eb4 100644 --- a/kalite/settings/__init__.py +++ b/kalite/settings/__init__.py @@ -74,9 +74,6 @@ def package_selected(package_name): if package_selected("UserRestricted"): LOG.info("UserRestricted package selected.") - - if CACHE_TIME != 0 and not hasattr(local_settings, KEY_PREFIX): - KEY_PREFIX += "|restricted" # this option changes templates DISABLE_SELF_ADMIN = True # hard-code facility app setting. diff --git a/kalite/settings/base.py b/kalite/settings/base.py index 254359155b..120008d231 100644 --- a/kalite/settings/base.py +++ b/kalite/settings/base.py @@ -447,11 +447,12 @@ } } +KEY_PREFIX = version.VERSION + # Cache is activated in every case, # EXCEPT: if CACHE_TIME=0 if CACHE_TIME != 0: # None can mean infinite caching to some functions # When we change versions, cache changes, too - KEY_PREFIX = ".".join(version.VERSION) # File-based cache install_location_hash = hashlib.sha1(".".join(version.VERSION)).hexdigest() @@ -460,10 +461,10 @@ CACHE_LOCATION = os.path.realpath(getattr(local_settings, "CACHE_LOCATION", os.path.join(tempfile.gettempdir(), cache_dir_name, install_location_hash))) + "/" CACHES["file_based_cache"] = { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', - 'LOCATION': CACHE_LOCATION, # this is kind of OS-specific, so dangerous. - 'TIMEOUT': CACHE_TIME, # should be consistent + 'LOCATION': CACHE_LOCATION, # this is kind of OS-specific, so dangerous. + 'TIMEOUT': CACHE_TIME, # should be consistent 'OPTIONS': { - 'MAX_ENTRIES': getattr(local_settings, "CACHE_MAX_ENTRIES", 5*2000) #2000 entries=~10,000 files + 'MAX_ENTRIES': getattr(local_settings, "CACHE_MAX_ENTRIES", 5*2000) # 2000 entries=~10,000 files }, } @@ -471,9 +472,9 @@ CACHES["mem_cache"] = { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'unique-snowflake', - 'TIMEOUT': CACHE_TIME, # should be consistent + 'TIMEOUT': CACHE_TIME, # should be consistent 'OPTIONS': { - 'MAX_ENTRIES': getattr(local_settings, "CACHE_MAX_ENTRIES", 5*2000) #2000 entries=~10,000 files + 'MAX_ENTRIES': getattr(local_settings, "CACHE_MAX_ENTRIES", 5*2000) # 2000 entries=~10,000 files }, } From c1615ad42562eb404970dae237aa25c1842b7ee7 Mon Sep 17 00:00:00 2001 From: Benjamin Bach Date: Mon, 20 Jul 2015 23:53:09 +0200 Subject: [PATCH 02/13] put membased cache in dev settings and switch to using the DEFAULT cache by DEFAULT, saving it to a persistent dir --- kalite/project/settings/dev.py | 9 +++++-- kalite/settings/base.py | 46 +++++++++++----------------------- 2 files changed, 21 insertions(+), 34 deletions(-) diff --git a/kalite/project/settings/dev.py b/kalite/project/settings/dev.py index c19a06cad9..fa5a6e4daf 100644 --- a/kalite/project/settings/dev.py +++ b/kalite/project/settings/dev.py @@ -66,8 +66,13 @@ 'debug_toolbar.panels.logging.LoggingPanel', 'debug_toolbar.panels.redirects.RedirectsPanel', # This belongs to DISABLE_PANELS by default ) + DEBUG_TOOLBAR_CONFIG = { 'ENABLE_STACKTRACES': True, } -# Debug toolbar must be set in conjunction with CACHE_TIME=0 -CACHE_TIME = 0 + +CACHES["default"] = { + 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', + 'LOCATION': 'unique-snowflake', + 'TIMEOUT': 24 * 60 * 60 # = 24 hours +} diff --git a/kalite/settings/base.py b/kalite/settings/base.py index 120008d231..97df6f27e2 100644 --- a/kalite/settings/base.py +++ b/kalite/settings/base.py @@ -438,49 +438,31 @@ _100_years = 100 * 365 * 24 * 60 * 60 _max_cache_time = min(_100_years, sys.maxint - time.time() - _5_years) CACHE_TIME = getattr(local_settings, "CACHE_TIME", _max_cache_time) -CACHE_NAME = getattr(local_settings, "CACHE_NAME", None) # without a cache defined, None is fine # Sessions use the default cache, and we want a local memory cache for that. +CACHE_LOCATION = os.path.realpath(getattr( + local_settings, + "CACHE_LOCATION", + os.path.join( + USER_DATA_ROOT, + 'cache', + ) +)) + CACHES = { "default": { - 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', - } -} - -KEY_PREFIX = version.VERSION - -# Cache is activated in every case, -# EXCEPT: if CACHE_TIME=0 -if CACHE_TIME != 0: # None can mean infinite caching to some functions - # When we change versions, cache changes, too - - # File-based cache - install_location_hash = hashlib.sha1(".".join(version.VERSION)).hexdigest() - username = getpass.getuser() or "unknown_user" - cache_dir_name = "kalite_web_cache_%s" % (username) - CACHE_LOCATION = os.path.realpath(getattr(local_settings, "CACHE_LOCATION", os.path.join(tempfile.gettempdir(), cache_dir_name, install_location_hash))) + "/" - CACHES["file_based_cache"] = { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 'LOCATION': CACHE_LOCATION, # this is kind of OS-specific, so dangerous. 'TIMEOUT': CACHE_TIME, # should be consistent 'OPTIONS': { - 'MAX_ENTRIES': getattr(local_settings, "CACHE_MAX_ENTRIES", 5*2000) # 2000 entries=~10,000 files + 'MAX_ENTRIES': getattr(local_settings, "CACHE_MAX_ENTRIES", 5 * 2000) # 2000 entries=~10,000 files }, } +} - # Memory-based cache - CACHES["mem_cache"] = { - 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', - 'LOCATION': 'unique-snowflake', - 'TIMEOUT': CACHE_TIME, # should be consistent - 'OPTIONS': { - 'MAX_ENTRIES': getattr(local_settings, "CACHE_MAX_ENTRIES", 5*2000) # 2000 entries=~10,000 files - }, - } - - # The chosen cache - CACHE_NAME = getattr(local_settings, "CACHE_NAME", "file_based_cache") - +# Prefix the cache with the version string so we don't experience problems with +# updates +KEY_PREFIX = version.VERSION # Separate session caching from file caching. SESSION_ENGINE = getattr( From 113b9bb779a25d26147eb459ef449d8760ce71c1 Mon Sep 17 00:00:00 2001 From: Benjamin Bach Date: Mon, 20 Jul 2015 23:58:02 +0200 Subject: [PATCH 03/13] unused imports --- python-packages/fle_utils/internet/webcache.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/python-packages/fle_utils/internet/webcache.py b/python-packages/fle_utils/internet/webcache.py index 48c1f05360..3213675b5b 100644 --- a/python-packages/fle_utils/internet/webcache.py +++ b/python-packages/fle_utils/internet/webcache.py @@ -9,12 +9,10 @@ from django.core.cache.backends.filebased import FileBasedCache from django.core.cache.backends.locmem import LocMemCache from django.core.urlresolvers import reverse -from django.db.models.signals import post_save, pre_delete -from django.dispatch import receiver from django.http import HttpRequest from django.test.client import Client from django.utils import translation -from django.utils.cache import get_cache_key as django_get_cache_key, get_cache, _generate_cache_key +from django.utils.cache import get_cache_key as django_get_cache_key, get_cache from django.views.decorators.cache import cache_control from django.views.decorators.cache import cache_page from django.views.decorators.http import condition From 9e13a911082b7dd05129d473891e73fd2e3b6928 Mon Sep 17 00:00:00 2001 From: Benjamin Bach Date: Tue, 21 Jul 2015 00:00:03 +0200 Subject: [PATCH 04/13] do not use CACHE_NAME --- python-packages/fle_utils/internet/webcache.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python-packages/fle_utils/internet/webcache.py b/python-packages/fle_utils/internet/webcache.py index 3213675b5b..2ba1b761c3 100644 --- a/python-packages/fle_utils/internet/webcache.py +++ b/python-packages/fle_utils/internet/webcache.py @@ -64,7 +64,7 @@ def backend_cache_page(handler, cache_time=None, cache_name=None): cache_time = settings.CACHE_TIME if not cache_name: - cache_name = settings.CACHE_NAME + cache_name = "default" if caching_is_enabled(): @condition(last_modified_func=partial(calc_last_modified, cache_name=cache_name)) @@ -87,7 +87,7 @@ def caching_is_enabled(): return settings.CACHE_TIME != 0 def get_web_cache(): - return get_cache(settings.CACHE_NAME) if caching_is_enabled() else None + return get_cache('default') if caching_is_enabled() else None def get_cache_key(path=None, url_name=None, cache=None, failure_ok=False): From f92337984509732880ef1192e5ae4f62bf950769 Mon Sep 17 00:00:00 2001 From: Benjamin Bach Date: Tue, 21 Jul 2015 00:05:56 +0200 Subject: [PATCH 05/13] do not invalidate the web cache at startup --- kalite/distributed/management/commands/initialize_kalite.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/kalite/distributed/management/commands/initialize_kalite.py b/kalite/distributed/management/commands/initialize_kalite.py index da22a76790..de4c548650 100644 --- a/kalite/distributed/management/commands/initialize_kalite.py +++ b/kalite/distributed/management/commands/initialize_kalite.py @@ -38,9 +38,6 @@ def setup_server_if_needed(self): def reinitialize_server(self): """Reset the server state.""" - logging.info("Invalidating the web cache.") - from fle_utils.internet.webcache import invalidate_web_cache - invalidate_web_cache() # Next, call videoscan. logging.info("Running videoscan.") From c2794b2a0c94ba92ced748ebf89fbd452462d23f Mon Sep 17 00:00:00 2001 From: Benjamin Bach Date: Tue, 21 Jul 2015 00:16:43 +0200 Subject: [PATCH 06/13] unused import --- kalite/distributed/views.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/kalite/distributed/views.py b/kalite/distributed/views.py index 58fa7ab3c8..ad6c4b7227 100755 --- a/kalite/distributed/views.py +++ b/kalite/distributed/views.py @@ -24,8 +24,6 @@ from fle_utils.internet.classes import JsonResponseMessageError from fle_utils.internet.functions import get_ip_addresses, set_query_params -from fle_utils.internet.webcache import backend_cache_page -from fle_utils.django_utils.paginate import paginate_data from kalite import topic_tools from kalite.shared.decorators.auth import require_admin from securesync.api_client import BaseClient From af34eee9ef896f8e15643d56ef5e2dede224d8be Mon Sep 17 00:00:00 2001 From: Benjamin Bach Date: Tue, 21 Jul 2015 00:34:14 +0200 Subject: [PATCH 07/13] do not use disk-based cache for what's already in memory --- kalite/caching/__init__.py | 2 +- kalite/main/api_views.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/kalite/caching/__init__.py b/kalite/caching/__init__.py index 9f2c71f94b..bb0c8f5467 100644 --- a/kalite/caching/__init__.py +++ b/kalite/caching/__init__.py @@ -24,7 +24,7 @@ from django.core.urlresolvers import reverse from django.test.client import Client -from fle_utils.internet.webcache import * +from fle_utils.internet.webcache import get_web_cache, has_cache_key, expire_page, caching_is_enabled, invalidate_web_cache from kalite import i18n, topic_tools from kalite.topic_tools.settings import DO_NOT_RELOAD_CONTENT_CACHE_AT_STARTUP diff --git a/kalite/main/api_views.py b/kalite/main/api_views.py index f240aa6a39..0fa29a58c1 100755 --- a/kalite/main/api_views.py +++ b/kalite/main/api_views.py @@ -9,14 +9,12 @@ from fle_utils.internet.decorators import api_handle_error_with_json from fle_utils.internet.classes import JsonResponse, JsonResponseMessageError -from fle_utils.internet.webcache import backend_cache_page from kalite.topic_tools import get_topic_tree from kalite.topic_tools.content_recommendation import get_resume_recommendations, get_next_recommendations, get_explore_recommendations from kalite.facility.models import FacilityUser @api_handle_error_with_json -@backend_cache_page def topic_tree(request, channel): parent = request.GET.get("parent") return JsonResponse(get_topic_tree(channel=channel, language=request.language, parent=parent)) From b94d4c50771a267c65f566021d604a5a5c04c258 Mon Sep 17 00:00:00 2001 From: Aron Fyodor Asor Date: Mon, 27 Jul 2015 12:19:48 -0700 Subject: [PATCH 08/13] make sure the language-specific i18n js catalog file is loaded before en.js. Or else, we get english catalog files, i.e. it won't get translated. --- kalite/distributed/templates/distributed/base.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kalite/distributed/templates/distributed/base.html b/kalite/distributed/templates/distributed/base.html index 468b27a83d..12abce5d3b 100644 --- a/kalite/distributed/templates/distributed/base.html +++ b/kalite/distributed/templates/distributed/base.html @@ -72,6 +72,9 @@ {% endif %} + + + {% if current_language != "en" %} @@ -79,9 +82,6 @@ {% compress js file basejs %} - - - {# Older versions of IE didn't have a JSON object, so this library adds it back in if needed #} From fed31e2f8c2c72bb144795eca0ab88ace97858b1 Mon Sep 17 00:00:00 2001 From: Aron Fyodor Asor Date: Mon, 27 Jul 2015 12:25:29 -0700 Subject: [PATCH 09/13] add a few more gettext calls. --- .../static/js/coachreports/coach_reports/views.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kalite/coachreports/static/js/coachreports/coach_reports/views.js b/kalite/coachreports/static/js/coachreports/coach_reports/views.js index 4e54a634c4..66a022071c 100644 --- a/kalite/coachreports/static/js/coachreports/coach_reports/views.js +++ b/kalite/coachreports/static/js/coachreports/coach_reports/views.js @@ -327,7 +327,7 @@ var TabularReportView = BaseView.extend({ }); this.append_views(row_views, ".student-data"); - + this.$('.headrowuser').css("min-width", this.$('.headrow.data').outerWidth()); if(this.complete_callback) { @@ -429,7 +429,7 @@ var CoachSummaryView = BaseView.extend({ var ref, ref1; if ((this.data_model != null ? this.data_model.get("learner_events") != null ? this.data_model.get("learner_events").length : void 0 : void 0) === 0) { - show_message("warning", "No recent learner data for this group is available."); + show_message("warning", gettext("No recent learner data for this group is available.")); } delete this.tabular_report_view; @@ -442,12 +442,12 @@ var CoachSummaryView = BaseView.extend({ this.$("#show_tabular_report").text("Loading"); this.$("#show_tabular_report").attr("disabled", "disabled"); this.tabular_report_view = new TabularReportView({model: this.model, complete: function() { - self.$("#show_tabular_report").text("Hide Tabular Report"); + self.$("#show_tabular_report").text(gettext("Hide Tabular Report")); self.$("#show_tabular_report").removeAttr("disabled"); }}); this.$("#detailed_report_view").append(this.tabular_report_view.el); } else { - this.$("#show_tabular_report").text("Show Tabular Report"); + this.$("#show_tabular_report").text(gettext("Show Tabular Report")); this.tabular_report_view.remove(); delete this.tabular_report_view; } @@ -596,4 +596,4 @@ var CoachReportView = BaseView.extend({ this.$('#facility-select-container').append(this.facility_select_view.el); this.$("#student_report_container").append(this.coach_summary_view.el); } -}); \ No newline at end of file +}); From 47d7c269b3d1b5ef06bb34f41e381891323e075d Mon Sep 17 00:00:00 2001 From: Richard Tibbles Date: Fri, 31 Jul 2015 19:25:49 -0700 Subject: [PATCH 10/13] Implements timepicker for coach report summary view. --- kalite/coachreports/api_views.py | 19 ++++- kalite/coachreports/dynamic_assets.py | 6 ++ .../coach_nav/datepicker.handlebars | 7 ++ .../hbtemplates/coach_nav/landing.handlebars | 2 +- .../coach_nav/reports-nav.handlebars | 5 ++ .../static/css/coachreports/landing_view.css | 8 ++ .../js/coachreports/coach_reports/models.js | 6 +- .../js/coachreports/coach_reports/views.js | 73 ++++++++++++++++++- .../templates/coachreports/coach.html | 2 + .../css/bootstrap-datepicker3.min.css | 8 ++ .../js/bootstrap-datepicker.min.js | 8 ++ 11 files changed, 135 insertions(+), 9 deletions(-) create mode 100644 kalite/coachreports/dynamic_assets.py create mode 100644 kalite/coachreports/hbtemplates/coach_nav/datepicker.handlebars create mode 100644 static-libraries/css/bootstrap-datepicker3.min.css create mode 100644 static-libraries/js/bootstrap-datepicker.min.js diff --git a/kalite/coachreports/api_views.py b/kalite/coachreports/api_views.py index d09b89b14f..dbcdb8f1f4 100644 --- a/kalite/coachreports/api_views.py +++ b/kalite/coachreports/api_views.py @@ -68,6 +68,10 @@ def learner_logs(request): # Look back a week by default time_window = request.GET.get("time_window", 7) + start_date = request.GET.get("start_date", None) + + end_date = request.GET.get("end_date", None) + topic_ids = request.GET.getlist("topic_id", []) learners = get_learners_from_GET(request) @@ -84,8 +88,9 @@ def learner_logs(request): output_objects = [] - start_date = datetime.datetime.now() - datetime.timedelta(time_window) - end_date = datetime.datetime.now() + end_date = datetime.datetime.strptime(end_date,'%Y/%m/%d') if end_date else datetime.datetime.now() + + start_date = datetime.datetime.strptime(start_date,'%Y/%m/%d') if start_date else end_date - datetime.timedelta(time_window) for log_type in log_types: LogModel, fields, id_field, obj_ids, objects = return_log_type_details(log_type, topic_ids) @@ -124,6 +129,10 @@ def aggregate_learner_logs(request): # Look back a week by default time_window = request.GET.get("time_window", 7) + start_date = request.GET.get("start_date", None) + + end_date = request.GET.get("end_date", None) + topic_ids = request.GET.getlist("topic_id", []) log_types = request.GET.getlist("log_type", ["exercise", "video", "content"]) @@ -135,8 +144,10 @@ def aggregate_learner_logs(request): "exercise_attempts": 0, "exercise_mastery": None, } - start_date = datetime.datetime.now() - datetime.timedelta(time_window) - end_date = datetime.datetime.now() + + end_date = datetime.datetime.strptime(end_date,'%Y/%m/%d') if end_date else datetime.datetime.now() + + start_date = datetime.datetime.strptime(start_date,'%Y/%m/%d') if start_date else end_date - datetime.timedelta(time_window) for log_type in log_types: diff --git a/kalite/coachreports/dynamic_assets.py b/kalite/coachreports/dynamic_assets.py new file mode 100644 index 0000000000..d32125da51 --- /dev/null +++ b/kalite/coachreports/dynamic_assets.py @@ -0,0 +1,6 @@ +from django.conf import settings + +from kalite.dynamic_assets import DynamicSettingsBase, fields + +class DynamicSettings(DynamicSettingsBase): + default_coach_report_day_range = fields.IntegerField(default=getattr(settings, "DEFAULT_COACH_REPORT_DAY_RANGE", 7), minimum=0) \ No newline at end of file diff --git a/kalite/coachreports/hbtemplates/coach_nav/datepicker.handlebars b/kalite/coachreports/hbtemplates/coach_nav/datepicker.handlebars new file mode 100644 index 0000000000..1246c9484b --- /dev/null +++ b/kalite/coachreports/hbtemplates/coach_nav/datepicker.handlebars @@ -0,0 +1,7 @@ + +
+ + + + +
\ No newline at end of file diff --git a/kalite/coachreports/hbtemplates/coach_nav/landing.handlebars b/kalite/coachreports/hbtemplates/coach_nav/landing.handlebars index 54a23614f2..a876d29cc8 100644 --- a/kalite/coachreports/hbtemplates/coach_nav/landing.handlebars +++ b/kalite/coachreports/hbtemplates/coach_nav/landing.handlebars @@ -3,7 +3,7 @@

{{ status.facility_name }}{{#if status.group_name }}: {{ status.group_name }}{{/if}}{{#if data.learner_events }}{{/if}}

-

{{_ "This week" }}

+

{{#if start_date}}{{ start_date }}{{#if end_date }} → {{ end_date }}{{/if}}{{else}}{{_ "This week" }}{{/if}}

{{#if data.total_time_logged }}{{ data.total_time_logged }}{{else}}{{_ "N/A" }}{{/if}}

diff --git a/kalite/coachreports/hbtemplates/coach_nav/reports-nav.handlebars b/kalite/coachreports/hbtemplates/coach_nav/reports-nav.handlebars index 184585ee92..eb41dfa51a 100644 --- a/kalite/coachreports/hbtemplates/coach_nav/reports-nav.handlebars +++ b/kalite/coachreports/hbtemplates/coach_nav/reports-nav.handlebars @@ -9,5 +9,10 @@ {{!-- Insert group select template here --}}
+
+
+ {{!-- Insert group select template here --}} +
+
\ No newline at end of file diff --git a/kalite/coachreports/static/css/coachreports/landing_view.css b/kalite/coachreports/static/css/coachreports/landing_view.css index 82e24d1ca9..06464ec8bf 100644 --- a/kalite/coachreports/static/css/coachreports/landing_view.css +++ b/kalite/coachreports/static/css/coachreports/landing_view.css @@ -151,3 +151,11 @@ ul { color: #000; } + +#datepicker { + top: 0px; +} + +span.setrange { + color: #fff; +} \ No newline at end of file diff --git a/kalite/coachreports/static/js/coachreports/coach_reports/models.js b/kalite/coachreports/static/js/coachreports/coach_reports/models.js index 4607817aa2..8ccbc01fd7 100644 --- a/kalite/coachreports/static/js/coachreports/coach_reports/models.js +++ b/kalite/coachreports/static/js/coachreports/coach_reports/models.js @@ -46,12 +46,16 @@ var CoachReportAggregateModel = Backbone.Model.extend({ initialize: function(options) { this.facility = options.facility; this.group = options.group; + this.start_date = options.start_date; + this.end_date = options.end_date; }, url: function() { return setGetParamDict(Urls.aggregate_learner_logs(), { facility_id: this.facility, - group_id: this.group + group_id: this.group, + start_date: this.start_date, + end_date: this.end_date }); } }); \ No newline at end of file diff --git a/kalite/coachreports/static/js/coachreports/coach_reports/views.js b/kalite/coachreports/static/js/coachreports/coach_reports/views.js index 040b7c54aa..25a25ae202 100644 --- a/kalite/coachreports/static/js/coachreports/coach_reports/views.js +++ b/kalite/coachreports/static/js/coachreports/coach_reports/views.js @@ -380,6 +380,63 @@ var TabularReportView = BaseView.extend({ }); +var date_string = function(date) { + if (date) { + return date.getFullYear() + "/" + (date.getMonth() + 1) + "/" + date.getDate(); + } +} + +var TimeSetView = BaseView.extend({ + template: HB.template("coach_nav/datepicker"), + + events: { + "click .setrange": "set_range" + }, + + initialize: function () { + var server_date_now = new Date(new Date().getTime() - window.statusModel.get("client_server_time_diff")); + var default_start_date = new Date(server_date_now.getTime()) + default_start_date = new Date(default_start_date.setDate(default_start_date.getDate()-ds.coachreports.default_coach_report_day_range)); + + this.model.set({ + "start_date": default_start_date, + "end_date": server_date_now + }); + this.render(); + }, + + render: function() { + this.$el.html(this.template({ + start_date: icu.getDateFormat("SHORT").format(this.model.get("start_date")), + end_date: icu.getDateFormat("SHORT").format(this.model.get("end_date")) + })); + + var format = icu.getDateFormatSymbols().order_short; + + format = format[0] + "/" + format[1] + "/" + format[2]; + + format = format.toLowerCase().replace("y", "yy"); + + this.datepicker = this.$('.date-range').each(function(){ + $(this).datepicker({ + format: format, + endDate: "0d", + todayBtn: "linked", + todayHighlight: true + }); + }); + }, + + set_range: function() { + this.model.set({ + start_date: this.$("#start").datepicker("getDate"), + end_date: this.$("#end").datepicker("getDate") + }); + this.model.trigger("set_time"); + return false; + } +}); + var CoachSummaryView = BaseView.extend({ /* This view displays summary stats for the currently selected facility (and optionally group) @@ -395,12 +452,16 @@ var CoachSummaryView = BaseView.extend({ _.bindAll(this); this.listenTo(this.model, "change:facility", this.set_data_model); this.listenTo(this.model, "change:group", this.set_data_model); + this.listenTo(this.model, "set_time", this.set_data_model); this.set_data_model(); }, set_data_model: function (){ if (this.data_model) { - if (this.data_model.get("facility") !== this.model.get("facility") || this.data_model.get("group") !== this.model.get("group")) { + var check_fields = ["facility", "group", "start_date", "end_date"]; + var data_fields = _.pick(this.data_model.attributes, check_fields); + var status_fields = _.pick(this.model.attributes, check_fields); + if (!_.isEqual(data_fields, status_fields)) { delete this.data_model; } } @@ -408,7 +469,9 @@ var CoachSummaryView = BaseView.extend({ if (!this.data_model) { this.data_model = new CoachReportAggregateModel({ facility: this.model.get("facility"), - group: this.model.get("group") + group: this.model.get("group"), + start_date: date_string(this.model.get("start_date")), + end_date: date_string(this.model.get("end_date")) }); if (this.model.get("facility")) { this.listenTo(this.data_model, "sync", this.render); @@ -420,7 +483,9 @@ var CoachSummaryView = BaseView.extend({ render: function() { this.$el.html(this.template({ status:this.model.attributes, - data: this.data_model.attributes + data: this.data_model.attributes, + start_date: icu.getDateFormat("SHORT").format(this.model.get("start_date")), + end_date: icu.getDateFormat("SHORT").format(this.model.get("end_date")) })); clear_messages(); @@ -593,6 +658,7 @@ var CoachReportView = BaseView.extend({ this.facility_select_view = new FacilitySelectView({model: this.model}); this.group_select_view = new GroupSelectView({model: this.model}); this.coach_summary_view = new CoachSummaryView({model: this.model}); + this.time_set_view = new TimeSetView({model: this.model}); this.render(); }, @@ -601,6 +667,7 @@ var CoachReportView = BaseView.extend({ this.$el.html(this.template()); this.$('#group-select-container').append(this.group_select_view.el); this.$('#facility-select-container').append(this.facility_select_view.el); + this.$("#time-set-container").append(this.time_set_view.el); this.$("#student_report_container").append(this.coach_summary_view.el); } }); diff --git a/kalite/coachreports/templates/coachreports/coach.html b/kalite/coachreports/templates/coachreports/coach.html index 04453595ab..1d588d2b16 100644 --- a/kalite/coachreports/templates/coachreports/coach.html +++ b/kalite/coachreports/templates/coachreports/coach.html @@ -10,6 +10,7 @@ + {% endblock headcss %} {% block headjs %}{{ block.super }} @@ -18,6 +19,7 @@ var GROUP_RESOURCE_URL = "{% url 'api_dispatch_list' resource_name='group' %}"; var FACILITY_ID = {% if facility_id %}"{{ facility_id }}"{% else %}undefined{% endif %}; + diff --git a/static-libraries/css/bootstrap-datepicker3.min.css b/static-libraries/css/bootstrap-datepicker3.min.css new file mode 100644 index 0000000000..679e25cc45 --- /dev/null +++ b/static-libraries/css/bootstrap-datepicker3.min.css @@ -0,0 +1,8 @@ +/*! + * Datepicker for Bootstrap v1.4.0 (https://github.com/eternicode/bootstrap-datepicker) + * + * Copyright 2012 Stefan Petre + * Improvements by Andrew Rowls + * Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) + */ +.datepicker{padding:4px;border-radius:4px;direction:ltr}.datepicker-inline{width:220px}.datepicker.datepicker-rtl{direction:rtl}.datepicker.datepicker-rtl table tr td span{float:right}.datepicker-dropdown{top:0;left:0}.datepicker-dropdown:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-top:0;border-bottom-color:rgba(0,0,0,.2);position:absolute}.datepicker-dropdown:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;border-top:0;position:absolute}.datepicker-dropdown.datepicker-orient-left:before{left:6px}.datepicker-dropdown.datepicker-orient-left:after{left:7px}.datepicker-dropdown.datepicker-orient-right:before{right:6px}.datepicker-dropdown.datepicker-orient-right:after{right:7px}.datepicker-dropdown.datepicker-orient-top:before{top:-7px}.datepicker-dropdown.datepicker-orient-top:after{top:-6px}.datepicker-dropdown.datepicker-orient-bottom:before{bottom:-7px;border-bottom:0;border-top:7px solid #999}.datepicker-dropdown.datepicker-orient-bottom:after{bottom:-6px;border-bottom:0;border-top:6px solid #fff}.datepicker>div{display:none}.datepicker.days .datepicker-days,.datepicker.months .datepicker-months,.datepicker.years .datepicker-years{display:block}.datepicker table{margin:0;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.datepicker table tr td,.datepicker table tr th{text-align:center;width:30px;height:30px;border-radius:4px;border:none}.table-striped .datepicker table tr td,.table-striped .datepicker table tr th{background-color:transparent}.datepicker table tr td.day:hover,.datepicker table tr td.day.focused{background:#eee;cursor:pointer}.datepicker table tr td.old,.datepicker table tr td.new{color:#999}.datepicker table tr td.disabled,.datepicker table tr td.disabled:hover{background:0 0;color:#999;cursor:default}.datepicker table tr td.today,.datepicker table tr td.today:hover,.datepicker table tr td.today.disabled,.datepicker table tr td.today.disabled:hover{color:#000;background-color:#ffdb99;border-color:#ffb733}.datepicker table tr td.today:hover,.datepicker table tr td.today:hover:hover,.datepicker table tr td.today.disabled:hover,.datepicker table tr td.today.disabled:hover:hover,.datepicker table tr td.today:focus,.datepicker table tr td.today:hover:focus,.datepicker table tr td.today.disabled:focus,.datepicker table tr td.today.disabled:hover:focus,.datepicker table tr td.today:active,.datepicker table tr td.today:hover:active,.datepicker table tr td.today.disabled:active,.datepicker table tr td.today.disabled:hover:active,.datepicker table tr td.today.active,.datepicker table tr td.today:hover.active,.datepicker table tr td.today.disabled.active,.datepicker table tr td.today.disabled:hover.active,.open .dropdown-toggle.datepicker table tr td.today,.open .dropdown-toggle.datepicker table tr td.today:hover,.open .dropdown-toggle.datepicker table tr td.today.disabled,.open .dropdown-toggle.datepicker table tr td.today.disabled:hover{color:#000;background-color:#ffcd70;border-color:#f59e00}.datepicker table tr td.today:active,.datepicker table tr td.today:hover:active,.datepicker table tr td.today.disabled:active,.datepicker table tr td.today.disabled:hover:active,.datepicker table tr td.today.active,.datepicker table tr td.today:hover.active,.datepicker table tr td.today.disabled.active,.datepicker table tr td.today.disabled:hover.active,.open .dropdown-toggle.datepicker table tr td.today,.open .dropdown-toggle.datepicker table tr td.today:hover,.open .dropdown-toggle.datepicker table tr td.today.disabled,.open .dropdown-toggle.datepicker table tr td.today.disabled:hover{background-image:none}.datepicker table tr td.today.disabled,.datepicker table tr td.today:hover.disabled,.datepicker table tr td.today.disabled.disabled,.datepicker table tr td.today.disabled:hover.disabled,.datepicker table tr td.today[disabled],.datepicker table tr td.today:hover[disabled],.datepicker table tr td.today.disabled[disabled],.datepicker table tr td.today.disabled:hover[disabled],fieldset[disabled] .datepicker table tr td.today,fieldset[disabled] .datepicker table tr td.today:hover,fieldset[disabled] .datepicker table tr td.today.disabled,fieldset[disabled] .datepicker table tr td.today.disabled:hover,.datepicker table tr td.today.disabled:hover,.datepicker table tr td.today:hover.disabled:hover,.datepicker table tr td.today.disabled.disabled:hover,.datepicker table tr td.today.disabled:hover.disabled:hover,.datepicker table tr td.today[disabled]:hover,.datepicker table tr td.today:hover[disabled]:hover,.datepicker table tr td.today.disabled[disabled]:hover,.datepicker table tr td.today.disabled:hover[disabled]:hover,fieldset[disabled] .datepicker table tr td.today:hover,fieldset[disabled] .datepicker table tr td.today:hover:hover,fieldset[disabled] .datepicker table tr td.today.disabled:hover,fieldset[disabled] .datepicker table tr td.today.disabled:hover:hover,.datepicker table tr td.today.disabled:focus,.datepicker table tr td.today:hover.disabled:focus,.datepicker table tr td.today.disabled.disabled:focus,.datepicker table tr td.today.disabled:hover.disabled:focus,.datepicker table tr td.today[disabled]:focus,.datepicker table tr td.today:hover[disabled]:focus,.datepicker table tr td.today.disabled[disabled]:focus,.datepicker table tr td.today.disabled:hover[disabled]:focus,fieldset[disabled] .datepicker table tr td.today:focus,fieldset[disabled] .datepicker table tr td.today:hover:focus,fieldset[disabled] .datepicker table tr td.today.disabled:focus,fieldset[disabled] .datepicker table tr td.today.disabled:hover:focus,.datepicker table tr td.today.disabled:active,.datepicker table tr td.today:hover.disabled:active,.datepicker table tr td.today.disabled.disabled:active,.datepicker table tr td.today.disabled:hover.disabled:active,.datepicker table tr td.today[disabled]:active,.datepicker table tr td.today:hover[disabled]:active,.datepicker table tr td.today.disabled[disabled]:active,.datepicker table tr td.today.disabled:hover[disabled]:active,fieldset[disabled] .datepicker table tr td.today:active,fieldset[disabled] .datepicker table tr td.today:hover:active,fieldset[disabled] .datepicker table tr td.today.disabled:active,fieldset[disabled] .datepicker table tr td.today.disabled:hover:active,.datepicker table tr td.today.disabled.active,.datepicker table tr td.today:hover.disabled.active,.datepicker table tr td.today.disabled.disabled.active,.datepicker table tr td.today.disabled:hover.disabled.active,.datepicker table tr td.today[disabled].active,.datepicker table tr td.today:hover[disabled].active,.datepicker table tr td.today.disabled[disabled].active,.datepicker table tr td.today.disabled:hover[disabled].active,fieldset[disabled] .datepicker table tr td.today.active,fieldset[disabled] .datepicker table tr td.today:hover.active,fieldset[disabled] .datepicker table tr td.today.disabled.active,fieldset[disabled] .datepicker table tr td.today.disabled:hover.active{background-color:#ffdb99;border-color:#ffb733}.datepicker table tr td.today:hover:hover{color:#000}.datepicker table tr td.today.active:hover{color:#fff}.datepicker table tr td.range,.datepicker table tr td.range:hover,.datepicker table tr td.range.disabled,.datepicker table tr td.range.disabled:hover{background:#eee;border-radius:0}.datepicker table tr td.range.today,.datepicker table tr td.range.today:hover,.datepicker table tr td.range.today.disabled,.datepicker table tr td.range.today.disabled:hover{color:#000;background-color:#f7ca77;border-color:#f1a417;border-radius:0}.datepicker table tr td.range.today:hover,.datepicker table tr td.range.today:hover:hover,.datepicker table tr td.range.today.disabled:hover,.datepicker table tr td.range.today.disabled:hover:hover,.datepicker table tr td.range.today:focus,.datepicker table tr td.range.today:hover:focus,.datepicker table tr td.range.today.disabled:focus,.datepicker table tr td.range.today.disabled:hover:focus,.datepicker table tr td.range.today:active,.datepicker table tr td.range.today:hover:active,.datepicker table tr td.range.today.disabled:active,.datepicker table tr td.range.today.disabled:hover:active,.datepicker table tr td.range.today.active,.datepicker table tr td.range.today:hover.active,.datepicker table tr td.range.today.disabled.active,.datepicker table tr td.range.today.disabled:hover.active,.open .dropdown-toggle.datepicker table tr td.range.today,.open .dropdown-toggle.datepicker table tr td.range.today:hover,.open .dropdown-toggle.datepicker table tr td.range.today.disabled,.open .dropdown-toggle.datepicker table tr td.range.today.disabled:hover{color:#000;background-color:#f4bb51;border-color:#bf800c}.datepicker table tr td.range.today:active,.datepicker table tr td.range.today:hover:active,.datepicker table tr td.range.today.disabled:active,.datepicker table tr td.range.today.disabled:hover:active,.datepicker table tr td.range.today.active,.datepicker table tr td.range.today:hover.active,.datepicker table tr td.range.today.disabled.active,.datepicker table tr td.range.today.disabled:hover.active,.open .dropdown-toggle.datepicker table tr td.range.today,.open .dropdown-toggle.datepicker table tr td.range.today:hover,.open .dropdown-toggle.datepicker table tr td.range.today.disabled,.open .dropdown-toggle.datepicker table tr td.range.today.disabled:hover{background-image:none}.datepicker table tr td.range.today.disabled,.datepicker table tr td.range.today:hover.disabled,.datepicker table tr td.range.today.disabled.disabled,.datepicker table tr td.range.today.disabled:hover.disabled,.datepicker table tr td.range.today[disabled],.datepicker table tr td.range.today:hover[disabled],.datepicker table tr td.range.today.disabled[disabled],.datepicker table tr td.range.today.disabled:hover[disabled],fieldset[disabled] .datepicker table tr td.range.today,fieldset[disabled] .datepicker table tr td.range.today:hover,fieldset[disabled] .datepicker table tr td.range.today.disabled,fieldset[disabled] .datepicker table tr td.range.today.disabled:hover,.datepicker table tr td.range.today.disabled:hover,.datepicker table tr td.range.today:hover.disabled:hover,.datepicker table tr td.range.today.disabled.disabled:hover,.datepicker table tr td.range.today.disabled:hover.disabled:hover,.datepicker table tr td.range.today[disabled]:hover,.datepicker table tr td.range.today:hover[disabled]:hover,.datepicker table tr td.range.today.disabled[disabled]:hover,.datepicker table tr td.range.today.disabled:hover[disabled]:hover,fieldset[disabled] .datepicker table tr td.range.today:hover,fieldset[disabled] .datepicker table tr td.range.today:hover:hover,fieldset[disabled] .datepicker table tr td.range.today.disabled:hover,fieldset[disabled] .datepicker table tr td.range.today.disabled:hover:hover,.datepicker table tr td.range.today.disabled:focus,.datepicker table tr td.range.today:hover.disabled:focus,.datepicker table tr td.range.today.disabled.disabled:focus,.datepicker table tr td.range.today.disabled:hover.disabled:focus,.datepicker table tr td.range.today[disabled]:focus,.datepicker table tr td.range.today:hover[disabled]:focus,.datepicker table tr td.range.today.disabled[disabled]:focus,.datepicker table tr td.range.today.disabled:hover[disabled]:focus,fieldset[disabled] .datepicker table tr td.range.today:focus,fieldset[disabled] .datepicker table tr td.range.today:hover:focus,fieldset[disabled] .datepicker table tr td.range.today.disabled:focus,fieldset[disabled] .datepicker table tr td.range.today.disabled:hover:focus,.datepicker table tr td.range.today.disabled:active,.datepicker table tr td.range.today:hover.disabled:active,.datepicker table tr td.range.today.disabled.disabled:active,.datepicker table tr td.range.today.disabled:hover.disabled:active,.datepicker table tr td.range.today[disabled]:active,.datepicker table tr td.range.today:hover[disabled]:active,.datepicker table tr td.range.today.disabled[disabled]:active,.datepicker table tr td.range.today.disabled:hover[disabled]:active,fieldset[disabled] .datepicker table tr td.range.today:active,fieldset[disabled] .datepicker table tr td.range.today:hover:active,fieldset[disabled] .datepicker table tr td.range.today.disabled:active,fieldset[disabled] .datepicker table tr td.range.today.disabled:hover:active,.datepicker table tr td.range.today.disabled.active,.datepicker table tr td.range.today:hover.disabled.active,.datepicker table tr td.range.today.disabled.disabled.active,.datepicker table tr td.range.today.disabled:hover.disabled.active,.datepicker table tr td.range.today[disabled].active,.datepicker table tr td.range.today:hover[disabled].active,.datepicker table tr td.range.today.disabled[disabled].active,.datepicker table tr td.range.today.disabled:hover[disabled].active,fieldset[disabled] .datepicker table tr td.range.today.active,fieldset[disabled] .datepicker table tr td.range.today:hover.active,fieldset[disabled] .datepicker table tr td.range.today.disabled.active,fieldset[disabled] .datepicker table tr td.range.today.disabled:hover.active{background-color:#f7ca77;border-color:#f1a417}.datepicker table tr td.selected,.datepicker table tr td.selected:hover,.datepicker table tr td.selected.disabled,.datepicker table tr td.selected.disabled:hover{color:#fff;background-color:#999;border-color:#555;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.datepicker table tr td.selected:hover,.datepicker table tr td.selected:hover:hover,.datepicker table tr td.selected.disabled:hover,.datepicker table tr td.selected.disabled:hover:hover,.datepicker table tr td.selected:focus,.datepicker table tr td.selected:hover:focus,.datepicker table tr td.selected.disabled:focus,.datepicker table tr td.selected.disabled:hover:focus,.datepicker table tr td.selected:active,.datepicker table tr td.selected:hover:active,.datepicker table tr td.selected.disabled:active,.datepicker table tr td.selected.disabled:hover:active,.datepicker table tr td.selected.active,.datepicker table tr td.selected:hover.active,.datepicker table tr td.selected.disabled.active,.datepicker table tr td.selected.disabled:hover.active,.open .dropdown-toggle.datepicker table tr td.selected,.open .dropdown-toggle.datepicker table tr td.selected:hover,.open .dropdown-toggle.datepicker table tr td.selected.disabled,.open .dropdown-toggle.datepicker table tr td.selected.disabled:hover{color:#fff;background-color:#858585;border-color:#373737}.datepicker table tr td.selected:active,.datepicker table tr td.selected:hover:active,.datepicker table tr td.selected.disabled:active,.datepicker table tr td.selected.disabled:hover:active,.datepicker table tr td.selected.active,.datepicker table tr td.selected:hover.active,.datepicker table tr td.selected.disabled.active,.datepicker table tr td.selected.disabled:hover.active,.open .dropdown-toggle.datepicker table tr td.selected,.open .dropdown-toggle.datepicker table tr td.selected:hover,.open .dropdown-toggle.datepicker table tr td.selected.disabled,.open .dropdown-toggle.datepicker table tr td.selected.disabled:hover{background-image:none}.datepicker table tr td.selected.disabled,.datepicker table tr td.selected:hover.disabled,.datepicker table tr td.selected.disabled.disabled,.datepicker table tr td.selected.disabled:hover.disabled,.datepicker table tr td.selected[disabled],.datepicker table tr td.selected:hover[disabled],.datepicker table tr td.selected.disabled[disabled],.datepicker table tr td.selected.disabled:hover[disabled],fieldset[disabled] .datepicker table tr td.selected,fieldset[disabled] .datepicker table tr td.selected:hover,fieldset[disabled] .datepicker table tr td.selected.disabled,fieldset[disabled] .datepicker table tr td.selected.disabled:hover,.datepicker table tr td.selected.disabled:hover,.datepicker table tr td.selected:hover.disabled:hover,.datepicker table tr td.selected.disabled.disabled:hover,.datepicker table tr td.selected.disabled:hover.disabled:hover,.datepicker table tr td.selected[disabled]:hover,.datepicker table tr td.selected:hover[disabled]:hover,.datepicker table tr td.selected.disabled[disabled]:hover,.datepicker table tr td.selected.disabled:hover[disabled]:hover,fieldset[disabled] .datepicker table tr td.selected:hover,fieldset[disabled] .datepicker table tr td.selected:hover:hover,fieldset[disabled] .datepicker table tr td.selected.disabled:hover,fieldset[disabled] .datepicker table tr td.selected.disabled:hover:hover,.datepicker table tr td.selected.disabled:focus,.datepicker table tr td.selected:hover.disabled:focus,.datepicker table tr td.selected.disabled.disabled:focus,.datepicker table tr td.selected.disabled:hover.disabled:focus,.datepicker table tr td.selected[disabled]:focus,.datepicker table tr td.selected:hover[disabled]:focus,.datepicker table tr td.selected.disabled[disabled]:focus,.datepicker table tr td.selected.disabled:hover[disabled]:focus,fieldset[disabled] .datepicker table tr td.selected:focus,fieldset[disabled] .datepicker table tr td.selected:hover:focus,fieldset[disabled] .datepicker table tr td.selected.disabled:focus,fieldset[disabled] .datepicker table tr td.selected.disabled:hover:focus,.datepicker table tr td.selected.disabled:active,.datepicker table tr td.selected:hover.disabled:active,.datepicker table tr td.selected.disabled.disabled:active,.datepicker table tr td.selected.disabled:hover.disabled:active,.datepicker table tr td.selected[disabled]:active,.datepicker table tr td.selected:hover[disabled]:active,.datepicker table tr td.selected.disabled[disabled]:active,.datepicker table tr td.selected.disabled:hover[disabled]:active,fieldset[disabled] .datepicker table tr td.selected:active,fieldset[disabled] .datepicker table tr td.selected:hover:active,fieldset[disabled] .datepicker table tr td.selected.disabled:active,fieldset[disabled] .datepicker table tr td.selected.disabled:hover:active,.datepicker table tr td.selected.disabled.active,.datepicker table tr td.selected:hover.disabled.active,.datepicker table tr td.selected.disabled.disabled.active,.datepicker table tr td.selected.disabled:hover.disabled.active,.datepicker table tr td.selected[disabled].active,.datepicker table tr td.selected:hover[disabled].active,.datepicker table tr td.selected.disabled[disabled].active,.datepicker table tr td.selected.disabled:hover[disabled].active,fieldset[disabled] .datepicker table tr td.selected.active,fieldset[disabled] .datepicker table tr td.selected:hover.active,fieldset[disabled] .datepicker table tr td.selected.disabled.active,fieldset[disabled] .datepicker table tr td.selected.disabled:hover.active{background-color:#999;border-color:#555}.datepicker table tr td.active,.datepicker table tr td.active:hover,.datepicker table tr td.active.disabled,.datepicker table tr td.active.disabled:hover{color:#fff;background-color:#428bca;border-color:#357ebd;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.datepicker table tr td.active:hover,.datepicker table tr td.active:hover:hover,.datepicker table tr td.active.disabled:hover,.datepicker table tr td.active.disabled:hover:hover,.datepicker table tr td.active:focus,.datepicker table tr td.active:hover:focus,.datepicker table tr td.active.disabled:focus,.datepicker table tr td.active.disabled:hover:focus,.datepicker table tr td.active:active,.datepicker table tr td.active:hover:active,.datepicker table tr td.active.disabled:active,.datepicker table tr td.active.disabled:hover:active,.datepicker table tr td.active.active,.datepicker table tr td.active:hover.active,.datepicker table tr td.active.disabled.active,.datepicker table tr td.active.disabled:hover.active,.open .dropdown-toggle.datepicker table tr td.active,.open .dropdown-toggle.datepicker table tr td.active:hover,.open .dropdown-toggle.datepicker table tr td.active.disabled,.open .dropdown-toggle.datepicker table tr td.active.disabled:hover{color:#fff;background-color:#3276b1;border-color:#285e8e}.datepicker table tr td.active:active,.datepicker table tr td.active:hover:active,.datepicker table tr td.active.disabled:active,.datepicker table tr td.active.disabled:hover:active,.datepicker table tr td.active.active,.datepicker table tr td.active:hover.active,.datepicker table tr td.active.disabled.active,.datepicker table tr td.active.disabled:hover.active,.open .dropdown-toggle.datepicker table tr td.active,.open .dropdown-toggle.datepicker table tr td.active:hover,.open .dropdown-toggle.datepicker table tr td.active.disabled,.open .dropdown-toggle.datepicker table tr td.active.disabled:hover{background-image:none}.datepicker table tr td.active.disabled,.datepicker table tr td.active:hover.disabled,.datepicker table tr td.active.disabled.disabled,.datepicker table tr td.active.disabled:hover.disabled,.datepicker table tr td.active[disabled],.datepicker table tr td.active:hover[disabled],.datepicker table tr td.active.disabled[disabled],.datepicker table tr td.active.disabled:hover[disabled],fieldset[disabled] .datepicker table tr td.active,fieldset[disabled] .datepicker table tr td.active:hover,fieldset[disabled] .datepicker table tr td.active.disabled,fieldset[disabled] .datepicker table tr td.active.disabled:hover,.datepicker table tr td.active.disabled:hover,.datepicker table tr td.active:hover.disabled:hover,.datepicker table tr td.active.disabled.disabled:hover,.datepicker table tr td.active.disabled:hover.disabled:hover,.datepicker table tr td.active[disabled]:hover,.datepicker table tr td.active:hover[disabled]:hover,.datepicker table tr td.active.disabled[disabled]:hover,.datepicker table tr td.active.disabled:hover[disabled]:hover,fieldset[disabled] .datepicker table tr td.active:hover,fieldset[disabled] .datepicker table tr td.active:hover:hover,fieldset[disabled] .datepicker table tr td.active.disabled:hover,fieldset[disabled] .datepicker table tr td.active.disabled:hover:hover,.datepicker table tr td.active.disabled:focus,.datepicker table tr td.active:hover.disabled:focus,.datepicker table tr td.active.disabled.disabled:focus,.datepicker table tr td.active.disabled:hover.disabled:focus,.datepicker table tr td.active[disabled]:focus,.datepicker table tr td.active:hover[disabled]:focus,.datepicker table tr td.active.disabled[disabled]:focus,.datepicker table tr td.active.disabled:hover[disabled]:focus,fieldset[disabled] .datepicker table tr td.active:focus,fieldset[disabled] .datepicker table tr td.active:hover:focus,fieldset[disabled] .datepicker table tr td.active.disabled:focus,fieldset[disabled] .datepicker table tr td.active.disabled:hover:focus,.datepicker table tr td.active.disabled:active,.datepicker table tr td.active:hover.disabled:active,.datepicker table tr td.active.disabled.disabled:active,.datepicker table tr td.active.disabled:hover.disabled:active,.datepicker table tr td.active[disabled]:active,.datepicker table tr td.active:hover[disabled]:active,.datepicker table tr td.active.disabled[disabled]:active,.datepicker table tr td.active.disabled:hover[disabled]:active,fieldset[disabled] .datepicker table tr td.active:active,fieldset[disabled] .datepicker table tr td.active:hover:active,fieldset[disabled] .datepicker table tr td.active.disabled:active,fieldset[disabled] .datepicker table tr td.active.disabled:hover:active,.datepicker table tr td.active.disabled.active,.datepicker table tr td.active:hover.disabled.active,.datepicker table tr td.active.disabled.disabled.active,.datepicker table tr td.active.disabled:hover.disabled.active,.datepicker table tr td.active[disabled].active,.datepicker table tr td.active:hover[disabled].active,.datepicker table tr td.active.disabled[disabled].active,.datepicker table tr td.active.disabled:hover[disabled].active,fieldset[disabled] .datepicker table tr td.active.active,fieldset[disabled] .datepicker table tr td.active:hover.active,fieldset[disabled] .datepicker table tr td.active.disabled.active,fieldset[disabled] .datepicker table tr td.active.disabled:hover.active{background-color:#428bca;border-color:#357ebd}.datepicker table tr td span{display:block;width:23%;height:54px;line-height:54px;float:left;margin:1%;cursor:pointer;border-radius:4px}.datepicker table tr td span:hover{background:#eee}.datepicker table tr td span.disabled,.datepicker table tr td span.disabled:hover{background:0 0;color:#999;cursor:default}.datepicker table tr td span.active,.datepicker table tr td span.active:hover,.datepicker table tr td span.active.disabled,.datepicker table tr td span.active.disabled:hover{color:#fff;background-color:#428bca;border-color:#357ebd;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.datepicker table tr td span.active:hover,.datepicker table tr td span.active:hover:hover,.datepicker table tr td span.active.disabled:hover,.datepicker table tr td span.active.disabled:hover:hover,.datepicker table tr td span.active:focus,.datepicker table tr td span.active:hover:focus,.datepicker table tr td span.active.disabled:focus,.datepicker table tr td span.active.disabled:hover:focus,.datepicker table tr td span.active:active,.datepicker table tr td span.active:hover:active,.datepicker table tr td span.active.disabled:active,.datepicker table tr td span.active.disabled:hover:active,.datepicker table tr td span.active.active,.datepicker table tr td span.active:hover.active,.datepicker table tr td span.active.disabled.active,.datepicker table tr td span.active.disabled:hover.active,.open .dropdown-toggle.datepicker table tr td span.active,.open .dropdown-toggle.datepicker table tr td span.active:hover,.open .dropdown-toggle.datepicker table tr td span.active.disabled,.open .dropdown-toggle.datepicker table tr td span.active.disabled:hover{color:#fff;background-color:#3276b1;border-color:#285e8e}.datepicker table tr td span.active:active,.datepicker table tr td span.active:hover:active,.datepicker table tr td span.active.disabled:active,.datepicker table tr td span.active.disabled:hover:active,.datepicker table tr td span.active.active,.datepicker table tr td span.active:hover.active,.datepicker table tr td span.active.disabled.active,.datepicker table tr td span.active.disabled:hover.active,.open .dropdown-toggle.datepicker table tr td span.active,.open .dropdown-toggle.datepicker table tr td span.active:hover,.open .dropdown-toggle.datepicker table tr td span.active.disabled,.open .dropdown-toggle.datepicker table tr td span.active.disabled:hover{background-image:none}.datepicker table tr td span.active.disabled,.datepicker table tr td span.active:hover.disabled,.datepicker table tr td span.active.disabled.disabled,.datepicker table tr td span.active.disabled:hover.disabled,.datepicker table tr td span.active[disabled],.datepicker table tr td span.active:hover[disabled],.datepicker table tr td span.active.disabled[disabled],.datepicker table tr td span.active.disabled:hover[disabled],fieldset[disabled] .datepicker table tr td span.active,fieldset[disabled] .datepicker table tr td span.active:hover,fieldset[disabled] .datepicker table tr td span.active.disabled,fieldset[disabled] .datepicker table tr td span.active.disabled:hover,.datepicker table tr td span.active.disabled:hover,.datepicker table tr td span.active:hover.disabled:hover,.datepicker table tr td span.active.disabled.disabled:hover,.datepicker table tr td span.active.disabled:hover.disabled:hover,.datepicker table tr td span.active[disabled]:hover,.datepicker table tr td span.active:hover[disabled]:hover,.datepicker table tr td span.active.disabled[disabled]:hover,.datepicker table tr td span.active.disabled:hover[disabled]:hover,fieldset[disabled] .datepicker table tr td span.active:hover,fieldset[disabled] .datepicker table tr td span.active:hover:hover,fieldset[disabled] .datepicker table tr td span.active.disabled:hover,fieldset[disabled] .datepicker table tr td span.active.disabled:hover:hover,.datepicker table tr td span.active.disabled:focus,.datepicker table tr td span.active:hover.disabled:focus,.datepicker table tr td span.active.disabled.disabled:focus,.datepicker table tr td span.active.disabled:hover.disabled:focus,.datepicker table tr td span.active[disabled]:focus,.datepicker table tr td span.active:hover[disabled]:focus,.datepicker table tr td span.active.disabled[disabled]:focus,.datepicker table tr td span.active.disabled:hover[disabled]:focus,fieldset[disabled] .datepicker table tr td span.active:focus,fieldset[disabled] .datepicker table tr td span.active:hover:focus,fieldset[disabled] .datepicker table tr td span.active.disabled:focus,fieldset[disabled] .datepicker table tr td span.active.disabled:hover:focus,.datepicker table tr td span.active.disabled:active,.datepicker table tr td span.active:hover.disabled:active,.datepicker table tr td span.active.disabled.disabled:active,.datepicker table tr td span.active.disabled:hover.disabled:active,.datepicker table tr td span.active[disabled]:active,.datepicker table tr td span.active:hover[disabled]:active,.datepicker table tr td span.active.disabled[disabled]:active,.datepicker table tr td span.active.disabled:hover[disabled]:active,fieldset[disabled] .datepicker table tr td span.active:active,fieldset[disabled] .datepicker table tr td span.active:hover:active,fieldset[disabled] .datepicker table tr td span.active.disabled:active,fieldset[disabled] .datepicker table tr td span.active.disabled:hover:active,.datepicker table tr td span.active.disabled.active,.datepicker table tr td span.active:hover.disabled.active,.datepicker table tr td span.active.disabled.disabled.active,.datepicker table tr td span.active.disabled:hover.disabled.active,.datepicker table tr td span.active[disabled].active,.datepicker table tr td span.active:hover[disabled].active,.datepicker table tr td span.active.disabled[disabled].active,.datepicker table tr td span.active.disabled:hover[disabled].active,fieldset[disabled] .datepicker table tr td span.active.active,fieldset[disabled] .datepicker table tr td span.active:hover.active,fieldset[disabled] .datepicker table tr td span.active.disabled.active,fieldset[disabled] .datepicker table tr td span.active.disabled:hover.active{background-color:#428bca;border-color:#357ebd}.datepicker table tr td span.old,.datepicker table tr td span.new{color:#999}.datepicker .datepicker-switch{width:145px}.datepicker thead tr:first-child th,.datepicker tfoot tr th{cursor:pointer}.datepicker thead tr:first-child th:hover,.datepicker tfoot tr th:hover{background:#eee}.datepicker .cw{font-size:10px;width:12px;padding:0 2px 0 5px;vertical-align:middle}.datepicker thead tr:first-child .cw{cursor:default;background-color:transparent}.input-group.date .input-group-addon{cursor:pointer}.input-daterange{width:100%}.input-daterange input{text-align:center}.input-daterange input:first-child{border-radius:3px 0 0 3px}.input-daterange input:last-child{border-radius:0 3px 3px 0}.input-daterange .input-group-addon{width:auto;min-width:16px;padding:4px 5px;font-weight:400;line-height:1.42857143;text-align:center;text-shadow:0 1px 0 #fff;vertical-align:middle;background-color:#eee;border:solid #ccc;border-width:1px 0;margin-left:-5px;margin-right:-5px} \ No newline at end of file diff --git a/static-libraries/js/bootstrap-datepicker.min.js b/static-libraries/js/bootstrap-datepicker.min.js new file mode 100644 index 0000000000..f26d0d8674 --- /dev/null +++ b/static-libraries/js/bootstrap-datepicker.min.js @@ -0,0 +1,8 @@ +/*! + * Datepicker for Bootstrap v1.4.0 (https://github.com/eternicode/bootstrap-datepicker) + * + * Copyright 2012 Stefan Petre + * Improvements by Andrew Rowls + * Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) + */ +!function(a,b){function c(){return new Date(Date.UTC.apply(Date,arguments))}function d(){var a=new Date;return c(a.getFullYear(),a.getMonth(),a.getDate())}function e(a,b){return a.getUTCFullYear()===b.getUTCFullYear()&&a.getUTCMonth()===b.getUTCMonth()&&a.getUTCDate()===b.getUTCDate()}function f(a){return function(){return this[a].apply(this,arguments)}}function g(b,c){function d(a,b){return b.toLowerCase()}var e,f=a(b).data(),g={},h=new RegExp("^"+c.toLowerCase()+"([A-Z])");c=new RegExp("^"+c.toLowerCase());for(var i in f)c.test(i)&&(e=i.replace(h,d),g[e]=f[i]);return g}function h(b){var c={};if(p[b]||(b=b.split("-")[0],p[b])){var d=p[b];return a.each(o,function(a,b){b in d&&(c[b]=d[b])}),c}}var i=function(){var b={get:function(a){return this.slice(a)[0]},contains:function(a){for(var b=a&&a.valueOf(),c=0,d=this.length;d>c;c++)if(this[c].valueOf()===b)return c;return-1},remove:function(a){this.splice(a,1)},replace:function(b){b&&(a.isArray(b)||(b=[b]),this.clear(),this.push.apply(this,b))},clear:function(){this.length=0},copy:function(){var a=new i;return a.replace(this),a}};return function(){var c=[];return c.push.apply(c,arguments),a.extend(c,b),c}}(),j=function(b,c){this._process_options(c),this.dates=new i,this.viewDate=this.o.defaultViewDate,this.focusDate=null,this.element=a(b),this.isInline=!1,this.isInput=this.element.is("input"),this.component=this.element.hasClass("date")?this.element.find(".add-on, .input-group-addon, .btn"):!1,this.hasInput=this.component&&this.element.find("input").length,this.component&&0===this.component.length&&(this.component=!1),this.picker=a(q.template),this._buildEvents(),this._attachEvents(),this.isInline?this.picker.addClass("datepicker-inline").appendTo(this.element):this.picker.addClass("datepicker-dropdown dropdown-menu"),this.o.rtl&&this.picker.addClass("datepicker-rtl"),this.viewMode=this.o.startView,this.o.calendarWeeks&&this.picker.find("tfoot .today, tfoot .clear").attr("colspan",function(a,b){return parseInt(b)+1}),this._allow_update=!1,this.setStartDate(this._o.startDate),this.setEndDate(this._o.endDate),this.setDaysOfWeekDisabled(this.o.daysOfWeekDisabled),this.setDatesDisabled(this.o.datesDisabled),this.fillDow(),this.fillMonths(),this._allow_update=!0,this.update(),this.showMode(),this.isInline&&this.show()};j.prototype={constructor:j,_process_options:function(e){this._o=a.extend({},this._o,e);var f=this.o=a.extend({},this._o),g=f.language;switch(p[g]||(g=g.split("-")[0],p[g]||(g=n.language)),f.language=g,f.startView){case 2:case"decade":f.startView=2;break;case 1:case"year":f.startView=1;break;default:f.startView=0}switch(f.minViewMode){case 1:case"months":f.minViewMode=1;break;case 2:case"years":f.minViewMode=2;break;default:f.minViewMode=0}f.startView=Math.max(f.startView,f.minViewMode),f.multidate!==!0&&(f.multidate=Number(f.multidate)||!1,f.multidate!==!1&&(f.multidate=Math.max(0,f.multidate))),f.multidateSeparator=String(f.multidateSeparator),f.weekStart%=7,f.weekEnd=(f.weekStart+6)%7;var h=q.parseFormat(f.format);if(f.startDate!==-1/0&&(f.startDate=f.startDate?f.startDate instanceof Date?this._local_to_utc(this._zero_time(f.startDate)):q.parseDate(f.startDate,h,f.language):-1/0),1/0!==f.endDate&&(f.endDate=f.endDate?f.endDate instanceof Date?this._local_to_utc(this._zero_time(f.endDate)):q.parseDate(f.endDate,h,f.language):1/0),f.daysOfWeekDisabled=f.daysOfWeekDisabled||[],a.isArray(f.daysOfWeekDisabled)||(f.daysOfWeekDisabled=f.daysOfWeekDisabled.split(/[,\s]*/)),f.daysOfWeekDisabled=a.map(f.daysOfWeekDisabled,function(a){return parseInt(a,10)}),f.datesDisabled=f.datesDisabled||[],!a.isArray(f.datesDisabled)){var i=[];i.push(q.parseDate(f.datesDisabled,h,f.language)),f.datesDisabled=i}f.datesDisabled=a.map(f.datesDisabled,function(a){return q.parseDate(a,h,f.language)});var j=String(f.orientation).toLowerCase().split(/\s+/g),k=f.orientation.toLowerCase();if(j=a.grep(j,function(a){return/^auto|left|right|top|bottom$/.test(a)}),f.orientation={x:"auto",y:"auto"},k&&"auto"!==k)if(1===j.length)switch(j[0]){case"top":case"bottom":f.orientation.y=j[0];break;case"left":case"right":f.orientation.x=j[0]}else k=a.grep(j,function(a){return/^left|right$/.test(a)}),f.orientation.x=k[0]||"auto",k=a.grep(j,function(a){return/^top|bottom$/.test(a)}),f.orientation.y=k[0]||"auto";else;if(f.defaultViewDate){var l=f.defaultViewDate.year||(new Date).getFullYear(),m=f.defaultViewDate.month||0,o=f.defaultViewDate.day||1;f.defaultViewDate=c(l,m,o)}else f.defaultViewDate=d();f.showOnFocus=f.showOnFocus!==b?f.showOnFocus:!0},_events:[],_secondaryEvents:[],_applyEvents:function(a){for(var c,d,e,f=0;fe?(this.picker.addClass("datepicker-orient-right"),n=k.left+m-b):this.picker.addClass("datepicker-orient-left");var p,q,r=this.o.orientation.y;if("auto"===r&&(p=-g+o-c,q=g+f-(o+l+c),r=Math.max(p,q)===q?"top":"bottom"),this.picker.addClass("datepicker-orient-"+r),"top"===r?o+=l:o-=c+parseInt(this.picker.css("padding-top")),this.o.rtl){var s=e-(n+m);this.picker.css({top:o,right:s,zIndex:j})}else this.picker.css({top:o,left:n,zIndex:j});return this},_allow_update:!0,update:function(){if(!this._allow_update)return this;var b=this.dates.copy(),c=[],d=!1;return arguments.length?(a.each(arguments,a.proxy(function(a,b){b instanceof Date&&(b=this._local_to_utc(b)),c.push(b)},this)),d=!0):(c=this.isInput?this.element.val():this.element.data("date")||this.element.find("input").val(),c=c&&this.o.multidate?c.split(this.o.multidateSeparator):[c],delete this.element.data().date),c=a.map(c,a.proxy(function(a){return q.parseDate(a,this.o.format,this.o.language)},this)),c=a.grep(c,a.proxy(function(a){return athis.o.endDate||!a},this),!0),this.dates.replace(c),this.dates.length?this.viewDate=new Date(this.dates.get(-1)):this.viewDatethis.o.endDate&&(this.viewDate=new Date(this.o.endDate)),d?this.setValue():c.length&&String(b)!==String(this.dates)&&this._trigger("changeDate"),!this.dates.length&&b.length&&this._trigger("clearDate"),this.fill(),this},fillDow:function(){var a=this.o.weekStart,b="";if(this.o.calendarWeeks){this.picker.find(".datepicker-days thead tr:first-child .datepicker-switch").attr("colspan",function(a,b){return parseInt(b)+1});var c=' ';b+=c}for(;a'+p[this.o.language].daysMin[a++%7]+"";b+="",this.picker.find(".datepicker-days thead").append(b)},fillMonths:function(){for(var a="",b=0;12>b;)a+=''+p[this.o.language].monthsShort[b++]+"";this.picker.find(".datepicker-months td").html(a)},setRange:function(b){b&&b.length?this.range=a.map(b,function(a){return a.valueOf()}):delete this.range,this.fill()},getClassNames:function(b){var c=[],d=this.viewDate.getUTCFullYear(),f=this.viewDate.getUTCMonth(),g=new Date;return b.getUTCFullYear()d||b.getUTCFullYear()===d&&b.getUTCMonth()>f)&&c.push("new"),this.focusDate&&b.valueOf()===this.focusDate.valueOf()&&c.push("focused"),this.o.todayHighlight&&b.getUTCFullYear()===g.getFullYear()&&b.getUTCMonth()===g.getMonth()&&b.getUTCDate()===g.getDate()&&c.push("today"),-1!==this.dates.contains(b)&&c.push("active"),(b.valueOf()this.o.endDate||-1!==a.inArray(b.getUTCDay(),this.o.daysOfWeekDisabled))&&c.push("disabled"),this.o.datesDisabled.length>0&&a.grep(this.o.datesDisabled,function(a){return e(b,a)}).length>0&&c.push("disabled","disabled-date"),this.range&&(b>this.range[0]&&b"),this.o.calendarWeeks)){var u=new Date(+n+(this.o.weekStart-n.getUTCDay()-7)%7*864e5),v=new Date(Number(u)+(11-u.getUTCDay())%7*864e5),w=new Date(Number(w=c(v.getUTCFullYear(),0,1))+(11-w.getUTCDay())%7*864e5),x=(v-w)/864e5/7+1;t.push(''+x+"")}if(s=this.getClassNames(n),s.push("day"),this.o.beforeShowDay!==a.noop){var y=this.o.beforeShowDay(this._utc_to_local(n));y===b?y={}:"boolean"==typeof y?y={enabled:y}:"string"==typeof y&&(y={classes:y}),y.enabled===!1&&s.push("disabled"),y.classes&&(s=s.concat(y.classes.split(/\s+/))),y.tooltip&&(d=y.tooltip)}s=a.unique(s),t.push('"+n.getUTCDate()+""),d=null,n.getUTCDay()===this.o.weekEnd&&t.push(""),n.setUTCDate(n.getUTCDate()+1)}this.picker.find(".datepicker-days tbody").empty().append(t.join(""));var z=this.picker.find(".datepicker-months").find("th:eq(1)").text(f).end().find("span").removeClass("active");if(a.each(this.dates,function(a,b){b.getUTCFullYear()===f&&z.eq(b.getUTCMonth()).addClass("active")}),(h>f||f>j)&&z.addClass("disabled"),f===h&&z.slice(0,i).addClass("disabled"),f===j&&z.slice(k+1).addClass("disabled"),this.o.beforeShowMonth!==a.noop){var A=this;a.each(z,function(b,c){if(!a(c).hasClass("disabled")){var d=new Date(f,b,1),e=A.o.beforeShowMonth(d);e===!1&&a(c).addClass("disabled")}})}t="",f=10*parseInt(f/10,10);var B=this.picker.find(".datepicker-years").find("th:eq(1)").text(f+"-"+(f+9)).end().find("td");f-=1;for(var C,D=a.map(this.dates,function(a){return a.getUTCFullYear()}),E=-1;11>E;E++)C=["year"],-1===E?C.push("old"):10===E&&C.push("new"),-1!==a.inArray(f,D)&&C.push("active"),(h>f||f>j)&&C.push("disabled"),t+=''+f+"",f+=1;B.html(t)}},updateNavArrows:function(){if(this._allow_update){var a=new Date(this.viewDate),b=a.getUTCFullYear(),c=a.getUTCMonth();switch(this.viewMode){case 0:this.picker.find(".prev").css(this.o.startDate!==-1/0&&b<=this.o.startDate.getUTCFullYear()&&c<=this.o.startDate.getUTCMonth()?{visibility:"hidden"}:{visibility:"visible"}),this.picker.find(".next").css(1/0!==this.o.endDate&&b>=this.o.endDate.getUTCFullYear()&&c>=this.o.endDate.getUTCMonth()?{visibility:"hidden"}:{visibility:"visible"});break;case 1:case 2:this.picker.find(".prev").css(this.o.startDate!==-1/0&&b<=this.o.startDate.getUTCFullYear()?{visibility:"hidden"}:{visibility:"visible"}),this.picker.find(".next").css(1/0!==this.o.endDate&&b>=this.o.endDate.getUTCFullYear()?{visibility:"hidden"}:{visibility:"visible"})}}},click:function(b){b.preventDefault();var d,e,f,g=a(b.target).closest("span, td, th");if(1===g.length)switch(g[0].nodeName.toLowerCase()){case"th":switch(g[0].className){case"datepicker-switch":this.showMode(1);break;case"prev":case"next":var h=q.modes[this.viewMode].navStep*("prev"===g[0].className?-1:1);switch(this.viewMode){case 0:this.viewDate=this.moveMonth(this.viewDate,h),this._trigger("changeMonth",this.viewDate);break;case 1:case 2:this.viewDate=this.moveYear(this.viewDate,h),1===this.viewMode&&this._trigger("changeYear",this.viewDate)}this.fill();break;case"today":var i=new Date;i=c(i.getFullYear(),i.getMonth(),i.getDate(),0,0,0),this.showMode(-2);var j="linked"===this.o.todayBtn?null:"view";this._setDate(i,j);break;case"clear":this.clearDates()}break;case"span":g.hasClass("disabled")||(this.viewDate.setUTCDate(1),g.hasClass("month")?(f=1,e=g.parent().find("span").index(g),d=this.viewDate.getUTCFullYear(),this.viewDate.setUTCMonth(e),this._trigger("changeMonth",this.viewDate),1===this.o.minViewMode&&this._setDate(c(d,e,f))):(f=1,e=0,d=parseInt(g.text(),10)||0,this.viewDate.setUTCFullYear(d),this._trigger("changeYear",this.viewDate),2===this.o.minViewMode&&this._setDate(c(d,e,f))),this.showMode(-1),this.fill());break;case"td":g.hasClass("day")&&!g.hasClass("disabled")&&(f=parseInt(g.text(),10)||1,d=this.viewDate.getUTCFullYear(),e=this.viewDate.getUTCMonth(),g.hasClass("old")?0===e?(e=11,d-=1):e-=1:g.hasClass("new")&&(11===e?(e=0,d+=1):e+=1),this._setDate(c(d,e,f)))}this.picker.is(":visible")&&this._focused_from&&a(this._focused_from).focus(),delete this._focused_from},_toggle_multidate:function(a){var b=this.dates.contains(a);if(a||this.dates.clear(),-1!==b?(this.o.multidate===!0||this.o.multidate>1||this.o.toggleActive)&&this.dates.remove(b):this.o.multidate===!1?(this.dates.clear(),this.dates.push(a)):this.dates.push(a),"number"==typeof this.o.multidate)for(;this.dates.length>this.o.multidate;)this.dates.remove(0)},_setDate:function(a,b){b&&"date"!==b||this._toggle_multidate(a&&new Date(a)),b&&"view"!==b||(this.viewDate=a&&new Date(a)),this.fill(),this.setValue(),b&&"view"===b||this._trigger("changeDate");var c;this.isInput?c=this.element:this.component&&(c=this.element.find("input")),c&&c.change(),!this.o.autoclose||b&&"date"!==b||this.hide()},moveMonth:function(a,c){if(!a)return b;if(!c)return a;var d,e,f=new Date(a.valueOf()),g=f.getUTCDate(),h=f.getUTCMonth(),i=Math.abs(c);if(c=c>0?1:-1,1===i)e=-1===c?function(){return f.getUTCMonth()===h}:function(){return f.getUTCMonth()!==d},d=h+c,f.setUTCMonth(d),(0>d||d>11)&&(d=(d+12)%12);else{for(var j=0;i>j;j++)f=this.moveMonth(f,c);d=f.getUTCMonth(),f.setUTCDate(g),e=function(){return d!==f.getUTCMonth()}}for(;e();)f.setUTCDate(--g),f.setUTCMonth(d);return f},moveYear:function(a,b){return this.moveMonth(a,12*b)},dateWithinRange:function(a){return a>=this.o.startDate&&a<=this.o.endDate},keydown:function(a){if(!this.picker.is(":visible"))return void(27===a.keyCode&&this.show());var b,c,e,f=!1,g=this.focusDate||this.viewDate;switch(a.keyCode){case 27:this.focusDate?(this.focusDate=null,this.viewDate=this.dates.get(-1)||this.viewDate,this.fill()):this.hide(),a.preventDefault();break;case 37:case 39:if(!this.o.keyboardNavigation)break;b=37===a.keyCode?-1:1,a.ctrlKey?(c=this.moveYear(this.dates.get(-1)||d(),b),e=this.moveYear(g,b),this._trigger("changeYear",this.viewDate)):a.shiftKey?(c=this.moveMonth(this.dates.get(-1)||d(),b),e=this.moveMonth(g,b),this._trigger("changeMonth",this.viewDate)):(c=new Date(this.dates.get(-1)||d()),c.setUTCDate(c.getUTCDate()+b),e=new Date(g),e.setUTCDate(g.getUTCDate()+b)),this.dateWithinRange(e)&&(this.focusDate=this.viewDate=e,this.setValue(),this.fill(),a.preventDefault());break;case 38:case 40:if(!this.o.keyboardNavigation)break;b=38===a.keyCode?-1:1,a.ctrlKey?(c=this.moveYear(this.dates.get(-1)||d(),b),e=this.moveYear(g,b),this._trigger("changeYear",this.viewDate)):a.shiftKey?(c=this.moveMonth(this.dates.get(-1)||d(),b),e=this.moveMonth(g,b),this._trigger("changeMonth",this.viewDate)):(c=new Date(this.dates.get(-1)||d()),c.setUTCDate(c.getUTCDate()+7*b),e=new Date(g),e.setUTCDate(g.getUTCDate()+7*b)),this.dateWithinRange(e)&&(this.focusDate=this.viewDate=e,this.setValue(),this.fill(),a.preventDefault());break;case 32:break;case 13:g=this.focusDate||this.dates.get(-1)||this.viewDate,this.o.keyboardNavigation&&(this._toggle_multidate(g),f=!0),this.focusDate=null,this.viewDate=this.dates.get(-1)||this.viewDate,this.setValue(),this.fill(),this.picker.is(":visible")&&(a.preventDefault(),"function"==typeof a.stopPropagation?a.stopPropagation():a.cancelBubble=!0,this.o.autoclose&&this.hide());break;case 9:this.focusDate=null,this.viewDate=this.dates.get(-1)||this.viewDate,this.fill(),this.hide()}if(f){this._trigger(this.dates.length?"changeDate":"clearDate");var h;this.isInput?h=this.element:this.component&&(h=this.element.find("input")),h&&h.change()}},showMode:function(a){a&&(this.viewMode=Math.max(this.o.minViewMode,Math.min(2,this.viewMode+a))),this.picker.children("div").hide().filter(".datepicker-"+q.modes[this.viewMode].clsName).css("display","block"),this.updateNavArrows()}};var k=function(b,c){this.element=a(b),this.inputs=a.map(c.inputs,function(a){return a.jquery?a[0]:a}),delete c.inputs,m.call(a(this.inputs),c).bind("changeDate",a.proxy(this.dateUpdated,this)),this.pickers=a.map(this.inputs,function(b){return a(b).data("datepicker")}),this.updateDates()};k.prototype={updateDates:function(){this.dates=a.map(this.pickers,function(a){return a.getUTCDate()}),this.updateRanges()},updateRanges:function(){var b=a.map(this.dates,function(a){return a.valueOf()});a.each(this.pickers,function(a,c){c.setRange(b)})},dateUpdated:function(b){if(!this.updating){this.updating=!0;var c=a(b.target).data("datepicker"),d=c.getUTCDate(),e=a.inArray(b.target,this.inputs),f=e-1,g=e+1,h=this.inputs.length;if(-1!==e){if(a.each(this.pickers,function(a,b){b.getUTCDate()||b.setUTCDate(d)}),d=0&&dthis.dates[g])for(;h>g&&d>this.dates[g];)this.pickers[g++].setUTCDate(d);this.updateDates(),delete this.updating}}},remove:function(){a.map(this.pickers,function(a){a.remove()}),delete this.element.data().datepicker}};var l=a.fn.datepicker,m=function(c){var d=Array.apply(null,arguments);d.shift();var e;return this.each(function(){var f=a(this),i=f.data("datepicker"),l="object"==typeof c&&c;if(!i){var m=g(this,"date"),o=a.extend({},n,m,l),p=h(o.language),q=a.extend({},n,p,m,l);if(f.hasClass("input-daterange")||q.inputs){var r={inputs:q.inputs||f.find("input").toArray()};f.data("datepicker",i=new k(this,a.extend(q,r)))}else f.data("datepicker",i=new j(this,q))}return"string"==typeof c&&"function"==typeof i[c]&&(e=i[c].apply(i,d),e!==b)?!1:void 0}),e!==b?e:this};a.fn.datepicker=m;var n=a.fn.datepicker.defaults={autoclose:!1,beforeShowDay:a.noop,beforeShowMonth:a.noop,calendarWeeks:!1,clearBtn:!1,toggleActive:!1,daysOfWeekDisabled:[],datesDisabled:[],endDate:1/0,forceParse:!0,format:"mm/dd/yyyy",keyboardNavigation:!0,language:"en",minViewMode:0,multidate:!1,multidateSeparator:",",orientation:"auto",rtl:!1,startDate:-1/0,startView:0,todayBtn:!1,todayHighlight:!1,weekStart:0,disableTouchKeyboard:!1,enableOnReadonly:!0,container:"body"},o=a.fn.datepicker.locale_opts=["format","rtl","weekStart"];a.fn.datepicker.Constructor=j;var p=a.fn.datepicker.dates={en:{days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat","Sun"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa","Su"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],today:"Today",clear:"Clear"}},q={modes:[{clsName:"days",navFnc:"Month",navStep:1},{clsName:"months",navFnc:"FullYear",navStep:1},{clsName:"years",navFnc:"FullYear",navStep:10}],isLeapYear:function(a){return a%4===0&&a%100!==0||a%400===0},getDaysInMonth:function(a,b){return[31,q.isLeapYear(a)?29:28,31,30,31,30,31,31,30,31,30,31][b]},validParts:/dd?|DD?|mm?|MM?|yy(?:yy)?/g,nonpunctuation:/[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g,parseFormat:function(a){var b=a.replace(this.validParts,"\x00").split("\x00"),c=a.match(this.validParts);if(!b||!b.length||!c||0===c.length)throw new Error("Invalid date format.");return{separators:b,parts:c}},parseDate:function(d,e,f){function g(){var a=this.slice(0,m[k].length),b=m[k].slice(0,a.length);return a.toLowerCase()===b.toLowerCase()}if(!d)return b;if(d instanceof Date)return d;"string"==typeof e&&(e=q.parseFormat(e));var h,i,k,l=/([\-+]\d+)([dmwy])/,m=d.match(/([\-+]\d+)([dmwy])/g);if(/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(d)){for(d=new Date,k=0;kb;)b+=12;for(b%=12,a.setUTCMonth(b);a.getUTCMonth()!==b;)a.setUTCDate(a.getUTCDate()-1);return a},d:function(a,b){return a.setUTCDate(b)}};t.M=t.MM=t.mm=t.m,t.dd=t.d,d=c(d.getFullYear(),d.getMonth(),d.getDate(),0,0,0);var u=e.parts.slice();if(m.length!==u.length&&(u=a(u).filter(function(b,c){return-1!==a.inArray(c,s)}).toArray()),m.length===u.length){var v;for(k=0,v=u.length;v>k;k++){if(n=parseInt(m[k],10),h=u[k],isNaN(n))switch(h){case"MM":o=a(p[f].months).filter(g),n=a.inArray(o[0],p[f].months)+1;break;case"M":o=a(p[f].monthsShort).filter(g),n=a.inArray(o[0],p[f].monthsShort)+1}r[h]=n}var w,x;for(k=0;k=g;g++)f.length&&b.push(f.shift()),b.push(e[c.parts[g]]);return b.join("")},headTemplate:'«»',contTemplate:'',footTemplate:''};q.template='
'+q.headTemplate+""+q.footTemplate+'
'+q.headTemplate+q.contTemplate+q.footTemplate+'
'+q.headTemplate+q.contTemplate+q.footTemplate+"
",a.fn.datepicker.DPGlobal=q,a.fn.datepicker.noConflict=function(){return a.fn.datepicker=l,this},a.fn.datepicker.version="1.4.0",a(document).on("focus.datepicker.data-api click.datepicker.data-api",'[data-provide="datepicker"]',function(b){var c=a(this);c.data("datepicker")||(b.preventDefault(),m.call(c,"show"))}),a(function(){m.call(a('[data-provide="datepicker-inline"]'))})}(window.jQuery); \ No newline at end of file From d1fb6b9405fb4f60bc2c977f0e15ccb18308fe8a Mon Sep 17 00:00:00 2001 From: Richard Tibbles Date: Fri, 31 Jul 2015 19:34:20 -0700 Subject: [PATCH 11/13] Set arbitrary date ranges on tabular report queries. --- .../js/coachreports/coach_reports/models.js | 6 +++++- .../js/coachreports/coach_reports/views.js | 16 +++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/kalite/coachreports/static/js/coachreports/coach_reports/models.js b/kalite/coachreports/static/js/coachreports/coach_reports/models.js index 8ccbc01fd7..13c44618bc 100644 --- a/kalite/coachreports/static/js/coachreports/coach_reports/models.js +++ b/kalite/coachreports/static/js/coachreports/coach_reports/models.js @@ -32,12 +32,16 @@ var CoachReportModel = Backbone.Model.extend({ initialize: function(options) { this.facility = options.facility; this.group = options.group; + this.start_date = options.start_date; + this.end_date = options.end_date; }, url: function() { return setGetParamDict(Urls.learner_logs(), { facility_id: this.facility, - group_id: this.group + group_id: this.group, + start_date: this.start_date, + end_date: this.end_date }); } }); diff --git a/kalite/coachreports/static/js/coachreports/coach_reports/views.js b/kalite/coachreports/static/js/coachreports/coach_reports/views.js index 25a25ae202..9a7d711092 100644 --- a/kalite/coachreports/static/js/coachreports/coach_reports/views.js +++ b/kalite/coachreports/static/js/coachreports/coach_reports/views.js @@ -12,6 +12,12 @@ CoachReportView: - DetailPanelBodyView */ +var date_string = function(date) { + if (date) { + return date.getFullYear() + "/" + (date.getMonth() + 1) + "/" + date.getDate(); + } +}; + var DetailsPanelBodyView = BaseView.extend({ /* This view displays details of individual attempt logs @@ -344,7 +350,9 @@ var TabularReportView = BaseView.extend({ var self = this; this.data_model = new CoachReportModel({ facility: this.model.get("facility"), - group: this.model.get("group") + group: this.model.get("group"), + start_date: date_string(this.model.get("start_date")), + end_date: date_string(this.model.get("end_date")) }); if (this.model.get("facility")) { this.data_model.fetch().then(function() { @@ -380,12 +388,6 @@ var TabularReportView = BaseView.extend({ }); -var date_string = function(date) { - if (date) { - return date.getFullYear() + "/" + (date.getMonth() + 1) + "/" + date.getDate(); - } -} - var TimeSetView = BaseView.extend({ template: HB.template("coach_nav/datepicker"), From fa7f18b5efd0f4ff53d46fa965b456052ddd052f Mon Sep 17 00:00:00 2001 From: Jamie Alexandre Date: Wed, 5 Aug 2015 09:48:56 -0700 Subject: [PATCH 12/13] Use kalite.version.VERSION for securesync; needed for minversion checks --- python-packages/securesync/__init__.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/python-packages/securesync/__init__.py b/python-packages/securesync/__init__.py index 6dd73bea7b..1afa7464f0 100755 --- a/python-packages/securesync/__init__.py +++ b/python-packages/securesync/__init__.py @@ -7,17 +7,7 @@ ID_MAX_LENGTH=32 IP_MAX_LENGTH=50 - -# TODO(benjaoming): So the version of securesync dynamic !? Not understanding -# this.. plus it adds problems in dependency testing... but that should be -# deleted anyways... for now just hard coding it at 1.0 and below will be -# left commented out. -# try: -# from kalite.version import VERSION -# except: -# VERSION = "1.0" - -VERSION = "1.0" +from kalite.version import VERSION # JsonResponseMessageError codes class ERROR_CODES: From 967bca1826b9956cdaff0b874919641ca2367b08 Mon Sep 17 00:00:00 2001 From: Jamie Alexandre Date: Wed, 5 Aug 2015 09:49:28 -0700 Subject: [PATCH 13/13] Properly handle dates when serializing models to JSON in readmodels --- kalite/testing/management/commands/readmodel.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kalite/testing/management/commands/readmodel.py b/kalite/testing/management/commands/readmodel.py index b14a3ee820..faae62910f 100644 --- a/kalite/testing/management/commands/readmodel.py +++ b/kalite/testing/management/commands/readmodel.py @@ -1,3 +1,4 @@ +import datetime import importlib import json import sys @@ -9,6 +10,8 @@ from django.core.management.base import BaseCommand, CommandError from django.core import serializers +dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime.datetime) else None + class Command(BaseCommand): args = "" @@ -40,7 +43,7 @@ def handle(self, *args, **options): serialized_data = serializers.serialize("python", [data])[0]["fields"] serialized_data["id"] = model_id logging.debug("Serialized data: '%s'" % serialized_data) - print json.dumps(serialized_data) + print json.dumps(serialized_data, default=dthandler) except Model.DoesNotExist: logging.error("Could not find '%s' entry with primary key: '%s'" % (model_path, model_id))