From 90cfdb9bef7025bb8b35f3821d6de967dfdeeb7c Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Thu, 26 Jan 2017 18:27:56 +0530 Subject: [PATCH 001/173] moved classify and classify_string_classifier_rule and their tests from reader to classifier_services and classifier_services_test --- core/controllers/reader.py | 106 +-------------------- core/controllers/reader_test.py | 63 ------------- core/domain/classifier_services.py | 119 ++++++++++++++++++++++++ core/domain/classifier_services_test.py | 85 +++++++++++++++++ 4 files changed, 207 insertions(+), 166 deletions(-) create mode 100644 core/domain/classifier_services.py create mode 100644 core/domain/classifier_services_test.py diff --git a/core/controllers/reader.py b/core/controllers/reader.py index 5409d0a05b8c..121915d8aee3 100644 --- a/core/controllers/reader.py +++ b/core/controllers/reader.py @@ -21,7 +21,7 @@ import jinja2 from core.controllers import base -from core.domain import classifier_registry +from core.domain import classifier_services from core.domain import collection_services from core.domain import config_domain from core.domain import dependency_registry @@ -79,56 +79,6 @@ def test_can_play(self, exploration_id, **kwargs): return test_can_play -def classify_string_classifier_rule(state, normalized_answer): - """Run the classifier if no prediction has been made yet. Currently this - is behind a development flag. - """ - best_matched_answer_group = None - best_matched_answer_group_index = len(state.interaction.answer_groups) - best_matched_rule_spec_index = None - - sc = classifier_registry.ClassifierRegistry.get_classifier_by_id( - feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput']) - - training_examples = [ - [doc, []] for doc in state.interaction.confirmed_unclassified_answers] - for (answer_group_index, answer_group) in enumerate( - state.interaction.answer_groups): - classifier_rule_spec_index = answer_group.get_classifier_rule_index() - if classifier_rule_spec_index is not None: - classifier_rule_spec = answer_group.rule_specs[ - classifier_rule_spec_index] - else: - classifier_rule_spec = None - if classifier_rule_spec is not None: - training_examples.extend([ - [doc, [str(answer_group_index)]] - for doc in classifier_rule_spec.inputs['training_data']]) - if len(training_examples) > 0: - sc.train(training_examples) - labels = sc.predict([normalized_answer]) - predicted_label = labels[0] - if predicted_label != feconf.DEFAULT_CLASSIFIER_LABEL: - predicted_answer_group_index = int(predicted_label) - predicted_answer_group = state.interaction.answer_groups[ - predicted_answer_group_index] - for rule_spec in predicted_answer_group.rule_specs: - if rule_spec.rule_type == exp_domain.CLASSIFIER_RULESPEC_STR: - best_matched_rule_spec_index = classifier_rule_spec_index - break - best_matched_answer_group = predicted_answer_group - best_matched_answer_group_index = predicted_answer_group_index - return { - 'outcome': best_matched_answer_group.outcome.to_dict(), - 'answer_group_index': best_matched_answer_group_index, - 'rule_spec_index': best_matched_rule_spec_index, - } - else: - return None - - return None - - def _get_exploration_player_data( exploration_id, version, collection_id, can_edit): try: @@ -191,56 +141,6 @@ def _get_exploration_player_data( } -def classify(state, answer): - """Classify the answer using the string classifier. - - This should only be called if the string classifier functionality is - enabled, and the interaction is trainable. - - Normalize the answer and classifies the answer if the interaction has a - classifier associated with it. Otherwise, classifies the answer to the - default outcome. - - Returns a dict with the following keys: - 'outcome': A dict representing the outcome of the answer group matched. - 'answer_group_index': An index into the answer groups list indicating - which one was selected as the group which this answer belongs to. - This is equal to the number of answer groups if the default outcome - was matched. - 'rule_spec_index': An index into the rule specs list of the matched - answer group which was selected that indicates which rule spec was - matched. This is equal to 0 if the default outcome is selected. - When the default rule is matched, outcome is the default_outcome of the - state's interaction. - """ - assert feconf.ENABLE_STRING_CLASSIFIER - - interaction_instance = interaction_registry.Registry.get_interaction_by_id( - state.interaction.id) - normalized_answer = interaction_instance.normalize_answer(answer) - response = None - - if interaction_instance.is_string_classifier_trainable: - response = classify_string_classifier_rule(state, normalized_answer) - else: - raise Exception('No classifier found for interaction.') - - if response is not None: - return response - elif state.interaction.default_outcome is not None: - return { - 'outcome': state.interaction.default_outcome.to_dict(), - 'answer_group_index': len(state.interaction.answer_groups), - 'classification_certainty': 0.0, - 'rule_spec_index': 0 - } - - raise Exception( - 'Something has seriously gone wrong with the exploration. Oppia does ' - 'not know what to do with this answer. Please contact the ' - 'exploration owner.') - - class ExplorationPageEmbed(base.BaseHandler): """Page describing a single embedded exploration.""" @@ -435,8 +335,8 @@ def post(self, unused_exploration_id): # The learner's parameter values. params = self.payload.get('params') params['answer'] = answer - - self.render_json(classify(old_state, answer)) + result = classifier_services.classify(old_state, answer) + self.render_json(result) class ReaderFeedbackHandler(base.BaseHandler): diff --git a/core/controllers/reader_test.py b/core/controllers/reader_test.py index 15dc11a16504..7a06e72e9c3d 100644 --- a/core/controllers/reader_test.py +++ b/core/controllers/reader_test.py @@ -17,7 +17,6 @@ import os from core.controllers import reader -from core.domain import classifier_registry from core.domain import exp_domain from core.domain import exp_services from core.domain import param_domain @@ -109,68 +108,6 @@ def test_published_explorations_are_visible_to_logged_in_users(self): self.assertEqual(response.status_int, 200) -class ReaderClassifyTests(test_utils.GenericTestBase): - """Test reader.classify using the sample explorations. - - Since the end to end tests cover correct classification, and frontend tests - test hard rules, ReaderClassifyTests is only checking that the string - classifier is actually called. - """ - - def setUp(self): - super(ReaderClassifyTests, self).setUp() - self._init_classify_inputs('16') - - def _init_classify_inputs(self, exploration_id): - test_exp_filepath = os.path.join( - feconf.TESTS_DATA_DIR, 'string_classifier_test.yaml') - yaml_content = utils.get_file_contents(test_exp_filepath) - assets_list = [] - exp_services.save_new_exploration_from_yaml_and_assets( - feconf.SYSTEM_COMMITTER_ID, yaml_content, exploration_id, - assets_list) - - self.exp_id = exploration_id - self.exp_state = ( - exp_services.get_exploration_by_id(exploration_id).states['Home']) - - def _is_string_classifier_called(self, answer): - sc = classifier_registry.ClassifierRegistry.get_classifier_by_id( - feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput']) - string_classifier_predict = ( - sc.__class__.predict) - predict_counter = test_utils.CallCounter( - string_classifier_predict) - - with self.swap(sc.__class__, 'predict', predict_counter): - response = reader.classify(self.exp_state, answer) - - answer_group_index = response['answer_group_index'] - rule_spec_index = response['rule_spec_index'] - answer_groups = self.exp_state.interaction.answer_groups - if answer_group_index == len(answer_groups): - return 'default' - - answer_group = answer_groups[answer_group_index] - return (answer_group.get_classifier_rule_index() == rule_spec_index and - predict_counter.times_called == 1) - - def test_string_classifier_classification(self): - """All these responses trigger the string classifier.""" - with self.swap(feconf, 'ENABLE_STRING_CLASSIFIER', True): - self.assertTrue( - self._is_string_classifier_called( - 'it\'s a permutation of 3 elements')) - self.assertTrue( - self._is_string_classifier_called( - 'There are 3 options for the first ball, and 2 for the ' - 'remaining two. So 3*2=6.')) - self.assertTrue( - self._is_string_classifier_called('abc acb bac bca cbb cba')) - self.assertTrue( - self._is_string_classifier_called('dunno, just guessed')) - - class FeedbackIntegrationTest(test_utils.GenericTestBase): """Test the handler for giving feedback.""" diff --git a/core/domain/classifier_services.py b/core/domain/classifier_services.py new file mode 100644 index 000000000000..b659c1a23704 --- /dev/null +++ b/core/domain/classifier_services.py @@ -0,0 +1,119 @@ +# Copyright 2017 The Oppia Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Services for classifier models""" + +from core.domain import classifier_registry +from core.domain import interaction_registry + +import feconf + +def classify(state, answer): + """Classify the answer using the string classifier. + + This should only be called if the string classifier functionality is + enabled, and the interaction is trainable. + + Normalize the answer and classifies the answer if the interaction has a + classifier associated with it. Otherwise, classifies the answer to the + default outcome. + + Returns a dict with the following keys: + 'outcome': A dict representing the outcome of the answer group matched. + 'answer_group_index': An index into the answer groups list indicating + which one was selected as the group which this answer belongs to. + This is equal to the number of answer groups if the default outcome + was matched. + 'rule_spec_index': An index into the rule specs list of the matched + answer group which was selected that indicates which rule spec was + matched. This is equal to 0 if the default outcome is selected. + When the default rule is matched, outcome is the default_outcome of the + state's interaction. + """ + assert feconf.ENABLE_STRING_CLASSIFIER + + interaction_instance = interaction_registry.Registry.get_interaction_by_id( + state.interaction.id) + normalized_answer = interaction_instance.normalize_answer(answer) + response = None + + if interaction_instance.is_string_classifier_trainable: + response = classify_string_classifier_rule(state, normalized_answer) + else: + raise Exception('No classifier found for interaction.') + + if response is not None: + return response + elif state.interaction.default_outcome is not None: + return { + 'outcome': state.interaction.default_outcome.to_dict(), + 'answer_group_index': len(state.interaction.answer_groups), + 'classification_certainty': 0.0, + 'rule_spec_index': 0 + } + + raise Exception( + 'Something has seriously gone wrong with the exploration. Oppia does ' + 'not know what to do with this answer. Please contact the ' + 'exploration owner.') + + +def classify_string_classifier_rule(state, normalized_answer): + """Run the classifier if no prediction has been made yet. Currently this + is behind a development flag. + """ + best_matched_answer_group = None + best_matched_answer_group_index = len(state.interaction.answer_groups) + best_matched_rule_spec_index = None + + sc = classifier_registry.ClassifierRegistry.get_classifier_by_id( + feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput']) + + training_examples = [ + [doc, []] for doc in state.interaction.confirmed_unclassified_answers] + for (answer_group_index, answer_group) in enumerate( + state.interaction.answer_groups): + classifier_rule_spec_index = answer_group.get_classifier_rule_index() + if classifier_rule_spec_index is not None: + classifier_rule_spec = answer_group.rule_specs[ + classifier_rule_spec_index] + else: + classifier_rule_spec = None + if classifier_rule_spec is not None: + training_examples.extend([ + [doc, [str(answer_group_index)]] + for doc in classifier_rule_spec.inputs['training_data']]) + if len(training_examples) > 0: + sc.train(training_examples) + labels = sc.predict([normalized_answer]) + predicted_label = labels[0] + if predicted_label != feconf.DEFAULT_CLASSIFIER_LABEL: + predicted_answer_group_index = int(predicted_label) + predicted_answer_group = state.interaction.answer_groups[ + predicted_answer_group_index] + for rule_spec in predicted_answer_group.rule_specs: + if rule_spec.rule_type == exp_domain.CLASSIFIER_RULESPEC_STR: + best_matched_rule_spec_index = classifier_rule_spec_index + break + best_matched_answer_group = predicted_answer_group + best_matched_answer_group_index = predicted_answer_group_index + return { + 'outcome': best_matched_answer_group.outcome.to_dict(), + 'answer_group_index': best_matched_answer_group_index, + 'rule_spec_index': best_matched_rule_spec_index, + } + else: + return None + + return None diff --git a/core/domain/classifier_services_test.py b/core/domain/classifier_services_test.py new file mode 100644 index 000000000000..eb599b4447b7 --- /dev/null +++ b/core/domain/classifier_services_test.py @@ -0,0 +1,85 @@ +# Copyright 2017 The Oppia Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests for classifier services""" + +import os + +from core.domain import classifier_registry +from core.domain import classifier_serivces +from core.domain import exp_services +from core.tests import test_utils +import feconf +import utils + +class ClassifierServicesTests(test_utils.GenericTestBase): + """Test reader.classify using the sample explorations. + + Since the end to end tests cover correct classification, and frontend tests + test hard rules, ReaderClassifyTests is only checking that the string + classifier is actually called. + """ + + def setUp(self): + super(ClassifierServicesTests, self).setUp() + self._init_classify_inputs('16') + + def _init_classify_inputs(self, exploration_id): + test_exp_filepath = os.path.join( + feconf.TESTS_DATA_DIR, 'string_classifier_test.yaml') + yaml_content = utils.get_file_contents(test_exp_filepath) + assets_list = [] + exp_services.save_new_exploration_from_yaml_and_assets( + feconf.SYSTEM_COMMITTER_ID, yaml_content, exploration_id, + assets_list) + + self.exp_id = exploration_id + self.exp_state = ( + exp_services.get_exploration_by_id(exploration_id).states['Home']) + + def _is_string_classifier_called(self, answer): + sc = classifier_registry.ClassifierRegistry.get_classifier_by_id( + feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput']) + string_classifier_predict = ( + sc.__class__.predict) + predict_counter = test_utils.CallCounter( + string_classifier_predict) + + with self.swap(sc.__class__, 'predict', predict_counter): + response = classifier_serivces.classify(self.exp_state, answer) + + answer_group_index = response['answer_group_index'] + rule_spec_index = response['rule_spec_index'] + answer_groups = self.exp_state.interaction.answer_groups + if answer_group_index == len(answer_groups): + return 'default' + + answer_group = answer_groups[answer_group_index] + return (answer_group.get_classifier_rule_index() == rule_spec_index and + predict_counter.times_called == 1) + + def test_string_classifier_classification(self): + """All these responses trigger the string classifier.""" + with self.swap(feconf, 'ENABLE_STRING_CLASSIFIER', True): + self.assertTrue( + self._is_string_classifier_called( + 'it\'s a permutation of 3 elements')) + self.assertTrue( + self._is_string_classifier_called( + 'There are 3 options for the first ball, and 2 for the ' + 'remaining two. So 3*2=6.')) + self.assertTrue( + self._is_string_classifier_called('abc acb bac bca cbb cba')) + self.assertTrue( + self._is_string_classifier_called('dunno, just guessed')) From 68e0d79733d5f075b9ca25b3dd8f7a1a3d72fe55 Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Thu, 26 Jan 2017 18:38:23 +0530 Subject: [PATCH 002/173] minor spelling mistakes --- core/domain/classifier_services.py | 1 + core/domain/classifier_services_test.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/domain/classifier_services.py b/core/domain/classifier_services.py index b659c1a23704..03f79967c0c8 100644 --- a/core/domain/classifier_services.py +++ b/core/domain/classifier_services.py @@ -15,6 +15,7 @@ """Services for classifier models""" from core.domain import classifier_registry +from core.domain import exp_domain from core.domain import interaction_registry import feconf diff --git a/core/domain/classifier_services_test.py b/core/domain/classifier_services_test.py index eb599b4447b7..dbf79d8a4a89 100644 --- a/core/domain/classifier_services_test.py +++ b/core/domain/classifier_services_test.py @@ -17,7 +17,7 @@ import os from core.domain import classifier_registry -from core.domain import classifier_serivces +from core.domain import classifier_services from core.domain import exp_services from core.tests import test_utils import feconf @@ -57,7 +57,7 @@ def _is_string_classifier_called(self, answer): string_classifier_predict) with self.swap(sc.__class__, 'predict', predict_counter): - response = classifier_serivces.classify(self.exp_state, answer) + response = classifier_services.classify(self.exp_state, answer) answer_group_index = response['answer_group_index'] rule_spec_index = response['rule_spec_index'] From 679040ad984741f202e5fe3ae93086bbf8565bd0 Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Thu, 26 Jan 2017 18:46:56 +0530 Subject: [PATCH 003/173] fix lint issues --- core/controllers/reader_test.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/controllers/reader_test.py b/core/controllers/reader_test.py index 7a06e72e9c3d..5ad7f10cd700 100644 --- a/core/controllers/reader_test.py +++ b/core/controllers/reader_test.py @@ -14,16 +14,12 @@ """Tests for the page that allows learners to play through an exploration.""" -import os - -from core.controllers import reader from core.domain import exp_domain from core.domain import exp_services from core.domain import param_domain from core.domain import rights_manager from core.tests import test_utils import feconf -import utils class ReaderPermissionsTest(test_utils.GenericTestBase): From f165ffdd9225c86a6635f205896e6b8a3bda3e6d Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Thu, 16 Feb 2017 00:01:23 +0530 Subject: [PATCH 004/173] Added get_classifier_from_model method --- core/domain/classifier_services.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/domain/classifier_services.py b/core/domain/classifier_services.py index 03f79967c0c8..98900736d3c8 100644 --- a/core/domain/classifier_services.py +++ b/core/domain/classifier_services.py @@ -15,9 +15,11 @@ """Services for classifier models""" from core.domain import classifier_registry +from core.domain import classifier_domain from core.domain import exp_domain from core.domain import interaction_registry + import feconf def classify(state, answer): @@ -118,3 +120,15 @@ def classify_string_classifier_rule(state, normalized_answer): return None return None + +def get_classifier_from_model(classifier_model): + """ + Returns a classifier domian object given a classifier model loaded + from datastore. + """ + return classifier_domain.Classifier( + classifier_model.id, classifier_model.exp_id, + classifier_model.exp_version_when_created, + classifier_model.state_name, classifier_model.algorithm_id, + classifier_model.cached_classifier_data, + classifier_model.data_schema_version) From c1f05269502d77de2c34cf78d4064454c6ff33fb Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Thu, 16 Feb 2017 00:52:59 +0530 Subject: [PATCH 005/173] added _save_classifier method --- core/domain/classifier_services.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/core/domain/classifier_services.py b/core/domain/classifier_services.py index 98900736d3c8..bb8f874e691a 100644 --- a/core/domain/classifier_services.py +++ b/core/domain/classifier_services.py @@ -18,10 +18,12 @@ from core.domain import classifier_domain from core.domain import exp_domain from core.domain import interaction_registry - +from core.platform import models import feconf +classifier_models = models.Registry.import_models(models.NAMES.classifier) + def classify(state, answer): """Classify the answer using the string classifier. @@ -132,3 +134,21 @@ def get_classifier_from_model(classifier_model): classifier_model.state_name, classifier_model.algorithm_id, classifier_model.cached_classifier_data, classifier_model.data_schema_version) + +def _save_classifier(classifier): + """ + + """ + classifier_model = classifier_models.ClassifierModel.get( + classifier.id, strict=False) + if classifier_model is None: + return None + else: + classifier_model.exp_version_when_created = ( + classifier.exp_version_when_created) + classifier_model.state_name = classifier.state_name + classifier_model.algorithm_id = classifier.algorithm_id + classifier_model.cached_classifier_data = ( + classifier.cached_classifier_data) + classifier_model.data_schema_version = classifier.data_schema_version + classifier_model.put() From c238c91bafd5cb472ab830a1c21bb2721dad7c45 Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Thu, 16 Feb 2017 01:07:27 +0530 Subject: [PATCH 006/173] added get_classifier_from_id method --- core/domain/classifier_services.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/core/domain/classifier_services.py b/core/domain/classifier_services.py index bb8f874e691a..4ea9d0ea6a2e 100644 --- a/core/domain/classifier_services.py +++ b/core/domain/classifier_services.py @@ -123,6 +123,7 @@ def classify_string_classifier_rule(state, normalized_answer): return None + def get_classifier_from_model(classifier_model): """ Returns a classifier domian object given a classifier model loaded @@ -135,9 +136,23 @@ def get_classifier_from_model(classifier_model): classifier_model.cached_classifier_data, classifier_model.data_schema_version) +def get_classifier_from_id(classifier_id): + """ + Returns a classifier domain object given a classifier id. + """ + classifier_model = classifier_models.ClassifierModel.get( + classifier_id, strict=False) + if classifier_model is None: + return None + else: + classifier = get_classifier_from_model(classifier_model) + return classifier + + def _save_classifier(classifier): """ - + Updates classifier model in the datastore given a classifier + domain object. """ classifier_model = classifier_models.ClassifierModel.get( classifier.id, strict=False) From ea494b935c59e5b876c966ef7c3099f020ff4451 Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Mon, 20 Feb 2017 01:48:21 +0530 Subject: [PATCH 007/173] Added to test to verify methods --- core/domain/classifier_services.py | 4 ++-- core/domain/classifier_services_test.py | 26 ++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/core/domain/classifier_services.py b/core/domain/classifier_services.py index 4ea9d0ea6a2e..56497bd972b3 100644 --- a/core/domain/classifier_services.py +++ b/core/domain/classifier_services.py @@ -22,7 +22,7 @@ import feconf -classifier_models = models.Registry.import_models(models.NAMES.classifier) +(classifier_models,) = models.Registry.import_models([models.NAMES.classifier]) def classify(state, answer): """Classify the answer using the string classifier. @@ -136,7 +136,7 @@ def get_classifier_from_model(classifier_model): classifier_model.cached_classifier_data, classifier_model.data_schema_version) -def get_classifier_from_id(classifier_id): +def get_classifier_by_id(classifier_id): """ Returns a classifier domain object given a classifier id. """ diff --git a/core/domain/classifier_services_test.py b/core/domain/classifier_services_test.py index dbf79d8a4a89..871aab65c3c1 100644 --- a/core/domain/classifier_services_test.py +++ b/core/domain/classifier_services_test.py @@ -19,10 +19,13 @@ from core.domain import classifier_registry from core.domain import classifier_services from core.domain import exp_services +from core.platform import models from core.tests import test_utils import feconf import utils +(classifier_models,) = models.Registry.import_models([models.NAMES.classifier]) + class ClassifierServicesTests(test_utils.GenericTestBase): """Test reader.classify using the sample explorations. @@ -30,7 +33,6 @@ class ClassifierServicesTests(test_utils.GenericTestBase): test hard rules, ReaderClassifyTests is only checking that the string classifier is actually called. """ - def setUp(self): super(ClassifierServicesTests, self).setUp() self._init_classify_inputs('16') @@ -83,3 +85,25 @@ def test_string_classifier_classification(self): self._is_string_classifier_called('abc acb bac bca cbb cba')) self.assertTrue( self._is_string_classifier_called('dunno, just guessed')) + + def test_retrieval_and_update_of_classifiers(self): + """ Test the get_classifier_by_id and _save_classifier methods""" + response = classifier_services.get_classifier_by_id('fake_id') + self.assertEqual(response, None) + + exp_id = u'1' + state = 'Home' + test_state = 'Test' + classifier_id = classifier_models.ClassifierModel.create( + exp_id, 1, state, feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput'], + [], 1) + classifier = classifier_services.get_classifier_by_id( + classifier_id) + self.assertEqual(classifier.exp_id, exp_id) + self.assertEqual(classifier.state_name, state) + classifier.state = test_state + classifier_services._save_classifier(classifier) + classifier = classifier_services.get_classifier_by_id( + classifier_id) + self.assertEqual(classifier.exp_id, exp_id) + self.assertEqual(classifier.state_name, state) From 0c5b36424a2923d4da06f016670111a9220742ca Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Mon, 20 Feb 2017 01:50:00 +0530 Subject: [PATCH 008/173] lint issues --- core/domain/classifier_services_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/domain/classifier_services_test.py b/core/domain/classifier_services_test.py index 871aab65c3c1..acbbc7defbf4 100644 --- a/core/domain/classifier_services_test.py +++ b/core/domain/classifier_services_test.py @@ -90,13 +90,13 @@ def test_retrieval_and_update_of_classifiers(self): """ Test the get_classifier_by_id and _save_classifier methods""" response = classifier_services.get_classifier_by_id('fake_id') self.assertEqual(response, None) - + exp_id = u'1' state = 'Home' test_state = 'Test' classifier_id = classifier_models.ClassifierModel.create( - exp_id, 1, state, feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput'], - [], 1) + exp_id, 1, state, + feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput'], [], 1) classifier = classifier_services.get_classifier_by_id( classifier_id) self.assertEqual(classifier.exp_id, exp_id) From f748d228c947d3b836309d5801345c3723206d6b Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Mon, 20 Feb 2017 01:51:25 +0530 Subject: [PATCH 009/173] lint issues --- core/domain/classifier_services_test.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/core/domain/classifier_services_test.py b/core/domain/classifier_services_test.py index acbbc7defbf4..ea4c3b61687a 100644 --- a/core/domain/classifier_services_test.py +++ b/core/domain/classifier_services_test.py @@ -87,13 +87,12 @@ def test_string_classifier_classification(self): self._is_string_classifier_called('dunno, just guessed')) def test_retrieval_and_update_of_classifiers(self): - """ Test the get_classifier_by_id and _save_classifier methods""" + """ Test the get_classifier_by_id method""" response = classifier_services.get_classifier_by_id('fake_id') self.assertEqual(response, None) exp_id = u'1' state = 'Home' - test_state = 'Test' classifier_id = classifier_models.ClassifierModel.create( exp_id, 1, state, feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput'], [], 1) @@ -101,9 +100,3 @@ def test_retrieval_and_update_of_classifiers(self): classifier_id) self.assertEqual(classifier.exp_id, exp_id) self.assertEqual(classifier.state_name, state) - classifier.state = test_state - classifier_services._save_classifier(classifier) - classifier = classifier_services.get_classifier_by_id( - classifier_id) - self.assertEqual(classifier.exp_id, exp_id) - self.assertEqual(classifier.state_name, state) From 97bf8bc13ca88209f2259426d3f8a05d4d3bb53a Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Tue, 21 Feb 2017 01:35:55 +0530 Subject: [PATCH 010/173] added update_classifier method. --- core/domain/classifier_services.py | 47 ++++++++++++++++--------- core/domain/classifier_services_test.py | 9 ++--- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/core/domain/classifier_services.py b/core/domain/classifier_services.py index 56497bd972b3..f1d6b514b5fd 100644 --- a/core/domain/classifier_services.py +++ b/core/domain/classifier_services.py @@ -125,8 +125,7 @@ def classify_string_classifier_rule(state, normalized_answer): def get_classifier_from_model(classifier_model): - """ - Returns a classifier domian object given a classifier model loaded + """Returns a classifier domian object given a classifier model loaded from datastore. """ return classifier_domain.Classifier( @@ -137,33 +136,49 @@ def get_classifier_from_model(classifier_model): classifier_model.data_schema_version) def get_classifier_by_id(classifier_id): - """ - Returns a classifier domain object given a classifier id. + """Returns a classifier domain object given a classifier id. """ classifier_model = classifier_models.ClassifierModel.get( classifier_id, strict=False) if classifier_model is None: - return None + raise Exception("No classifier found for the classifer's id.") else: classifier = get_classifier_from_model(classifier_model) return classifier -def _save_classifier(classifier): +def _create_classifier(classifier): + """Creates classifier model in the datastoer given a classifier + domain object. """ - Updates classifier model in the datastore given a classifier + classifier_models.ClassifierModel.create( + classifier.exp_id, classifier.exp_version_when_created, + classifier.state_name, classifer.algorithm_id, + classifier.cached_classifier_data, classifier.data_schema_version, + ) + + +def _save_classifier(classifier_model, classifier): + """Updates classifier model in the datastore given a classifier domain object. """ + classifier_model.exp_version_when_created = ( + classifier.exp_version_when_created) + classifier_model.state_name = classifier.state_name + classifier_model.algorithm_id = classifier.algorithm_id + classifier_model.cached_classifier_data = ( + classifier.cached_classifier_data) + classifier_model.data_schema_version = classifier.data_schema_version + classifier_model.put() + + +def update_classifier(classifier): + """Checks if model exists and updates the classifier model using + _save_classifier method. + """ classifier_model = classifier_models.ClassifierModel.get( classifier.id, strict=False) if classifier_model is None: - return None + raise Exception("No classifier found for the classifer's id.") else: - classifier_model.exp_version_when_created = ( - classifier.exp_version_when_created) - classifier_model.state_name = classifier.state_name - classifier_model.algorithm_id = classifier.algorithm_id - classifier_model.cached_classifier_data = ( - classifier.cached_classifier_data) - classifier_model.data_schema_version = classifier.data_schema_version - classifier_model.put() + _save_classifier(classifier_model, classifier) diff --git a/core/domain/classifier_services_test.py b/core/domain/classifier_services_test.py index ea4c3b61687a..6a4123b2fc3c 100644 --- a/core/domain/classifier_services_test.py +++ b/core/domain/classifier_services_test.py @@ -86,10 +86,11 @@ def test_string_classifier_classification(self): self.assertTrue( self._is_string_classifier_called('dunno, just guessed')) - def test_retrieval_and_update_of_classifiers(self): - """ Test the get_classifier_by_id method""" - response = classifier_services.get_classifier_by_id('fake_id') - self.assertEqual(response, None) + def test_retrieval_of_classifiers(self): + """Test the get_classifier_by_id method.""" + with self.assertRaisesRegexp(Exception, ( + "No classifier found for the classifer's id.")): + exp_services.get_classifier_by_id('fake_id') exp_id = u'1' state = 'Home' From 273522c0ed99499ffce2a75df99f9632d3dd88ee Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Tue, 21 Feb 2017 01:48:34 +0530 Subject: [PATCH 011/173] added tests to update_classifier method. --- core/domain/classifier_services_test.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/core/domain/classifier_services_test.py b/core/domain/classifier_services_test.py index 6a4123b2fc3c..bdbe8e8eacbf 100644 --- a/core/domain/classifier_services_test.py +++ b/core/domain/classifier_services_test.py @@ -90,7 +90,7 @@ def test_retrieval_of_classifiers(self): """Test the get_classifier_by_id method.""" with self.assertRaisesRegexp(Exception, ( "No classifier found for the classifer's id.")): - exp_services.get_classifier_by_id('fake_id') + classifier_services.get_classifier_by_id('fake_id') exp_id = u'1' state = 'Home' @@ -101,3 +101,21 @@ def test_retrieval_of_classifiers(self): classifier_id) self.assertEqual(classifier.exp_id, exp_id) self.assertEqual(classifier.state_name, state) + + + def test_update_of_classifiers(self): + """Test the update_classifier method.""" + exp_id = u'1' + state = 'Home' + test_state = 'State' + classifier_id = classifier_models.ClassifierModel.create( + exp_id, 1, state, + feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput'], [], 1) + classifier = classifier_services.get_classifier_by_id( + classifier_id) + classifier.state_name = test_state + classifier_services.update_classifier(classifier) + classifier = classifier_services.get_classifier_by_id( + classifier_id) + self.assertEqual(classifier.exp_id, exp_id) + self.assertEqual(classifier.state_name, test_state) From d1b751c75ffe97c95c67a2bf423cde9af6f056a6 Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Tue, 21 Feb 2017 01:56:11 +0530 Subject: [PATCH 012/173] Added delete_classifier method. --- core/domain/classifier_services.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/domain/classifier_services.py b/core/domain/classifier_services.py index f1d6b514b5fd..797c3b5d03b3 100644 --- a/core/domain/classifier_services.py +++ b/core/domain/classifier_services.py @@ -182,3 +182,15 @@ def update_classifier(classifier): raise Exception("No classifier found for the classifer's id.") else: _save_classifier(classifier_model, classifier) + + +def delete_classifier(classifier_id): + """Deletes classifier model in the datastore given classifier_id. + """ + classifier_model = classifier_models.ClassifierModel.get( + classifier.id, strict=False) + if classifier_model is None: + raise Exception("No classifier found for the classifer's id.") + else: + classifier_model.delete() + From a19c10c0733fd8ec78fe606fabc88a1661da4bb8 Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Tue, 21 Feb 2017 02:02:09 +0530 Subject: [PATCH 013/173] added tests to delete_classifier method. --- core/domain/classifier_services.py | 3 +-- core/domain/classifier_services_test.py | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/core/domain/classifier_services.py b/core/domain/classifier_services.py index 797c3b5d03b3..fa08a1cafa8e 100644 --- a/core/domain/classifier_services.py +++ b/core/domain/classifier_services.py @@ -188,9 +188,8 @@ def delete_classifier(classifier_id): """Deletes classifier model in the datastore given classifier_id. """ classifier_model = classifier_models.ClassifierModel.get( - classifier.id, strict=False) + classifier_id, strict=False) if classifier_model is None: raise Exception("No classifier found for the classifer's id.") else: classifier_model.delete() - diff --git a/core/domain/classifier_services_test.py b/core/domain/classifier_services_test.py index bdbe8e8eacbf..7d42764e0fd4 100644 --- a/core/domain/classifier_services_test.py +++ b/core/domain/classifier_services_test.py @@ -119,3 +119,18 @@ def test_update_of_classifiers(self): classifier_id) self.assertEqual(classifier.exp_id, exp_id) self.assertEqual(classifier.state_name, test_state) + + + def test_deletion_of_classifiers(self): + """Test the delete_classifier method.""" + exp_id = u'1' + state = 'Home' + test_state = 'State' + classifier_id = classifier_models.ClassifierModel.create( + exp_id, 1, state, + feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput'], [], 1) + classifier_services.delete_classifier(classifier_id) + with self.assertRaisesRegexp(Exception, ( + "No classifier found for the classifer's id.")): + classifier_services.get_classifier_by_id(classifier_id) + From ef9ae2c71f3dbdd369606fbfe50b41054ece73fa Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Tue, 21 Feb 2017 02:04:29 +0530 Subject: [PATCH 014/173] lint issues --- core/domain/classifier_services.py | 2 +- core/domain/classifier_services_test.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/core/domain/classifier_services.py b/core/domain/classifier_services.py index fa08a1cafa8e..1851a85887fa 100644 --- a/core/domain/classifier_services.py +++ b/core/domain/classifier_services.py @@ -153,7 +153,7 @@ def _create_classifier(classifier): """ classifier_models.ClassifierModel.create( classifier.exp_id, classifier.exp_version_when_created, - classifier.state_name, classifer.algorithm_id, + classifier.state_name, classifier.algorithm_id, classifier.cached_classifier_data, classifier.data_schema_version, ) diff --git a/core/domain/classifier_services_test.py b/core/domain/classifier_services_test.py index 7d42764e0fd4..edbc7dfb691e 100644 --- a/core/domain/classifier_services_test.py +++ b/core/domain/classifier_services_test.py @@ -125,7 +125,6 @@ def test_deletion_of_classifiers(self): """Test the delete_classifier method.""" exp_id = u'1' state = 'Home' - test_state = 'State' classifier_id = classifier_models.ClassifierModel.create( exp_id, 1, state, feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput'], [], 1) From c7eecc8048f00d7722b85a30f5e5c85a3d78085e Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Tue, 21 Feb 2017 15:00:39 +0530 Subject: [PATCH 015/173] Addressed review comments --- core/domain/classifier_services.py | 12 +++--------- core/domain/classifier_services_test.py | 3 --- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/core/domain/classifier_services.py b/core/domain/classifier_services.py index 1851a85887fa..c8e4fa32ce5c 100644 --- a/core/domain/classifier_services.py +++ b/core/domain/classifier_services.py @@ -14,8 +14,8 @@ """Services for classifier models""" -from core.domain import classifier_registry from core.domain import classifier_domain +from core.domain import classifier_registry from core.domain import exp_domain from core.domain import interaction_registry from core.platform import models @@ -125,7 +125,7 @@ def classify_string_classifier_rule(state, normalized_answer): def get_classifier_from_model(classifier_model): - """Returns a classifier domian object given a classifier model loaded + """Returns a classifier domain object given a classifier model loaded from datastore. """ return classifier_domain.Classifier( @@ -148,7 +148,7 @@ def get_classifier_by_id(classifier_id): def _create_classifier(classifier): - """Creates classifier model in the datastoer given a classifier + """Creates classifier model in the datastore given a classifier domain object. """ classifier_models.ClassifierModel.create( @@ -162,13 +162,7 @@ def _save_classifier(classifier_model, classifier): """Updates classifier model in the datastore given a classifier domain object. """ - classifier_model.exp_version_when_created = ( - classifier.exp_version_when_created) classifier_model.state_name = classifier.state_name - classifier_model.algorithm_id = classifier.algorithm_id - classifier_model.cached_classifier_data = ( - classifier.cached_classifier_data) - classifier_model.data_schema_version = classifier.data_schema_version classifier_model.put() diff --git a/core/domain/classifier_services_test.py b/core/domain/classifier_services_test.py index edbc7dfb691e..fddd68288dd0 100644 --- a/core/domain/classifier_services_test.py +++ b/core/domain/classifier_services_test.py @@ -102,7 +102,6 @@ def test_retrieval_of_classifiers(self): self.assertEqual(classifier.exp_id, exp_id) self.assertEqual(classifier.state_name, state) - def test_update_of_classifiers(self): """Test the update_classifier method.""" exp_id = u'1' @@ -120,7 +119,6 @@ def test_update_of_classifiers(self): self.assertEqual(classifier.exp_id, exp_id) self.assertEqual(classifier.state_name, test_state) - def test_deletion_of_classifiers(self): """Test the delete_classifier method.""" exp_id = u'1' @@ -132,4 +130,3 @@ def test_deletion_of_classifiers(self): with self.assertRaisesRegexp(Exception, ( "No classifier found for the classifer's id.")): classifier_services.get_classifier_by_id(classifier_id) - From f6eb4f9bbdab8a12c73a9e759957fdf74a9edb29 Mon Sep 17 00:00:00 2001 From: MAKOSCAFEE Date: Wed, 22 Feb 2017 20:28:48 +0300 Subject: [PATCH 016/173] remove image uploader directive from base.html --- core/templates/dev/head/pages/base.html | 2 -- .../dev/head/pages/exploration_editor/exploration_editor.html | 1 + core/templates/dev/head/pages/library/library.html | 2 ++ core/templates/dev/head/pages/preferences/preferences.html | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/templates/dev/head/pages/base.html b/core/templates/dev/head/pages/base.html index 4c0e74f0b7b2..20e3ba7d1699 100644 --- a/core/templates/dev/head/pages/base.html +++ b/core/templates/dev/head/pages/base.html @@ -238,14 +238,12 @@

- - diff --git a/core/templates/dev/head/pages/exploration_editor/exploration_editor.html b/core/templates/dev/head/pages/exploration_editor/exploration_editor.html index 7fdc189cea43..e89479babd2b 100644 --- a/core/templates/dev/head/pages/exploration_editor/exploration_editor.html +++ b/core/templates/dev/head/pages/exploration_editor/exploration_editor.html @@ -189,6 +189,7 @@ + diff --git a/core/templates/dev/head/pages/library/library.html b/core/templates/dev/head/pages/library/library.html index 953febbca284..6a7d1ab975ad 100644 --- a/core/templates/dev/head/pages/library/library.html +++ b/core/templates/dev/head/pages/library/library.html @@ -218,6 +218,8 @@

{{ super() }} + {% endblock footer_js %} From 97bcaa97bbd9afd7a868d5a2d55ce34d1a150825 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 23 Feb 2017 09:10:55 +0100 Subject: [PATCH 017/173] Localisation updates from https://translatewiki.net. --- assets/i18n/ast.json | 3 + assets/i18n/bn.json | 7 +- assets/i18n/de.json | 3 + assets/i18n/es.json | 7 +- assets/i18n/fa.json | 2 +- assets/i18n/fi.json | 183 +++++++++++++++++++++++ assets/i18n/fr.json | 3 + assets/i18n/it.json | 4 + assets/i18n/ja.json | 304 +++++++++++++++++++++++++++++++++++++++ assets/i18n/ko.json | 96 ++++++++++++- assets/i18n/lb.json | 3 + assets/i18n/mk.json | 3 + assets/i18n/nl.json | 9 +- assets/i18n/oc.json | 3 + assets/i18n/qqq.json | 6 +- assets/i18n/ru.json | 2 + assets/i18n/sv.json | 4 +- assets/i18n/tr.json | 4 +- assets/i18n/vi.json | 2 +- assets/i18n/zh-hans.json | 7 +- assets/i18n/zh-hant.json | 3 + 21 files changed, 642 insertions(+), 16 deletions(-) create mode 100644 assets/i18n/fi.json create mode 100644 assets/i18n/ja.json diff --git a/assets/i18n/ast.json b/assets/i18n/ast.json index fd55835d1fe0..b86e4141d286 100644 --- a/assets/i18n/ast.json +++ b/assets/i18n/ast.json @@ -18,6 +18,7 @@ "I18N_DASHBOARD_COLLECTIONS": "Colecciones", "I18N_DASHBOARD_EXPLORATIONS": "Esploraciones", "I18N_DASHBOARD_OPEN_FEEDBACK": "Abrir los comentarios", + "I18N_DASHBOARD_SUBSCRIBERS": "Suscriptores", "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "Abasna una imaxe equí", "I18N_DIRECTIVES_OR_SELECT_FILE": "o escueye un ficheru d'imaxe:", "I18N_ERROR_DISABLED_EXPLORATION": "Esploración desactivada", @@ -158,6 +159,7 @@ "I18N_LIBRARY_SUB_HEADER": "Empieza l'aventura restolando ente les nueses esploraciones.", "I18N_LIBRARY_VIEWS_TOOLTIP": "Vistes", "I18N_LIBRARY_VIEW_ALL": "Ver toes", + "I18N_ONE_SUBSCRIBER_TEXT": "Tienes 1 suscriptor.", "I18N_PLAYER_BACK": "Anterior", "I18N_PLAYER_CARD_NUMBER_TOOLTIP": "Tarxeta #", "I18N_PLAYER_COMMUNITY_EDITABLE_TOOLTIP": "Editable pola comunidá", @@ -297,5 +299,6 @@ "I18N_TOPNAV_PREFERENCES": "Preferencies", "I18N_TOPNAV_SIGN_IN": "Entrar", "I18N_TOPNAV_TEACH_WITH_OPPIA": "Enseñar con Oppia", + "I18N_TOTAL_SUBSCRIBERS_TEXT": "Tienes un total de <[totalSubscribers]> suscriptores.", "I18N_UNSUBSCRIBE_BUTTON_TEXT": "Quitar la suscripción" } diff --git a/assets/i18n/bn.json b/assets/i18n/bn.json index cce5293b248f..42e06702611d 100644 --- a/assets/i18n/bn.json +++ b/assets/i18n/bn.json @@ -18,6 +18,7 @@ "I18N_DASHBOARD_COLLECTIONS": "সংগ্রহ", "I18N_DASHBOARD_EXPLORATIONS": "অন্বেষণ", "I18N_DASHBOARD_OPEN_FEEDBACK": "প্রতিক্রিয়া খুলুন", + "I18N_DASHBOARD_SUBSCRIBERS": "গ্রাহক", "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "চিত্র এখানে টেনে আনুন", "I18N_DIRECTIVES_OR_SELECT_FILE": "বা একটি চিত্রের ফাইল নির্বাচন:", "I18N_ERROR_DISABLED_EXPLORATION": "অন্বেষণ নিষ্ক্রিয় আছে", @@ -158,6 +159,7 @@ "I18N_LIBRARY_SUB_HEADER": "আমাদের অন্বেষণ ব্রাউজ করার দ্বারা আপনার দুঃসাহসিক কাজ শুরু করুন।", "I18N_LIBRARY_VIEWS_TOOLTIP": "টি দর্শন", "I18N_LIBRARY_VIEW_ALL": "সব দেখুন", + "I18N_ONE_SUBSCRIBER_TEXT": "আপনার ১ জন গ্রাহক রয়েছে।", "I18N_PLAYER_BACK": "পিছনে", "I18N_PLAYER_CARD_NUMBER_TOOLTIP": "কার্ড #", "I18N_PLAYER_COMMUNITY_EDITABLE_TOOLTIP": "সম্প্রদায়ের সম্পাদনাযোগ্য", @@ -280,6 +282,7 @@ "I18N_SPLASH_SUBTITLE": "Oppia ইন্টারেক্টিভ পাঠ তৈরি করা সহজ করে তোলে যা শেখায় ও আকর্ষণীয়।", "I18N_SPLASH_THIRD_EXPLORATION_DESCRIPTION": "Oppia আপনাকে বিষয়ের একটি বিস্তৃত পরিসর উপর অন্বেষণ তৈরিভাগ করতে দেয়, যা শুধুমাত্র আপনার কল্পনা দ্বারা সীমাবদ্ধ।", "I18N_SPLASH_TITLE": "বইয়ের বাইরে চিন্তা করুন।", + "I18N_SUBSCRIBE_BUTTON_TEXT": "সদস্যতা নিন", "I18N_TOPNAV_ABOUT": "সম্পর্কে", "I18N_TOPNAV_ABOUT_OPPIA": "Oppia সম্পর্কে", "I18N_TOPNAV_ADMIN_PAGE": "প্রশাসকের পাতা", @@ -295,5 +298,7 @@ "I18N_TOPNAV_NOTIFICATIONS": "বিজ্ঞপ্তি", "I18N_TOPNAV_PREFERENCES": "পছন্দসমূহ", "I18N_TOPNAV_SIGN_IN": "প্রবেশ", - "I18N_TOPNAV_TEACH_WITH_OPPIA": "Oppia দিয়ে শিখান" + "I18N_TOPNAV_TEACH_WITH_OPPIA": "Oppia দিয়ে শিখান", + "I18N_TOTAL_SUBSCRIBERS_TEXT": "আপনার মোট <[totalSubscribers]> জন গ্রাহক রয়েছে।", + "I18N_UNSUBSCRIBE_BUTTON_TEXT": "সদস্যতা ত্যাগ" } diff --git a/assets/i18n/de.json b/assets/i18n/de.json index e87e0b44fed1..c3b767fc21fd 100644 --- a/assets/i18n/de.json +++ b/assets/i18n/de.json @@ -18,6 +18,7 @@ "I18N_DASHBOARD_COLLECTIONS": "Sammlungen", "I18N_DASHBOARD_EXPLORATIONS": "Erforschungen", "I18N_DASHBOARD_OPEN_FEEDBACK": "Rückmeldung eröffnen", + "I18N_DASHBOARD_SUBSCRIBERS": "Abonnenten", "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "Bild hier ablegen", "I18N_DIRECTIVES_OR_SELECT_FILE": "oder wähle eine Bilddatei aus:", "I18N_ERROR_DISABLED_EXPLORATION": "Deaktivierte Erforschung", @@ -158,6 +159,7 @@ "I18N_LIBRARY_SUB_HEADER": "Beginne dein Abenteuer mit dem Durchsuchen unserer Erforschungen.", "I18N_LIBRARY_VIEWS_TOOLTIP": "Aufrufe", "I18N_LIBRARY_VIEW_ALL": "Alle ansehen", + "I18N_ONE_SUBSCRIBER_TEXT": "Du hast einen Abonnenten.", "I18N_PLAYER_BACK": "Zurück", "I18N_PLAYER_CARD_NUMBER_TOOLTIP": "Kartennummer", "I18N_PLAYER_COMMUNITY_EDITABLE_TOOLTIP": "Durch die Gemeinschaft bearbeitbar", @@ -297,5 +299,6 @@ "I18N_TOPNAV_PREFERENCES": "Einstellungen", "I18N_TOPNAV_SIGN_IN": "Anmelden", "I18N_TOPNAV_TEACH_WITH_OPPIA": "Mit Oppia unterrichten", + "I18N_TOTAL_SUBSCRIBERS_TEXT": "Du hast insgesamt <[totalSubscribers]> Abonnenten.", "I18N_UNSUBSCRIBE_BUTTON_TEXT": "Abmelden" } diff --git a/assets/i18n/es.json b/assets/i18n/es.json index f7267bb0cb25..55d4f42a288c 100644 --- a/assets/i18n/es.json +++ b/assets/i18n/es.json @@ -18,6 +18,7 @@ "I18N_DASHBOARD_COLLECTIONS": "Colecciones", "I18N_DASHBOARD_EXPLORATIONS": "Exploraciones", "I18N_DASHBOARD_OPEN_FEEDBACK": "Retroalimentación abierta", + "I18N_DASHBOARD_SUBSCRIBERS": "Suscriptores", "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "Arrastra una imagen aquí", "I18N_DIRECTIVES_OR_SELECT_FILE": "o selecciona un archivo de imagen:", "I18N_ERROR_DISABLED_EXPLORATION": "Exploración deshabilitada", @@ -158,6 +159,7 @@ "I18N_LIBRARY_SUB_HEADER": "Comienza tu aventura dando un vistazo a nuestras exploraciones.", "I18N_LIBRARY_VIEWS_TOOLTIP": "Visitas", "I18N_LIBRARY_VIEW_ALL": "Ver todo", + "I18N_ONE_SUBSCRIBER_TEXT": "Tienes 1 suscriptor.", "I18N_PLAYER_BACK": "Volver", "I18N_PLAYER_CARD_NUMBER_TOOLTIP": "Carta #", "I18N_PLAYER_COMMUNITY_EDITABLE_TOOLTIP": "Editable por la comunidad", @@ -280,6 +282,7 @@ "I18N_SPLASH_SUBTITLE": "Con Oppia es fácil crear lecciones interactivas que educan y entretienen.", "I18N_SPLASH_THIRD_EXPLORATION_DESCRIPTION": "Oppia te permite crear y compartir exploraciones en una gran variedad de temas, limitado sólo por tu imaginación.", "I18N_SPLASH_TITLE": "Piensa más allá de los libros.", + "I18N_SUBSCRIBE_BUTTON_TEXT": "Suscribirse", "I18N_TOPNAV_ABOUT": "Acerca de", "I18N_TOPNAV_ABOUT_OPPIA": "Acerca de Oppia", "I18N_TOPNAV_ADMIN_PAGE": "Página de administración", @@ -295,5 +298,7 @@ "I18N_TOPNAV_NOTIFICATIONS": "Notificaciones", "I18N_TOPNAV_PREFERENCES": "Preferencias", "I18N_TOPNAV_SIGN_IN": "Acceder", - "I18N_TOPNAV_TEACH_WITH_OPPIA": "Enseña con Oppia" + "I18N_TOPNAV_TEACH_WITH_OPPIA": "Enseña con Oppia", + "I18N_TOTAL_SUBSCRIBERS_TEXT": "Tienes un total de <[totalSubscribers]> suscriptores.", + "I18N_UNSUBSCRIBE_BUTTON_TEXT": "Desuscribirse" } diff --git a/assets/i18n/fa.json b/assets/i18n/fa.json index fe699aa71ffa..71662b3c59c7 100644 --- a/assets/i18n/fa.json +++ b/assets/i18n/fa.json @@ -15,7 +15,7 @@ "I18N_CREATE_EXPLORATION_UPLOAD": "بارگذاری", "I18N_CREATE_NO_THANKS": "نه, ممنون", "I18N_CREATE_YES_PLEASE": "بله, لطفا", - "I18N_DASHBOARD_COLLECTIONS": "مجموعه", + "I18N_DASHBOARD_COLLECTIONS": "مجموعه‌ها", "I18N_DASHBOARD_EXPLORATIONS": "تجارب من", "I18N_DASHBOARD_OPEN_FEEDBACK": "بازخورد باز", "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "تصویر را به اینجا بکشید", diff --git a/assets/i18n/fi.json b/assets/i18n/fi.json new file mode 100644 index 000000000000..40c922aaea60 --- /dev/null +++ b/assets/i18n/fi.json @@ -0,0 +1,183 @@ +{ + "I18N_ABOUT_PAGE_TITLE": "Tietoja meistä – Oppia", + "I18N_ACTION_APPLY_TO_TEACH_WITH_OPPIA": "Hae ”Teach with Oppia” -ohjelmaan", + "I18N_ACTION_BROWSE_LESSONS": "Selaa oppituntejamme", + "I18N_ACTION_CREATE_LESSON": "Luo oma oppitunti", + "I18N_CREATE_ACTIVITY_QUESTION": "Mitä haluat luoda?", + "I18N_CREATE_ACTIVITY_TITLE": "Luo aktiviteetti", + "I18N_CREATE_COLLECTION": "Luo kokoelma", + "I18N_CREATE_EXPLORATION_CREATE": "Luo", + "I18N_CREATE_EXPLORATION_QUESTION": "Haluatko luoda tutkimuksen?", + "I18N_CREATE_EXPLORATION_UPLOAD": "Lähetä", + "I18N_CREATE_NO_THANKS": "Ei kiitos", + "I18N_CREATE_YES_PLEASE": "Kyllä, kiitos!", + "I18N_DASHBOARD_COLLECTIONS": "Kokoelmat", + "I18N_DASHBOARD_EXPLORATIONS": "Tutkimukset", + "I18N_DASHBOARD_OPEN_FEEDBACK": "Avoin palaute", + "I18N_DASHBOARD_SUBSCRIBERS": "Tilaajat", + "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "Pudota kuva tähän", + "I18N_DIRECTIVES_OR_SELECT_FILE": "tai valitse kuvatiedosto:", + "I18N_ERROR_DISABLED_EXPLORATION": "Toimintakyvytön tutkimus", + "I18N_ERROR_DISABLED_EXPLORATION_MESSAGE": "Anteeksi, mutta tutkimus jtoa klikkasit on tällä hetkellä toimintakyvytön. Ole hyvä, ja yritä myöhemmin uudelleen.", + "I18N_ERROR_DISABLED_EXPLORATION_PAGE_TITLE": "Toimintakyvytön tutkimus", + "I18N_ERROR_HEADER_400": "Virhe 400", + "I18N_ERROR_HEADER_401": "Virhe 401", + "I18N_ERROR_HEADER_404": "Virhe 404", + "I18N_ERROR_HEADER_500": "Virhe 500", + "I18N_ERROR_MESSAGE_400": "Joskus koneet eivät ymmärrä ihmisiä. Tämä on yksi niistä hetkistä.", + "I18N_ERROR_MESSAGE_401": "Et voi päästä tänne. Nopeasti, mene takaisin, ennen kuin opettaja tulee.", + "I18N_ERROR_MESSAGE_404": "Anteeksi, etsimme ja etsimme, mutta emme löytäneet tätä sivua.", + "I18N_ERROR_MESSAGE_500": "Jotain meni pahasti pieleen. Mutta se ei ollut sinun vikasi. Sisäinen virhe.", + "I18N_ERROR_NEXT_STEPS": "Paras asia jonka voit nyt tehdä, on luultavasti palata \">kotisivulle. Kuitenkin, jos tämä asia toistuu, ja mielestäsi ei pitäisi, ole hyvä ja kerro meille \" target=\"_blank\">issue tracker. Anteeksi tästä.", + "I18N_ERROR_PAGE_TITLE_400": "Virhe 400 – Oppia", + "I18N_ERROR_PAGE_TITLE_401": "Virhe 401 – Oppia", + "I18N_ERROR_PAGE_TITLE_404": "Virhe 404 – Oppia", + "I18N_ERROR_PAGE_TITLE_500": "Virhe 500 – Oppia", + "I18N_FOOTER_ABOUT": "Tietoja", + "I18N_FOOTER_ABOUT_ALL_CAPS": "TIETOJA OPPIASTA", + "I18N_FOOTER_AUTHOR_PROFILES": "Tekijän profiilit", + "I18N_FOOTER_BROWSE_LIBRARY": "Selaa kirjastoa", + "I18N_FOOTER_CONTACT_US": "Ota yhteyttä", + "I18N_FOOTER_CONTRIBUTE_ALL_CAPS": "OSALLISTU", + "I18N_FOOTER_CREDITS": "Tekijätiedot", + "I18N_FOOTER_DONATE": "Lahjoita", + "I18N_FOOTER_FOLLOW_US": "Seuraa meitä", + "I18N_FOOTER_FORUM": "Keskustelupalsta", + "I18N_FOOTER_GET_INVOLVED": "Osallistu", + "I18N_FOOTER_GET_STARTED": "Aloittaminen", + "I18N_FOOTER_OPPIA_FOUNDATION": "Oppia Foundation", + "I18N_FOOTER_PARTICIPATION_PLAYBOOK": "Osanotto pelikirjaan", + "I18N_FOOTER_PRIVACY_POLICY": "Tietosuojakäytäntö", + "I18N_FOOTER_TEACH": "Teach with Oppia", + "I18N_FOOTER_TEACH_LEARN_ALL_CAPS": "OPETA/OPI", + "I18N_FOOTER_TERMS_OF_SERVICE": "Käyttöehdot", + "I18N_FORMS_TYPE_NUMBER": "Kirjoita numero", + "I18N_FORMS_TYPE_NUMBER_AT_LEAST": "Ole hyvä ja syötä numero joka on vähintään <[minValue]>.", + "I18N_FORMS_TYPE_NUMBER_AT_MOST": "Ole hyvä syötä numero joka on enintään", + "I18N_FORMS_TYPE_NUMBER_INVALID_DECIMAL": "Ole hyvä ja syötä olemassa oleva desimaali numero", + "I18N_GET_STARTED_PAGE_TITLE": "Aloittaa", + "I18N_INTERACTIONS_GRAPH_ADD_EDGE": "liitä reuna", + "I18N_INTERACTIONS_GRAPH_ADD_NODE": "liitä solmu", + "I18N_INTERACTIONS_GRAPH_DELETE": "Poista", + "I18N_INTERACTIONS_GRAPH_ERROR_INVALID": "Pätemätön kaavio", + "I18N_INTERACTIONS_GRAPH_MOVE": "Siirry", + "I18N_INTERACTIONS_GRAPH_RESET_BUTTON": "Palautus", + "I18N_INTERACTIONS_GRAPH_RESPONSE_EDGE": "<[edges]> Reuna", + "I18N_INTERACTIONS_GRAPH_RESPONSE_EDGES": "<[edges]> Reunat", + "I18N_INTERACTIONS_GRAPH_RESPONSE_VERTEX": "ja <[vertices]> Kärki", + "I18N_INTERACTIONS_GRAPH_RESPONSE_VERTICES": "ja <[vertices]> Kärkipisteet", + "I18N_INTERACTIONS_GRAPH_SUBMIT_BUTTON": "Lähetä", + "I18N_INTERACTIONS_GRAPH_UPDATE_LABEL": "Päivitä sivu", + "I18N_INTERACTIONS_GRAPH_UPDATE_WEIGHT": "Päivitä merkitys", + "I18N_INTERACTIONS_IMAGE_CLICK_SELECT": "[Valitse kuva näytölle]", + "I18N_INTERACTIONS_ITEM_SELECTION_SUBMIT": "Lähetä", + "I18N_INTERACTIONS_LOGIC_PROOF_CORRECT_ANSWERS": "Oikein: <[answer]>", + "I18N_INTERACTIONS_LOGIC_PROOF_INCORRECT_ANSWERS": "Väärin: <[answer]>", + "I18N_INTERACTIONS_LOGIC_PROOF_POSSIBLE_LINES": "Mahdolliset linjat", + "I18N_INTERACTIONS_LOGIC_PROOF_QUESTION_STR_ASSUMPTIONS": "Olettaen <[assumptions]>; Todista <[target]>.", + "I18N_INTERACTIONS_LOGIC_PROOF_QUESTION_STR_NO_ASSUMPTION": "Todista <[Kohde]>.", + "I18N_INTERACTIONS_LOGIC_PROOF_SUBMIT": "Lähetä", + "I18N_INTERACTIONS_MUSIC_CLEAR": "Tyhjennä", + "I18N_INTERACTIONS_MUSIC_PLAY": "Toista", + "I18N_INTERACTIONS_MUSIC_PLAY_SEQUENCE": "Toista kohdejärjestys", + "I18N_INTERACTIONS_MUSIC_SUBMIT": "Lähetä", + "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "Lisää kohde", + "I18N_INTERACTIONS_SET_INPUT_DUPLICATES_ERROR": "oho, se näyttää siltä että lähetit kaksoiskappaleet!", + "I18N_INTERACTIONS_SET_INPUT_EMPTY_SET": "(Lisää yksi kohde per rivi.)", + "I18N_INTERACTIONS_SET_INPUT_NO_ANSWER": "Ei vastauksia.", + "I18N_INTERACTIONS_SET_INPUT_SUBMIT": "Lähetä", + "I18N_LANGUAGE_FOOTER_VIEW_IN": "Näytä Oppia:", + "I18N_LIBRARY_ALL_CATEGORIES": "Kaikki kategoriat", + "I18N_LIBRARY_ALL_CATEGORIES_SELECTED": "Kaikki kategoriat valittu", + "I18N_LIBRARY_ALL_LANGUAGES": "Kaikki kielet", + "I18N_LIBRARY_ALL_LANGUAGES_SELECTED": "Kaikki kielet valittu", + "I18N_LIBRARY_CATEGORIES_ALGORITHMS": "Algoritmit", + "I18N_LIBRARY_CATEGORIES_ARCHITECTURE": "Arkkitehtuuri", + "I18N_LIBRARY_CATEGORIES_ART": "Taide", + "I18N_LIBRARY_CATEGORIES_BIOLOGY": "Biologia", + "I18N_LIBRARY_CATEGORIES_BUSINESS": "Liiketoiminta", + "I18N_LIBRARY_CATEGORIES_CHEMISTRY": "Kemia", + "I18N_LIBRARY_CATEGORIES_CODING": "Koodaus", + "I18N_LIBRARY_CATEGORIES_COMPUTING": "Tietojenkäsittely", + "I18N_LIBRARY_CATEGORIES_ECONOMICS": "Taloustieteet", + "I18N_LIBRARY_CATEGORIES_EDUCATION": "Koulutus", + "I18N_LIBRARY_CATEGORIES_ENGINEERING": "Tekniikka", + "I18N_LIBRARY_CATEGORIES_ENGLISH": "Englanti", + "I18N_LIBRARY_CATEGORIES_ENVIRONMENT": "Ympäristö", + "I18N_LIBRARY_CATEGORIES_GEOGRAPHY": "Maantiede", + "I18N_LIBRARY_CATEGORIES_GOVERNMENT": "Hallitus", + "I18N_LIBRARY_CATEGORIES_HISTORY": "Historia", + "I18N_LIBRARY_CATEGORIES_HOBBIES": "Harrastukset", + "I18N_LIBRARY_CATEGORIES_INTERACTIVE_FICTION": "Interaktiivinen Fiktio", + "I18N_LIBRARY_CATEGORIES_LANGUAGES": "Kielet", + "I18N_LIBRARY_CATEGORIES_LAW": "Laki", + "I18N_LIBRARY_CATEGORIES_LIFE_SKILLS": "Elämän taidot", + "I18N_LIBRARY_CATEGORIES_MATHEMATICS": "Matematiikka", + "I18N_LIBRARY_CATEGORIES_MATHS": "Matematiikka", + "I18N_LIBRARY_CATEGORIES_MEDICINE": "Lääke", + "I18N_LIBRARY_CATEGORIES_MUSIC": "Musiikki", + "I18N_LIBRARY_CATEGORIES_PHILOSOPHY": "Filosofia", + "I18N_LIBRARY_CATEGORIES_PHYSICS": "Fysiikka", + "I18N_LIBRARY_CATEGORIES_PROGRAMMING": "Ohjemointi", + "I18N_LIBRARY_CATEGORIES_PSYCHOLOGY": "Psykologia", + "I18N_LIBRARY_CATEGORIES_PUZZLES": "Palat", + "I18N_LIBRARY_CATEGORIES_READING": "Käsittely", + "I18N_LIBRARY_CATEGORIES_RELIGION": "Relaatio", + "I18N_LIBRARY_CATEGORIES_SPORT": "Urheilu", + "I18N_LIBRARY_CATEGORIES_STATISTICS": "Tilastot", + "I18N_LIBRARY_CATEGORIES_TEST": "Kokeilu", + "I18N_LIBRARY_CATEGORIES_WELCOME": "Tervetuloa", + "I18N_LIBRARY_CREATE_EXPLORATION_QUESTION": "Haluaisitko luoda yhden?", + "I18N_LIBRARY_GROUPS_COMPUTING": "Tietojenkäsittely", + "I18N_LIBRARY_GROUPS_FEATURED_ACTIVITIES": "Vierailu Aktiviteetit", + "I18N_LIBRARY_GROUPS_HUMANITIES": "Humanistiset tieteet", + "I18N_LIBRARY_GROUPS_LANGUAGES": "Kielet", + "I18N_LIBRARY_GROUPS_MATHEMATICS_&_STATISTICS": "Matematiikka ja Tilastot", + "I18N_LIBRARY_GROUPS_RECENTLY_PUBLISHED": "Hiljattain julkaistu", + "I18N_LIBRARY_GROUPS_SCIENCE": "Tiede", + "I18N_LIBRARY_GROUPS_SOCIAL_SCIENCE": "Sosiaalinen tiede", + "I18N_LIBRARY_LAST_UPDATED": "Viimeksi päivitetty", + "I18N_LIBRARY_LOADING": "Ladataan", + "I18N_LIBRARY_N/A": "N/A", + "I18N_PLAYER_BACK": "Takaisin", + "I18N_PLAYER_CARD_NUMBER_TOOLTIP": "Kortti #", + "I18N_PLAYER_COMMUNITY_EDITABLE_TOOLTIP": "Yhteisön muokattavissa", + "I18N_PLAYER_CONTINUE_BUTTON": "Jatka", + "I18N_PLAYER_CONTINUE_NEXT_CARD": "Jatka seuraavaan korttiin", + "I18N_PLAYER_CONTRIBUTORS_TOOLTIP": "Osallistujat", + "I18N_PLAYER_EDIT_TOOLTIP": "Muokkaa", + "I18N_PLAYER_EMBED_TOOLTIP": "Upota", + "I18N_PLAYER_FEEDBACK_TOOLTIP": "Palaute", + "I18N_PLAYER_FORWARD": "Eteenpäin", + "I18N_PLAYER_GO_TO_LIBRARY": "Mene kirjastoon", + "I18N_PLAYER_INFO_TOOLTIP": "Tiedot", + "I18N_PLAYER_LAST_UPDATED_TOOLTIP": "Viimeksi päivitetty", + "I18N_PLAYER_LEAVE_FEEDBACK": "Jätä palautetta tekijöille...", + "I18N_PLAYER_LOADING": "Ladataan...", + "I18N_PLAYER_NO_OBJECTIVE": "Tavoitetta ei ole määritelty.", + "I18N_PLAYER_REPORT_MODAL_BODY_OTHER": "Muu", + "I18N_PLAYER_REPORT_MODAL_BODY_POOR_EXPERIENCE": "Huono oppimiskokemus", + "I18N_PLAYER_REPORT_MODAL_FOOTER_CANCEL": "Peruuta", + "I18N_PLAYER_REPORT_MODAL_FOOTER_SUBMIT": "Lähetä", + "I18N_PLAYER_REPORT_SUCCESS_MODAL_CLOSE": "Sulje", + "I18N_PLAYER_REPORT_SUCCESS_MODAL_HEADER": "Kiitos!", + "I18N_PLAYER_STAY_ANONYMOUS": "Pysyä nimettömänä", + "I18N_PLAYER_SUBMIT_BUTTON": "Lähetä", + "I18N_PLAYER_TAGS_TOOLTIP": "Merkkaukset", + "I18N_PLAYER_THANK_FEEDBACK": "Kiitos palautteesta!", + "I18N_PLAYER_UNRATED": "Luokittelematon", + "I18N_PREFERENCES_CANCEL_BUTTON": "Peruuta", + "I18N_PREFERENCES_EMAIL": "Sähköpostiosoite", + "I18N_PREFERENCES_PICTURE": "Kuva", + "I18N_SIDEBAR_ABOUT_LINK": "Tietoja", + "I18N_SIDEBAR_BLOG": "Blogi", + "I18N_SIDEBAR_CONTACT_US": "Ota yhteyttä", + "I18N_SIDEBAR_DONATE": "Lahjoita", + "I18N_SIDEBAR_FORUM": "Foorumi", + "I18N_SIDEBAR_GET_STARTED": "Aloittaminen", + "I18N_SIDEBAR_LIBRARY_LINK": "Kirjasto", + "I18N_SIGNUP_CLOSE_BUTTON": "Sulje", + "I18N_SIGNUP_EMAIL": "Sähköpostiosoite", + "I18N_SUBSCRIBE_BUTTON_TEXT": "Tilaa", + "I18N_UNSUBSCRIBE_BUTTON_TEXT": "Peruuta tilaus" +} diff --git a/assets/i18n/fr.json b/assets/i18n/fr.json index 98daaa24fa31..6561256ff2b7 100644 --- a/assets/i18n/fr.json +++ b/assets/i18n/fr.json @@ -18,6 +18,7 @@ "I18N_DASHBOARD_COLLECTIONS": "Collections", "I18N_DASHBOARD_EXPLORATIONS": "Explorations", "I18N_DASHBOARD_OPEN_FEEDBACK": "Ouvrir les commentaires", + "I18N_DASHBOARD_SUBSCRIBERS": "Abonnés", "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "Faire glisser l’image ici", "I18N_DIRECTIVES_OR_SELECT_FILE": "ou sélectionner un fichier image :", "I18N_ERROR_DISABLED_EXPLORATION": "Exploration désactivée", @@ -158,6 +159,7 @@ "I18N_LIBRARY_SUB_HEADER": "Commencez votre aventure en parcourant nos explorations.", "I18N_LIBRARY_VIEWS_TOOLTIP": "Vues", "I18N_LIBRARY_VIEW_ALL": "Afficher tout", + "I18N_ONE_SUBSCRIBER_TEXT": "Vous avez 1 abonné.", "I18N_PLAYER_BACK": "Retour", "I18N_PLAYER_CARD_NUMBER_TOOLTIP": "Numéro de carte", "I18N_PLAYER_COMMUNITY_EDITABLE_TOOLTIP": "Modifiable par la communauté", @@ -297,5 +299,6 @@ "I18N_TOPNAV_PREFERENCES": "Préférences", "I18N_TOPNAV_SIGN_IN": "Connexion", "I18N_TOPNAV_TEACH_WITH_OPPIA": "Enseigner avec Oppia", + "I18N_TOTAL_SUBSCRIBERS_TEXT": "Vous avez un total de <[totalSubscribers]> abonnés.", "I18N_UNSUBSCRIBE_BUTTON_TEXT": "Se désabonner" } diff --git a/assets/i18n/it.json b/assets/i18n/it.json index ba5b2dcc4e48..462866ed0d10 100644 --- a/assets/i18n/it.json +++ b/assets/i18n/it.json @@ -80,6 +80,8 @@ "I18N_LIBRARY_GROUPS_SOCIAL_SCIENCE": "Scienze sociali", "I18N_PLAYER_GO_TO_LIBRARY": "Vai alla libreria", "I18N_PLAYER_REPORT_MODAL_FOOTER_CANCEL": "Annulla", + "I18N_PLAYER_REPORT_SUCCESS_MODAL_CLOSE": "Chiudi", + "I18N_PLAYER_REPORT_SUCCESS_MODAL_HEADER": "Grazie!", "I18N_PLAYER_SUBMIT_BUTTON": "Invia", "I18N_PREFERENCES_BREADCRUMB": "Preferenze", "I18N_PREFERENCES_CANCEL_BUTTON": "Annulla", @@ -98,6 +100,8 @@ "I18N_SIGNUP_EMAIL_PREFERENCES": "Preferenze di posta elettronica", "I18N_SIGNUP_LOADING": "Caricamento", "I18N_SIGNUP_USERNAME": "Nome utente", + "I18N_SIGNUP_WHY_LICENSE": "Perché CC BY-SA?", + "I18N_SPLASH_JAVASCRIPT_ERROR_THANKS": "Grazie.", "I18N_SUBSCRIBE_BUTTON_TEXT": "Iscriviti", "I18N_TOPNAV_BLOG": "Blog", "I18N_TOPNAV_CONTACT_US": "Contattaci", diff --git a/assets/i18n/ja.json b/assets/i18n/ja.json new file mode 100644 index 000000000000..04a116d93d48 --- /dev/null +++ b/assets/i18n/ja.json @@ -0,0 +1,304 @@ +{ + "I18N_ABOUT_PAGE_TITLE": "Oppiaについて", + "I18N_ACTION_APPLY_TO_TEACH_WITH_OPPIA": "「Teach With Oppia」に申し込む", + "I18N_ACTION_BROWSE_EXPLORATIONS": "探検の一覧を見る", + "I18N_ACTION_BROWSE_LESSONS": "レッスンの一覧を見る", + "I18N_ACTION_CREATE_EXPLORATION": "探検を作る", + "I18N_ACTION_CREATE_LESSON": "自分のレッスンを作る", + "I18N_CREATE_ACTIVITY_QUESTION": "何を作りますか?", + "I18N_CREATE_ACTIVITY_TITLE": "アクティビティーを作る", + "I18N_CREATE_COLLECTION": "コレクションを作る", + "I18N_CREATE_EXPLORATION": "探検を作る", + "I18N_CREATE_EXPLORATION_CREATE": "作る", + "I18N_CREATE_EXPLORATION_QUESTION": "探検を作りますか?", + "I18N_CREATE_EXPLORATION_TITLE": "探検を作る", + "I18N_CREATE_EXPLORATION_UPLOAD": "アップロード", + "I18N_CREATE_NO_THANKS": "いいえ", + "I18N_CREATE_YES_PLEASE": "はい", + "I18N_DASHBOARD_COLLECTIONS": "コレクション", + "I18N_DASHBOARD_EXPLORATIONS": "探検", + "I18N_DASHBOARD_OPEN_FEEDBACK": "公開フィードバック", + "I18N_DASHBOARD_SUBSCRIBERS": "サブスクライブしている人", + "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "画像をここにドラッグ", + "I18N_DIRECTIVES_OR_SELECT_FILE": "または画像を選択:", + "I18N_ERROR_DISABLED_EXPLORATION": "無効な状態の探検", + "I18N_ERROR_DISABLED_EXPLORATION_MESSAGE": "残念ながら、クリックした探検は現在無効です。あとで再度試してください。", + "I18N_ERROR_DISABLED_EXPLORATION_PAGE_TITLE": "無効な状態の探検 - Oppia", + "I18N_ERROR_HEADER_400": "エラー 400", + "I18N_ERROR_HEADER_401": "エラー 401", + "I18N_ERROR_HEADER_404": "エラー 404", + "I18N_ERROR_HEADER_500": "エラー 500", + "I18N_ERROR_MESSAGE_400": "コンピューターは人間の指示に対応できないこともあります。残念ながら今回はそれです。", + "I18N_ERROR_MESSAGE_401": "こんなところに居てはダメ。先生に見つかる前に戻りなさい。", + "I18N_ERROR_MESSAGE_404": "指定のページを探しましたが、残念ながら見つかりませんでした。", + "I18N_ERROR_MESSAGE_500": "何らかの問題が発生しています。あなたのミスではなく、サーバー内でエラーが発生しました。", + "I18N_ERROR_NEXT_STEPS": "恐らく\">ホームページに戻るのが良いでしょう。ただし、もしこの問題が再発していて改善を希望される場合、\" target=\"_blank\">イシュー・トラッカーからお知らせください。ご迷惑をお掛けします。", + "I18N_ERROR_PAGE_TITLE_400": "エラー 400 - Oppia", + "I18N_ERROR_PAGE_TITLE_401": "エラー 401 - Oppia", + "I18N_ERROR_PAGE_TITLE_404": "エラー 404 - Oppia", + "I18N_ERROR_PAGE_TITLE_500": "エラー 500 - Oppia", + "I18N_FOOTER_ABOUT": "Oppiaについて", + "I18N_FOOTER_ABOUT_ALL_CAPS": "OPPIAについて", + "I18N_FOOTER_AUTHOR_PROFILES": "著者プロフィール", + "I18N_FOOTER_BROWSE_LIBRARY": "ライブラリーを見る", + "I18N_FOOTER_CONTACT_US": "連絡先", + "I18N_FOOTER_CONTRIBUTE_ALL_CAPS": "貢献する", + "I18N_FOOTER_CREDITS": "貢献者", + "I18N_FOOTER_DONATE": "寄付する", + "I18N_FOOTER_FOLLOW_US": "フォローする", + "I18N_FOOTER_FORUM": "フォーラム", + "I18N_FOOTER_GET_INVOLVED": "参加する", + "I18N_FOOTER_GET_STARTED": "始める", + "I18N_FOOTER_OPPIA_FOUNDATION": "Oppia Foundation", + "I18N_FOOTER_PARTICIPATION_PLAYBOOK": "参加の注意事項", + "I18N_FOOTER_PRIVACY_POLICY": "プライバシー・ポリシー", + "I18N_FOOTER_TEACH": "Teach with Oppia", + "I18N_FOOTER_TEACH_LEARN_ALL_CAPS": "教える/学ぶ", + "I18N_FOOTER_TERMS_OF_SERVICE": "サービス利用規約", + "I18N_FORMS_TYPE_NUMBER": "数字を入力", + "I18N_FORMS_TYPE_NUMBER_AT_LEAST": "<[minValue]>以上の数字を入力してください。", + "I18N_FORMS_TYPE_NUMBER_AT_MOST": "<[maxValue]>以下の数字を入力してください。", + "I18N_FORMS_TYPE_NUMBER_INVALID_DECIMAL": "有効な数字を入力してください。", + "I18N_GET_STARTED_PAGE_TITLE": "始める", + "I18N_INTERACTIONS_GRAPH_ADD_EDGE": "辺を追加", + "I18N_INTERACTIONS_GRAPH_ADD_NODE": "頂点を追加", + "I18N_INTERACTIONS_GRAPH_DELETE": "削除", + "I18N_INTERACTIONS_GRAPH_ERROR_INVALID": "無効なグラフ!", + "I18N_INTERACTIONS_GRAPH_MOVE": "移動", + "I18N_INTERACTIONS_GRAPH_RESET_BUTTON": "リセット", + "I18N_INTERACTIONS_GRAPH_RESPONSE_EDGE": "<[edges]>つの辺", + "I18N_INTERACTIONS_GRAPH_RESPONSE_EDGES": "<[edges]>つの辺", + "I18N_INTERACTIONS_GRAPH_RESPONSE_VERTEX": "と<[vertices]>つの頂点", + "I18N_INTERACTIONS_GRAPH_RESPONSE_VERTICES": "と<[vertices]>つの頂点", + "I18N_INTERACTIONS_GRAPH_SUBMIT_BUTTON": "提出", + "I18N_INTERACTIONS_GRAPH_UPDATE_LABEL": "ラベルを更新", + "I18N_INTERACTIONS_GRAPH_UPDATE_WEIGHT": "重みを更新", + "I18N_INTERACTIONS_IMAGE_CLICK_SELECT": "[表示する画像を選択]", + "I18N_INTERACTIONS_ITEM_SELECTION_NOT_ENOUGH": "{minChoiceNumber, plural, one{少なくとも1個選択してください。} other{少なくとも#個選択してください。}}", + "I18N_INTERACTIONS_ITEM_SELECTION_PREVENT_MORE": "{maxAllowableSelectionCount, plural, one{選択できるのは最大で1個です。} other{選択できるのは最大で#個です。}}", + "I18N_INTERACTIONS_ITEM_SELECTION_SUBMIT": "提出", + "I18N_INTERACTIONS_LOGIC_PROOF_CORRECT_ANSWERS": "正解:<[answer]>", + "I18N_INTERACTIONS_LOGIC_PROOF_INCORRECT_ANSWERS": "不正解:<[answer]>", + "I18N_INTERACTIONS_LOGIC_PROOF_POSSIBLE_LINES": "入力の候補", + "I18N_INTERACTIONS_LOGIC_PROOF_QUESTION_STR_ASSUMPTIONS": "<[assumptions]>ならば、<[target]>であることを証明せよ。", + "I18N_INTERACTIONS_LOGIC_PROOF_QUESTION_STR_NO_ASSUMPTION": "<[target]>であることを証明せよ。", + "I18N_INTERACTIONS_LOGIC_PROOF_SUBMIT": "提出", + "I18N_INTERACTIONS_MUSIC_CLEAR": "消去", + "I18N_INTERACTIONS_MUSIC_PLAY": "再生", + "I18N_INTERACTIONS_MUSIC_PLAY_SEQUENCE": "目標のシーケンスを再生", + "I18N_INTERACTIONS_MUSIC_SUBMIT": "提出", + "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "項目を追加", + "I18N_INTERACTIONS_SET_INPUT_DUPLICATES_ERROR": "重複した項目があるようです。", + "I18N_INTERACTIONS_SET_INPUT_EMPTY_SET": "(1行に1つ入力してください。)", + "I18N_INTERACTIONS_SET_INPUT_NO_ANSWER": "解答がありません。", + "I18N_INTERACTIONS_SET_INPUT_SUBMIT": "提出", + "I18N_LANGUAGE_FOOTER_VIEW_IN": "Oppiaの表示言語:", + "I18N_LIBRARY_ALL_CATEGORIES": "すべてのカテゴリー", + "I18N_LIBRARY_ALL_CATEGORIES_SELECTED": "すべてのカテゴリーが選択済み", + "I18N_LIBRARY_ALL_LANGUAGES": "すべての言語", + "I18N_LIBRARY_ALL_LANGUAGES_SELECTED": "すべての言語が選択済み", + "I18N_LIBRARY_CATEGORIES_ALGORITHMS": "アルゴリズム", + "I18N_LIBRARY_CATEGORIES_ARCHITECTURE": "建築", + "I18N_LIBRARY_CATEGORIES_ART": "芸術", + "I18N_LIBRARY_CATEGORIES_BIOLOGY": "生物", + "I18N_LIBRARY_CATEGORIES_BUSINESS": "ビジネス", + "I18N_LIBRARY_CATEGORIES_CHEMISTRY": "化学", + "I18N_LIBRARY_CATEGORIES_CODING": "コーディング", + "I18N_LIBRARY_CATEGORIES_COMPUTING": "コンピューター", + "I18N_LIBRARY_CATEGORIES_ECONOMICS": "経済", + "I18N_LIBRARY_CATEGORIES_EDUCATION": "教育", + "I18N_LIBRARY_CATEGORIES_ENGINEERING": "工学", + "I18N_LIBRARY_CATEGORIES_ENGLISH": "英語", + "I18N_LIBRARY_CATEGORIES_ENVIRONMENT": "環境", + "I18N_LIBRARY_CATEGORIES_GEOGRAPHY": "地理", + "I18N_LIBRARY_CATEGORIES_GOVERNMENT": "政治", + "I18N_LIBRARY_CATEGORIES_HISTORY": "歴史", + "I18N_LIBRARY_CATEGORIES_HOBBIES": "趣味", + "I18N_LIBRARY_CATEGORIES_INTERACTIVE_FICTION": "インタラクティブ小説", + "I18N_LIBRARY_CATEGORIES_LANGUAGES": "言語", + "I18N_LIBRARY_CATEGORIES_LAW": "法律", + "I18N_LIBRARY_CATEGORIES_LIFE_SKILLS": "日常スキル", + "I18N_LIBRARY_CATEGORIES_MATHEMATICS": "数学", + "I18N_LIBRARY_CATEGORIES_MATHS": "数学", + "I18N_LIBRARY_CATEGORIES_MEDICINE": "医学", + "I18N_LIBRARY_CATEGORIES_MUSIC": "音楽", + "I18N_LIBRARY_CATEGORIES_PHILOSOPHY": "哲学", + "I18N_LIBRARY_CATEGORIES_PHYSICS": "物理", + "I18N_LIBRARY_CATEGORIES_PROGRAMMING": "プログラミング", + "I18N_LIBRARY_CATEGORIES_PSYCHOLOGY": "心理学", + "I18N_LIBRARY_CATEGORIES_PUZZLES": "パズル", + "I18N_LIBRARY_CATEGORIES_READING": "リーディング", + "I18N_LIBRARY_CATEGORIES_RELIGION": "宗教", + "I18N_LIBRARY_CATEGORIES_SPORT": "スポーツ", + "I18N_LIBRARY_CATEGORIES_STATISTICS": "統計", + "I18N_LIBRARY_CATEGORIES_TEST": "テスト", + "I18N_LIBRARY_CATEGORIES_WELCOME": "ようこそ", + "I18N_LIBRARY_CREATE_EXPLORATION_QUESTION": "作成しますか?", + "I18N_LIBRARY_GROUPS_COMPUTING": "コンピューター", + "I18N_LIBRARY_GROUPS_FEATURED_ACTIVITIES": "おすすめのアクティビティー", + "I18N_LIBRARY_GROUPS_HUMANITIES": "人文学", + "I18N_LIBRARY_GROUPS_LANGUAGES": "言語", + "I18N_LIBRARY_GROUPS_MATHEMATICS_&_STATISTICS": "数学と統計", + "I18N_LIBRARY_GROUPS_RECENTLY_PUBLISHED": "最近の公開", + "I18N_LIBRARY_GROUPS_SCIENCE": "科学", + "I18N_LIBRARY_GROUPS_SOCIAL_SCIENCE": "社会科学", + "I18N_LIBRARY_GROUPS_TOP_RATED_EXPLORATIONS": "評価の高い探検", + "I18N_LIBRARY_LAST_UPDATED": "最終更新", + "I18N_LIBRARY_LOADING": "読み込み中", + "I18N_LIBRARY_MAIN_HEADER": "今日は何を学ぼうか...", + "I18N_LIBRARY_N/A": "なし", + "I18N_LIBRARY_NO_EXPLORATIONS": "表示する探検がありません。", + "I18N_LIBRARY_NO_EXPLORATION_FOR_QUERY": "検索にマッチする探検がありません。", + "I18N_LIBRARY_NO_EXPLORATION_GROUPS": "表示できる探検がありません。", + "I18N_LIBRARY_NO_OBJECTIVE": "目標の説明がありません。", + "I18N_LIBRARY_N_CATEGORIES": "{categoriesCount, plural, =1{1カテゴリー} other{#カテゴリー}}", + "I18N_LIBRARY_N_LANGUAGES": "{languagesCount, plural, =1{1言語} other{#言語}}", + "I18N_LIBRARY_PAGE_TITLE": "探検のライブラリー - Oppia", + "I18N_LIBRARY_RATINGS_TOOLTIP": "評価", + "I18N_LIBRARY_SEARCH_PLACEHOLDER": "何に興味がありますか?", + "I18N_LIBRARY_SUB_HEADER": "探検の一覧から探しましょう。", + "I18N_LIBRARY_VIEWS_TOOLTIP": "閲覧数", + "I18N_LIBRARY_VIEW_ALL": "すべて表示", + "I18N_ONE_SUBSCRIBER_TEXT": "サブスクライブしている人は1人です。", + "I18N_PLAYER_BACK": "戻る", + "I18N_PLAYER_CARD_NUMBER_TOOLTIP": "Card #", + "I18N_PLAYER_COMMUNITY_EDITABLE_TOOLTIP": "コミュニティーで編集可能", + "I18N_PLAYER_CONTINUE_BUTTON": "次へ", + "I18N_PLAYER_CONTINUE_NEXT_CARD": "次のカードへ", + "I18N_PLAYER_CONTRIBUTORS_TOOLTIP": "著者", + "I18N_PLAYER_EDIT_TOOLTIP": "編集", + "I18N_PLAYER_EMBED_TOOLTIP": "埋め込む", + "I18N_PLAYER_FEEDBACK_TOOLTIP": "フィードバック", + "I18N_PLAYER_FORWARD": "進む", + "I18N_PLAYER_GO_TO_LIBRARY": "ライブラリーに移動", + "I18N_PLAYER_INFO_TOOLTIP": "情報", + "I18N_PLAYER_LAST_UPDATED_TOOLTIP": "最終更新", + "I18N_PLAYER_LEAVE_FEEDBACK": "著者にフィードバックを残す...", + "I18N_PLAYER_LOADING": "読み込み中...", + "I18N_PLAYER_NO_OBJECTIVE": "目標の説明がありません。", + "I18N_PLAYER_NO_TAGS": "タグ指定なし。", + "I18N_PLAYER_PLUS_TAGS": "さらに<[additionalTagNumber]>以上のタグ", + "I18N_PLAYER_PREVIOUS_RESPONSES": "以前の解答(<[previousResponses]>)", + "I18N_PLAYER_RATE_EXPLORATION": "新しいことは学べましたか? この探検を評価しますか?", + "I18N_PLAYER_RATINGS_TOOLTIP": "評価", + "I18N_PLAYER_RECOMMEND_EXPLORATIONS": "次におすすめの探検:", + "I18N_PLAYER_REPORT_MODAL_BODY_AD": "誤解を招くコンテンツ、スパム、または広告", + "I18N_PLAYER_REPORT_MODAL_BODY_ADDITIONAL_DETAILS": "モデレーターに詳しく教えてください:", + "I18N_PLAYER_REPORT_MODAL_BODY_HEADER": "どのような問題ですか?", + "I18N_PLAYER_REPORT_MODAL_BODY_OTHER": "その他", + "I18N_PLAYER_REPORT_MODAL_BODY_POOR_EXPERIENCE": "不十分な学習体験", + "I18N_PLAYER_REPORT_MODAL_FOOTER_CANCEL": "キャンセル", + "I18N_PLAYER_REPORT_MODAL_FOOTER_SUBMIT": "送信", + "I18N_PLAYER_REPORT_MODAL_HEADER": "この探検を報告", + "I18N_PLAYER_REPORT_SUCCESS_MODAL_BODY": "報告が送信されました。モデレーターが確認します。", + "I18N_PLAYER_REPORT_SUCCESS_MODAL_CLOSE": "閉じる", + "I18N_PLAYER_REPORT_SUCCESS_MODAL_HEADER": "ありがとうございました!", + "I18N_PLAYER_REPORT_TOOLTIP": "探検を報告", + "I18N_PLAYER_RETURN_TO_COLLECTION": "<[collectionTitle]>に戻る", + "I18N_PLAYER_RETURN_TO_EDITOR": "エディターに戻る", + "I18N_PLAYER_SHARE_EXPLORATION": "この探検は楽しめましたか? 友達と共有しましょう。", + "I18N_PLAYER_SHARE_THIS_EXPLORATION": "この探検を共有", + "I18N_PLAYER_STAY_ANONYMOUS": "匿名にする", + "I18N_PLAYER_SUBMIT_BUTTON": "送信", + "I18N_PLAYER_TAGS_TOOLTIP": "タグ", + "I18N_PLAYER_THANK_FEEDBACK": "フィードバックをありがとうございます。", + "I18N_PLAYER_UNRATED": "評価なし", + "I18N_PLAYER_VIEWS_TOOLTIP": "閲覧数", + "I18N_PREFERENCES_BIO": "自己紹介", + "I18N_PREFERENCES_BIO_EXPLAIN_TEXT": "このフィールドはオプションです。ここに書いた内容は公開され、世界のどこからでも表示できます。", + "I18N_PREFERENCES_BREADCRUMB": "個人設定", + "I18N_PREFERENCES_CANCEL_BUTTON": "キャンセル", + "I18N_PREFERENCES_CHANGE_PICTURE": "プロフィール画像を変更", + "I18N_PREFERENCES_EMAIL": "メール", + "I18N_PREFERENCES_EMAIL_EXPLAIN": "モデレーターとサイト管理者のみが見られます。", + "I18N_PREFERENCES_EMAIL_RECEIVE_NEWS": "このサイトに関するニュースと更新を受け取る", + "I18N_PREFERENCES_EMAIL_RECEIVE_SUBSCRIPTION_NEWS": "サブスクライブした著者が新しい探検を公開したときにメールを受け取る", + "I18N_PREFERENCES_HEADING": "個人設定", + "I18N_PREFERENCES_HEADING_SUBTEXT": "このページで実行した変更は自動保存されます。", + "I18N_PREFERENCES_PAGE_TITLE": "プロフィールの個人設定を変更 - Oppia", + "I18N_PREFERENCES_PICTURE": "画像", + "I18N_PREFERENCES_PREFERRED_EXPLORATION_LANGUAGE": "探検で優先する言語", + "I18N_PREFERENCES_PREFERRED_EXPLORATION_LANGUAGE_EXPLAIN": "探検を検索するとき、これらの言語がデフォルトで選択されます。", + "I18N_PREFERENCES_PREFERRED_SITE_LANGUAGE": "サイトで優先する言語", + "I18N_PREFERENCES_PREFERRED_SITE_LANGUAGE_EXPLAIN": "このサイトが表示される言語です。", + "I18N_PREFERENCES_PREFERRED_SITE_LANGUAGE_PLACEHOLDER": "サイトで優先する言語", + "I18N_PREFERENCES_PROFILE_PICTURE_ADD": "プロフィール画像を追加", + "I18N_PREFERENCES_PROFILE_PICTURE_DRAG": "ドラッグして切り抜きまたはサイズ変更", + "I18N_PREFERENCES_PROFILE_PICTURE_ERROR": "エラー:画像ファイルが読み込めません。", + "I18N_PREFERENCES_PROFILE_PICTURE_UPLOAD": "プロフィール画像をアップロード", + "I18N_PREFERENCES_SELECT_EXPLORATION_LANGUAGE": "優先する言語を選択...", + "I18N_PREFERENCES_SUBJECT_INTERESTS": "興味のある科目", + "I18N_PREFERENCES_SUBJECT_INTERESTS_HELP_BLOCK": "例:mathematics、computer science、art、...", + "I18N_PREFERENCES_SUBJECT_INTERESTS_INVALID_SEARCH": "興味のある科目を新しく追加(英小文字とスペースで)...", + "I18N_PREFERENCES_SUBJECT_INTERESTS_PLACEHOLDER": "興味のある科目を入力...", + "I18N_PREFERENCES_USERNAME": "ユーザー名", + "I18N_PREFERENCES_USERNAME_NOT_SELECTED": "未選択", + "I18N_SIDEBAR_ABOUT_LINK": "このサイトについて", + "I18N_SIDEBAR_BLOG": "ブログ", + "I18N_SIDEBAR_CONTACT_US": "連絡先", + "I18N_SIDEBAR_DONATE": "寄付する", + "I18N_SIDEBAR_FORUM": "フォーラム", + "I18N_SIDEBAR_GET_STARTED": "始める", + "I18N_SIDEBAR_LIBRARY_LINK": "ライブラリー", + "I18N_SIDEBAR_TEACH_WITH_OPPIA": "Teach with Oppia", + "I18N_SIGNUP_AGREE_LICENSE_DESCRIPTION": "このテキストの左にあるボックスをチェックすることで、あなたは<[sitename]>の利用規約(こちら)に拘束されることを確認、同意、および受諾します。", + "I18N_SIGNUP_BUTTON_SUBMIT": "送信して貢献を始める", + "I18N_SIGNUP_CC_TITLE": "クリエイティブ・コモンズ・ライセンス", + "I18N_SIGNUP_CLOSE_BUTTON": "閉じる", + "I18N_SIGNUP_COMPLETE_REGISTRATION": "登録を完了する", + "I18N_SIGNUP_DO_NOT_SEND_EMAILS": "こういったメールは受け取らない", + "I18N_SIGNUP_EMAIL": "メール", + "I18N_SIGNUP_EMAIL_PREFERENCES": "メール設定", + "I18N_SIGNUP_EMAIL_PREFERENCES_EXPLAIN": "この設定は個人設定ページからいつでも変更できます。", + "I18N_SIGNUP_ERROR_MUST_AGREE_TO_TERMS": "このサイトの探検を編集するには、規約に同意する必要があります。", + "I18N_SIGNUP_ERROR_NO_USERNAME": "ユーザー名を入力してください。", + "I18N_SIGNUP_ERROR_USERNAME_MORE_50_CHARS": "ユーザー名は最大50文字です。", + "I18N_SIGNUP_ERROR_USERNAME_NOT_AVAILABLE": "このユーザー名は使用できません。", + "I18N_SIGNUP_ERROR_USERNAME_ONLY_ALPHANUM": "ユーザー名は英数字のみです。", + "I18N_SIGNUP_ERROR_USERNAME_TAKEN": "残念ながら、このユーザー名はすでに使用されています。", + "I18N_SIGNUP_ERROR_USERNAME_WITH_ADMIN": "「admin」と付くユーザー名は使えません", + "I18N_SIGNUP_ERROR_USERNAME_WITH_SPACES": "ユーザー名にスペースを入れることはできません。", + "I18N_SIGNUP_FIELD_REQUIRED": "このフィールドは必須です。", + "I18N_SIGNUP_LICENSE_NOTE": "利用規約に同意することで、あなたが当サイトで作成するコンテンツまたは行う貢献は、表示(BY)要件を放棄したCC-BY-SA v.4.0でライセンスされることに同意するという点にご注意ください。このライセンス許諾に関する詳細は、当サイトの利用規約をお読みください。CC-BY-SAについてはこちらをクリックしてください。", + "I18N_SIGNUP_LICENSE_OBJECTIVE": "<[licenselink]>ライセンスによって、探検コンテンツは自由に複製、再利用、リミックス、および再配布できます。主な条件は、誰かが当該資料をリミックス、改変、または別の作品のベースにした場合、その人も自分の作品を同じく自由なライセンスで配布しなければならない点です。", + "I18N_SIGNUP_LOADING": "読み込み中", + "I18N_SIGNUP_PAGE_TITLE": "コミュニティーに参加 - Oppia", + "I18N_SIGNUP_REGISTRATION": "登録", + "I18N_SIGNUP_SEND_ME_NEWS": "このサイトのニュースと更新を受け取る", + "I18N_SIGNUP_SITE_DESCRIPTION": "<[sitename]>は学習リソースに関するオープンなコモンズです。提供されるすべての資料は自由に再利用および共有できます。", + "I18N_SIGNUP_SITE_OBJECTIVE": "<[sitename]>は、誰もが自由に使える良質な学習リソースの作成および継続的改善を支援することを使命としています。", + "I18N_SIGNUP_UPDATE_WARNING": "利用規約を最近更新しました。", + "I18N_SIGNUP_USERNAME": "ユーザー名", + "I18N_SIGNUP_USERNAME_EXPLANATION": "ユーザー名は貢献したコンテンツの隣に表示されます。", + "I18N_SIGNUP_WAIVER_OBJECTIVE": "表示(BY)要件の放棄とは、もし誰かがこのコンテンツを再利用した場合、著者名を表示する必要がないということです。ただし、あなたがある探検に対して行なったすべての貢献は、当サイト上の探検変更ログから確認可能です。また探検を再利用する人はこのページにリンクすることを推奨(ただし要件ではない)されます。", + "I18N_SIGNUP_WHY_LICENSE": "なぜCC-BY-SA?", + "I18N_SPLASH_FIRST_EXPLORATION_DESCRIPTION": "Oppiaの授業、別名「探検」では、対話のないビデオやテキストよりも夢中になれます。ユーザーは行動することで学べます。", + "I18N_SPLASH_JAVASCRIPT_ERROR_DESCRIPTION": "Oppiaは無料でオープンソースの学習プラットフォームで、「探検」と呼ばれるインタラクティブなアクティビティーが満載です。Oppiaが正しく動作するにはウェブ・ブラウザーでJavaScriptを有効にする必要がありますが、お使いのブラウザーではJavaScriptが無効になっています。JavaScriptを有効にするヘルプは\">こちらをクリックしてください。", + "I18N_SPLASH_JAVASCRIPT_ERROR_THANKS": "ありがとうございます。", + "I18N_SPLASH_JAVASCRIPT_ERROR_TITLE": "お使いのブラウザーでJavaScriptが必要", + "I18N_SPLASH_PAGE_TITLE": "Oppia:教える、学ぶ、探検する", + "I18N_SPLASH_SECOND_EXPLORATION_DESCRIPTION": "探検はシンプルに作成できます。探検をした世界中の学習者からのフィードバックやトレンドを受けて簡単に改良できます。", + "I18N_SPLASH_SITE_FEEDBACK": "サイトへのフィードバック", + "I18N_SPLASH_SUBTITLE": "Oppiaでは、学習と参加を促すインタラクティブな授業を簡単に作れます。", + "I18N_SPLASH_THIRD_EXPLORATION_DESCRIPTION": "Oppiaでは、想像し得る限りさまざまな科目の探検を作ることも共有することもできます。", + "I18N_SPLASH_TITLE": "書を捨てよう。", + "I18N_SUBSCRIBE_BUTTON_TEXT": "サブスクライブする", + "I18N_TOPNAV_ABOUT": "このサイトについて", + "I18N_TOPNAV_ABOUT_OPPIA": "Oppiaについて", + "I18N_TOPNAV_ADMIN_PAGE": "管理者用ページ", + "I18N_TOPNAV_BLOG": "ブログ", + "I18N_TOPNAV_CONTACT_US": "連絡先", + "I18N_TOPNAV_DASHBOARD": "著者ダッシュボード", + "I18N_TOPNAV_DONATE": "寄付する", + "I18N_TOPNAV_FORUM": "フォーラム", + "I18N_TOPNAV_GET_STARTED": "始める", + "I18N_TOPNAV_LIBRARY": "ライブラリー", + "I18N_TOPNAV_LOGOUT": "ログアウト", + "I18N_TOPNAV_MODERATOR_PAGE": "モデレーター用ページ", + "I18N_TOPNAV_NOTIFICATIONS": "通知", + "I18N_TOPNAV_PREFERENCES": "個人設定", + "I18N_TOPNAV_SIGN_IN": "サインイン", + "I18N_TOPNAV_TEACH_WITH_OPPIA": "Teach with Oppia", + "I18N_TOTAL_SUBSCRIBERS_TEXT": "サブスクライブしている人は合計<[totalSubscribers]>人です。", + "I18N_UNSUBSCRIBE_BUTTON_TEXT": "サブスクライブ解除" +} diff --git a/assets/i18n/ko.json b/assets/i18n/ko.json index d2e4ec40308b..ea025642b00b 100644 --- a/assets/i18n/ko.json +++ b/assets/i18n/ko.json @@ -1,15 +1,37 @@ { "I18N_ABOUT_PAGE_TITLE": "정보 - Oppia", + "I18N_ACTION_APPLY_TO_TEACH_WITH_OPPIA": "Teach with Oppia에 가입하기", + "I18N_ACTION_BROWSE_EXPLORATIONS": "우리의 탐험 탐색하기", + "I18N_ACTION_BROWSE_LESSONS": "우리의 강의 탐색하기", + "I18N_ACTION_CREATE_EXPLORATION": "탐험 만들기", + "I18N_ACTION_CREATE_LESSON": "자신만의 강의 만들기", + "I18N_CREATE_ACTIVITY_QUESTION": "무엇을 만드시겠습니까?", + "I18N_CREATE_ACTIVITY_TITLE": "활동 추가", + "I18N_CREATE_COLLECTION": "모음집 만들기", + "I18N_CREATE_EXPLORATION": "탐험 만들기", "I18N_CREATE_EXPLORATION_CREATE": "만들기", + "I18N_CREATE_EXPLORATION_QUESTION": "탐험을 생성하시겠습니까?", + "I18N_CREATE_EXPLORATION_TITLE": "탐험 만들기", "I18N_CREATE_EXPLORATION_UPLOAD": "업로드", "I18N_CREATE_NO_THANKS": "아니오, 괜찮습니다", "I18N_CREATE_YES_PLEASE": "예!", + "I18N_DASHBOARD_COLLECTIONS": "모음집", + "I18N_DASHBOARD_EXPLORATIONS": "탐험", + "I18N_DASHBOARD_OPEN_FEEDBACK": "피드백 열기", + "I18N_DASHBOARD_SUBSCRIBERS": "구독자", "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "그림을 여기로 끌어놓으십시오", "I18N_DIRECTIVES_OR_SELECT_FILE": "아니면 그림 파일을 선택하십시오:", + "I18N_ERROR_DISABLED_EXPLORATION": "사용할 수 없는 탐험", + "I18N_ERROR_DISABLED_EXPLORATION_MESSAGE": "죄송합니다, 클릭하신 탐험은 현재 사용할 수 없습니다. 나중에 다시 시도해 주세요.", + "I18N_ERROR_DISABLED_EXPLORATION_PAGE_TITLE": "사용할 수 없는 탐험 - Oppia", "I18N_ERROR_HEADER_400": "오류 400", "I18N_ERROR_HEADER_401": "오류 401", "I18N_ERROR_HEADER_404": "오류 404", "I18N_ERROR_HEADER_500": "오류 500", + "I18N_ERROR_MESSAGE_400": "가끔 기계가 사람은 이해하지 못하는 경우가 있습니다. 지금이 그런 상황입니다.", + "I18N_ERROR_MESSAGE_401": "당신은 들어갈 수 없습니다. 선생님이 오기 전에 빨리 되돌아 가세요.", + "I18N_ERROR_MESSAGE_404": "죄송합니다, 저희는 최선을 다해 찾아보았지만 해당 페이지를 찾지 못했습니다.", + "I18N_ERROR_MESSAGE_500": "무언가 많이 잘못되었습니다. 하지만 그것은 당신이 잘못이 아닙니다. 내부 오류가 발생했습니다.", "I18N_ERROR_PAGE_TITLE_400": "오류 400 - Oppia", "I18N_ERROR_PAGE_TITLE_401": "오류 401 - Oppia", "I18N_ERROR_PAGE_TITLE_404": "오류 404 - Oppia", @@ -17,7 +39,10 @@ "I18N_FOOTER_ABOUT": "정보", "I18N_FOOTER_ABOUT_ALL_CAPS": "OPPIA 정보", "I18N_FOOTER_AUTHOR_PROFILES": "제작자 프로필", + "I18N_FOOTER_BROWSE_LIBRARY": "라이브러리 탐색하기", "I18N_FOOTER_CONTACT_US": "문의하기", + "I18N_FOOTER_CONTRIBUTE_ALL_CAPS": "기여", + "I18N_FOOTER_CREDITS": "크레딧", "I18N_FOOTER_DONATE": "기부", "I18N_FOOTER_FOLLOW_US": "폴로잉하기", "I18N_FOOTER_FORUM": "포럼", @@ -25,59 +50,96 @@ "I18N_FOOTER_GET_STARTED": "시작하기", "I18N_FOOTER_OPPIA_FOUNDATION": "Oppia 재단", "I18N_FOOTER_PRIVACY_POLICY": "개인정보 정책", + "I18N_FOOTER_TEACH": "Teach with Oppia", + "I18N_FOOTER_TEACH_LEARN_ALL_CAPS": "가르치기/배우기", "I18N_FOOTER_TERMS_OF_SERVICE": "이용 약관", + "I18N_FORMS_TYPE_NUMBER": "숫자 입력", + "I18N_FORMS_TYPE_NUMBER_AT_LEAST": "최소 <[minValue]> 개의 숫자를 입력해 주세요.", + "I18N_FORMS_TYPE_NUMBER_AT_MOST": "최대 <[maxValue]> 개의 숫자를 입력해 주세요.", + "I18N_FORMS_TYPE_NUMBER_INVALID_DECIMAL": "유효한 숫자를 입력해 주세요", "I18N_GET_STARTED_PAGE_TITLE": "시작하기", + "I18N_INTERACTIONS_GRAPH_ADD_EDGE": "테두리 추가", + "I18N_INTERACTIONS_GRAPH_ADD_NODE": "점 추가", "I18N_INTERACTIONS_GRAPH_DELETE": "삭제", + "I18N_INTERACTIONS_GRAPH_ERROR_INVALID": "유효하지 않은 그래프!", "I18N_INTERACTIONS_GRAPH_MOVE": "이동", "I18N_INTERACTIONS_GRAPH_RESET_BUTTON": "초기화", + "I18N_INTERACTIONS_GRAPH_RESPONSE_EDGE": "<[edges]> 테두리", "I18N_INTERACTIONS_GRAPH_SUBMIT_BUTTON": "제출", + "I18N_INTERACTIONS_GRAPH_UPDATE_LABEL": "라벨 업데이트", "I18N_INTERACTIONS_IMAGE_CLICK_SELECT": "[표시할 그림을 선택하십시오]", "I18N_INTERACTIONS_ITEM_SELECTION_SUBMIT": "제출", + "I18N_INTERACTIONS_LOGIC_PROOF_CORRECT_ANSWERS": "정답: <[answer]>", "I18N_INTERACTIONS_LOGIC_PROOF_INCORRECT_ANSWERS": "틀렸습니다: <[answer]>", "I18N_INTERACTIONS_LOGIC_PROOF_SUBMIT": "제출", "I18N_INTERACTIONS_MUSIC_CLEAR": "지우기", + "I18N_INTERACTIONS_MUSIC_PLAY": "재생", "I18N_INTERACTIONS_MUSIC_SUBMIT": "제출", "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "항목 추가", + "I18N_INTERACTIONS_SET_INPUT_NO_ANSWER": "정답을 입력하지 않았습니다.", "I18N_INTERACTIONS_SET_INPUT_SUBMIT": "제출", + "I18N_LANGUAGE_FOOTER_VIEW_IN": "다음 언어로 Oppia 보기:", "I18N_LIBRARY_ALL_CATEGORIES": "모든 분류", "I18N_LIBRARY_ALL_CATEGORIES_SELECTED": "모든 분류 선택됨", "I18N_LIBRARY_ALL_LANGUAGES": "모든 언어", "I18N_LIBRARY_ALL_LANGUAGES_SELECTED": "모든 언어 선택됨", "I18N_LIBRARY_CATEGORIES_ALGORITHMS": "알고리즘", "I18N_LIBRARY_CATEGORIES_ARCHITECTURE": "건축", + "I18N_LIBRARY_CATEGORIES_ART": "미술", "I18N_LIBRARY_CATEGORIES_BIOLOGY": "생물학", + "I18N_LIBRARY_CATEGORIES_BUSINESS": "비즈니스", "I18N_LIBRARY_CATEGORIES_CHEMISTRY": "화학", "I18N_LIBRARY_CATEGORIES_CODING": "코딩", "I18N_LIBRARY_CATEGORIES_COMPUTING": "컴퓨팅", "I18N_LIBRARY_CATEGORIES_ECONOMICS": "경제학", "I18N_LIBRARY_CATEGORIES_EDUCATION": "교육", + "I18N_LIBRARY_CATEGORIES_ENGINEERING": "엔지니어링", "I18N_LIBRARY_CATEGORIES_ENGLISH": "영어", "I18N_LIBRARY_CATEGORIES_ENVIRONMENT": "환경", "I18N_LIBRARY_CATEGORIES_GEOGRAPHY": "지질학", + "I18N_LIBRARY_CATEGORIES_GOVERNMENT": "정부", "I18N_LIBRARY_CATEGORIES_HISTORY": "역사", + "I18N_LIBRARY_CATEGORIES_HOBBIES": "취미", "I18N_LIBRARY_CATEGORIES_LANGUAGES": "언어", + "I18N_LIBRARY_CATEGORIES_LAW": "법", "I18N_LIBRARY_CATEGORIES_MATHEMATICS": "수학", + "I18N_LIBRARY_CATEGORIES_MATHS": "수학", + "I18N_LIBRARY_CATEGORIES_MEDICINE": "약", "I18N_LIBRARY_CATEGORIES_MUSIC": "음악", "I18N_LIBRARY_CATEGORIES_PHILOSOPHY": "철학", "I18N_LIBRARY_CATEGORIES_PHYSICS": "물리학", "I18N_LIBRARY_CATEGORIES_PROGRAMMING": "프로그래밍", "I18N_LIBRARY_CATEGORIES_PSYCHOLOGY": "심리학", "I18N_LIBRARY_CATEGORIES_PUZZLES": "퍼즐", + "I18N_LIBRARY_CATEGORIES_READING": "독서", "I18N_LIBRARY_CATEGORIES_RELIGION": "종교", "I18N_LIBRARY_CATEGORIES_SPORT": "스포츠", "I18N_LIBRARY_CATEGORIES_STATISTICS": "통계", "I18N_LIBRARY_CATEGORIES_TEST": "테스트", "I18N_LIBRARY_CATEGORIES_WELCOME": "환영합니다", + "I18N_LIBRARY_CREATE_EXPLORATION_QUESTION": "새로 만드시겠습니까?", "I18N_LIBRARY_GROUPS_COMPUTING": "컴퓨팅", + "I18N_LIBRARY_GROUPS_FEATURED_ACTIVITIES": "추천된 활동", "I18N_LIBRARY_GROUPS_LANGUAGES": "언어", "I18N_LIBRARY_GROUPS_MATHEMATICS_&_STATISTICS": "수학 및 통계", "I18N_LIBRARY_GROUPS_RECENTLY_PUBLISHED": "최근에 게시됨", "I18N_LIBRARY_GROUPS_SCIENCE": "과학", "I18N_LIBRARY_GROUPS_SOCIAL_SCIENCE": "사회과학", + "I18N_LIBRARY_GROUPS_TOP_RATED_EXPLORATIONS": "인기있는 탐험", + "I18N_LIBRARY_LAST_UPDATED": "최근 업데이트", "I18N_LIBRARY_LOADING": "불러오는 중", + "I18N_LIBRARY_MAIN_HEADER": "오늘 당신이 무엇을 배울지 상상하세요...", "I18N_LIBRARY_N/A": "N/A", + "I18N_LIBRARY_NO_EXPLORATIONS": "이런, 표시할 탐험이 없습니다.", + "I18N_LIBRARY_NO_EXPLORATION_FOR_QUERY": "이런, 당신의 검색과 일치하는 탐험이 없습니다.", + "I18N_LIBRARY_NO_EXPLORATION_GROUPS": "표시할 수 있는 탐험 그룹이 없습니다.", + "I18N_LIBRARY_PAGE_TITLE": "탐험 라이브러리 - Oppia", + "I18N_LIBRARY_RATINGS_TOOLTIP": "평가", "I18N_LIBRARY_SEARCH_PLACEHOLDER": "궁금하신 점이 있으십니까?", + "I18N_LIBRARY_SUB_HEADER": "우리의 탐험을 탐색하는 것으로 당신의 모험을 시작하세요.", + "I18N_LIBRARY_VIEWS_TOOLTIP": "조회수", "I18N_LIBRARY_VIEW_ALL": "모두 보기", + "I18N_ONE_SUBSCRIBER_TEXT": "당신에게 1명의 구독자가 있습니다.", "I18N_PLAYER_BACK": "뒤로", "I18N_PLAYER_CARD_NUMBER_TOOLTIP": "카드 번호", "I18N_PLAYER_CONTINUE_BUTTON": "계속", @@ -85,30 +147,48 @@ "I18N_PLAYER_CONTRIBUTORS_TOOLTIP": "기여자", "I18N_PLAYER_EDIT_TOOLTIP": "편집", "I18N_PLAYER_FEEDBACK_TOOLTIP": "피드백", + "I18N_PLAYER_GO_TO_LIBRARY": "라이브러리로 이동", "I18N_PLAYER_INFO_TOOLTIP": "정보", + "I18N_PLAYER_LAST_UPDATED_TOOLTIP": "최근 업데이트", + "I18N_PLAYER_LEAVE_FEEDBACK": "만든 이에게 피드백 남기기...", "I18N_PLAYER_LOADING": "불러오는 중...", "I18N_PLAYER_NO_TAGS": "지정한 태그가 없습니다.", + "I18N_PLAYER_RATE_EXPLORATION": "새로운 것을 배우셨나요? 이 탐험을 평가하시는 것은 어떤가요?", + "I18N_PLAYER_RATINGS_TOOLTIP": "평가하기", + "I18N_PLAYER_RECOMMEND_EXPLORATIONS": "추천:", "I18N_PLAYER_REPORT_MODAL_BODY_OTHER": "기타", "I18N_PLAYER_REPORT_MODAL_FOOTER_CANCEL": "취소", "I18N_PLAYER_REPORT_MODAL_FOOTER_SUBMIT": "제출", + "I18N_PLAYER_REPORT_MODAL_HEADER": "이 탐험 보고하기", "I18N_PLAYER_REPORT_SUCCESS_MODAL_CLOSE": "닫기", "I18N_PLAYER_REPORT_SUCCESS_MODAL_HEADER": "감사합니다!", + "I18N_PLAYER_REPORT_TOOLTIP": "탐험 보고하기", "I18N_PLAYER_RETURN_TO_COLLECTION": "<[collectionTitle]>(으)로 돌아가기", "I18N_PLAYER_RETURN_TO_EDITOR": "편집기로 돌아가기", + "I18N_PLAYER_SHARE_EXPLORATION": "이 탐험을 즐기셨나요? 친구들과 공유해 보세요!", + "I18N_PLAYER_SHARE_THIS_EXPLORATION": "이 탐험 공유하기", "I18N_PLAYER_STAY_ANONYMOUS": "익명 상태 유지", "I18N_PLAYER_SUBMIT_BUTTON": "제출", "I18N_PLAYER_TAGS_TOOLTIP": "태그", "I18N_PLAYER_THANK_FEEDBACK": "의견 주셔서 감사합니다!", + "I18N_PLAYER_VIEWS_TOOLTIP": "조회수", + "I18N_PREFERENCES_BIO": "자기 소개", "I18N_PREFERENCES_BREADCRUMB": "환경 설정", "I18N_PREFERENCES_CANCEL_BUTTON": "취소", "I18N_PREFERENCES_CHANGE_PICTURE": "프로필 사진 바꾸기", "I18N_PREFERENCES_EMAIL": "이메일", + "I18N_PREFERENCES_EMAIL_RECEIVE_NEWS": "이 사이트에 대한 소식이나 업데이트 받기", "I18N_PREFERENCES_HEADING": "환경 설정", + "I18N_PREFERENCES_HEADING_SUBTEXT": "당신이 이 페이지에 한 모든 것들은 자동 저장됩니다.", "I18N_PREFERENCES_PICTURE": "사진", + "I18N_PREFERENCES_PREFERRED_EXPLORATION_LANGUAGE": "선호된 탐험 언어", "I18N_PREFERENCES_PREFERRED_SITE_LANGUAGE": "선호하는 사이트 언어", "I18N_PREFERENCES_PREFERRED_SITE_LANGUAGE_EXPLAIN": "이것은 사이트에 표시되고 있는 언어입니다.", "I18N_PREFERENCES_PREFERRED_SITE_LANGUAGE_PLACEHOLDER": "선호하는 사이트 언어", "I18N_PREFERENCES_PROFILE_PICTURE_ADD": "프로필 사진 추가", + "I18N_PREFERENCES_PROFILE_PICTURE_DRAG": "자르기와 크기 조정을 위해 드래그:", + "I18N_PREFERENCES_PROFILE_PICTURE_ERROR": "오류: 그림 파일을 읽을 수 없습니다.", + "I18N_PREFERENCES_PROFILE_PICTURE_UPLOAD": "프로필 사진 올리기", "I18N_PREFERENCES_SELECT_EXPLORATION_LANGUAGE": "선호하는 언어 선택...", "I18N_PREFERENCES_USERNAME": "사용자 이름", "I18N_PREFERENCES_USERNAME_NOT_SELECTED": "아직 선택하지 않음", @@ -118,24 +198,36 @@ "I18N_SIDEBAR_DONATE": "기부", "I18N_SIDEBAR_FORUM": "포럼", "I18N_SIDEBAR_GET_STARTED": "시작하기", + "I18N_SIDEBAR_LIBRARY_LINK": "라이브러리", + "I18N_SIDEBAR_TEACH_WITH_OPPIA": "Teach with Oppia", + "I18N_SIGNUP_BUTTON_SUBMIT": "제출하고 기여 시작하기", "I18N_SIGNUP_CC_TITLE": "크리에이티브 커먼즈 라이선스", "I18N_SIGNUP_CLOSE_BUTTON": "닫기", + "I18N_SIGNUP_COMPLETE_REGISTRATION": "당신의 가입을 완료하세요", + "I18N_SIGNUP_DO_NOT_SEND_EMAILS": "이 이메일을 보내지 마세요", "I18N_SIGNUP_EMAIL": "이메일", "I18N_SIGNUP_EMAIL_PREFERENCES": "이메일 환경 설정", "I18N_SIGNUP_EMAIL_PREFERENCES_EXPLAIN": "이 설정은 환경 설정 페이지에서 언제든지 바꿀 수 있습니다.", "I18N_SIGNUP_ERROR_NO_USERNAME": "사용자 이름을 입력해 주십시오.", - "I18N_SIGNUP_ERROR_USERNAME_MORE_50_CHARS": "사용자 이름은 최대 50자여야 합니다.", + "I18N_SIGNUP_ERROR_USERNAME_MORE_50_CHARS": "사용자 이름은 최대 50자까지 가능합니다.", "I18N_SIGNUP_ERROR_USERNAME_NOT_AVAILABLE": "이 사용자 이름은 사용할 수 없습니다.", "I18N_SIGNUP_ERROR_USERNAME_ONLY_ALPHANUM": "사용자 이름은 영숫자만 포함할 수 있습니다.", "I18N_SIGNUP_ERROR_USERNAME_TAKEN": "죄송합니다. 이 사용자 이름은 이미 사용 중입니다.", "I18N_SIGNUP_ERROR_USERNAME_WITH_ADMIN": "사용자 이름 'admin'은 예약어입니다.", + "I18N_SIGNUP_ERROR_USERNAME_WITH_SPACES": "당신의 사용자 이름에는 공백이 없어야 합니다.", "I18N_SIGNUP_FIELD_REQUIRED": "이 칸은 필수입니다.", "I18N_SIGNUP_LOADING": "불러오는 중", + "I18N_SIGNUP_PAGE_TITLE": "공동체 가입하기 - Oppia", "I18N_SIGNUP_REGISTRATION": "등록", + "I18N_SIGNUP_SEND_ME_NEWS": "이 사이트에 대한 소식과 업데이트를 받겠습니다", "I18N_SIGNUP_USERNAME": "사용자 이름", + "I18N_SIGNUP_USERNAME_EXPLANATION": "당신의 사용자 이름은 당신의 기여 다음에 나타납니다.", "I18N_SIGNUP_WHY_LICENSE": "왜 CC-BY-SA입니까?", "I18N_SPLASH_JAVASCRIPT_ERROR_THANKS": "감사합니다.", + "I18N_SPLASH_JAVASCRIPT_ERROR_TITLE": "당신의 브라우저에 자바 스크립트가 필요합니다", + "I18N_SPLASH_PAGE_TITLE": "Oppia: 가르치고, 배우고, 탐험하세요", "I18N_SPLASH_SITE_FEEDBACK": "사이트 피드백", + "I18N_SPLASH_TITLE": "책 밖에서 생각하세요.", "I18N_SUBSCRIBE_BUTTON_TEXT": "구독", "I18N_TOPNAV_ABOUT": "정보", "I18N_TOPNAV_ABOUT_OPPIA": "Oppia 정보", @@ -149,5 +241,7 @@ "I18N_TOPNAV_NOTIFICATIONS": "알림", "I18N_TOPNAV_PREFERENCES": "환경 설정", "I18N_TOPNAV_SIGN_IN": "로그인", + "I18N_TOPNAV_TEACH_WITH_OPPIA": "Oppia로 가르치기", + "I18N_TOTAL_SUBSCRIBERS_TEXT": "당신은 총 <[totalSubscribers]>명의 구독자가 있습니다.", "I18N_UNSUBSCRIBE_BUTTON_TEXT": "구독 해지" } diff --git a/assets/i18n/lb.json b/assets/i18n/lb.json index 473c7029a0d9..2dfe44816d5d 100644 --- a/assets/i18n/lb.json +++ b/assets/i18n/lb.json @@ -4,6 +4,8 @@ "I18N_CREATE_EXPLORATION_UPLOAD": "Eroplueden", "I18N_CREATE_NO_THANKS": "Nee, Merci!", "I18N_CREATE_YES_PLEASE": "Jo, w.e.g.!", + "I18N_DASHBOARD_COLLECTIONS": "Sammlungen", + "I18N_DASHBOARD_SUBSCRIBERS": "Abonnenten", "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "Bild heihin zéien", "I18N_DIRECTIVES_OR_SELECT_FILE": "oder sich e Biller-Fichier eraus:", "I18N_ERROR_HEADER_400": "Feeler 400", @@ -82,6 +84,7 @@ "I18N_LIBRARY_RATINGS_TOOLTIP": "Bewäertungen", "I18N_LIBRARY_SEARCH_PLACEHOLDER": "Wourop sidd Dir virwëtzeg?", "I18N_LIBRARY_VIEW_ALL": "All weisen", + "I18N_ONE_SUBSCRIBER_TEXT": "Dir hutt 1 Abonnent.", "I18N_PLAYER_BACK": "Zréck", "I18N_PLAYER_CARD_NUMBER_TOOLTIP": "Kaart #", "I18N_PLAYER_CONTINUE_BUTTON": "Virufueren", diff --git a/assets/i18n/mk.json b/assets/i18n/mk.json index 6c3a5876eaa4..0c1ad42e805f 100644 --- a/assets/i18n/mk.json +++ b/assets/i18n/mk.json @@ -18,6 +18,7 @@ "I18N_DASHBOARD_COLLECTIONS": "збирки", "I18N_DASHBOARD_EXPLORATIONS": "истражувања", "I18N_DASHBOARD_OPEN_FEEDBACK": "Отвори мислења", + "I18N_DASHBOARD_SUBSCRIBERS": "Претплатници", "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "Овде довлечете ја сликата", "I18N_DIRECTIVES_OR_SELECT_FILE": "или изберете податотека:", "I18N_ERROR_DISABLED_EXPLORATION": "Оневозможено истражување", @@ -158,6 +159,7 @@ "I18N_LIBRARY_SUB_HEADER": "Почнете ја вашата авантура прелистувајќи по нашите истражувања.", "I18N_LIBRARY_VIEWS_TOOLTIP": "Посети", "I18N_LIBRARY_VIEW_ALL": "Сите", + "I18N_ONE_SUBSCRIBER_TEXT": "Имате 1 претплатник.", "I18N_PLAYER_BACK": "Назад", "I18N_PLAYER_CARD_NUMBER_TOOLTIP": "Картичка бр.", "I18N_PLAYER_COMMUNITY_EDITABLE_TOOLTIP": "Уредливо од заедницата", @@ -297,5 +299,6 @@ "I18N_TOPNAV_PREFERENCES": "Поставки", "I18N_TOPNAV_SIGN_IN": "Најава", "I18N_TOPNAV_TEACH_WITH_OPPIA": "Подучувајте со Опија", + "I18N_TOTAL_SUBSCRIBERS_TEXT": "Имате вкупно <[totalSubscribers]> претплатници.", "I18N_UNSUBSCRIBE_BUTTON_TEXT": "Отпиши ме" } diff --git a/assets/i18n/nl.json b/assets/i18n/nl.json index 6cd6f3ee4a6c..27af90e1cc75 100644 --- a/assets/i18n/nl.json +++ b/assets/i18n/nl.json @@ -3,7 +3,7 @@ "I18N_ACTION_APPLY_TO_TEACH_WITH_OPPIA": "Aanmelden voor \"Teach With Oppia\"", "I18N_ACTION_BROWSE_EXPLORATIONS": "Onze verkenningen bekijken", "I18N_ACTION_BROWSE_LESSONS": "Onze lessen bekijken", - "I18N_ACTION_CREATE_EXPLORATION": "Uw eigen verkenning maken", + "I18N_ACTION_CREATE_EXPLORATION": "Maak een verkenning", "I18N_ACTION_CREATE_LESSON": "Uw eigen les maken", "I18N_CREATE_ACTIVITY_QUESTION": "Wat wilt u maken?", "I18N_CREATE_ACTIVITY_TITLE": "Activiteit maken", @@ -210,6 +210,7 @@ "I18N_PREFERENCES_EMAIL": "E-mailadres", "I18N_PREFERENCES_EMAIL_EXPLAIN": "Alleen moderators en beheerders kunnen dit zien.", "I18N_PREFERENCES_EMAIL_RECEIVE_NEWS": "Nieuws en updates over de site ontvangen", + "I18N_PREFERENCES_EMAIL_RECEIVE_SUBSCRIPTION_NEWS": "E-mails ontvangen als de maker waarop u geabonneerd bent een nieuwe verkenning publiceert", "I18N_PREFERENCES_HEADING": "Voorkeuren", "I18N_PREFERENCES_HEADING_SUBTEXT": "Alle wijzigingen die u op deze pagina maakt worden automatisch opgeslagen.", "I18N_PREFERENCES_PAGE_TITLE": "Uw profielvoorkeuren wijzigen - Oppia", @@ -277,8 +278,9 @@ "I18N_SPLASH_SECOND_EXPLORATION_DESCRIPTION": "Verkenningen zijn eenvoudig te maken. Ze kunnen gemakkelijk aangepast worden op basis van terugkoppeling van individuele studenten en trends in de ervaring van uw leerlingen over de hele wereld.", "I18N_SPLASH_SITE_FEEDBACK": "Terugkoppeling over de site", "I18N_SPLASH_SUBTITLE": "Oppia maakt het makkelijk om interactieve lessen te maken die informeren en betrekken.", - "I18N_SPLASH_THIRD_EXPLORATION_DESCRIPTION": "Met Oppia kunt u verkenningen maken en delen over een breed scala aan onderwerpen, alleen beperkt door uw verbeelding.", + "I18N_SPLASH_THIRD_EXPLORATION_DESCRIPTION": "Met Oppia kunt u verkenningen maken en delen over een breed scala aan onderwerpen, alleen beperkt door uw verbeelding.", "I18N_SPLASH_TITLE": "Denk buiten de boeken.", + "I18N_SUBSCRIBE_BUTTON_TEXT": "Abonneren", "I18N_TOPNAV_ABOUT": "Over", "I18N_TOPNAV_ABOUT_OPPIA": "Over Oppia", "I18N_TOPNAV_ADMIN_PAGE": "Beheerderspagina", @@ -294,5 +296,6 @@ "I18N_TOPNAV_NOTIFICATIONS": "Meldingen", "I18N_TOPNAV_PREFERENCES": "Voorkeuren", "I18N_TOPNAV_SIGN_IN": "Aanmelden", - "I18N_TOPNAV_TEACH_WITH_OPPIA": "Onderwijzen met Oppia" + "I18N_TOPNAV_TEACH_WITH_OPPIA": "Onderwijzen met Oppia", + "I18N_UNSUBSCRIBE_BUTTON_TEXT": "Uitschrijven" } diff --git a/assets/i18n/oc.json b/assets/i18n/oc.json index 1207af5fb321..0703ec7b9123 100644 --- a/assets/i18n/oc.json +++ b/assets/i18n/oc.json @@ -40,6 +40,7 @@ "I18N_INTERACTIONS_MUSIC_SUBMIT": "Sometre", "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "Apondre l'element", "I18N_INTERACTIONS_SET_INPUT_SUBMIT": "Sometre", + "I18N_LIBRARY_ALL_CATEGORIES": "Totas las categorias", "I18N_LIBRARY_CATEGORIES_ALGORITHMS": "Algoritmes", "I18N_LIBRARY_CATEGORIES_ARCHITECTURE": "Arquitectura", "I18N_LIBRARY_CATEGORIES_ART": "Art", @@ -85,6 +86,7 @@ "I18N_LIBRARY_GROUPS_SOCIAL_SCIENCE": "Sciéncias socialas", "I18N_LIBRARY_LOADING": "Cargament", "I18N_LIBRARY_N/A": "N/A", + "I18N_PLAYER_INFO_TOOLTIP": "Informacion", "I18N_PLAYER_REPORT_MODAL_BODY_OTHER": "Autre", "I18N_PLAYER_REPORT_MODAL_FOOTER_CANCEL": "Anullar", "I18N_PLAYER_REPORT_MODAL_FOOTER_SUBMIT": "Sometre", @@ -110,6 +112,7 @@ "I18N_SIDEBAR_TEACH_WITH_OPPIA": "Ensenhar amb Oppia", "I18N_SIGNUP_CLOSE_BUTTON": "Tampar", "I18N_SIGNUP_EMAIL": "Adreça electronica", + "I18N_SIGNUP_EMAIL_PREFERENCES": "Preferéncias de corrièr electronic", "I18N_SIGNUP_LOADING": "Cargament", "I18N_SIGNUP_REGISTRATION": "Enregistrament", "I18N_SIGNUP_USERNAME": "Nom d'utilizaire", diff --git a/assets/i18n/qqq.json b/assets/i18n/qqq.json index 338f2ce85042..8869357fae87 100644 --- a/assets/i18n/qqq.json +++ b/assets/i18n/qqq.json @@ -15,10 +15,10 @@ "I18N_CREATE_EXPLORATION_UPLOAD": "Text displayed in a button in the navigation bar\n{{Identical|Upload}}", "I18N_CREATE_NO_THANKS": "Text of the cancel button of a dialog. - see I18N_CREATE_EXPLORATION_CREATE.\n{{Identical|No thanks}}", "I18N_CREATE_YES_PLEASE": "Text of the confirmation button of a dialog. - see I18N_CREATE_EXPLORATION_CREATE.", - "I18N_DASHBOARD_COLLECTIONS": "Title shown in a tab header in the user's dashboard page. - Clicking on this tab header shows a list of the collections the user has created.", - "I18N_DASHBOARD_EXPLORATIONS": "Title shown in a tab header in the user's dashboard page. - Clicking on this tab header shows a list of the explorations the user has created.", + "I18N_DASHBOARD_COLLECTIONS": "Title shown in a tab header in the user's dashboard page. - Clicking on this tab header shows a list of the collections the user has created.\n{{Identical|Collection}}", + "I18N_DASHBOARD_EXPLORATIONS": "Title shown in a tab header in the user's dashboard page. - Clicking on this tab header shows a list of the explorations the user has created.\n{{Identical|Exploration}}", "I18N_DASHBOARD_OPEN_FEEDBACK": "Text of feedback icon shown in summary tile of an exploration in card view in the user's dashboard page.", - "I18N_DASHBOARD_SUBSCRIBERS": "Title shown in a tab header in the user's dashboard page. - Clicking on this tab header shows the list of users who have subscribed to the user.", + "I18N_DASHBOARD_SUBSCRIBERS": "Title shown in a tab header in the user's dashboard page. - Clicking on this tab header shows the list of users who have subscribed to the user.\n{{Identical|Subscriber}}", "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "Text displayed in the preferences page. - Text shown in the dialog shown to upload a profile picture. The user can upload the picture by dragging a file to the area with this text.", "I18N_DIRECTIVES_OR_SELECT_FILE": "Text displayed in the preferences page. - Text shown in the dialog shown to upload a profile picture below the area where user can upload the picture by dragging a file and up the button to upload a picture from the local file system.", "I18N_ERROR_DISABLED_EXPLORATION": "Text displayed as title when an exploration is unavailable due to having been disabled.", diff --git a/assets/i18n/ru.json b/assets/i18n/ru.json index 04eb1dd96aef..425b59720230 100644 --- a/assets/i18n/ru.json +++ b/assets/i18n/ru.json @@ -10,6 +10,7 @@ "I18N_CREATE_YES_PLEASE": "Да, пожалуйста!", "I18N_DASHBOARD_COLLECTIONS": "коллекции", "I18N_DASHBOARD_EXPLORATIONS": "исследования", + "I18N_DASHBOARD_SUBSCRIBERS": "Подписчики", "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "Перетащите картинку сюда", "I18N_DIRECTIVES_OR_SELECT_FILE": "или выберите файл с изображением:", "I18N_ERROR_HEADER_400": "Ошибка 400", @@ -87,6 +88,7 @@ "I18N_LIBRARY_LOADING": "Загрузка", "I18N_LIBRARY_RATINGS_TOOLTIP": "Рейтинги", "I18N_LIBRARY_VIEW_ALL": "Посмотреть все", + "I18N_ONE_SUBSCRIBER_TEXT": "У вас 1 подписчик.", "I18N_PLAYER_BACK": "Назад", "I18N_PLAYER_CONTINUE_BUTTON": "Продолжить", "I18N_PLAYER_CONTINUE_NEXT_CARD": "Перейти на следующую карту", diff --git a/assets/i18n/sv.json b/assets/i18n/sv.json index c32e36f40830..6ac4f21353a4 100644 --- a/assets/i18n/sv.json +++ b/assets/i18n/sv.json @@ -11,6 +11,7 @@ "I18N_CREATE_YES_PLEASE": "Ja, tack!", "I18N_DASHBOARD_COLLECTIONS": "Samlingar", "I18N_DASHBOARD_OPEN_FEEDBACK": "Skicka återkoppling", + "I18N_DASHBOARD_SUBSCRIBERS": "Prenumeranter", "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "Dra bild hit", "I18N_DIRECTIVES_OR_SELECT_FILE": "eller välj en bildfil:", "I18N_ERROR_HEADER_400": "Fel 400", @@ -196,5 +197,6 @@ "I18N_TOPNAV_NOTIFICATIONS": "Notifikationer", "I18N_TOPNAV_PREFERENCES": "Inställningar", "I18N_TOPNAV_SIGN_IN": "Logga in", - "I18N_TOPNAV_TEACH_WITH_OPPIA": "Undervisa med Oppia" + "I18N_TOPNAV_TEACH_WITH_OPPIA": "Undervisa med Oppia", + "I18N_TOTAL_SUBSCRIBERS_TEXT": "Du har totalt <[totalSubscribers]> prenumeranter." } diff --git a/assets/i18n/tr.json b/assets/i18n/tr.json index c56b02b5ebf0..6e5137fd20d4 100644 --- a/assets/i18n/tr.json +++ b/assets/i18n/tr.json @@ -3,7 +3,7 @@ "I18N_ACTION_APPLY_TO_TEACH_WITH_OPPIA": "Oppia ile Öğretmek İçin Başvur", "I18N_ACTION_BROWSE_EXPLORATIONS": "Konu Anlatımlarını Keşfet", "I18N_ACTION_BROWSE_LESSONS": "Derslerİ İncele", - "I18N_ACTION_CREATE_EXPLORATION": "Bir Araştırma Oluşturun", + "I18N_ACTION_CREATE_EXPLORATION": "Yeni bir Keşif oluşturun", "I18N_ACTION_CREATE_LESSON": "Kendİ Dersİnİ Oluştur", "I18N_CREATE_ACTIVITY_QUESTION": "Ne oluşturmak istiyorsun?", "I18N_CREATE_ACTIVITY_TITLE": "Bir Etkinlik Oluştur", @@ -210,7 +210,7 @@ "I18N_PREFERENCES_EMAIL": "Eposta", "I18N_PREFERENCES_EMAIL_EXPLAIN": "Sadece moderatörler ve site yöneticileri bu alanı görüntüleyebilir.", "I18N_PREFERENCES_EMAIL_RECEIVE_NEWS": "Site hakkında haberleri ve güncellemeleri al", - "I18N_PREFERENCES_EMAIL_RECEIVE_SUBSCRIPTION_NEWS": "Abone olduğunuz bir içerik oluşturucunun yeni bir arama yayınladığında e-postalarını alın", + "I18N_PREFERENCES_EMAIL_RECEIVE_SUBSCRIPTION_NEWS": "Abone olduğunuz bir içerik oluşturucusu yeni bir keşif yayınladığında e-posta alın", "I18N_PREFERENCES_HEADING": "Tercihler", "I18N_PREFERENCES_HEADING_SUBTEXT": "Bu sayfa üzerinde yaptığın herhangi bir değişiklik otomatik olarak kaydedilecek.", "I18N_PREFERENCES_PAGE_TITLE": "Profil tercihlerini değiştir - Oppia", diff --git a/assets/i18n/vi.json b/assets/i18n/vi.json index 36895fa80d1a..134a3de3586e 100644 --- a/assets/i18n/vi.json +++ b/assets/i18n/vi.json @@ -16,7 +16,7 @@ "I18N_CREATE_NO_THANKS": "Không Phải", "I18N_CREATE_YES_PLEASE": "Đúng Rồi!", "I18N_DASHBOARD_COLLECTIONS": "Sưu Tầm", - "I18N_DASHBOARD_EXPLORATIONS": "Cách khám mạch", + "I18N_DASHBOARD_EXPLORATIONS": "Cách khám mạch", "I18N_DASHBOARD_OPEN_FEEDBACK": "Phản hồi", "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "Kéo bức hình tới đây", "I18N_DIRECTIVES_OR_SELECT_FILE": "Duyệt tìm một bức hình trên máy tính của bạn:", diff --git a/assets/i18n/zh-hans.json b/assets/i18n/zh-hans.json index 1edf8144d3d0..b09f925f4c93 100644 --- a/assets/i18n/zh-hans.json +++ b/assets/i18n/zh-hans.json @@ -15,9 +15,10 @@ "I18N_CREATE_EXPLORATION_UPLOAD": "上传", "I18N_CREATE_NO_THANKS": "不用了,谢谢", "I18N_CREATE_YES_PLEASE": "是的,请!", - "I18N_DASHBOARD_COLLECTIONS": "采集", - "I18N_DASHBOARD_EXPLORATIONS": "冒险", + "I18N_DASHBOARD_COLLECTIONS": "收藏", + "I18N_DASHBOARD_EXPLORATIONS": "探险", "I18N_DASHBOARD_OPEN_FEEDBACK": "打开反馈", + "I18N_DASHBOARD_SUBSCRIBERS": "订阅者", "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "在此拖入图片", "I18N_DIRECTIVES_OR_SELECT_FILE": "或选择一个图片文件:", "I18N_ERROR_DISABLED_EXPLORATION": "禁用的探险", @@ -158,6 +159,7 @@ "I18N_LIBRARY_SUB_HEADER": "通过浏览我们的探险,开始您的探险经历。", "I18N_LIBRARY_VIEWS_TOOLTIP": "查看", "I18N_LIBRARY_VIEW_ALL": "查看全部", + "I18N_ONE_SUBSCRIBER_TEXT": "您有1位订阅者。", "I18N_PLAYER_BACK": "返回", "I18N_PLAYER_CARD_NUMBER_TOOLTIP": "卡片#", "I18N_PLAYER_COMMUNITY_EDITABLE_TOOLTIP": "社群可编辑", @@ -297,5 +299,6 @@ "I18N_TOPNAV_PREFERENCES": "参数设置", "I18N_TOPNAV_SIGN_IN": "登录", "I18N_TOPNAV_TEACH_WITH_OPPIA": "通过Oppia教学", + "I18N_TOTAL_SUBSCRIBERS_TEXT": "您共有<[totalSubscribers]>位订阅者。", "I18N_UNSUBSCRIBE_BUTTON_TEXT": "取消订阅" } diff --git a/assets/i18n/zh-hant.json b/assets/i18n/zh-hant.json index 2198432328ec..3ec40b856976 100644 --- a/assets/i18n/zh-hant.json +++ b/assets/i18n/zh-hant.json @@ -18,6 +18,7 @@ "I18N_DASHBOARD_COLLECTIONS": "采集", "I18N_DASHBOARD_EXPLORATIONS": "寻求", "I18N_DASHBOARD_OPEN_FEEDBACK": "打開意見回饋", + "I18N_DASHBOARD_SUBSCRIBERS": "訂閱者", "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "把圖片拖到此處", "I18N_DIRECTIVES_OR_SELECT_FILE": "或是選擇一個圖片文件", "I18N_ERROR_DISABLED_EXPLORATION": "該探索已被禁用", @@ -158,6 +159,7 @@ "I18N_LIBRARY_SUB_HEADER": "在開始您的冒險前瀏覽我們的探索。", "I18N_LIBRARY_VIEWS_TOOLTIP": "檢視", "I18N_LIBRARY_VIEW_ALL": "檢視全部", + "I18N_ONE_SUBSCRIBER_TEXT": "您擁有1位訂閱者。", "I18N_PLAYER_BACK": "返回", "I18N_PLAYER_CARD_NUMBER_TOOLTIP": "卡片#", "I18N_PLAYER_COMMUNITY_EDITABLE_TOOLTIP": "社群可編輯", @@ -297,5 +299,6 @@ "I18N_TOPNAV_PREFERENCES": "偏好設定", "I18N_TOPNAV_SIGN_IN": "登入", "I18N_TOPNAV_TEACH_WITH_OPPIA": "以Oppia教學", + "I18N_TOTAL_SUBSCRIBERS_TEXT": "您一共擁有<[totalSubscribers]>位訂閱者。", "I18N_UNSUBSCRIBE_BUTTON_TEXT": "取消訂閱" } From 679112f598c6823213162969b3ec62f9219d1d42 Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Fri, 24 Feb 2017 01:42:14 +0530 Subject: [PATCH 018/173] addressed review comments --- core/domain/classifier_services.py | 23 +++++++---------------- core/domain/classifier_services_test.py | 5 +++-- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/core/domain/classifier_services.py b/core/domain/classifier_services.py index c8e4fa32ce5c..412ae2dba3cd 100644 --- a/core/domain/classifier_services.py +++ b/core/domain/classifier_services.py @@ -139,12 +139,9 @@ def get_classifier_by_id(classifier_id): """Returns a classifier domain object given a classifier id. """ classifier_model = classifier_models.ClassifierModel.get( - classifier_id, strict=False) - if classifier_model is None: - raise Exception("No classifier found for the classifer's id.") - else: - classifier = get_classifier_from_model(classifier_model) - return classifier + classifier_id) + classifier = get_classifier_from_model(classifier_model) + return classifier def _create_classifier(classifier): @@ -171,19 +168,13 @@ def update_classifier(classifier): _save_classifier method. """ classifier_model = classifier_models.ClassifierModel.get( - classifier.id, strict=False) - if classifier_model is None: - raise Exception("No classifier found for the classifer's id.") - else: - _save_classifier(classifier_model, classifier) + classifier.id) + _save_classifier(classifier_model, classifier) def delete_classifier(classifier_id): """Deletes classifier model in the datastore given classifier_id. """ classifier_model = classifier_models.ClassifierModel.get( - classifier_id, strict=False) - if classifier_model is None: - raise Exception("No classifier found for the classifer's id.") - else: - classifier_model.delete() + classifier_id) + classifier_model.delete() diff --git a/core/domain/classifier_services_test.py b/core/domain/classifier_services_test.py index fddd68288dd0..27800bea3588 100644 --- a/core/domain/classifier_services_test.py +++ b/core/domain/classifier_services_test.py @@ -89,7 +89,7 @@ def test_string_classifier_classification(self): def test_retrieval_of_classifiers(self): """Test the get_classifier_by_id method.""" with self.assertRaisesRegexp(Exception, ( - "No classifier found for the classifer's id.")): + "Entity for class ClassifierModel with id fake_id not found")): classifier_services.get_classifier_by_id('fake_id') exp_id = u'1' @@ -128,5 +128,6 @@ def test_deletion_of_classifiers(self): feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput'], [], 1) classifier_services.delete_classifier(classifier_id) with self.assertRaisesRegexp(Exception, ( - "No classifier found for the classifer's id.")): + "Entity for class ClassifierModel with id %s not found" %( + classifier_id))): classifier_services.get_classifier_by_id(classifier_id) From c10606cb1d5d1d86b1039f788f8454c652a11fdb Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Mon, 27 Feb 2017 20:27:26 +0530 Subject: [PATCH 019/173] addressed review comments --- core/domain/classifier_services.py | 40 +++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/core/domain/classifier_services.py b/core/domain/classifier_services.py index 412ae2dba3cd..e09577a2609c 100644 --- a/core/domain/classifier_services.py +++ b/core/domain/classifier_services.py @@ -125,8 +125,13 @@ def classify_string_classifier_rule(state, normalized_answer): def get_classifier_from_model(classifier_model): - """Returns a classifier domain object given a classifier model loaded - from datastore. + """Gets a classifier domain object from a classifier model. + + Args: + classifier_model: Classifier model instance in datastore. + + Returns: + classifier: Domain object for the classifier. """ return classifier_domain.Classifier( classifier_model.id, classifier_model.exp_id, @@ -136,7 +141,13 @@ def get_classifier_from_model(classifier_model): classifier_model.data_schema_version) def get_classifier_by_id(classifier_id): - """Returns a classifier domain object given a classifier id. + """Gets a classifier from a classifier id. + + Args: + classifier_id: Str. Id of the classifier. + + Returns: + classifier: Domain object for the classifier. """ classifier_model = classifier_models.ClassifierModel.get( classifier_id) @@ -147,6 +158,10 @@ def get_classifier_by_id(classifier_id): def _create_classifier(classifier): """Creates classifier model in the datastore given a classifier domain object. + + Args: + classifier: Domain object for the classifier. + """ classifier_models.ClassifierModel.create( classifier.exp_id, classifier.exp_version_when_created, @@ -158,6 +173,19 @@ def _create_classifier(classifier): def _save_classifier(classifier_model, classifier): """Updates classifier model in the datastore given a classifier domain object. + + Args: + classifier_model: Classifier model instance in datastore. + classifier: Domain object for the classifier. + + Note: Most of the properties of a classifier are immutable. + The only property that can change is the state_name. Since, + exp_version_when_created will never change, algorithm_id of + the algorithm used to generate this model will not change, + cached_classifier_data is essentially the model generated + which won't change (if you change that you will have to + create a new ClassifierModel instance itself!) and + data_schema_version should also not change. """ classifier_model.state_name = classifier.state_name classifier_model.put() @@ -166,6 +194,9 @@ def _save_classifier(classifier_model, classifier): def update_classifier(classifier): """Checks if model exists and updates the classifier model using _save_classifier method. + + Args: + classifier: Domain object for the classifier. """ classifier_model = classifier_models.ClassifierModel.get( classifier.id) @@ -174,6 +205,9 @@ def update_classifier(classifier): def delete_classifier(classifier_id): """Deletes classifier model in the datastore given classifier_id. + + Args: + classifier_id: Str. Id of the classifier. """ classifier_model = classifier_models.ClassifierModel.get( classifier_id) From 357d239c9730953d176d3d381126887edd80c4e2 Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Mon, 27 Feb 2017 20:28:30 +0530 Subject: [PATCH 020/173] lint issues --- core/domain/classifier_services.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/domain/classifier_services.py b/core/domain/classifier_services.py index e09577a2609c..27df41d9baf4 100644 --- a/core/domain/classifier_services.py +++ b/core/domain/classifier_services.py @@ -158,7 +158,7 @@ def get_classifier_by_id(classifier_id): def _create_classifier(classifier): """Creates classifier model in the datastore given a classifier domain object. - + Args: classifier: Domain object for the classifier. @@ -177,7 +177,7 @@ def _save_classifier(classifier_model, classifier): Args: classifier_model: Classifier model instance in datastore. classifier: Domain object for the classifier. - + Note: Most of the properties of a classifier are immutable. The only property that can change is the state_name. Since, exp_version_when_created will never change, algorithm_id of @@ -205,7 +205,7 @@ def update_classifier(classifier): def delete_classifier(classifier_id): """Deletes classifier model in the datastore given classifier_id. - + Args: classifier_id: Str. Id of the classifier. """ From eb97f1ebdb868a52925087d27843360fafd6d819 Mon Sep 17 00:00:00 2001 From: MAKOSCAFEE Date: Tue, 28 Feb 2017 23:28:12 +0300 Subject: [PATCH 021/173] last PR which removes all globally unused javascripts --- core/templates/dev/head/pages/admin/admin.html | 3 ++- core/templates/dev/head/pages/base.html | 5 ----- .../head/pages/collection_editor/collection_editor.html | 7 ++++++- .../head/pages/exploration_editor/exploration_editor.html | 7 ++++++- .../head/pages/exploration_player/exploration_player.html | 5 +++++ 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/core/templates/dev/head/pages/admin/admin.html b/core/templates/dev/head/pages/admin/admin.html index 40ef1864657a..7e0d2d6fec01 100644 --- a/core/templates/dev/head/pages/admin/admin.html +++ b/core/templates/dev/head/pages/admin/admin.html @@ -89,6 +89,8 @@ {% include 'pages/admin/misc_tab/admin_misc_tab_directive.html' %} + + @@ -103,7 +105,6 @@ - diff --git a/core/templates/dev/head/pages/base.html b/core/templates/dev/head/pages/base.html index 20e3ba7d1699..a69fe432d4ac 100644 --- a/core/templates/dev/head/pages/base.html +++ b/core/templates/dev/head/pages/base.html @@ -237,7 +237,6 @@

- @@ -255,10 +254,6 @@

- - - - {% block footer_js %} {% endblock footer_js %} diff --git a/core/templates/dev/head/pages/collection_editor/collection_editor.html b/core/templates/dev/head/pages/collection_editor/collection_editor.html index be3f4f6997f7..4da1acb1729f 100644 --- a/core/templates/dev/head/pages/collection_editor/collection_editor.html +++ b/core/templates/dev/head/pages/collection_editor/collection_editor.html @@ -71,6 +71,12 @@ {% block footer_js %} {{ super() }} + + + + + + @@ -110,5 +116,4 @@ - {% endblock footer_js %} diff --git a/core/templates/dev/head/pages/exploration_editor/exploration_editor.html b/core/templates/dev/head/pages/exploration_editor/exploration_editor.html index e89479babd2b..c157f6aa199f 100644 --- a/core/templates/dev/head/pages/exploration_editor/exploration_editor.html +++ b/core/templates/dev/head/pages/exploration_editor/exploration_editor.html @@ -170,6 +170,7 @@ + @@ -237,9 +238,10 @@ + + - @@ -304,6 +306,9 @@ + + + diff --git a/core/templates/dev/head/pages/exploration_player/exploration_player.html b/core/templates/dev/head/pages/exploration_player/exploration_player.html index c6189f7c18ec..a09b587ac0f5 100644 --- a/core/templates/dev/head/pages/exploration_player/exploration_player.html +++ b/core/templates/dev/head/pages/exploration_player/exploration_player.html @@ -171,9 +171,11 @@

Suggest a Change

{% block footer_js %} {{ super() }} + + @@ -210,6 +212,9 @@

Suggest a Change

+ + + From cdf9898934b06841143a1a6e8110d92081dd3c97 Mon Sep 17 00:00:00 2001 From: kirankonduru Date: Wed, 1 Mar 2017 22:46:36 +0530 Subject: [PATCH 022/173] Image region resizable by clicking and dragging --- .../templates/ImageWithRegionsEditor.js | 438 +++++++++++++++++- .../templates/image_with_regions_editor.html | 1 + 2 files changed, 437 insertions(+), 2 deletions(-) diff --git a/extensions/objects/templates/ImageWithRegionsEditor.js b/extensions/objects/templates/ImageWithRegionsEditor.js index 594db4511dfc..12f4a2b19128 100644 --- a/extensions/objects/templates/ImageWithRegionsEditor.js +++ b/extensions/objects/templates/ImageWithRegionsEditor.js @@ -69,11 +69,27 @@ oppia.directive('imageWithRegionsEditor', [ $scope.rectY = 0; $scope.rectWidth = 0; $scope.rectHeight = 0; - + // Flags for the cursor direction in which region is to be resized + $scope.nResize = false; + $scope.sResize = false; + $scope.eResize = false; + $scope.wResize = false; + $scope.neResize = false; + $scope.nwResize = false; + $scope.seResize = false; + $scope.nwResize = false; + // A flag to check direction change while resizing + $scope.flag = 0; // Is user currently drawing a new region? $scope.userIsCurrentlyDrawing = false; // Is user currently dragging an existing region? $scope.userIsCurrentlyDragging = false; + // Is user currently resizing an existing region? + $scope.userIsCurrentlyResizing = false; + // The region is being resized along which direction? + $scope.resizeDirection = ''; + // The region along borders which when hovered provides resize cursor + $scope.resizeRegionBorder = 10; // Dimensions of original image $scope.originalImageWidth = 0; $scope.originalImageHeight = 0; @@ -81,6 +97,8 @@ oppia.directive('imageWithRegionsEditor', [ $scope.regionDrawMode = false; // Index of region currently hovered over $scope.hoveredRegion = null; + // Index of region currently moved over + $scope.moveRegion = null; // Index of region currently selected $scope.selectedRegion = null; @@ -150,6 +168,18 @@ oppia.directive('imageWithRegionsEditor', [ return false; }; + // Called whenever the direction cursor is to be hidden + $scope.hideDirectionCursor = function() { + $scope.nResize = false; + $scope.eResize = false; + $scope.wResize = false; + $scope.sResize = false; + $scope.neResize = false; + $scope.nwResize = false; + $scope.seResize = false; + $scope.swResize = false; + }; + $scope.regionLabelGetterSetter = function(index) { return function(label) { if (angular.isDefined(label)) { @@ -173,6 +203,8 @@ oppia.directive('imageWithRegionsEditor', [ // Convert to and from region area (which is stored as a fraction of // image width and height) and actual width and height var regionAreaFromCornerAndDimensions = function(x, y, width, height) { + height = Math.abs(height); + width = Math.abs(width); return [ convertCoordsToFraction( [x, y], @@ -193,6 +225,319 @@ oppia.directive('imageWithRegionsEditor', [ }; }; + var resizeRegion = function() { + var labeledRegions = $scope.$parent.value.labeledRegions; + var resizedRegion = labeledRegions[$scope.selectedRegion].region; + var deltaX = $scope.mouseX - $scope.originalMouseX; + var deltaY = $scope.mouseY - $scope.originalMouseY; + if ($scope.resizeDirection === 'n') { + if ($scope.originalRectArea.height - deltaY <= 0) { + $scope.flag = 1; + $scope.sResize = true; + $scope.nResize = false; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x, + $scope.originalMouseY + $scope.originalRectArea.height, + $scope.originalRectArea.width, + $scope.originalRectArea.height - deltaY + ); + } else { + $scope.flag = 0; + $scope.sResize = false; + $scope.nResize = true; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x, + $scope.originalRectArea.y + deltaY, + $scope.originalRectArea.width, + $scope.originalRectArea.height - deltaY + ); + } + } else if ($scope.resizeDirection === 's') { + if ($scope.originalRectArea.height + deltaY <= 0) { + $scope.sResize = false; + $scope.nResize = true; + $scope.flag = 1; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x, + $scope.originalRectArea.y + deltaY + + $scope.originalRectArea.height, + $scope.originalRectArea.width, + $scope.originalRectArea.height + deltaY + ); + } else { + $scope.sResize = true; + $scope.nResize = false; + $scope.flag = 0; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x, + $scope.originalRectArea.y, + $scope.originalRectArea.width, + $scope.originalRectArea.height + deltaY + ); + } + } else if ($scope.resizeDirection === 'e') { + if ($scope.originalRectArea.width + deltaX <= 0) { + $scope.eResize = false; + $scope.wResize = true; + $scope.flag = 1; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x + deltaX + + $scope.originalRectArea.width, + $scope.originalRectArea.y, + $scope.originalRectArea.width + deltaX, + $scope.originalRectArea.height + ); + } else { + $scope.eResize = true; + $scope.wResize = false; + $scope.flag = 0; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x, + $scope.originalRectArea.y, + $scope.originalRectArea.width + deltaX, + $scope.originalRectArea.height + ); + } + } else if ($scope.resizeDirection === 'w') { + if ($scope.originalRectArea.width - deltaX <= 0) { + $scope.eResize = true; + $scope.wResize = false; + $scope.flag = 1; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x + $scope.originalRectArea.width, + $scope.originalRectArea.y, + $scope.originalRectArea.width - deltaX, + $scope.originalRectArea.height + ); + } else { + $scope.eResize = false; + $scope.wResize = true; + $scope.flag = 0; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x + deltaX, + $scope.originalRectArea.y, + $scope.originalRectArea.width - deltaX, + $scope.originalRectArea.height + ); + } + } else if ($scope.resizeDirection === 'ne') { + if ($scope.originalRectArea.height - deltaY <= 0 && + $scope.originalRectArea.width + deltaX <= 0) { + $scope.swResize = true; + $scope.neResize = false; + $scope.nwResize = false; + $scope.seResize = false; + $scope.flag = 1; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x + deltaX + + $scope.originalRectArea.width, + $scope.originalRectArea.y + $scope.originalRectArea.height, + $scope.originalRectArea.width + deltaX, + $scope.originalRectArea.height - deltaY + ); + } else if ($scope.originalRectArea.width + deltaX <= 0) { + $scope.swResize = false; + $scope.neResize = false; + $scope.nwResize = true; + $scope.seResize = false; + $scope.flag = 1; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x + deltaX + + $scope.originalRectArea.width, + $scope.originalRectArea.y + deltaY, + $scope.originalRectArea.width + deltaX, + $scope.originalRectArea.height - deltaY + ); + } else if ($scope.originalRectArea.height - deltaY <= 0) { + $scope.swResize = false; + $scope.neResize = false; + $scope.nwResize = false; + $scope.seResize = true; + $scope.flag = 1; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x, + $scope.originalMouseY + $scope.originalRectArea.height, + $scope.originalRectArea.width + deltaX, + $scope.originalRectArea.height - deltaY + ); + } else { + $scope.swResize = false; + $scope.neResize = true; + $scope.nwResize = false; + $scope.seResize = false; + $scope.flag = 0; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x, + $scope.originalRectArea.y + deltaY, + $scope.originalRectArea.width + deltaX, + $scope.originalRectArea.height - deltaY + ); + } + } else if ($scope.resizeDirection === 'nw') { + if ($scope.originalRectArea.height - deltaY <= 0 && + $scope.originalRectArea.width - deltaX <= 0) { + $scope.swResize = false; + $scope.neResize = false; + $scope.nwResize = false; + $scope.seResize = true; + $scope.flag = 1; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x + $scope.originalRectArea.width, + $scope.originalRectArea.y + $scope.originalRectArea.height, + $scope.originalRectArea.width - deltaX, + $scope.originalRectArea.height - deltaY + ); + } else if ($scope.originalRectArea.width - deltaX <= 0) { + $scope.swResize = false; + $scope.neResize = true; + $scope.nwResize = false; + $scope.seResize = false; + $scope.flag = 1; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x + $scope.originalRectArea.width, + $scope.originalRectArea.y + deltaY, + $scope.originalRectArea.width - deltaX, + $scope.originalRectArea.height - deltaY + ); + } else if ($scope.originalRectArea.height - deltaY <= 0) { + $scope.swResize = true; + $scope.neResize = false; + $scope.nwResize = false; + $scope.seResize = false; + $scope.flag = 1; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x + deltaX, + $scope.originalRectArea.y + $scope.originalRectArea.height, + $scope.originalRectArea.width - deltaX, + $scope.originalRectArea.height - deltaY + ); + } else { + $scope.swResize = false; + $scope.neResize = false; + $scope.nwResize = true; + $scope.seResize = false; + $scope.flag = 0; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x + deltaX, + $scope.originalRectArea.y + deltaY, + $scope.originalRectArea.width - deltaX, + $scope.originalRectArea.height - deltaY + ); + } + } else if ($scope.resizeDirection === 'se') { + if ($scope.originalRectArea.height + deltaY <= 0 && + $scope.originalRectArea.width + deltaX <= 0) { + $scope.swResize = false; + $scope.neResize = false; + $scope.nwResize = true; + $scope.seResize = false; + $scope.flag = 1; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x + deltaX + + $scope.originalRectArea.width, + $scope.originalRectArea.y + deltaY + + $scope.originalRectArea.height, + $scope.originalRectArea.width + deltaX, + $scope.originalRectArea.height + deltaY + ); + } else if ($scope.originalRectArea.width + deltaX <= 0) { + $scope.swResize = true; + $scope.neResize = false; + $scope.nwResize = false; + $scope.seResize = false; + $scope.flag = 1; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x + deltaX + + $scope.originalRectArea.width, + $scope.originalRectArea.y, + $scope.originalRectArea.width + deltaX, + $scope.originalRectArea.height + deltaY + ); + } else if ($scope.originalRectArea.height + deltaY <= 0) { + $scope.swResize = false; + $scope.neResize = true; + $scope.nwResize = false; + $scope.seResize = false; + $scope.flag = 1; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x, + $scope.originalRectArea.y + deltaY + + $scope.originalRectArea.height, + $scope.originalRectArea.width + deltaX, + $scope.originalRectArea.height + deltaY + ); + } else { + $scope.swResize = false; + $scope.neResize = false; + $scope.nwResize = false; + $scope.seResize = true; + $scope.flag = 0; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x, + $scope.originalRectArea.y, + $scope.originalRectArea.width + deltaX, + $scope.originalRectArea.height + deltaY + ); + } + } else if ($scope.resizeDirection === 'sw') { + if ($scope.originalRectArea.height + deltaY <= 0 && + $scope.originalRectArea.width - deltaX <= 0) { + $scope.swResize = false; + $scope.neResize = true; + $scope.nwResize = false; + $scope.seResize = false; + $scope.flag = 1; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x + $scope.originalRectArea.width, + $scope.originalRectArea.y + deltaY + + $scope.originalRectArea.height, + $scope.originalRectArea.width - deltaX, + $scope.originalRectArea.height + deltaY + ); + } else if ($scope.originalRectArea.width - deltaX <= 0) { + $scope.swResize = false; + $scope.neResize = false; + $scope.nwResize = false; + $scope.seResize = true; + $scope.flag = 1; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x + $scope.originalRectArea.width, + $scope.originalRectArea.y, + $scope.originalRectArea.width - deltaX, + $scope.originalRectArea.height + deltaY + ); + } else if ($scope.originalRectArea.height + deltaY <= 0) { + $scope.swResize = false; + $scope.neResize = false; + $scope.nwResize = true; + $scope.seResize = false; + $scope.flag = 1; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x + deltaX, + $scope.originalRectArea.y + deltaY + + $scope.originalRectArea.height, + $scope.originalRectArea.width - deltaX, + $scope.originalRectArea.height + deltaY + ); + } else { + $scope.swResize = true; + $scope.neResize = false; + $scope.nwResize = false; + $scope.seResize = false; + $scope.flag = 1; + resizedRegion.area = regionAreaFromCornerAndDimensions( + $scope.originalRectArea.x + deltaX, + $scope.originalRectArea.y, + $scope.originalRectArea.width - deltaX, + $scope.originalRectArea.height + deltaY + ); + } + } else { + $scope.resizeDirection = ''; + $scope.userIsCurrentlyResizing = false; + } + }; + $scope.onSvgMouseMove = function(evt) { var svgElement = $($element).find( '.oppia-image-with-regions-editor-svg'); @@ -214,6 +559,8 @@ oppia.directive('imageWithRegionsEditor', [ $scope.originalRectArea.width, $scope.originalRectArea.height ); + } else if ($scope.userIsCurrentlyResizing) { + resizeRegion(); } }; @@ -233,6 +580,9 @@ oppia.directive('imageWithRegionsEditor', [ } $scope.userIsCurrentlyDrawing = false; $scope.userIsCurrentlyDragging = false; + $scope.userIsCurrentlyResizing = false; + $scope.flag = 0; + $scope.hideDirectionCursor(); if ($scope.regionDrawMode) { $scope.regionDrawMode = false; if ($scope.rectWidth !== 0 && $scope.rectHeight !== 0) { @@ -272,15 +622,83 @@ oppia.directive('imageWithRegionsEditor', [ $scope.onMouseoverRegion = function(index) { if ($scope.hoveredRegion === null) { $scope.hoveredRegion = index; + $scope.moveRegion = index; + } + }; + $scope.onMouseMoveRegion = function(index) { + if ($scope.moveRegion !== index) { + hideDirectionCursor(); + } + region = reg = cornerAndDimensionsFromRegionArea( + $scope.$parent.value.labeledRegions[ + $scope.hoveredRegion].region.area); + if ($scope.mouseY <= region.y + $scope.resizeRegionBorder && + $scope.mouseX >= region.width + region.x - + $scope.resizeRegionBorder && $scope.flag === 0 && + $scope.resizeDirection !== 'nw' && + $scope.resizeDirection !== 'se') { + $scope.resizeDirection = 'ne'; + $scope.nResize = false; + $scope.eResize = false; + $scope.neResize = true; + } else if ($scope.mouseY <= region.y + $scope.resizeRegionBorder && + $scope.mouseX <= region.x + $scope.resizeRegionBorder && + $scope.flag === 0 && $scope.resizeDirection !== 'se') { + $scope.resizeDirection = 'nw'; + $scope.nResize = false; + $scope.wResize = false; + $scope.nwResize = true; + } else if ($scope.mouseY >= region.height + region.y - + $scope.resizeRegionBorder && $scope.mouseX >= region.width + + region.x - $scope.resizeRegionBorder && $scope.flag === 0) { + $scope.resizeDirection = 'se'; + $scope.sResize = false; + $scope.eResize = false; + $scope.seResize = true; + } else if ($scope.mouseY >= region.height + region.y - + $scope.resizeRegionBorder && $scope.mouseX <= region.x + + $scope.resizeRegionBorder && $scope.flag === 0) { + $scope.resizeDirection = 'sw'; + $scope.sResize = false; + $scope.wResize = false; + $scope.swResize = true; + } else if ($scope.mouseY <= region.y + $scope.resizeRegionBorder && + $scope.flag === 0 && $scope.resizeDirection !== 's' && + $scope.resizeDirection !== 'se') { + $scope.resizeDirection = 'n'; + $scope.nResize = true; + } else if ($scope.mouseX <= region.x + $scope.resizeRegionBorder && + $scope.flag === 0 && $scope.resizeDirection !== 'e') { + $scope.resizeDirection = 'w'; + $scope.wResize = true; + } else if ($scope.mouseX >= region.width + region.x - + $scope.resizeRegionBorder && $scope.flag === 0 && + $scope.resizeDirection !== 'se') { + $scope.resizeDirection = 'e'; + $scope.eResize = true; + } else if ($scope.mouseY >= region.height + region.y - + $scope.resizeRegionBorder && $scope.flag === 0) { + $scope.resizeDirection = 's'; + $scope.sResize = true; + } else { + $scope.hideDirectionCursor(); + if ($scope.flag === 0) { + $scope.resizeDirection = ''; + } } }; $scope.onMouseoutRegion = function(index) { if ($scope.hoveredRegion === index) { $scope.hoveredRegion = null; } + $scope.hideDirectionCursor(); }; $scope.onMousedownRegion = function() { $scope.userIsCurrentlyDragging = true; + if ($scope.resizeDirection !== '') { + $scope.userIsCurrentlyDragging = false; + $scope.userIsCurrentlyResizing = true; + } $scope.selectedRegion = $scope.hoveredRegion; $scope.originalRectArea = cornerAndDimensionsFromRegionArea( $scope.$parent.value.labeledRegions[ @@ -293,11 +711,27 @@ oppia.directive('imageWithRegionsEditor', [ } }; $document.on('mouseup', $scope.onDocumentMouseUp); - $scope.setDrawMode = function() { $scope.regionDrawMode = true; }; $scope.getCursorStyle = function() { + if ($scope.nResize) { + return 'n-resize'; + } else if ($scope.eResize) { + return 'e-resize'; + } else if ($scope.wResize) { + return 'w-resize'; + } else if ($scope.sResize) { + return 's-resize'; + } else if ($scope.neResize) { + return 'ne-resize'; + } else if ($scope.seResize) { + return 'se-resize'; + } else if ($scope.nwResize) { + return 'nw-resize'; + } else if ($scope.swResize) { + return 'sw-resize'; + } return ($scope.regionDrawMode) ? 'crosshair' : 'default'; }; diff --git a/extensions/objects/templates/image_with_regions_editor.html b/extensions/objects/templates/image_with_regions_editor.html index a629634cafe1..847cc9c82755 100644 --- a/extensions/objects/templates/image_with_regions_editor.html +++ b/extensions/objects/templates/image_with_regions_editor.html @@ -33,6 +33,7 @@ ng-attr-style="<[getRegionStyle($index)]>" ng-mouseover="onMouseoverRegion($index)" ng-mouseout="onMouseoutRegion($index)" + ng-mousemove="onMouseMoveRegion($index)" ng-mousedown="onMousedownRegion($index)"> Date: Fri, 3 Mar 2017 19:23:46 +0530 Subject: [PATCH 023/173] addressed review comments --- core/domain/classifier_services.py | 12 ++++-------- core/domain/classifier_services_test.py | 6 +++++- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/core/domain/classifier_services.py b/core/domain/classifier_services.py index 27df41d9baf4..b43fb05ca34f 100644 --- a/core/domain/classifier_services.py +++ b/core/domain/classifier_services.py @@ -24,6 +24,7 @@ (classifier_models,) = models.Registry.import_models([models.NAMES.classifier]) + def classify(state, answer): """Classify the answer using the string classifier. @@ -140,11 +141,12 @@ def get_classifier_from_model(classifier_model): classifier_model.cached_classifier_data, classifier_model.data_schema_version) + def get_classifier_by_id(classifier_id): """Gets a classifier from a classifier id. Args: - classifier_id: Str. Id of the classifier. + classifier_id: str. ID of the classifier. Returns: classifier: Domain object for the classifier. @@ -179,13 +181,7 @@ def _save_classifier(classifier_model, classifier): classifier: Domain object for the classifier. Note: Most of the properties of a classifier are immutable. - The only property that can change is the state_name. Since, - exp_version_when_created will never change, algorithm_id of - the algorithm used to generate this model will not change, - cached_classifier_data is essentially the model generated - which won't change (if you change that you will have to - create a new ClassifierModel instance itself!) and - data_schema_version should also not change. + state_name is the only mutable property. """ classifier_model.state_name = classifier.state_name classifier_model.put() diff --git a/core/domain/classifier_services_test.py b/core/domain/classifier_services_test.py index 27800bea3588..3665af80f0b5 100644 --- a/core/domain/classifier_services_test.py +++ b/core/domain/classifier_services_test.py @@ -27,7 +27,7 @@ (classifier_models,) = models.Registry.import_models([models.NAMES.classifier]) class ClassifierServicesTests(test_utils.GenericTestBase): - """Test reader.classify using the sample explorations. + """Test classify using the sample explorations. Since the end to end tests cover correct classification, and frontend tests test hard rules, ReaderClassifyTests is only checking that the string @@ -73,6 +73,7 @@ def _is_string_classifier_called(self, answer): def test_string_classifier_classification(self): """All these responses trigger the string classifier.""" + with self.swap(feconf, 'ENABLE_STRING_CLASSIFIER', True): self.assertTrue( self._is_string_classifier_called( @@ -88,6 +89,7 @@ def test_string_classifier_classification(self): def test_retrieval_of_classifiers(self): """Test the get_classifier_by_id method.""" + with self.assertRaisesRegexp(Exception, ( "Entity for class ClassifierModel with id fake_id not found")): classifier_services.get_classifier_by_id('fake_id') @@ -104,6 +106,7 @@ def test_retrieval_of_classifiers(self): def test_update_of_classifiers(self): """Test the update_classifier method.""" + exp_id = u'1' state = 'Home' test_state = 'State' @@ -121,6 +124,7 @@ def test_update_of_classifiers(self): def test_deletion_of_classifiers(self): """Test the delete_classifier method.""" + exp_id = u'1' state = 'Home' classifier_id = classifier_models.ClassifierModel.create( From 9d147b4d0644e92b40442ed0c7b8bad7d4bbafa0 Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Sat, 4 Mar 2017 17:34:03 +0530 Subject: [PATCH 024/173] addressed review comments --- core/domain/classifier_services.py | 8 ++++---- core/domain/classifier_services_test.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/domain/classifier_services.py b/core/domain/classifier_services.py index b43fb05ca34f..6690dd06aa68 100644 --- a/core/domain/classifier_services.py +++ b/core/domain/classifier_services.py @@ -172,7 +172,7 @@ def _create_classifier(classifier): ) -def _save_classifier(classifier_model, classifier): +def _update_classifier(classifier_model, classifier): """Updates classifier model in the datastore given a classifier domain object. @@ -187,9 +187,9 @@ def _save_classifier(classifier_model, classifier): classifier_model.put() -def update_classifier(classifier): +def save_classifier(classifier): """Checks if model exists and updates the classifier model using - _save_classifier method. + _update_classifier method. Args: classifier: Domain object for the classifier. @@ -203,7 +203,7 @@ def delete_classifier(classifier_id): """Deletes classifier model in the datastore given classifier_id. Args: - classifier_id: Str. Id of the classifier. + classifier_id: str. ID of the classifier. """ classifier_model = classifier_models.ClassifierModel.get( classifier_id) diff --git a/core/domain/classifier_services_test.py b/core/domain/classifier_services_test.py index 3665af80f0b5..d73a2c5298cc 100644 --- a/core/domain/classifier_services_test.py +++ b/core/domain/classifier_services_test.py @@ -116,7 +116,7 @@ def test_update_of_classifiers(self): classifier = classifier_services.get_classifier_by_id( classifier_id) classifier.state_name = test_state - classifier_services.update_classifier(classifier) + classifier_services.save_classifier(classifier) classifier = classifier_services.get_classifier_by_id( classifier_id) self.assertEqual(classifier.exp_id, exp_id) From 7ca92887d639e63c09ef7ddbb4643d75ca6354fd Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Sat, 4 Mar 2017 17:34:49 +0530 Subject: [PATCH 025/173] addressed review comments --- core/domain/classifier_services.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/domain/classifier_services.py b/core/domain/classifier_services.py index 6690dd06aa68..b4fece62ab24 100644 --- a/core/domain/classifier_services.py +++ b/core/domain/classifier_services.py @@ -196,7 +196,7 @@ def save_classifier(classifier): """ classifier_model = classifier_models.ClassifierModel.get( classifier.id) - _save_classifier(classifier_model, classifier) + _update_classifier(classifier_model, classifier) def delete_classifier(classifier_id): From 3ce71a698cea96d929489c22c761235fcfa7136f Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Mon, 6 Mar 2017 03:35:25 +0530 Subject: [PATCH 026/173] addressed review comments --- core/domain/classifier_services.py | 14 ++++++-- core/domain/classifier_services_test.py | 44 +++++++++++++++---------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/core/domain/classifier_services.py b/core/domain/classifier_services.py index b4fece62ab24..173c3ed89239 100644 --- a/core/domain/classifier_services.py +++ b/core/domain/classifier_services.py @@ -165,11 +165,12 @@ def _create_classifier(classifier): classifier: Domain object for the classifier. """ - classifier_models.ClassifierModel.create( + classifier_id = classifier_models.ClassifierModel.create( classifier.exp_id, classifier.exp_version_when_created, classifier.state_name, classifier.algorithm_id, classifier.cached_classifier_data, classifier.data_schema_version, ) + return classifier_id def _update_classifier(classifier_model, classifier): @@ -193,10 +194,17 @@ def save_classifier(classifier): Args: classifier: Domain object for the classifier. + + Returns: + classifier_id: str. ID of the classifier. """ classifier_model = classifier_models.ClassifierModel.get( - classifier.id) - _update_classifier(classifier_model, classifier) + classifier.id, False) + if(classifier_model is None): + classifier.id = _create_classifier(classifier) + else: + _update_classifier(classifier_model, classifier) + return classifier.id def delete_classifier(classifier_id): diff --git a/core/domain/classifier_services_test.py b/core/domain/classifier_services_test.py index d73a2c5298cc..d30465f398eb 100644 --- a/core/domain/classifier_services_test.py +++ b/core/domain/classifier_services_test.py @@ -104,34 +104,44 @@ def test_retrieval_of_classifiers(self): self.assertEqual(classifier.exp_id, exp_id) self.assertEqual(classifier.state_name, state) - def test_update_of_classifiers(self): - """Test the update_classifier method.""" + def test_deletion_of_classifiers(self): + """Test the delete_classifier method.""" exp_id = u'1' state = 'Home' - test_state = 'State' classifier_id = classifier_models.ClassifierModel.create( exp_id, 1, state, feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput'], [], 1) + classifier_services.delete_classifier(classifier_id) + with self.assertRaisesRegexp(Exception, ( + "Entity for class ClassifierModel with id %s not found" %( + classifier_id))): + classifier_services.get_classifier_by_id(classifier_id) + + def test_update_of_classifiers(self): + """Test the save_classifier method.""" + + exp_id = u'1' + state = 'Home' + test_state = 'State' + classifier = type('', (), {})() + classifier.id = '1' + classifier.exp_id = exp_id + classifier.exp_version_when_created = 1 + classifier.state_name = state + classifier.algorithm_id = ( + feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput']) + classifier.cached_classifier_data = [] + classifier.data_schema_version = 1 + classifier_id = ( + classifier_services.save_classifier(classifier)) classifier = classifier_services.get_classifier_by_id( classifier_id) + self.assertEqual(classifier.exp_id, exp_id) + self.assertEqual(classifier.state_name, state) classifier.state_name = test_state classifier_services.save_classifier(classifier) classifier = classifier_services.get_classifier_by_id( classifier_id) self.assertEqual(classifier.exp_id, exp_id) self.assertEqual(classifier.state_name, test_state) - - def test_deletion_of_classifiers(self): - """Test the delete_classifier method.""" - - exp_id = u'1' - state = 'Home' - classifier_id = classifier_models.ClassifierModel.create( - exp_id, 1, state, - feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput'], [], 1) - classifier_services.delete_classifier(classifier_id) - with self.assertRaisesRegexp(Exception, ( - "Entity for class ClassifierModel with id %s not found" %( - classifier_id))): - classifier_services.get_classifier_by_id(classifier_id) From 0b0897fe46954755c76d1668d46391fa93186ddd Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Mon, 6 Mar 2017 03:36:37 +0530 Subject: [PATCH 027/173] lint issues --- core/domain/classifier_services.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/domain/classifier_services.py b/core/domain/classifier_services.py index 173c3ed89239..cdc3057cba1a 100644 --- a/core/domain/classifier_services.py +++ b/core/domain/classifier_services.py @@ -200,7 +200,7 @@ def save_classifier(classifier): """ classifier_model = classifier_models.ClassifierModel.get( classifier.id, False) - if(classifier_model is None): + if classifier_model is None: classifier.id = _create_classifier(classifier) else: _update_classifier(classifier_model, classifier) From a8787ab81283e9893db2871effbb4ca46a554e51 Mon Sep 17 00:00:00 2001 From: Anthony-Alridge Date: Mon, 6 Mar 2017 06:06:11 +0000 Subject: [PATCH 028/173] Fix #2039: Allow editors to preview summary tiles (#3069) * Added mapping from categories to colors to globals. * Modifyed to work with preview summary tiles. * Added preview summary tile * added categories to colors mapping to GLOBALS. * Changed close button to return to editor button * Adjusted files according to review comments. * made changes in response to review comments. * Changed summary tile to summary card and grayout css to name matching style guide. * Addressed review comments and added protractor test. * Fix linting errors * Fix lint error. * Fix default thumbnail. * Addressing review comments. * Add summary tile to components in exploration editor * Addressed review comments * Return null for values which aren't present. * Code style improvement. * Refactor preview summary tiles test into open and close preview summary tiles tests. * Revert tests to previous state and add padding to summary tile when objective not present. * Fix lint errors. * Renaming preview summary tile test. * Renaming preview summary tile test. * Fixing indentation. * Removed a script load from base.html. * Added script load for CollectionSummaryTileDirective. * Remove unused import. --- core/controllers/base.py | 4 ++ .../ExplorationSummaryTileDirective.js | 16 +++++-- .../exploration_summary_tile_directive.html | 26 +++++++--- core/templates/dev/head/pages/base.html | 6 +++ .../exploration_editor.html | 4 ++ .../settings_tab/SettingsTab.js | 47 ++++++++++++++++++- .../settings_tab/settings_tab.html | 29 ++++++++++++ core/tests/protractor/editorAndPlayer.js | 1 + core/tests/protractor_utils/editor.js | 14 ++++++ 9 files changed, 137 insertions(+), 10 deletions(-) diff --git a/core/controllers/base.py b/core/controllers/base.py index 604079d6f124..54398024495f 100644 --- a/core/controllers/base.py +++ b/core/controllers/base.py @@ -302,7 +302,10 @@ def render_template( 'BEFORE_END_HEAD_TAG_HOOK': jinja2.utils.Markup( BEFORE_END_HEAD_TAG_HOOK.value), 'CAN_SEND_ANALYTICS_EVENTS': feconf.CAN_SEND_ANALYTICS_EVENTS, + 'CATEGORIES_TO_COLORS': feconf.CATEGORIES_TO_COLORS, 'DEFAULT_LANGUAGE_CODE': feconf.ALL_LANGUAGE_CODES[0]['code'], + 'DEFAULT_CATEGORY_ICON': feconf.DEFAULT_THUMBNAIL_ICON, + 'DEFAULT_COLOR': feconf.DEFAULT_COLOR, 'DEV_MODE': feconf.DEV_MODE, 'MINIFICATION': feconf.IS_MINIFIED, 'DOMAIN_URL': '%s://%s' % (scheme, netloc), @@ -350,6 +353,7 @@ def render_template( if redirect_url_on_logout is None: redirect_url_on_logout = self.request.uri + if self.user_id: values['login_url'] = None values['logout_url'] = ( diff --git a/core/templates/dev/head/components/summary_tile/ExplorationSummaryTileDirective.js b/core/templates/dev/head/components/summary_tile/ExplorationSummaryTileDirective.js index 421ae60db741..0226c6b7840e 100644 --- a/core/templates/dev/head/components/summary_tile/ExplorationSummaryTileDirective.js +++ b/core/templates/dev/head/components/summary_tile/ExplorationSummaryTileDirective.js @@ -107,19 +107,29 @@ oppia.directive('explorationSummaryTile', [function() { $scope.MAX_AVATARS_TO_DISPLAY = 5; $scope.getAverageRating = function() { + if (!$scope.getRatings()) { + return null; + } return RatingComputationService.computeAverageRating( $scope.getRatings()); }; $scope.getLastUpdatedDatetime = function() { + if (!$scope.getLastUpdatedMsec()) { + return null; + } return oppiaDatetimeFormatter.getLocaleAbbreviatedDatetimeString( $scope.getLastUpdatedMsec()); }; $scope.getExplorationLink = function() { - var result = '/explore/' + $scope.getExplorationId(); - if ($scope.getCollectionId()) { - result += ('?collection_id=' + $scope.getCollectionId()); + if (!$scope.getExplorationId()) { + return '#'; + } else { + var result = '/explore/' + $scope.getExplorationId(); + if ($scope.getCollectionId()) { + result += ('?collection_id=' + $scope.getCollectionId()); + } } return result; }; diff --git a/core/templates/dev/head/components/summary_tile/exploration_summary_tile_directive.html b/core/templates/dev/head/components/summary_tile/exploration_summary_tile_directive.html index efcbb2f211f4..2b7801c9cc65 100644 --- a/core/templates/dev/head/components/summary_tile/exploration_summary_tile_directive.html +++ b/core/templates/dev/head/components/summary_tile/exploration_summary_tile_directive.html @@ -20,13 +20,15 @@

-
<[getObjective() | truncateAndCapitalize: 95]>
- - +
+ + +
+
  • @@ -36,7 +38,7 @@

    <[getAverageRating() | number:1]> - +

  • @@ -44,16 +46,28 @@

    <['I18N_LIBRARY_VIEWS_TOOLTIP' | translate]> - <[getNumViews() | summarizeNonnegativeNumber]> + + <[getNumViews() | summarizeNonnegativeNumber]> + + +
  • - + Last Updated <[getLastUpdatedDatetime()]> + +
+ + diff --git a/core/templates/dev/head/pages/base.html b/core/templates/dev/head/pages/base.html index 20e3ba7d1699..800d2874f1d7 100644 --- a/core/templates/dev/head/pages/base.html +++ b/core/templates/dev/head/pages/base.html @@ -88,9 +88,15 @@ '{{can_create_collections|js_string}}'), CAN_SEND_ANALYTICS_EVENTS: JSON.parse( '{{CAN_SEND_ANALYTICS_EVENTS|js_string}}'), + CATEGORIES_TO_COLORS: JSON.parse( + '{{CATEGORIES_TO_COLORS|js_string}}'), csrf_token: JSON.parse('{{csrf_token|js_string}}'), DEFAULT_LANGUAGE_CODE: JSON.parse( '{{DEFAULT_LANGUAGE_CODE|js_string}}'), + DEFAULT_CATEGORY_ICON: JSON.parse( + '{{DEFAULT_CATEGORY_ICON|js_string}}'), + DEFAULT_COLOR: JSON.parse( + '{{DEFAULT_COLOR|js_string}}'), DEV_MODE: JSON.parse('{{DEV_MODE|js_string}}'), INVALID_NAME_CHARS: JSON.parse('{{INVALID_NAME_CHARS|js_string}}'), MINIFICATION: JSON.parse('{{MINIFICATION|js_string}}'), diff --git a/core/templates/dev/head/pages/exploration_editor/exploration_editor.html b/core/templates/dev/head/pages/exploration_editor/exploration_editor.html index e89479babd2b..f3e44007f41a 100644 --- a/core/templates/dev/head/pages/exploration_editor/exploration_editor.html +++ b/core/templates/dev/head/pages/exploration_editor/exploration_editor.html @@ -237,6 +237,10 @@ + + + + diff --git a/core/templates/dev/head/pages/exploration_editor/settings_tab/SettingsTab.js b/core/templates/dev/head/pages/exploration_editor/settings_tab/SettingsTab.js index 3a6f00534cb8..b77e4fc7c6fb 100644 --- a/core/templates/dev/head/pages/exploration_editor/settings_tab/SettingsTab.js +++ b/core/templates/dev/head/pages/exploration_editor/settings_tab/SettingsTab.js @@ -26,6 +26,7 @@ oppia.controller('SettingsTab', [ 'explorationParamChangesService', 'explorationWarningsService', 'explorationAdvancedFeaturesService', 'ALL_CATEGORIES', 'EXPLORATION_TITLE_INPUT_FOCUS_LABEL', 'UserEmailPreferencesService', + 'UrlInterpolationService', function( $scope, $http, $window, $modal, $rootScope, explorationData, explorationTitleService, explorationCategoryService, @@ -35,7 +36,8 @@ oppia.controller('SettingsTab', [ changeListService, alertsService, explorationStatesService, explorationParamChangesService, explorationWarningsService, explorationAdvancedFeaturesService, ALL_CATEGORIES, - EXPLORATION_TITLE_INPUT_FOCUS_LABEL, UserEmailPreferencesService) { + EXPLORATION_TITLE_INPUT_FOCUS_LABEL, UserEmailPreferencesService, + UrlInterpolationService) { $scope.EXPLORATION_TITLE_INPUT_FOCUS_LABEL = ( EXPLORATION_TITLE_INPUT_FOCUS_LABEL); @@ -226,6 +228,49 @@ oppia.controller('SettingsTab', [ /******************************************** * Methods relating to control buttons. ********************************************/ + $scope.previewSummaryTile = function() { + alertsService.clearWarnings(); + $modal.open({ + templateUrl: 'modals/previewSummaryTile', + backdrop: true, + controller: [ + '$scope', '$modalInstance', function($scope, $modalInstance) { + $scope.getExplorationTitle = function() { + return explorationTitleService.displayed; + }; + $scope.getExplorationObjective = function() { + return explorationObjectiveService.displayed; + }; + $scope.getExplorationCategory = function() { + return explorationCategoryService.displayed; + }; + $scope.getThumbnailIconUrl = function() { + var category = explorationCategoryService.displayed; + if (GLOBALS.ALL_CATEGORIES.indexOf(category) === -1) { + category = GLOBALS.DEFAULT_CATEGORY_ICON; + } + return UrlInterpolationService.getStaticImageUrl( + '/subjects/' + category + '.svg'); + }; + $scope.getThumbnailBgColor = function() { + var category = explorationCategoryService.displayed; + if (!GLOBALS.CATEGORIES_TO_COLORS.hasOwnProperty(category)) { + var color = GLOBALS.DEFAULT_COLOR; + } else { + var color = GLOBALS.CATEGORIES_TO_COLORS[category]; + } + return color; + }; + + $scope.close = function() { + $modalInstance.dismiss(); + alertsService.clearWarnings(); + }; + } + ] + }); + }; + $scope.showTransferExplorationOwnershipModal = function() { alertsService.clearWarnings(); $modal.open({ diff --git a/core/templates/dev/head/pages/exploration_editor/settings_tab/settings_tab.html b/core/templates/dev/head/pages/exploration_editor/settings_tab/settings_tab.html index 59ab4339121f..5917d5a1ff31 100644 --- a/core/templates/dev/head/pages/exploration_editor/settings_tab/settings_tab.html +++ b/core/templates/dev/head/pages/exploration_editor/settings_tab/settings_tab.html @@ -86,6 +86,13 @@

Basic Settings

+
+ +
@@ -586,3 +593,25 @@

Delete Exploration

+ + diff --git a/core/tests/protractor/editorAndPlayer.js b/core/tests/protractor/editorAndPlayer.js index 1946e2551b8b..a1cce3442d6e 100644 --- a/core/tests/protractor/editorAndPlayer.js +++ b/core/tests/protractor/editorAndPlayer.js @@ -205,6 +205,7 @@ describe('Full exploration editor', function() { editor.setTitle('Testing multiple rules'); editor.setCategory('Answer Groups'); editor.setObjective('To assess happiness.'); + editor.openAndClosePreviewSummaryTile(); editor.saveChanges(); workflow.publishExploration(); diff --git a/core/tests/protractor_utils/editor.js b/core/tests/protractor_utils/editor.js index 326d91dc0801..49c7b2fa7356 100644 --- a/core/tests/protractor_utils/editor.js +++ b/core/tests/protractor_utils/editor.js @@ -1066,6 +1066,19 @@ var enableFallbacks = function() { }); }; +var openAndClosePreviewSummaryTile = function() { + runFromSettingsTab(function() { + element(by.css('.protractor-test-open-preview-summary-modal')).click(); + general.waitForSystem(); + expect(element(by.css( + '.protractor-test-exploration-summary-tile')).isPresent()).toBeTruthy(); + element(by.css('.protractor-test-close-preview-summary-modal')).click(); + general.waitForSystem(); + expect(element(by.css( + '.protractor-test-exploration-summary-tile')).isPresent()).toBeFalsy(); + }); +}; + // CONTROLS var saveChanges = function(commitMessage) { @@ -1445,6 +1458,7 @@ exports.setFirstState = setFirstState; exports.enableParameters = enableParameters; exports.enableGadgets = enableGadgets; exports.enableFallbacks = enableFallbacks; +exports.openAndClosePreviewSummaryTile = openAndClosePreviewSummaryTile; exports.saveChanges = saveChanges; exports.discardChanges = discardChanges; From 8b8d918b21274fa6f4f831589a0b73e231b4dfcc Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 6 Mar 2017 09:01:49 +0100 Subject: [PATCH 029/173] Localisation updates from https://translatewiki.net. --- assets/i18n/ast.json | 4 + assets/i18n/bn.json | 3 + assets/i18n/br.json | 12 ++- assets/i18n/de.json | 4 + assets/i18n/diq.json | 50 +++++++++++- assets/i18n/es.json | 4 + assets/i18n/fi.json | 50 ++++++++++++ assets/i18n/fr.json | 4 + assets/i18n/hi.json | 4 + assets/i18n/it.json | 1 + assets/i18n/ko.json | 2 + assets/i18n/lb.json | 5 ++ assets/i18n/lt.json | 2 + assets/i18n/mk.json | 4 + assets/i18n/ne.json | 163 +++++++++++++++++++++++++++++++++++++++ assets/i18n/qqq.json | 6 +- assets/i18n/ru.json | 4 + assets/i18n/sv.json | 6 ++ assets/i18n/tr.json | 7 ++ assets/i18n/zh-hans.json | 4 + assets/i18n/zh-hant.json | 4 + 21 files changed, 335 insertions(+), 8 deletions(-) create mode 100644 assets/i18n/ne.json diff --git a/assets/i18n/ast.json b/assets/i18n/ast.json index b86e4141d286..fe7084d27478 100644 --- a/assets/i18n/ast.json +++ b/assets/i18n/ast.json @@ -87,6 +87,10 @@ "I18N_INTERACTIONS_MUSIC_PLAY": "Reproducir", "I18N_INTERACTIONS_MUSIC_PLAY_SEQUENCE": "Reproducir la secuencia destín", "I18N_INTERACTIONS_MUSIC_SUBMIT": "Unviar", + "I18N_INTERACTIONS_PENCILCODEEDITOR_ARE_YOU_SURE_YOU_WANT_TO_RESET_YOUR_CODE": "¿Tas seguru de que quies restablecer el códigu?", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "Encaboxar", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CONFIRMATION_REQUIRED": "Ríquese confirmación", + "I18N_INTERACTIONS_PENCILCODEEDITOR_RESET_CODE": "Restablecer códigu", "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "Amestar un elementu", "I18N_INTERACTIONS_SET_INPUT_DUPLICATES_ERROR": "Ups, paez qu'el to conxuntu tien duplicaos", "I18N_INTERACTIONS_SET_INPUT_EMPTY_SET": "(Amestar un elementu per llinia.)", diff --git a/assets/i18n/bn.json b/assets/i18n/bn.json index 42e06702611d..a0ed5362fdab 100644 --- a/assets/i18n/bn.json +++ b/assets/i18n/bn.json @@ -87,6 +87,9 @@ "I18N_INTERACTIONS_MUSIC_PLAY": "চালান", "I18N_INTERACTIONS_MUSIC_PLAY_SEQUENCE": "লক্ষ্য অনুক্রম চালান", "I18N_INTERACTIONS_MUSIC_SUBMIT": "জমা দিন", + "I18N_INTERACTIONS_PENCILCODEEDITOR_ARE_YOU_SURE_YOU_WANT_TO_RESET_YOUR_CODE": "আপনি কি নিশ্চিত যে আপনি আপনার কোড পুনঃস্থাপন করতে চান?", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "বাতিল", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CONFIRMATION_REQUIRED": "অনুমোদন প্রয়োজন", "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "উপাদান যোগ করুন", "I18N_INTERACTIONS_SET_INPUT_DUPLICATES_ERROR": "ওহো, এটা দেখে মনে হচ্ছে আপনার সেটে সদৃশ্য রয়েছে!", "I18N_INTERACTIONS_SET_INPUT_EMPTY_SET": "(লাইন প্রতি একটি উপাদান যোগ করুন।)", diff --git a/assets/i18n/br.json b/assets/i18n/br.json index 3ba1eedf15d9..56158fd3fc97 100644 --- a/assets/i18n/br.json +++ b/assets/i18n/br.json @@ -16,6 +16,7 @@ "I18N_DASHBOARD_COLLECTIONS": "Dastumadoù", "I18N_DASHBOARD_EXPLORATIONS": "Ergerzhadennoù", "I18N_DASHBOARD_OPEN_FEEDBACK": "Digeriñ an evezhiadennoù", + "I18N_DASHBOARD_SUBSCRIBERS": "Tud koumanantet", "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "Lakaat ar skeudenn da riklañ amañ", "I18N_DIRECTIVES_OR_SELECT_FILE": "pe diuz ur restr skeudenn :", "I18N_ERROR_DISABLED_EXPLORATION": "Ergerzhadenn diweredekaet", @@ -78,6 +79,10 @@ "I18N_INTERACTIONS_MUSIC_CLEAR": "Diverkañ", "I18N_INTERACTIONS_MUSIC_PLAY": "C'hoari", "I18N_INTERACTIONS_MUSIC_SUBMIT": "Kas", + "I18N_INTERACTIONS_PENCILCODEEDITOR_ARE_YOU_SURE_YOU_WANT_TO_RESET_YOUR_CODE": "Ha sur oc'h hoc'h eus c'hoant da adderaouekaat ho kod ?", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "Nullañ", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CONFIRMATION_REQUIRED": "Ret eo kadarnaat", + "I18N_INTERACTIONS_PENCILCODEEDITOR_RESET_CODE": "Adderaouekaat ar c'hod", "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "Ouzhpennañ an elfenn", "I18N_INTERACTIONS_SET_INPUT_DUPLICATES_ERROR": "C'hem, seblantout a ra ez eus doublennoù en ho hollad !", "I18N_INTERACTIONS_SET_INPUT_EMPTY_SET": "(Ouzhpennañ un elfenn dre linenn.)", @@ -147,6 +152,7 @@ "I18N_LIBRARY_RATINGS_TOOLTIP": "Priziadennoù", "I18N_LIBRARY_VIEWS_TOOLTIP": "Gweladennoù", "I18N_LIBRARY_VIEW_ALL": "Gwelet pep tra", + "I18N_ONE_SUBSCRIBER_TEXT": "1 den koumanantet zo.", "I18N_PLAYER_BACK": "Distreiñ", "I18N_PLAYER_CARD_NUMBER_TOOLTIP": "Niverenn gartenn", "I18N_PLAYER_COMMUNITY_EDITABLE_TOOLTIP": "A c'hall bezañ kemmet gant ar gumuniezh", @@ -163,8 +169,8 @@ "I18N_PLAYER_LEAVE_FEEDBACK": "Lezit un evezhiadenn evit an aozerien", "I18N_PLAYER_LOADING": "O kargañ...", "I18N_PLAYER_NO_OBJECTIVE": "Pal diferet ebet.", - "I18N_PLAYER_NO_TAGS": "N'eus bet diferet balizenn ebet.", - "I18N_PLAYER_PLUS_TAGS": "<[additionalTagNumber]>+ muioc'h a valizennoù", + "I18N_PLAYER_NO_TAGS": "N'eus bet spisaet tikedenn ebet.", + "I18N_PLAYER_PLUS_TAGS": "<[additionalTagNumber]>+ muioc'h a dikedennoù", "I18N_PLAYER_PREVIOUS_RESPONSES": "Respontoù kent (<[previousResponses]>)", "I18N_PLAYER_RATE_EXPLORATION": "Desket ho peus un dra bennak nevez ? Peseurt notenn al lakfec'h d'an ergerhadenn-mañ ?", "I18N_PLAYER_RATINGS_TOOLTIP": "Priziañ", @@ -181,7 +187,7 @@ "I18N_PLAYER_SHARE_THIS_EXPLORATION": "Rannañ an ergerzhadenn-mañ", "I18N_PLAYER_STAY_ANONYMOUS": "Chom dizanv", "I18N_PLAYER_SUBMIT_BUTTON": "Kas", - "I18N_PLAYER_TAGS_TOOLTIP": "Balizennoù", + "I18N_PLAYER_TAGS_TOOLTIP": "Tikedennoù", "I18N_PLAYER_THANK_FEEDBACK": "Trugarez da vezañ roet ho soñj.", "I18N_PLAYER_UNRATED": "N'eo ket notennet", "I18N_PLAYER_VIEWS_TOOLTIP": "Gweladennoù", diff --git a/assets/i18n/de.json b/assets/i18n/de.json index c3b767fc21fd..5e6a93a7e8e2 100644 --- a/assets/i18n/de.json +++ b/assets/i18n/de.json @@ -87,6 +87,10 @@ "I18N_INTERACTIONS_MUSIC_PLAY": "Spielen", "I18N_INTERACTIONS_MUSIC_PLAY_SEQUENCE": "Zielsequenz spielen", "I18N_INTERACTIONS_MUSIC_SUBMIT": "Absenden", + "I18N_INTERACTIONS_PENCILCODEEDITOR_ARE_YOU_SURE_YOU_WANT_TO_RESET_YOUR_CODE": "Bist du sicher, dass du deinen Code zurücksetzen möchtest?", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "Abbrechen", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CONFIRMATION_REQUIRED": "Bestätigung erforderlich", + "I18N_INTERACTIONS_PENCILCODEEDITOR_RESET_CODE": "Code zurücksetzen", "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "Objekt hinzufügen", "I18N_INTERACTIONS_SET_INPUT_DUPLICATES_ERROR": "Huch! Es scheint, als ob dein Satz Duplikate hat!", "I18N_INTERACTIONS_SET_INPUT_EMPTY_SET": "(Ein Objekt pro Zeile hinzufügen.)", diff --git a/assets/i18n/diq.json b/assets/i18n/diq.json index 7eb25313ce01..048d1f34dbf4 100644 --- a/assets/i18n/diq.json +++ b/assets/i18n/diq.json @@ -3,7 +3,7 @@ "I18N_ACTION_APPLY_TO_TEACH_WITH_OPPIA": "Musayış be Oppiay bıgure", "I18N_ACTION_BROWSE_EXPLORATIONS": "Çım berz Keşfanê ma", "I18N_ACTION_BROWSE_LESSONS": "Çım berz Dersanê ma", - "I18N_ACTION_CREATE_EXPLORATION": "Keşfê xo Vıraz", + "I18N_ACTION_CREATE_EXPLORATION": "Keşf Vıraz", "I18N_ACTION_CREATE_LESSON": "Dersê xo Vıraze", "I18N_CREATE_ACTIVITY_QUESTION": "Şıma wazenê kı çıçi vırazê?", "I18N_CREATE_ACTIVITY_TITLE": "Fealiyet vıraze", @@ -15,11 +15,14 @@ "I18N_CREATE_EXPLORATION_UPLOAD": "Bar ke", "I18N_CREATE_NO_THANKS": "Nê, teşekur", "I18N_CREATE_YES_PLEASE": "E, reca kena!", - "I18N_DASHBOARD_COLLECTIONS": "Koleksiyonê", - "I18N_DASHBOARD_EXPLORATIONS": "Keşfê", + "I18N_DASHBOARD_COLLECTIONS": "Koleksiyoni", + "I18N_DASHBOARD_EXPLORATIONS": "Diyayışi", "I18N_DASHBOARD_OPEN_FEEDBACK": "Peyd rışyen akerê", + "I18N_DASHBOARD_SUBSCRIBERS": "Aboney", "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "resimi kaşê tiyay kı", "I18N_DIRECTIVES_OR_SELECT_FILE": "ya zi dosyay asayışi weçine.", + "I18N_ERROR_DISABLED_EXPLORATION": "Keşf disable biyo", + "I18N_ERROR_DISABLED_EXPLORATION_PAGE_TITLE": "Keşf disable biyo- Oppia", "I18N_ERROR_HEADER_400": "Xırab 400", "I18N_ERROR_HEADER_401": "Xırab 401", "I18N_ERROR_HEADER_404": "Xırab 404", @@ -143,11 +146,50 @@ "I18N_PLAYER_GO_TO_LIBRARY": "Şo Kıtabxane", "I18N_PLAYER_INFO_TOOLTIP": "Melumat", "I18N_PLAYER_LAST_UPDATED_TOOLTIP": "Rocanekerdışo Peyên", + "I18N_PLAYER_LOADING": "Bar beno...", + "I18N_PLAYER_RATINGS_TOOLTIP": "Reytingî", + "I18N_PLAYER_REPORT_MODAL_BODY_OTHER": "Bin", "I18N_PLAYER_REPORT_MODAL_FOOTER_CANCEL": "İbtal", "I18N_PLAYER_REPORT_MODAL_FOOTER_SUBMIT": "Bırışe", "I18N_PLAYER_REPORT_SUCCESS_MODAL_CLOSE": "Racın", "I18N_PLAYER_REPORT_SUCCESS_MODAL_HEADER": "Teşekur keno/kena!", + "I18N_PLAYER_SUBMIT_BUTTON": "Dekew", + "I18N_PLAYER_TAGS_TOOLTIP": "Etiketi", + "I18N_PLAYER_UNRATED": "Bêrat", "I18N_PLAYER_VIEWS_TOOLTIP": "Asanyayışi", + "I18N_PREFERENCES_BIO": "Bio", + "I18N_PREFERENCES_BREADCRUMB": "Tercihi", + "I18N_PREFERENCES_CANCEL_BUTTON": "Bıtexelne", + "I18N_PREFERENCES_CHANGE_PICTURE": "Resmê profili bıvurne", + "I18N_PREFERENCES_EMAIL": "E-posta", + "I18N_PREFERENCES_USERNAME": "Namey karberi", + "I18N_PREFERENCES_USERNAME_NOT_SELECTED": "Theba nêweçineya", + "I18N_SIDEBAR_ABOUT_LINK": "Heqa", + "I18N_SIDEBAR_BLOG": "Blog", + "I18N_SIDEBAR_CONTACT_US": "İrtıbati", + "I18N_SIDEBAR_DONATE": "Bèc", + "I18N_SIDEBAR_FORUM": "Forum", + "I18N_SIDEBAR_GET_STARTED": "Start bıke", + "I18N_SIDEBAR_LIBRARY_LINK": "Kıtıbxane", + "I18N_SIDEBAR_TEACH_WITH_OPPIA": "Oppia ya bomos", + "I18N_SIGNUP_CLOSE_BUTTON": "qefelnr", + "I18N_SIGNUP_EMAIL": "E-posta", + "I18N_SUBSCRIBE_BUTTON_TEXT": "Abone be", + "I18N_TOPNAV_ABOUT": "Heqa", + "I18N_TOPNAV_ABOUT_OPPIA": "Oppia heqe", + "I18N_TOPNAV_ADMIN_PAGE": "ripel admin", + "I18N_TOPNAV_BLOG": "Blog", + "I18N_TOPNAV_CONTACT_US": "İrtıbat", + "I18N_TOPNAV_DASHBOARD": "Dashboard vıraz", "I18N_TOPNAV_DONATE": "Beğş", - "I18N_TOPNAV_FORUM": "Forum" + "I18N_TOPNAV_FORUM": "Forum", + "I18N_TOPNAV_GET_STARTED": "Start ke", + "I18N_TOPNAV_LIBRARY": "Kıtıbxane", + "I18N_TOPNAV_LOGOUT": "Veciyayış", + "I18N_TOPNAV_MODERATOR_PAGE": "ripel moderator", + "I18N_TOPNAV_NOTIFICATIONS": "Hesnayeni", + "I18N_TOPNAV_PREFERENCES": "Tercihi", + "I18N_TOPNAV_SIGN_IN": "Cı kewe", + "I18N_TOPNAV_TEACH_WITH_OPPIA": "Oppia bomos", + "I18N_UNSUBSCRIBE_BUTTON_TEXT": "Abone bevec" } diff --git a/assets/i18n/es.json b/assets/i18n/es.json index 55d4f42a288c..f178afa2a3c8 100644 --- a/assets/i18n/es.json +++ b/assets/i18n/es.json @@ -87,6 +87,10 @@ "I18N_INTERACTIONS_MUSIC_PLAY": "Reproducir", "I18N_INTERACTIONS_MUSIC_PLAY_SEQUENCE": "Reproducir secuencia objetivo", "I18N_INTERACTIONS_MUSIC_SUBMIT": "Enviar", + "I18N_INTERACTIONS_PENCILCODEEDITOR_ARE_YOU_SURE_YOU_WANT_TO_RESET_YOUR_CODE": "¿Confirmas que quieres restablecer tu código?", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "Cancelar", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CONFIRMATION_REQUIRED": "Confirmación requerida", + "I18N_INTERACTIONS_PENCILCODEEDITOR_RESET_CODE": "Código de restablecimiento", "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "Agregar elemento", "I18N_INTERACTIONS_SET_INPUT_DUPLICATES_ERROR": "Ups, parece que tu conjunto tiene duplicados!", "I18N_INTERACTIONS_SET_INPUT_EMPTY_SET": "(Agrega un elemento por línea.)", diff --git a/assets/i18n/fi.json b/assets/i18n/fi.json index 40c922aaea60..00be88eab361 100644 --- a/assets/i18n/fi.json +++ b/assets/i18n/fi.json @@ -81,6 +81,9 @@ "I18N_INTERACTIONS_MUSIC_PLAY": "Toista", "I18N_INTERACTIONS_MUSIC_PLAY_SEQUENCE": "Toista kohdejärjestys", "I18N_INTERACTIONS_MUSIC_SUBMIT": "Lähetä", + "I18N_INTERACTIONS_PENCILCODEEDITOR_ARE_YOU_SURE_YOU_WANT_TO_RESET_YOUR_CODE": "Oletko varma, että haluat nollata koodisi?", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "Peruuta", + "I18N_INTERACTIONS_PENCILCODEEDITOR_RESET_CODE": "Nollaa koodi", "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "Lisää kohde", "I18N_INTERACTIONS_SET_INPUT_DUPLICATES_ERROR": "oho, se näyttää siltä että lähetit kaksoiskappaleet!", "I18N_INTERACTIONS_SET_INPUT_EMPTY_SET": "(Lisää yksi kohde per rivi.)", @@ -139,6 +142,8 @@ "I18N_LIBRARY_LAST_UPDATED": "Viimeksi päivitetty", "I18N_LIBRARY_LOADING": "Ladataan", "I18N_LIBRARY_N/A": "N/A", + "I18N_LIBRARY_RATINGS_TOOLTIP": "Arvioinnit", + "I18N_ONE_SUBSCRIBER_TEXT": "Sinulla on 1 tilaaja.", "I18N_PLAYER_BACK": "Takaisin", "I18N_PLAYER_CARD_NUMBER_TOOLTIP": "Kortti #", "I18N_PLAYER_COMMUNITY_EDITABLE_TOOLTIP": "Yhteisön muokattavissa", @@ -155,20 +160,37 @@ "I18N_PLAYER_LEAVE_FEEDBACK": "Jätä palautetta tekijöille...", "I18N_PLAYER_LOADING": "Ladataan...", "I18N_PLAYER_NO_OBJECTIVE": "Tavoitetta ei ole määritelty.", + "I18N_PLAYER_NO_TAGS": "Arvoja ei ole määritetty.", + "I18N_PLAYER_RATINGS_TOOLTIP": "Arvioinnit", "I18N_PLAYER_REPORT_MODAL_BODY_OTHER": "Muu", "I18N_PLAYER_REPORT_MODAL_BODY_POOR_EXPERIENCE": "Huono oppimiskokemus", "I18N_PLAYER_REPORT_MODAL_FOOTER_CANCEL": "Peruuta", "I18N_PLAYER_REPORT_MODAL_FOOTER_SUBMIT": "Lähetä", "I18N_PLAYER_REPORT_SUCCESS_MODAL_CLOSE": "Sulje", "I18N_PLAYER_REPORT_SUCCESS_MODAL_HEADER": "Kiitos!", + "I18N_PLAYER_RETURN_TO_EDITOR": "Palaa muokkaimeen", "I18N_PLAYER_STAY_ANONYMOUS": "Pysyä nimettömänä", "I18N_PLAYER_SUBMIT_BUTTON": "Lähetä", "I18N_PLAYER_TAGS_TOOLTIP": "Merkkaukset", "I18N_PLAYER_THANK_FEEDBACK": "Kiitos palautteesta!", "I18N_PLAYER_UNRATED": "Luokittelematon", + "I18N_PLAYER_VIEWS_TOOLTIP": "Näyttökerrat", + "I18N_PREFERENCES_BREADCRUMB": "Asetukset", "I18N_PREFERENCES_CANCEL_BUTTON": "Peruuta", + "I18N_PREFERENCES_CHANGE_PICTURE": "Vaihda profiilikuva", "I18N_PREFERENCES_EMAIL": "Sähköpostiosoite", + "I18N_PREFERENCES_HEADING": "Asetukset", + "I18N_PREFERENCES_HEADING_SUBTEXT": "Kaikki muutokset, jotka teet tällä sivulla tallennetaan automaattisesti.", + "I18N_PREFERENCES_PAGE_TITLE": "Muuta profiiliasetuksiasi - Oppia", "I18N_PREFERENCES_PICTURE": "Kuva", + "I18N_PREFERENCES_PREFERRED_SITE_LANGUAGE": "Ensisijainen sivuston kieli", + "I18N_PREFERENCES_PREFERRED_SITE_LANGUAGE_EXPLAIN": "Tämä on kieli, jolla sivusto näytetään.", + "I18N_PREFERENCES_PREFERRED_SITE_LANGUAGE_PLACEHOLDER": "Ensisijainen sivuston kieli", + "I18N_PREFERENCES_PROFILE_PICTURE_ADD": "Lisää profiilikuva", + "I18N_PREFERENCES_PROFILE_PICTURE_UPLOAD": "Lataa profiilikuva", + "I18N_PREFERENCES_SELECT_EXPLORATION_LANGUAGE": "Valitse haluamasi kielet...", + "I18N_PREFERENCES_USERNAME": "Käyttäjänimi", + "I18N_PREFERENCES_USERNAME_NOT_SELECTED": "Ei vielä valittu", "I18N_SIDEBAR_ABOUT_LINK": "Tietoja", "I18N_SIDEBAR_BLOG": "Blogi", "I18N_SIDEBAR_CONTACT_US": "Ota yhteyttä", @@ -177,7 +199,35 @@ "I18N_SIDEBAR_GET_STARTED": "Aloittaminen", "I18N_SIDEBAR_LIBRARY_LINK": "Kirjasto", "I18N_SIGNUP_CLOSE_BUTTON": "Sulje", + "I18N_SIGNUP_DO_NOT_SEND_EMAILS": "Älä lähetä näitä sähköposteja", "I18N_SIGNUP_EMAIL": "Sähköpostiosoite", + "I18N_SIGNUP_EMAIL_PREFERENCES": "Sähköpostiasetukset", + "I18N_SIGNUP_ERROR_USERNAME_MORE_50_CHARS": "Käyttäjätunnus voi olla enintään 50 merkkiä.", + "I18N_SIGNUP_ERROR_USERNAME_NOT_AVAILABLE": "Tämä käyttäjätunnus ei ole saatavilla.", + "I18N_SIGNUP_ERROR_USERNAME_ONLY_ALPHANUM": "Käyttäjätunnuksissa voi olla vain aakkosnumeerisia merkkejä.", + "I18N_SIGNUP_ERROR_USERNAME_TAKEN": "Anteeksi, tämä käyttäjätunnus on jo varattu.", + "I18N_SIGNUP_ERROR_USERNAME_WITH_SPACES": "Varmista, että käyttäjätunnuksessasi ei ole välilyöntejä.", + "I18N_SIGNUP_FIELD_REQUIRED": "Tämä kenttä on pakollinen.", + "I18N_SIGNUP_LOADING": "Ladataan", + "I18N_SIGNUP_PAGE_TITLE": "Liity yhteisöön - Oppia", + "I18N_SIGNUP_REGISTRATION": "Rekisteröinti", + "I18N_SIGNUP_USERNAME": "Käyttäjänimi", + "I18N_SIGNUP_WHY_LICENSE": "Miksi CC-BY-SA?", + "I18N_SPLASH_JAVASCRIPT_ERROR_THANKS": "Kiitos.", "I18N_SUBSCRIBE_BUTTON_TEXT": "Tilaa", + "I18N_TOPNAV_ABOUT": "Tietoja", + "I18N_TOPNAV_ABOUT_OPPIA": "Tietoja Oppiasta", + "I18N_TOPNAV_BLOG": "Blogi", + "I18N_TOPNAV_CONTACT_US": "Ota yhteyttä", + "I18N_TOPNAV_DASHBOARD": "Luojan kojelauta", + "I18N_TOPNAV_DONATE": "Lahjoita", + "I18N_TOPNAV_FORUM": "Foorumi", + "I18N_TOPNAV_GET_STARTED": "Aloittaminen", + "I18N_TOPNAV_LIBRARY": "Kirjasto", + "I18N_TOPNAV_LOGOUT": "Kirjaudu ulos", + "I18N_TOPNAV_NOTIFICATIONS": "Ilmoitukset", + "I18N_TOPNAV_PREFERENCES": "Asetukset", + "I18N_TOPNAV_SIGN_IN": "Kirjaudu sisään", + "I18N_TOTAL_SUBSCRIBERS_TEXT": "Sinulla on yhteensä <[totalSubscribers]> tilaajaa.", "I18N_UNSUBSCRIBE_BUTTON_TEXT": "Peruuta tilaus" } diff --git a/assets/i18n/fr.json b/assets/i18n/fr.json index 6561256ff2b7..f5910f72a869 100644 --- a/assets/i18n/fr.json +++ b/assets/i18n/fr.json @@ -87,6 +87,10 @@ "I18N_INTERACTIONS_MUSIC_PLAY": "Jouer", "I18N_INTERACTIONS_MUSIC_PLAY_SEQUENCE": "Jouer la séquence cible", "I18N_INTERACTIONS_MUSIC_SUBMIT": "Soumettre", + "I18N_INTERACTIONS_PENCILCODEEDITOR_ARE_YOU_SURE_YOU_WANT_TO_RESET_YOUR_CODE": "Êtes-vous sûr de vouloir réinitialiser votre code ?", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "Annuler", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CONFIRMATION_REQUIRED": "Confirmation demandée", + "I18N_INTERACTIONS_PENCILCODEEDITOR_RESET_CODE": "Effacer le code", "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "Ajouter l’élément", "I18N_INTERACTIONS_SET_INPUT_DUPLICATES_ERROR": "Oups, il semble que votre ensemble a des doublons !", "I18N_INTERACTIONS_SET_INPUT_EMPTY_SET": "(Ajouter un élément par ligne.)", diff --git a/assets/i18n/hi.json b/assets/i18n/hi.json index 42f9487c8733..bd819818cf9e 100644 --- a/assets/i18n/hi.json +++ b/assets/i18n/hi.json @@ -87,6 +87,10 @@ "I18N_INTERACTIONS_MUSIC_PLAY": "चलाएँ", "I18N_INTERACTIONS_MUSIC_PLAY_SEQUENCE": "लक्ष्य अनुक्रम चलाएँ", "I18N_INTERACTIONS_MUSIC_SUBMIT": "जमा करें", + "I18N_INTERACTIONS_PENCILCODEEDITOR_ARE_YOU_SURE_YOU_WANT_TO_RESET_YOUR_CODE": "क्या आप वाकई कोड रीसेट करना चाहते हैं", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "नहींं", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CONFIRMATION_REQUIRED": "पुष्टिकरण आवश्यक", + "I18N_INTERACTIONS_PENCILCODEEDITOR_RESET_CODE": "रीसेट", "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "जोडें", "I18N_INTERACTIONS_SET_INPUT_DUPLICATES_ERROR": "ओह, यह लग रहा है की आपके सेट में कुछ समान वस्तुएं है !", "I18N_INTERACTIONS_SET_INPUT_EMPTY_SET": "(प्रति लाइन एक आइटम जोड़े। )", diff --git a/assets/i18n/it.json b/assets/i18n/it.json index 462866ed0d10..c6c186179959 100644 --- a/assets/i18n/it.json +++ b/assets/i18n/it.json @@ -41,6 +41,7 @@ "I18N_INTERACTIONS_MUSIC_CLEAR": "Pulisci", "I18N_INTERACTIONS_MUSIC_PLAY": "Riproduci", "I18N_INTERACTIONS_MUSIC_SUBMIT": "Invia", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "Annulla", "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "Aggiungi elemento", "I18N_INTERACTIONS_SET_INPUT_EMPTY_SET": "(Aggiungi un elemento per riga.)", "I18N_INTERACTIONS_SET_INPUT_SUBMIT": "Invia", diff --git a/assets/i18n/ko.json b/assets/i18n/ko.json index ea025642b00b..b3417749474e 100644 --- a/assets/i18n/ko.json +++ b/assets/i18n/ko.json @@ -75,6 +75,8 @@ "I18N_INTERACTIONS_MUSIC_CLEAR": "지우기", "I18N_INTERACTIONS_MUSIC_PLAY": "재생", "I18N_INTERACTIONS_MUSIC_SUBMIT": "제출", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "취소", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CONFIRMATION_REQUIRED": "확인 필요", "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "항목 추가", "I18N_INTERACTIONS_SET_INPUT_NO_ANSWER": "정답을 입력하지 않았습니다.", "I18N_INTERACTIONS_SET_INPUT_SUBMIT": "제출", diff --git a/assets/i18n/lb.json b/assets/i18n/lb.json index 2dfe44816d5d..7dbf9130c745 100644 --- a/assets/i18n/lb.json +++ b/assets/i18n/lb.json @@ -5,6 +5,7 @@ "I18N_CREATE_NO_THANKS": "Nee, Merci!", "I18N_CREATE_YES_PLEASE": "Jo, w.e.g.!", "I18N_DASHBOARD_COLLECTIONS": "Sammlungen", + "I18N_DASHBOARD_EXPLORATIONS": "Erfuerschungen", "I18N_DASHBOARD_SUBSCRIBERS": "Abonnenten", "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "Bild heihin zéien", "I18N_DIRECTIVES_OR_SELECT_FILE": "oder sich e Biller-Fichier eraus:", @@ -38,6 +39,9 @@ "I18N_INTERACTIONS_LOGIC_PROOF_POSSIBLE_LINES": "Méiglech Linnen", "I18N_INTERACTIONS_MUSIC_CLEAR": "Eidel maachen", "I18N_INTERACTIONS_MUSIC_PLAY": "Ofspillen", + "I18N_INTERACTIONS_PENCILCODEEDITOR_ARE_YOU_SURE_YOU_WANT_TO_RESET_YOUR_CODE": "Sidd Dir sécher datt Dir Äre Code zrécksetze wëllt?", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "Ofbriechen", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CONFIRMATION_REQUIRED": "Confirmatioun erfuerdert", "I18N_INTERACTIONS_SET_INPUT_NO_ANSWER": "Keng Äntwert ginn.", "I18N_LIBRARY_ALL_CATEGORIES": "All Kategorien", "I18N_LIBRARY_ALL_LANGUAGES": "All Sproochen", @@ -154,5 +158,6 @@ "I18N_TOPNAV_NOTIFICATIONS": "Notifikatiounen", "I18N_TOPNAV_PREFERENCES": "Astellungen", "I18N_TOPNAV_SIGN_IN": "Aloggen", + "I18N_TOTAL_SUBSCRIBERS_TEXT": "Dir hutt am Ganzen <[totalSubscribers]> Abonnenten.", "I18N_UNSUBSCRIBE_BUTTON_TEXT": "Ofbestellen" } diff --git a/assets/i18n/lt.json b/assets/i18n/lt.json index 307243e25ceb..01a489c28d8f 100644 --- a/assets/i18n/lt.json +++ b/assets/i18n/lt.json @@ -61,6 +61,8 @@ "I18N_INTERACTIONS_MUSIC_CLEAR": "Išvalyti", "I18N_INTERACTIONS_MUSIC_PLAY": "Groti", "I18N_INTERACTIONS_MUSIC_SUBMIT": "Siųsti", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "Atšaukti", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CONFIRMATION_REQUIRED": "Reikalingas Patvirtinimas", "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "Pridėti elementą", "I18N_INTERACTIONS_SET_INPUT_DUPLICATES_ERROR": "Oi, atrodo, kad jūsų rinkinys turi dublikatų!", "I18N_INTERACTIONS_SET_INPUT_EMPTY_SET": "(Pridėkite vieną elementą vienoje eilutėje.)", diff --git a/assets/i18n/mk.json b/assets/i18n/mk.json index 0c1ad42e805f..e943fbe7f283 100644 --- a/assets/i18n/mk.json +++ b/assets/i18n/mk.json @@ -87,6 +87,10 @@ "I18N_INTERACTIONS_MUSIC_PLAY": "Пушти", "I18N_INTERACTIONS_MUSIC_PLAY_SEQUENCE": "Пушти ја целната низа", "I18N_INTERACTIONS_MUSIC_SUBMIT": "Поднеси", + "I18N_INTERACTIONS_PENCILCODEEDITOR_ARE_YOU_SURE_YOU_WANT_TO_RESET_YOUR_CODE": "Дали сте сигурни дека сакате да го вратите кодот по основно?", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "Откажи", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CONFIRMATION_REQUIRED": "Се бара потврда", + "I18N_INTERACTIONS_PENCILCODEEDITOR_RESET_CODE": "Врати го кодот", "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "Додај ставка", "I18N_INTERACTIONS_SET_INPUT_DUPLICATES_ERROR": "Се чини дека вашиот збир има дупликати!", "I18N_INTERACTIONS_SET_INPUT_EMPTY_SET": "(Ставајте по една ставка во секој нов ред.)", diff --git a/assets/i18n/ne.json b/assets/i18n/ne.json new file mode 100644 index 000000000000..3d534b7fd25f --- /dev/null +++ b/assets/i18n/ne.json @@ -0,0 +1,163 @@ +{ + "I18N_ABOUT_PAGE_TITLE": "हाम्रप बारेमा - Oppia", + "I18N_ACTION_APPLY_TO_TEACH_WITH_OPPIA": "Oppia को साथमा पढाउनको लागि आवेदन दिनुहोस्", + "I18N_ACTION_BROWSE_EXPLORATIONS": "हाम्रो उपलब्धिहरू हेर्नुहोस्", + "I18N_ACTION_BROWSE_LESSONS": "हाम्रा पाठहरू हेर्नुहोस्", + "I18N_ACTION_CREATE_EXPLORATION": "अन्वेषण निर्माण गर्नुहोस्", + "I18N_ACTION_CREATE_LESSON": "तपाईँको आफ्नै पाठ निर्माण गर्नुहोस्", + "I18N_CREATE_ACTIVITY_QUESTION": "के बनाउन चाहनुहुन्छ?", + "I18N_CREATE_ACTIVITY_TITLE": "क्रियाकलाप बनाउनुहोस्", + "I18N_CREATE_COLLECTION": "सङ्ग्रह बनाउनुहोस्", + "I18N_CREATE_EXPLORATION": "अन्वेषण निर्माण गर्नुहोस्", + "I18N_CREATE_EXPLORATION_CREATE": "बनाउनुहोस्", + "I18N_CREATE_EXPLORATION_QUESTION": "के तपाईँ अन्वेषण निर्माण गर्न चाहनुहुन्छ?", + "I18N_CREATE_EXPLORATION_TITLE": "अन्वेषण निर्माण गर्नुहोस्", + "I18N_CREATE_EXPLORATION_UPLOAD": "अपलोड गर्ने", + "I18N_CREATE_NO_THANKS": "हैन पर्दैन", + "I18N_CREATE_YES_PLEASE": "हो, धन्यवाद!", + "I18N_DASHBOARD_COLLECTIONS": "सङ्ग्रहहरू", + "I18N_DASHBOARD_EXPLORATIONS": "अन्वेषण", + "I18N_ERROR_DISABLED_EXPLORATION_MESSAGE": "Sorry, but the exploration you clicked on is currently disabled. कृपया केही समय पछि पुनः प्रयास गर्नुहोस् ।", + "I18N_ERROR_MESSAGE_500": "Something went horribly wrong. But it wasn't your fault. एउटा आन्तरिक त्रुटि रहन गयो ।", + "I18N_ERROR_NEXT_STEPS": "The best thing to do now is probably to return to the \">home page. However, if this issue recurs, and you think it shouldn't, please let us know on our \" target=\"_blank\">issue tracker. यसको लागि माफ गर्नुहोस्।", + "I18N_ERROR_PAGE_TITLE_400": "त्रुटी 400 - Oppia", + "I18N_ERROR_PAGE_TITLE_401": "त्रुटी 401 - Oppia", + "I18N_ERROR_PAGE_TITLE_404": "त्रुटी 404 - Oppia", + "I18N_ERROR_PAGE_TITLE_500": "त्रुटी 500 - Oppia", + "I18N_FOOTER_ABOUT": "बारेमा", + "I18N_FOOTER_ABOUT_ALL_CAPS": "OPPIA को बारेमा", + "I18N_FOOTER_AUTHOR_PROFILES": "लेखब प्रोफाइलहरू", + "I18N_FOOTER_BROWSE_LIBRARY": "पुस्तकालय हेर्नुहोस", + "I18N_FOOTER_CONTACT_US": "हामीलाई सम्पर्क गर्नुहोस्", + "I18N_FOOTER_CONTRIBUTE_ALL_CAPS": "योगदान गर्नुहोस्", + "I18N_FOOTER_CREDITS": "श्रेयहरू", + "I18N_FOOTER_DONATE": "दान गर्नेDonate", + "I18N_FOOTER_FOLLOW_US": "हामीलाई पछ्याउनुहोस्", + "I18N_FOOTER_FORUM": "फोरम", + "I18N_FOOTER_GET_INVOLVED": "लागि पर्नुहोस्", + "I18N_FOOTER_GET_STARTED": "सुरु गर्नुहोस्", + "I18N_FOOTER_OPPIA_FOUNDATION": "Oppia फाउन्डेसन", + "I18N_FOOTER_PARTICIPATION_PLAYBOOK": "सहभागिता निर्देशिका", + "I18N_FOOTER_PRIVACY_POLICY": "गोपनीयता नीति", + "I18N_FOOTER_TEACH": "Oppia को साथमा पढाउनुहोस्", + "I18N_FOOTER_TEACH_LEARN_ALL_CAPS": "पढाउनुहोस्/पढ्नुहोस्", + "I18N_FOOTER_TERMS_OF_SERVICE": "सेवाको सर्तहरू", + "I18N_FORMS_TYPE_NUMBER": "नम्बर लेक्नुहोस्", + "I18N_FORMS_TYPE_NUMBER_AT_LEAST": "नम्बर कम्तिमा <[minValue]> लेख्नुहोस् ।", + "I18N_FORMS_TYPE_NUMBER_AT_MOST": "बढीमा <[maxValue]> नम्बर लेख्नुहोस्", + "I18N_FORMS_TYPE_NUMBER_INVALID_DECIMAL": "कृपया मान्य दसमलव नम्बर लेख्नुहोस्", + "I18N_GET_STARTED_PAGE_TITLE": "सुरु गर्नुहोस्", + "I18N_INTERACTIONS_GRAPH_ADD_EDGE": "अन्त थप्नुहोस्", + "I18N_INTERACTIONS_GRAPH_ADD_NODE": "नोड थप्नुहोस्", + "I18N_INTERACTIONS_GRAPH_DELETE": "हटाउनुहोस्", + "I18N_INTERACTIONS_GRAPH_ERROR_INVALID": "अमान्य ग्राफ!", + "I18N_INTERACTIONS_GRAPH_MOVE": "लैजानुहोस्", + "I18N_INTERACTIONS_GRAPH_RESET_BUTTON": "रिसेट गर्नुहोस्", + "I18N_INTERACTIONS_GRAPH_RESPONSE_EDGE": "<[edges]> अन्त", + "I18N_INTERACTIONS_GRAPH_RESPONSE_EDGES": "<[edges]> अन्तहरू", + "I18N_INTERACTIONS_GRAPH_RESPONSE_VERTEX": "र <[vertices]> शीर्ष", + "I18N_INTERACTIONS_GRAPH_RESPONSE_VERTICES": "र <[vertices]> शीर्षहरू", + "I18N_INTERACTIONS_GRAPH_SUBMIT_BUTTON": "पेश गर्नुहोस्", + "I18N_INTERACTIONS_GRAPH_UPDATE_LABEL": "लेबल अद्यावधिक गर्नुहोस्", + "I18N_INTERACTIONS_ITEM_SELECTION_SUBMIT": "पेश गर्नुहोस्", + "I18N_INTERACTIONS_LOGIC_PROOF_SUBMIT": "पेश गर्नुहोस्", + "I18N_INTERACTIONS_MUSIC_CLEAR": "खाली गर्नुहोस्", + "I18N_INTERACTIONS_MUSIC_PLAY": "बजाउनुहोस्", + "I18N_INTERACTIONS_MUSIC_SUBMIT": "पेश गर्नुहोस्", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "रद्द गर्नुहोस्", + "I18N_INTERACTIONS_SET_INPUT_SUBMIT": "पेश गर्नुहोस्", + "I18N_LIBRARY_CATEGORIES_BUSINESS": "ब्यवसाय", + "I18N_LIBRARY_CATEGORIES_COMPUTING": "गणन", + "I18N_LIBRARY_CATEGORIES_EDUCATION": "शिक्षा", + "I18N_LIBRARY_CATEGORIES_ENGINEERING": "इन्जिनीयरिङ", + "I18N_LIBRARY_CATEGORIES_ENGLISH": "नेपाली", + "I18N_LIBRARY_CATEGORIES_ENVIRONMENT": "वातावरण", + "I18N_LIBRARY_CATEGORIES_GEOGRAPHY": "भूगोल", + "I18N_LIBRARY_CATEGORIES_GOVERNMENT": "सरकार", + "I18N_LIBRARY_CATEGORIES_HISTORY": "इतिहास", + "I18N_LIBRARY_CATEGORIES_HOBBIES": "सोखहरू", + "I18N_LIBRARY_CATEGORIES_LANGUAGES": "भाषाहरू", + "I18N_LIBRARY_CATEGORIES_LAW": "कानुन", + "I18N_LIBRARY_CATEGORIES_LIFE_SKILLS": "जीवन कला", + "I18N_LIBRARY_CATEGORIES_MATHEMATICS": "गणित", + "I18N_LIBRARY_CATEGORIES_MATHS": "गणित", + "I18N_LIBRARY_CATEGORIES_MEDICINE": "चिकित्साशास्त्र", + "I18N_LIBRARY_CATEGORIES_MUSIC": "सङ्गित", + "I18N_LIBRARY_CATEGORIES_PHILOSOPHY": "दर्शन", + "I18N_LIBRARY_CATEGORIES_PHYSICS": "भौतिकशास्त्र", + "I18N_LIBRARY_CATEGORIES_PROGRAMMING": "प्रोग्रामिङ्ग", + "I18N_LIBRARY_CATEGORIES_PSYCHOLOGY": "मनोविज्ञान", + "I18N_LIBRARY_CATEGORIES_PUZZLES": "पजलहरू", + "I18N_LIBRARY_CATEGORIES_READING": "अध्ययन", + "I18N_LIBRARY_CATEGORIES_RELIGION": "धर्मधर्म", + "I18N_LIBRARY_CATEGORIES_SPORT": "खेलकुद", + "I18N_LIBRARY_CATEGORIES_STATISTICS": "तथ्याङ्क", + "I18N_LIBRARY_CATEGORIES_TEST": "परीक्षण", + "I18N_LIBRARY_CATEGORIES_WELCOME": "स्वागतम्", + "I18N_LIBRARY_CREATE_EXPLORATION_QUESTION": "के सिर्जना गर्न चाहनुहुन्छ?", + "I18N_LIBRARY_GROUPS_COMPUTING": "गणन", + "I18N_LIBRARY_GROUPS_FEATURED_ACTIVITIES": "छानिएको क्रियाकलाप", + "I18N_LIBRARY_GROUPS_HUMANITIES": "मानविकी", + "I18N_LIBRARY_GROUPS_LANGUAGES": "भाषाहरू", + "I18N_LIBRARY_LOADING": "लोड हुँदैछ", + "I18N_LIBRARY_VIEWS_TOOLTIP": "दृश्यहरू", + "I18N_PLAYER_BACK": "पछाडि", + "I18N_PLAYER_CONTINUE_BUTTON": "जारी राख्नुहोस्", + "I18N_PLAYER_CONTRIBUTORS_TOOLTIP": "योगदानकर्ताहरू", + "I18N_PLAYER_EDIT_TOOLTIP": "सम्पादन", + "I18N_PLAYER_EMBED_TOOLTIP": "सम्मिलित", + "I18N_PLAYER_FEEDBACK_TOOLTIP": "प्रतिक्रिया", + "I18N_PLAYER_FORWARD": "अगाडि", + "I18N_PLAYER_INFO_TOOLTIP": "सूचना", + "I18N_PLAYER_LAST_UPDATED_TOOLTIP": "अन्तिममा अद्यावधिक भएको", + "I18N_PLAYER_LOADING": "लोड हुदैछ...", + "I18N_PLAYER_REPORT_MODAL_BODY_OTHER": "अन्य", + "I18N_PLAYER_REPORT_MODAL_FOOTER_CANCEL": "रद्द गर्नुहोस्", + "I18N_PLAYER_REPORT_MODAL_FOOTER_SUBMIT": "पेश गर्नुहोस्", + "I18N_PLAYER_REPORT_SUCCESS_MODAL_CLOSE": "बन्द गर्नुहोस्", + "I18N_PLAYER_REPORT_SUCCESS_MODAL_HEADER": "धन्यवाद!", + "I18N_PLAYER_SUBMIT_BUTTON": "पेश गर्नुहोस्", + "I18N_PLAYER_TAGS_TOOLTIP": "ट्यागहरू", + "I18N_PLAYER_VIEWS_TOOLTIP": "दृश्यहरू", + "I18N_PREFERENCES_BREADCRUMB": "प्राथमिकताहरू", + "I18N_PREFERENCES_CANCEL_BUTTON": "रद्द गर्नुहोस्", + "I18N_PREFERENCES_CHANGE_PICTURE": "प्रोफाइल तस्वीर परिवर्तन गर्नुहोस्", + "I18N_PREFERENCES_EMAIL": "इमेल", + "I18N_PREFERENCES_HEADING": "प्राथमिकताहरू", + "I18N_PREFERENCES_PICTURE": "तस्वीर", + "I18N_PREFERENCES_USERNAME": "प्रयोगकर्ताको नाम", + "I18N_SIDEBAR_ABOUT_LINK": "बारेमा", + "I18N_SIDEBAR_BLOG": "ब्लग", + "I18N_SIDEBAR_CONTACT_US": "हामीलाई सम्पर्क गर्नुहोस्", + "I18N_SIDEBAR_DONATE": "दान गर्नेDonate", + "I18N_SIDEBAR_FORUM": "फोरम", + "I18N_SIDEBAR_GET_STARTED": "सुरु गर्नुहोस्", + "I18N_SIDEBAR_LIBRARY_LINK": "पुस्तकालय", + "I18N_SIDEBAR_TEACH_WITH_OPPIA": "Oppia को साथमा पढाउनुहोस्", + "I18N_SIGNUP_CLOSE_BUTTON": "बन्द गर्नुहोस्", + "I18N_SIGNUP_EMAIL": "इमेल", + "I18N_SIGNUP_LOADING": "लोड हुँदैछ", + "I18N_SIGNUP_REGISTRATION": "दर्ता", + "I18N_SIGNUP_USERNAME": "प्रयोगकर्ताको नाम", + "I18N_SPLASH_JAVASCRIPT_ERROR_THANKS": "धन्यवाद ।", + "I18N_SPLASH_TITLE": "पुस्तक भन्दा बाहिर सोच्नुहोस्।", + "I18N_SUBSCRIBE_BUTTON_TEXT": "सवस्क्रिप्ट", + "I18N_TOPNAV_ABOUT": "बारेमा", + "I18N_TOPNAV_ABOUT_OPPIA": "Oppia को बारेमा", + "I18N_TOPNAV_ADMIN_PAGE": "व्यवस्थापन पृष्ठ", + "I18N_TOPNAV_BLOG": "ब्लग", + "I18N_TOPNAV_CONTACT_US": "हामीलाई सम्पर्क गर्नुहोस्", + "I18N_TOPNAV_DASHBOARD": "सर्जक ड्यसबोर्ड", + "I18N_TOPNAV_DONATE": "दान गर्नेDonate", + "I18N_TOPNAV_FORUM": "फोरम", + "I18N_TOPNAV_GET_STARTED": "सुरु गर्नुहोस्", + "I18N_TOPNAV_LIBRARY": "पुस्तकालय", + "I18N_TOPNAV_LOGOUT": "लगआउट", + "I18N_TOPNAV_MODERATOR_PAGE": "पृष्ठ व्यवस्थापन गर्नुहोस्", + "I18N_TOPNAV_NOTIFICATIONS": "सूचनाहरू", + "I18N_TOPNAV_PREFERENCES": "प्राथमिकताहरू", + "I18N_TOPNAV_SIGN_IN": "साइन इन", + "I18N_TOPNAV_TEACH_WITH_OPPIA": "Oppia को साथमा पढाउनुहोस्", + "I18N_TOTAL_SUBSCRIBERS_TEXT": "तपाईँका कूल <[totalSubscribers]> सदस्यहरू छन्।", + "I18N_UNSUBSCRIBE_BUTTON_TEXT": "सदस्यता खारेज गर्नुहोस्" +} diff --git a/assets/i18n/qqq.json b/assets/i18n/qqq.json index 8869357fae87..6d81fea9a777 100644 --- a/assets/i18n/qqq.json +++ b/assets/i18n/qqq.json @@ -87,6 +87,10 @@ "I18N_INTERACTIONS_MUSIC_PLAY": "Text displayed in a music interaction. - This text is shown inside a button. When the user clicks the button, the sequences of notes the user has introduced is reproduced.\n{{Identical|Play}}", "I18N_INTERACTIONS_MUSIC_PLAY_SEQUENCE": "Text displayed in a music interaction. - This text is shown inside a button. When the user clicks the button, the sequences of notes the user has to copy is reproduced.", "I18N_INTERACTIONS_MUSIC_SUBMIT": "Text displayed in a music interaction. - The text is shown inside a button. When the user clicks the button, the system checks if the answer is correct and continues to the following action.\n{{Identical|Submit}}", + "I18N_INTERACTIONS_PENCILCODEEDITOR_ARE_YOU_SURE_YOU_WANT_TO_RESET_YOUR_CODE": "This text is displayed in pencil code editor interaction - This text is shown when user clicks on the reset button. This is body of the modal and asks the user whether they really want to proceed with the action", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "Text displayed in a pencil code editor interaction - This text is shown when user clicks on reset code button. If user clicks this button in the modal then it cancels user's current action\n{{Identical|Cancel}}", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CONFIRMATION_REQUIRED": "Text displayed in pencil code editor interaction - This text is shown when user clicks on the reset code button. This is title of a modal asking for confirmation of user's action", + "I18N_INTERACTIONS_PENCILCODEEDITOR_RESET_CODE": "Text displayed in a pencil code editor interaction - This text is shown when user clicks on reset code button. If user clicks this button, the code editor is reset to its initial state", "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "Text displayed in a set interaction. - This text is shown inside a button. When the user clicks the button, a new line to introduce elements to the set appears above the button.", "I18N_INTERACTIONS_SET_INPUT_DUPLICATES_ERROR": "Text displayed in a set interaction. - This text is shown in red when the user tries to submit a set that has duplicate elements.", "I18N_INTERACTIONS_SET_INPUT_EMPTY_SET": "Text displayed in a set interaction. - The user is asked to introduce items of a set described in the problem. This text indicates that each one of those items has to be written is a separate line. It's shown when the user has not entered any item yet.", @@ -283,7 +287,7 @@ "I18N_SPLASH_THIRD_EXPLORATION_DESCRIPTION": "Paragraph describing Oppia showed in the splash page next to an image.", "I18N_SPLASH_TITLE": "The main title shown in Oppia's landing page - This is an English idiom, it can be changed for a local common saying with similar meaning. It should not exceed one line in length.", "I18N_SUBSCRIBE_BUTTON_TEXT": "The text that appears on the subscribe button, which allows users to subscribe to creators.\n{{Identical|Subscribe}}", - "I18N_TOPNAV_ABOUT": "Text displayed in the navigation bar. - When the user hovers over the button, a list with several information pages is displayed.\n{{Identical|Cancel}}", + "I18N_TOPNAV_ABOUT": "Text displayed in the navigation bar. - When the user hovers over the button, a list with several information pages is displayed.\n{{Identical|About}}", "I18N_TOPNAV_ABOUT_OPPIA": "Text displayed inside drop-down menu - The list is shown after the user hovers over the I18N_TOPNAV_ABOUT button. When the option is clicked, the about oppia page is loaded which contains a more general description of the site.", "I18N_TOPNAV_ADMIN_PAGE": "Text displayed inside a drop-down menu. - The list is shown after the user hovers over their profile picture. When the option is clicked, the admin page is loaded.", "I18N_TOPNAV_BLOG": "Text displayed inside a drop-down menu. - The list is shown after the user hovers over the I18N_TOPNAV_ABOUT button. When the option is clicked, the Oppia blog is loaded.\n{{Identical|Blog}}", diff --git a/assets/i18n/ru.json b/assets/i18n/ru.json index 425b59720230..65522a79aa85 100644 --- a/assets/i18n/ru.json +++ b/assets/i18n/ru.json @@ -44,6 +44,9 @@ "I18N_INTERACTIONS_LOGIC_PROOF_SUBMIT": "Отправить", "I18N_INTERACTIONS_MUSIC_CLEAR": "Очистить", "I18N_INTERACTIONS_MUSIC_SUBMIT": "Отправить", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "Отмена", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CONFIRMATION_REQUIRED": "Требуется подтверждение", + "I18N_INTERACTIONS_PENCILCODEEDITOR_RESET_CODE": "Код сброса", "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "Добавить элемент", "I18N_INTERACTIONS_SET_INPUT_SUBMIT": "Отправить", "I18N_LIBRARY_ALL_CATEGORIES": "Все категории", @@ -164,5 +167,6 @@ "I18N_TOPNAV_PREFERENCES": "Настройки", "I18N_TOPNAV_SIGN_IN": "Войти", "I18N_TOPNAV_TEACH_WITH_OPPIA": "Учить с Oppia", + "I18N_TOTAL_SUBSCRIBERS_TEXT": "У Вас <[totalSubscribers]> подписчиков.", "I18N_UNSUBSCRIBE_BUTTON_TEXT": "Отписаться" } diff --git a/assets/i18n/sv.json b/assets/i18n/sv.json index 6ac4f21353a4..3f0d643de89b 100644 --- a/assets/i18n/sv.json +++ b/assets/i18n/sv.json @@ -10,6 +10,7 @@ "I18N_CREATE_NO_THANKS": "Nej, tack", "I18N_CREATE_YES_PLEASE": "Ja, tack!", "I18N_DASHBOARD_COLLECTIONS": "Samlingar", + "I18N_DASHBOARD_EXPLORATIONS": "Utforskande", "I18N_DASHBOARD_OPEN_FEEDBACK": "Skicka återkoppling", "I18N_DASHBOARD_SUBSCRIBERS": "Prenumeranter", "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "Dra bild hit", @@ -61,6 +62,10 @@ "I18N_INTERACTIONS_MUSIC_PLAY": "Spela upp", "I18N_INTERACTIONS_MUSIC_PLAY_SEQUENCE": "Spela upp målsekvens", "I18N_INTERACTIONS_MUSIC_SUBMIT": "Skicka", + "I18N_INTERACTIONS_PENCILCODEEDITOR_ARE_YOU_SURE_YOU_WANT_TO_RESET_YOUR_CODE": "Är du säker på att du vill återställa din kod?", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "Avbryt", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CONFIRMATION_REQUIRED": "Bekräftelse krävs", + "I18N_INTERACTIONS_PENCILCODEEDITOR_RESET_CODE": "Återställ kod", "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "Lägg till objekt", "I18N_INTERACTIONS_SET_INPUT_NO_ANSWER": "Inget angivet svar.", "I18N_INTERACTIONS_SET_INPUT_SUBMIT": "Skicka", @@ -118,6 +123,7 @@ "I18N_LIBRARY_SEARCH_PLACEHOLDER": "Vad är du nyfiken på?", "I18N_LIBRARY_VIEWS_TOOLTIP": "Visningar", "I18N_LIBRARY_VIEW_ALL": "Visa alla", + "I18N_ONE_SUBSCRIBER_TEXT": "Du har 1 prenumerant.", "I18N_PLAYER_BACK": "Tillbaka", "I18N_PLAYER_CARD_NUMBER_TOOLTIP": "Kortnummer", "I18N_PLAYER_CONTINUE_BUTTON": "Fortsätt", diff --git a/assets/i18n/tr.json b/assets/i18n/tr.json index 6e5137fd20d4..fc15c5558767 100644 --- a/assets/i18n/tr.json +++ b/assets/i18n/tr.json @@ -18,6 +18,7 @@ "I18N_DASHBOARD_COLLECTIONS": "Koleksiyonlarım", "I18N_DASHBOARD_EXPLORATIONS": "Konu Anlatımlarım", "I18N_DASHBOARD_OPEN_FEEDBACK": "Dönütleri aç", + "I18N_DASHBOARD_SUBSCRIBERS": "Aboneler", "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "Resmi buraya sürükleyin", "I18N_DIRECTIVES_OR_SELECT_FILE": "veya bir resim dosyası seçin", "I18N_ERROR_DISABLED_EXPLORATION": "Kapalı Konu Anlatımı", @@ -86,6 +87,10 @@ "I18N_INTERACTIONS_MUSIC_PLAY": "Oynat", "I18N_INTERACTIONS_MUSIC_PLAY_SEQUENCE": "Hedef Diziyi Oynat", "I18N_INTERACTIONS_MUSIC_SUBMIT": "Gönder", + "I18N_INTERACTIONS_PENCILCODEEDITOR_ARE_YOU_SURE_YOU_WANT_TO_RESET_YOUR_CODE": "Kodunuzu sıfırlamak istediğinizden emin misiniz?", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "İptal", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CONFIRMATION_REQUIRED": "Onay Gerekiyor", + "I18N_INTERACTIONS_PENCILCODEEDITOR_RESET_CODE": "Kodu Sıfırla", "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "Madde ekle", "I18N_INTERACTIONS_SET_INPUT_DUPLICATES_ERROR": "Eyvah, setinde kopyalar var gibi gözüküyor!", "I18N_INTERACTIONS_SET_INPUT_EMPTY_SET": "(Her satıra bir madde ekle.)", @@ -158,6 +163,7 @@ "I18N_LIBRARY_SUB_HEADER": "Konu anlatımlarını inceleyerek macerana başla.", "I18N_LIBRARY_VIEWS_TOOLTIP": "Görüntülenme", "I18N_LIBRARY_VIEW_ALL": "Hepsini görüntüle", + "I18N_ONE_SUBSCRIBER_TEXT": "1 abonen var.", "I18N_PLAYER_BACK": "Geri", "I18N_PLAYER_CARD_NUMBER_TOOLTIP": "Kart #", "I18N_PLAYER_COMMUNITY_EDITABLE_TOOLTIP": "Birlikte düzenlenebilir", @@ -297,5 +303,6 @@ "I18N_TOPNAV_PREFERENCES": "Tercihler", "I18N_TOPNAV_SIGN_IN": "Gİrİş Yap", "I18N_TOPNAV_TEACH_WITH_OPPIA": "Oppia ile Öğret", + "I18N_TOTAL_SUBSCRIBERS_TEXT": "Toplam <[totalSubscribers]> aboneniz var.", "I18N_UNSUBSCRIBE_BUTTON_TEXT": "Abonelikten çık" } diff --git a/assets/i18n/zh-hans.json b/assets/i18n/zh-hans.json index b09f925f4c93..0b3c7485a50b 100644 --- a/assets/i18n/zh-hans.json +++ b/assets/i18n/zh-hans.json @@ -87,6 +87,10 @@ "I18N_INTERACTIONS_MUSIC_PLAY": "播放", "I18N_INTERACTIONS_MUSIC_PLAY_SEQUENCE": "播放目标序列", "I18N_INTERACTIONS_MUSIC_SUBMIT": "提交", + "I18N_INTERACTIONS_PENCILCODEEDITOR_ARE_YOU_SURE_YOU_WANT_TO_RESET_YOUR_CODE": "您确定要重置您的代码么?", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "取消", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CONFIRMATION_REQUIRED": "需要确认", + "I18N_INTERACTIONS_PENCILCODEEDITOR_RESET_CODE": "重置代码", "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "添加项目", "I18N_INTERACTIONS_SET_INPUT_DUPLICATES_ERROR": "天哪,看起来您的集存在重复项!", "I18N_INTERACTIONS_SET_INPUT_EMPTY_SET": "(每行添加一个项目。)", diff --git a/assets/i18n/zh-hant.json b/assets/i18n/zh-hant.json index 3ec40b856976..c6f2be496e9d 100644 --- a/assets/i18n/zh-hant.json +++ b/assets/i18n/zh-hant.json @@ -87,6 +87,10 @@ "I18N_INTERACTIONS_MUSIC_PLAY": "播放", "I18N_INTERACTIONS_MUSIC_PLAY_SEQUENCE": "播放目標序列", "I18N_INTERACTIONS_MUSIC_SUBMIT": "提交", + "I18N_INTERACTIONS_PENCILCODEEDITOR_ARE_YOU_SURE_YOU_WANT_TO_RESET_YOUR_CODE": "您確定您要重設您的代碼?", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "取消", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CONFIRMATION_REQUIRED": "需要確認", + "I18N_INTERACTIONS_PENCILCODEEDITOR_RESET_CODE": "重設代碼", "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "添加項目", "I18N_INTERACTIONS_SET_INPUT_DUPLICATES_ERROR": "阿咧,看起來您的內容存在重複項!", "I18N_INTERACTIONS_SET_INPUT_EMPTY_SET": "(每行添加一個項目。)", From f09f22a7f53c734eeff321b85167b8cd5ed9d3a9 Mon Sep 17 00:00:00 2001 From: Piyush Agrawal Date: Tue, 7 Mar 2017 23:27:02 +0530 Subject: [PATCH 030/173] Fixed the alignment of the submit button in teachOppiaModal --- extensions/interactions/TextInput/TextInput.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/interactions/TextInput/TextInput.html b/extensions/interactions/TextInput/TextInput.html index 12f6e9f824ee..6d2d82060a61 100644 --- a/extensions/interactions/TextInput/TextInput.html +++ b/extensions/interactions/TextInput/TextInput.html @@ -8,7 +8,7 @@ - From 56e6733a5de6d90b4cf29a7602776269fa1e0a81 Mon Sep 17 00:00:00 2001 From: Jordan Stapinski Date: Wed, 8 Mar 2017 02:47:58 -0500 Subject: [PATCH 031/173] Fix #3122 : Number Fields Bring Up Numeric Mobile Keyboards (#3167) * changed type for shcema based float editor directive * added number input, verified with android * verified number issue on ios * Removed extra webtest zip file --- .../schema_editors/schema_based_float_editor_directive.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/templates/dev/head/components/forms/schema_editors/schema_based_float_editor_directive.html b/core/templates/dev/head/components/forms/schema_editors/schema_based_float_editor_directive.html index 1cf650287a06..7ad109589c00 100644 --- a/core/templates/dev/head/components/forms/schema_editors/schema_based_float_editor_directive.html +++ b/core/templates/dev/head/components/forms/schema_editors/schema_based_float_editor_directive.html @@ -2,7 +2,7 @@ - Date: Thu, 9 Mar 2017 02:28:53 +0530 Subject: [PATCH 032/173] addressed review comments --- core/domain/classifier_services.py | 21 ++++++++++++--------- core/domain/classifier_services_test.py | 19 +++++++++---------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/core/domain/classifier_services.py b/core/domain/classifier_services.py index cdc3057cba1a..347fc422f967 100644 --- a/core/domain/classifier_services.py +++ b/core/domain/classifier_services.py @@ -150,6 +150,9 @@ def get_classifier_by_id(classifier_id): Returns: classifier: Domain object for the classifier. + + Raises: + Exception: Entity for class ClassifierModel with id not found. """ classifier_model = classifier_models.ClassifierModel.get( classifier_id) @@ -168,12 +171,11 @@ def _create_classifier(classifier): classifier_id = classifier_models.ClassifierModel.create( classifier.exp_id, classifier.exp_version_when_created, classifier.state_name, classifier.algorithm_id, - classifier.cached_classifier_data, classifier.data_schema_version, - ) + classifier.cached_classifier_data, classifier.data_schema_version) return classifier_id -def _update_classifier(classifier_model, classifier): +def _update_classifier(classifier_model, state_name): """Updates classifier model in the datastore given a classifier domain object. @@ -181,16 +183,17 @@ def _update_classifier(classifier_model, classifier): classifier_model: Classifier model instance in datastore. classifier: Domain object for the classifier. - Note: Most of the properties of a classifier are immutable. - state_name is the only mutable property. + Note: All of the properties of a classifier are immutable, + except for state_name. """ - classifier_model.state_name = classifier.state_name + classifier_model.state_name = state_name classifier_model.put() def save_classifier(classifier): - """Checks if model exists and updates the classifier model using - _update_classifier method. + """Checks for the existence of the model. + If the model exists, it is updated using _update_classifier method. + If the model doesn't exist, it is created using _create_classifier method. Args: classifier: Domain object for the classifier. @@ -203,7 +206,7 @@ def save_classifier(classifier): if classifier_model is None: classifier.id = _create_classifier(classifier) else: - _update_classifier(classifier_model, classifier) + _update_classifier(classifier_model, classifier.state_name) return classifier.id diff --git a/core/domain/classifier_services_test.py b/core/domain/classifier_services_test.py index d30465f398eb..2d83aab9cbfe 100644 --- a/core/domain/classifier_services_test.py +++ b/core/domain/classifier_services_test.py @@ -16,6 +16,7 @@ import os +from core.domain import classifier_domain from core.domain import classifier_registry from core.domain import classifier_services from core.domain import exp_services @@ -27,7 +28,7 @@ (classifier_models,) = models.Registry.import_models([models.NAMES.classifier]) class ClassifierServicesTests(test_utils.GenericTestBase): - """Test classify using the sample explorations. + """Test "classify" using the sample explorations. Since the end to end tests cover correct classification, and frontend tests test hard rules, ReaderClassifyTests is only checking that the string @@ -107,6 +108,10 @@ def test_retrieval_of_classifiers(self): def test_deletion_of_classifiers(self): """Test the delete_classifier method.""" + with self.assertRaisesRegexp(Exception, ( + "Entity for class ClassifierModel with id fake_id not found")): + classifier_services.delete_classifier('fake_id') + exp_id = u'1' state = 'Home' classifier_id = classifier_models.ClassifierModel.create( @@ -124,15 +129,9 @@ def test_update_of_classifiers(self): exp_id = u'1' state = 'Home' test_state = 'State' - classifier = type('', (), {})() - classifier.id = '1' - classifier.exp_id = exp_id - classifier.exp_version_when_created = 1 - classifier.state_name = state - classifier.algorithm_id = ( - feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput']) - classifier.cached_classifier_data = [] - classifier.data_schema_version = 1 + classifier = classifier_domain.Classifier( + '1', exp_id, 1, state, + feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput'], [], 1) classifier_id = ( classifier_services.save_classifier(classifier)) classifier = classifier_services.get_classifier_by_id( From d94183ec107b9789a5b27ece2272236cbecf5ede Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Thu, 9 Mar 2017 02:30:13 +0530 Subject: [PATCH 033/173] lint issues --- core/domain/classifier_services.py | 2 +- core/domain/classifier_services_test.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/domain/classifier_services.py b/core/domain/classifier_services.py index 347fc422f967..6f245e9f2709 100644 --- a/core/domain/classifier_services.py +++ b/core/domain/classifier_services.py @@ -193,7 +193,7 @@ def _update_classifier(classifier_model, state_name): def save_classifier(classifier): """Checks for the existence of the model. If the model exists, it is updated using _update_classifier method. - If the model doesn't exist, it is created using _create_classifier method. + If the model doesn't exist, it is created using _create_classifier method. Args: classifier: Domain object for the classifier. diff --git a/core/domain/classifier_services_test.py b/core/domain/classifier_services_test.py index 2d83aab9cbfe..008ffe6d007f 100644 --- a/core/domain/classifier_services_test.py +++ b/core/domain/classifier_services_test.py @@ -130,8 +130,8 @@ def test_update_of_classifiers(self): state = 'Home' test_state = 'State' classifier = classifier_domain.Classifier( - '1', exp_id, 1, state, - feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput'], [], 1) + '1', exp_id, 1, state, + feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput'], [], 1) classifier_id = ( classifier_services.save_classifier(classifier)) classifier = classifier_services.get_classifier_by_id( From 6ad6c9cd278f3f40201ed2b264c3a3a56b316ce3 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 9 Mar 2017 08:16:17 +0100 Subject: [PATCH 034/173] Localisation updates from https://translatewiki.net. --- assets/i18n/zh-hant.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/i18n/zh-hant.json b/assets/i18n/zh-hant.json index c6f2be496e9d..3cf2806c5349 100644 --- a/assets/i18n/zh-hant.json +++ b/assets/i18n/zh-hant.json @@ -1,6 +1,6 @@ { "I18N_ABOUT_PAGE_TITLE": "關於我們 - Oppia", - "I18N_ACTION_APPLY_TO_TEACH_WITH_OPPIA": "申请通过Oppia教学", + "I18N_ACTION_APPLY_TO_TEACH_WITH_OPPIA": "申請以Oppia教學", "I18N_ACTION_BROWSE_EXPLORATIONS": "瀏覽我們的探索", "I18N_ACTION_BROWSE_LESSONS": "瀏覽我們的課程", "I18N_ACTION_CREATE_EXPLORATION": "創建一個探索", @@ -15,8 +15,8 @@ "I18N_CREATE_EXPLORATION_UPLOAD": "上傳", "I18N_CREATE_NO_THANKS": "不用了,謝謝", "I18N_CREATE_YES_PLEASE": "是的,謝謝!", - "I18N_DASHBOARD_COLLECTIONS": "采集", - "I18N_DASHBOARD_EXPLORATIONS": "寻求", + "I18N_DASHBOARD_COLLECTIONS": "收藏", + "I18N_DASHBOARD_EXPLORATIONS": "探索", "I18N_DASHBOARD_OPEN_FEEDBACK": "打開意見回饋", "I18N_DASHBOARD_SUBSCRIBERS": "訂閱者", "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "把圖片拖到此處", From baf05bea241a95f5524025b200f3bc7a52ea7621 Mon Sep 17 00:00:00 2001 From: Corey Date: Thu, 9 Mar 2017 01:52:53 -0800 Subject: [PATCH 035/173] Fix #2828: Add IE9 support for sidebar. (#3178) * Add IE9 support for sidebar. * Alphabetize/put prefixed style first. --- core/templates/dev/head/css/oppia.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/templates/dev/head/css/oppia.css b/core/templates/dev/head/css/oppia.css index f2330cbd1cc4..d29211ff696f 100644 --- a/core/templates/dev/head/css/oppia.css +++ b/core/templates/dev/head/css/oppia.css @@ -582,6 +582,7 @@ textarea { left: 0; position: fixed; top: 0; + -ms-transform: translate(-100%, 0); -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); width: 270px; @@ -686,6 +687,7 @@ textarea { .oppia-sidebar-menu-open .oppia-sidebar-menu { box-shadow: 1px 0 3px rgba(0,0,0,0.12), 1px 0 2px rgba(0,0,0,0.24); overflow-y: scroll; + -ms-transform: translate(0, 0); -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); visibility: visible; From f28979787b30f8f70266e7f20a6fc337ecb1110c Mon Sep 17 00:00:00 2001 From: Piyush Agrawal Date: Fri, 10 Mar 2017 18:21:13 +0530 Subject: [PATCH 036/173] backdrop set to false for teachOppiaModel --- .../head/pages/exploration_editor/editor_tab/StateResponses.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/templates/dev/head/pages/exploration_editor/editor_tab/StateResponses.js b/core/templates/dev/head/pages/exploration_editor/editor_tab/StateResponses.js index 6a403b212ef3..d1c6d4cda112 100644 --- a/core/templates/dev/head/pages/exploration_editor/editor_tab/StateResponses.js +++ b/core/templates/dev/head/pages/exploration_editor/editor_tab/StateResponses.js @@ -564,7 +564,7 @@ oppia.controller('StateResponses', [ $modal.open({ templateUrl: 'modals/teachOppia', - backdrop: true, + backdrop: false, controller: [ '$scope', '$modalInstance', 'oppiaExplorationHtmlFormatterService', 'stateInteractionIdService', 'stateCustomizationArgsService', From 93c230aeb9278db04102236b63e5471e21258d5a Mon Sep 17 00:00:00 2001 From: S Giritheja Date: Sat, 11 Mar 2017 01:23:55 +0530 Subject: [PATCH 037/173] Added validation to classifier domain --- core/domain/classifier_domain.py | 46 ++++++++++++++++++++++++- core/domain/classifier_services.py | 1 + core/domain/classifier_services_test.py | 3 +- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/core/domain/classifier_domain.py b/core/domain/classifier_domain.py index 5a659425bd77..b5025e4936e5 100644 --- a/core/domain/classifier_domain.py +++ b/core/domain/classifier_domain.py @@ -15,6 +15,8 @@ """Domain objects for classifier models""" import copy +import feconf +import utils class Classifier(object): """Domain object for a classifier. @@ -45,7 +47,7 @@ def __init__(self, classifier_id, exp_id, exp_version_when_created, Args: classifier_id: str. The unique id of the classifier. exp_id: str. The exploration id to which the classifier belongs. - exp_version_when_created: str. The version of the exploration when + exp_version_when_created: int. The version of the exploration when this classification model was created. state_name: str. The name of the state to which the classifier belongs. @@ -80,3 +82,45 @@ def to_dict(self): 'cached_classifier_data': self.cached_classifier_data, 'data_schema_version': self.data_schema_version } + + def validate(self): + """Validates the classifier before it is saved to storage.""" + + if not isinstance(self.id, basestring): + raise utils.ValidationError( + 'Expected id to be a string, received %s' % self.id) + utils.require_valid_name( + self.id, 'the classifier id') + + if not isinstance(self.exp_id, basestring): + raise utils.ValidationError( + 'Expected exp_id to be a string, received %s' % self.exp_id) + utils.require_valid_name( + self.exp_id, 'the exploration id') + + if not isinstance(self.exp_version_when_created, int): + raise utils.ValidationError( + ('Expected exp_version_when_created to be a integer,') + ( + 'received %d' % self.exp_version_when_created)) + + if not isinstance(self.state_name, basestring): + raise utils.ValidationError( + 'Expected id to be a string, received %s' % self.state_name) + utils.require_valid_name( + self.state_name, 'the state name') + + if not isinstance(self.algorithm_id, basestring): + raise utils.ValidationError( + 'Expected algorithm_id to be a string, received %s' %( + self.algorithm_id)) + utils.require_valid_name( + self.algorithm_id, 'the algorithm id') + if not (self.algorithm_id == ( + feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput'])): + raise utils.ValidationError( + 'Invalid algorithm_id: %s' % self.algorithm_id) + + if not isinstance(self.cached_classifier_data, dict): + raise utils.ValidationError( + 'Expected cached_classifier_data to be a dict, received %s' %( + self.cached_classifier_data)) diff --git a/core/domain/classifier_services.py b/core/domain/classifier_services.py index 6f245e9f2709..ebcdd1059b37 100644 --- a/core/domain/classifier_services.py +++ b/core/domain/classifier_services.py @@ -203,6 +203,7 @@ def save_classifier(classifier): """ classifier_model = classifier_models.ClassifierModel.get( classifier.id, False) + classifier.validate() if classifier_model is None: classifier.id = _create_classifier(classifier) else: diff --git a/core/domain/classifier_services_test.py b/core/domain/classifier_services_test.py index 008ffe6d007f..de5d267afc8e 100644 --- a/core/domain/classifier_services_test.py +++ b/core/domain/classifier_services_test.py @@ -129,9 +129,10 @@ def test_update_of_classifiers(self): exp_id = u'1' state = 'Home' test_state = 'State' + empty_dict = {} classifier = classifier_domain.Classifier( '1', exp_id, 1, state, - feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput'], [], 1) + feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput'], empty_dict, 1) classifier_id = ( classifier_services.save_classifier(classifier)) classifier = classifier_services.get_classifier_by_id( From a669a20b05977d09cb49d2504fa295be23434920 Mon Sep 17 00:00:00 2001 From: giritheja Date: Sat, 11 Mar 2017 01:26:55 +0530 Subject: [PATCH 038/173] fixed lint issues --- core/domain/classifier_domain.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/domain/classifier_domain.py b/core/domain/classifier_domain.py index b5025e4936e5..4438953d14d1 100644 --- a/core/domain/classifier_domain.py +++ b/core/domain/classifier_domain.py @@ -101,7 +101,7 @@ def validate(self): if not isinstance(self.exp_version_when_created, int): raise utils.ValidationError( ('Expected exp_version_when_created to be a integer,') + ( - 'received %d' % self.exp_version_when_created)) + 'received %d' % self.exp_version_when_created)) if not isinstance(self.state_name, basestring): raise utils.ValidationError( @@ -115,8 +115,8 @@ def validate(self): self.algorithm_id)) utils.require_valid_name( self.algorithm_id, 'the algorithm id') - if not (self.algorithm_id == ( - feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput'])): + if (self.algorithm_id != ( + feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput'])): raise utils.ValidationError( 'Invalid algorithm_id: %s' % self.algorithm_id) From affd108bf76e9366f8724d790c3c3dbb38391743 Mon Sep 17 00:00:00 2001 From: kirankonduru Date: Sat, 11 Mar 2017 02:39:31 +0530 Subject: [PATCH 039/173] Image region resizable code refactor --- .../templates/ImageWithRegionsEditor.js | 535 +++++------------- .../templates/image_with_regions_editor.html | 2 +- 2 files changed, 128 insertions(+), 409 deletions(-) diff --git a/extensions/objects/templates/ImageWithRegionsEditor.js b/extensions/objects/templates/ImageWithRegionsEditor.js index 12f4a2b19128..1dfdb79655d1 100644 --- a/extensions/objects/templates/ImageWithRegionsEditor.js +++ b/extensions/objects/templates/ImageWithRegionsEditor.js @@ -69,25 +69,21 @@ oppia.directive('imageWithRegionsEditor', [ $scope.rectY = 0; $scope.rectWidth = 0; $scope.rectHeight = 0; - // Flags for the cursor direction in which region is to be resized - $scope.nResize = false; - $scope.sResize = false; - $scope.eResize = false; - $scope.wResize = false; - $scope.neResize = false; - $scope.nwResize = false; - $scope.seResize = false; - $scope.nwResize = false; - // A flag to check direction change while resizing - $scope.flag = 0; // Is user currently drawing a new region? $scope.userIsCurrentlyDrawing = false; // Is user currently dragging an existing region? $scope.userIsCurrentlyDragging = false; // Is user currently resizing an existing region? $scope.userIsCurrentlyResizing = false; - // The region is being resized along which direction? - $scope.resizeDirection = ''; + // The horizontal direction along which user resize occurs + // 1 -> Left -1 -> Right 0 -> No resize + $scope.xDirection = 0; + // The vertical direction along which user resize occurs + // 1 -> Top -1 -> Bottom 0 -> No resize + $scope.yDirection = 0; + // Flags to check whether the direction changes while resizing + $scope.yDirectionToggled = 0; + $scope.xDirectionToggled = 0; // The region along borders which when hovered provides resize cursor $scope.resizeRegionBorder = 10; // Dimensions of original image @@ -97,8 +93,6 @@ oppia.directive('imageWithRegionsEditor', [ $scope.regionDrawMode = false; // Index of region currently hovered over $scope.hoveredRegion = null; - // Index of region currently moved over - $scope.moveRegion = null; // Index of region currently selected $scope.selectedRegion = null; @@ -168,18 +162,6 @@ oppia.directive('imageWithRegionsEditor', [ return false; }; - // Called whenever the direction cursor is to be hidden - $scope.hideDirectionCursor = function() { - $scope.nResize = false; - $scope.eResize = false; - $scope.wResize = false; - $scope.sResize = false; - $scope.neResize = false; - $scope.nwResize = false; - $scope.seResize = false; - $scope.swResize = false; - }; - $scope.regionLabelGetterSetter = function(index) { return function(label) { if (angular.isDefined(label)) { @@ -224,318 +206,47 @@ oppia.directive('imageWithRegionsEditor', [ height: (area[1][1] - area[0][1]) * $scope.getImageHeight() }; }; - var resizeRegion = function() { var labeledRegions = $scope.$parent.value.labeledRegions; var resizedRegion = labeledRegions[$scope.selectedRegion].region; var deltaX = $scope.mouseX - $scope.originalMouseX; var deltaY = $scope.mouseY - $scope.originalMouseY; - if ($scope.resizeDirection === 'n') { - if ($scope.originalRectArea.height - deltaY <= 0) { - $scope.flag = 1; - $scope.sResize = true; - $scope.nResize = false; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x, - $scope.originalMouseY + $scope.originalRectArea.height, - $scope.originalRectArea.width, - $scope.originalRectArea.height - deltaY - ); - } else { - $scope.flag = 0; - $scope.sResize = false; - $scope.nResize = true; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x, - $scope.originalRectArea.y + deltaY, - $scope.originalRectArea.width, - $scope.originalRectArea.height - deltaY - ); - } - } else if ($scope.resizeDirection === 's') { - if ($scope.originalRectArea.height + deltaY <= 0) { - $scope.sResize = false; - $scope.nResize = true; - $scope.flag = 1; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x, - $scope.originalRectArea.y + deltaY + - $scope.originalRectArea.height, - $scope.originalRectArea.width, - $scope.originalRectArea.height + deltaY - ); - } else { - $scope.sResize = true; - $scope.nResize = false; - $scope.flag = 0; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x, - $scope.originalRectArea.y, - $scope.originalRectArea.width, - $scope.originalRectArea.height + deltaY - ); - } - } else if ($scope.resizeDirection === 'e') { - if ($scope.originalRectArea.width + deltaX <= 0) { - $scope.eResize = false; - $scope.wResize = true; - $scope.flag = 1; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x + deltaX + - $scope.originalRectArea.width, - $scope.originalRectArea.y, - $scope.originalRectArea.width + deltaX, - $scope.originalRectArea.height - ); - } else { - $scope.eResize = true; - $scope.wResize = false; - $scope.flag = 0; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x, - $scope.originalRectArea.y, - $scope.originalRectArea.width + deltaX, - $scope.originalRectArea.height - ); - } - } else if ($scope.resizeDirection === 'w') { - if ($scope.originalRectArea.width - deltaX <= 0) { - $scope.eResize = true; - $scope.wResize = false; - $scope.flag = 1; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x + $scope.originalRectArea.width, - $scope.originalRectArea.y, - $scope.originalRectArea.width - deltaX, - $scope.originalRectArea.height - ); - } else { - $scope.eResize = false; - $scope.wResize = true; - $scope.flag = 0; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x + deltaX, - $scope.originalRectArea.y, - $scope.originalRectArea.width - deltaX, - $scope.originalRectArea.height - ); - } - } else if ($scope.resizeDirection === 'ne') { - if ($scope.originalRectArea.height - deltaY <= 0 && - $scope.originalRectArea.width + deltaX <= 0) { - $scope.swResize = true; - $scope.neResize = false; - $scope.nwResize = false; - $scope.seResize = false; - $scope.flag = 1; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x + deltaX + - $scope.originalRectArea.width, - $scope.originalRectArea.y + $scope.originalRectArea.height, - $scope.originalRectArea.width + deltaX, - $scope.originalRectArea.height - deltaY - ); - } else if ($scope.originalRectArea.width + deltaX <= 0) { - $scope.swResize = false; - $scope.neResize = false; - $scope.nwResize = true; - $scope.seResize = false; - $scope.flag = 1; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x + deltaX + - $scope.originalRectArea.width, - $scope.originalRectArea.y + deltaY, - $scope.originalRectArea.width + deltaX, - $scope.originalRectArea.height - deltaY - ); - } else if ($scope.originalRectArea.height - deltaY <= 0) { - $scope.swResize = false; - $scope.neResize = false; - $scope.nwResize = false; - $scope.seResize = true; - $scope.flag = 1; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x, - $scope.originalMouseY + $scope.originalRectArea.height, - $scope.originalRectArea.width + deltaX, - $scope.originalRectArea.height - deltaY - ); - } else { - $scope.swResize = false; - $scope.neResize = true; - $scope.nwResize = false; - $scope.seResize = false; - $scope.flag = 0; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x, - $scope.originalRectArea.y + deltaY, - $scope.originalRectArea.width + deltaX, - $scope.originalRectArea.height - deltaY - ); - } - } else if ($scope.resizeDirection === 'nw') { - if ($scope.originalRectArea.height - deltaY <= 0 && - $scope.originalRectArea.width - deltaX <= 0) { - $scope.swResize = false; - $scope.neResize = false; - $scope.nwResize = false; - $scope.seResize = true; - $scope.flag = 1; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x + $scope.originalRectArea.width, - $scope.originalRectArea.y + $scope.originalRectArea.height, - $scope.originalRectArea.width - deltaX, - $scope.originalRectArea.height - deltaY - ); - } else if ($scope.originalRectArea.width - deltaX <= 0) { - $scope.swResize = false; - $scope.neResize = true; - $scope.nwResize = false; - $scope.seResize = false; - $scope.flag = 1; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x + $scope.originalRectArea.width, - $scope.originalRectArea.y + deltaY, - $scope.originalRectArea.width - deltaX, - $scope.originalRectArea.height - deltaY - ); - } else if ($scope.originalRectArea.height - deltaY <= 0) { - $scope.swResize = true; - $scope.neResize = false; - $scope.nwResize = false; - $scope.seResize = false; - $scope.flag = 1; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x + deltaX, - $scope.originalRectArea.y + $scope.originalRectArea.height, - $scope.originalRectArea.width - deltaX, - $scope.originalRectArea.height - deltaY - ); - } else { - $scope.swResize = false; - $scope.neResize = false; - $scope.nwResize = true; - $scope.seResize = false; - $scope.flag = 0; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x + deltaX, - $scope.originalRectArea.y + deltaY, - $scope.originalRectArea.width - deltaX, - $scope.originalRectArea.height - deltaY - ); - } - } else if ($scope.resizeDirection === 'se') { - if ($scope.originalRectArea.height + deltaY <= 0 && - $scope.originalRectArea.width + deltaX <= 0) { - $scope.swResize = false; - $scope.neResize = false; - $scope.nwResize = true; - $scope.seResize = false; - $scope.flag = 1; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x + deltaX + - $scope.originalRectArea.width, - $scope.originalRectArea.y + deltaY + - $scope.originalRectArea.height, - $scope.originalRectArea.width + deltaX, - $scope.originalRectArea.height + deltaY - ); - } else if ($scope.originalRectArea.width + deltaX <= 0) { - $scope.swResize = true; - $scope.neResize = false; - $scope.nwResize = false; - $scope.seResize = false; - $scope.flag = 1; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x + deltaX + - $scope.originalRectArea.width, - $scope.originalRectArea.y, - $scope.originalRectArea.width + deltaX, - $scope.originalRectArea.height + deltaY - ); - } else if ($scope.originalRectArea.height + deltaY <= 0) { - $scope.swResize = false; - $scope.neResize = true; - $scope.nwResize = false; - $scope.seResize = false; - $scope.flag = 1; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x, - $scope.originalRectArea.y + deltaY + - $scope.originalRectArea.height, - $scope.originalRectArea.width + deltaX, - $scope.originalRectArea.height + deltaY - ); - } else { - $scope.swResize = false; - $scope.neResize = false; - $scope.nwResize = false; - $scope.seResize = true; - $scope.flag = 0; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x, - $scope.originalRectArea.y, - $scope.originalRectArea.width + deltaX, - $scope.originalRectArea.height + deltaY - ); - } - } else if ($scope.resizeDirection === 'sw') { - if ($scope.originalRectArea.height + deltaY <= 0 && - $scope.originalRectArea.width - deltaX <= 0) { - $scope.swResize = false; - $scope.neResize = true; - $scope.nwResize = false; - $scope.seResize = false; - $scope.flag = 1; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x + $scope.originalRectArea.width, - $scope.originalRectArea.y + deltaY + - $scope.originalRectArea.height, - $scope.originalRectArea.width - deltaX, - $scope.originalRectArea.height + deltaY - ); - } else if ($scope.originalRectArea.width - deltaX <= 0) { - $scope.swResize = false; - $scope.neResize = false; - $scope.nwResize = false; - $scope.seResize = true; - $scope.flag = 1; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x + $scope.originalRectArea.width, - $scope.originalRectArea.y, - $scope.originalRectArea.width - deltaX, - $scope.originalRectArea.height + deltaY - ); - } else if ($scope.originalRectArea.height + deltaY <= 0) { - $scope.swResize = false; - $scope.neResize = false; - $scope.nwResize = true; - $scope.seResize = false; - $scope.flag = 1; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x + deltaX, - $scope.originalRectArea.y + deltaY + - $scope.originalRectArea.height, - $scope.originalRectArea.width - deltaX, - $scope.originalRectArea.height + deltaY - ); - } else { - $scope.swResize = true; - $scope.neResize = false; - $scope.nwResize = false; - $scope.seResize = false; - $scope.flag = 1; - resizedRegion.area = regionAreaFromCornerAndDimensions( - $scope.originalRectArea.x + deltaX, - $scope.originalRectArea.y, - $scope.originalRectArea.width - deltaX, - $scope.originalRectArea.height + deltaY - ); - } - } else { - $scope.resizeDirection = ''; - $scope.userIsCurrentlyResizing = false; + var x = $scope.originalRectArea.x; + var y = $scope.originalRectArea.y; + var width = $scope.originalRectArea.width; + var height = $scope.originalRectArea.height; + if (height - $scope.yDirection * deltaY <= 0 && + $scope.yDirectionToggled === 0) { + $scope.yDirectionToggled = 1; + } else if (height - $scope.yDirection * deltaY >= 0 && + $scope.yDirectionToggled === 1) { + $scope.yDirectionToggled = 0; + } + if ($scope.yDirection === 1) { + y = y + !$scope.yDirectionToggled * deltaY + + $scope.yDirectionToggled * height; + } else if ($scope.yDirection === -1) { + y = y + $scope.yDirectionToggled * deltaY + + $scope.yDirectionToggled * height; + } + if (width - $scope.xDirection * deltaX <= 0 && + $scope.xDirectionToggled === 0) { + $scope.xDirectionToggled = 1; + } else if (width - $scope.xDirection * deltaX >= 0 && + $scope.xDirectionToggled === 1) { + $scope.xDirectionToggled = 0; } + if ($scope.xDirection === 1) { + x = x + !$scope.xDirectionToggled * deltaX + + $scope.xDirectionToggled * width; + } else if ($scope.xDirection === -1) { + x = x + $scope.xDirectionToggled * deltaX + + $scope.xDirectionToggled * width; + } + height = height - $scope.yDirection * deltaY; + width = width - $scope.xDirection * deltaX; + resizedRegion.area = regionAreaFromCornerAndDimensions(x, y, + width, height); }; $scope.onSvgMouseMove = function(evt) { @@ -581,8 +292,12 @@ oppia.directive('imageWithRegionsEditor', [ $scope.userIsCurrentlyDrawing = false; $scope.userIsCurrentlyDragging = false; $scope.userIsCurrentlyResizing = false; - $scope.flag = 0; - $scope.hideDirectionCursor(); + if ($scope.xDirectionToggled || $scope.yDirectionToggled) { + $scope.xDirection = 0; + $scope.yDirection = 0; + } + $scope.yDirectionToggled = 0; + $scope.xDirectionToggled = 0; if ($scope.regionDrawMode) { $scope.regionDrawMode = false; if ($scope.rectWidth !== 0 && $scope.rectHeight !== 0) { @@ -622,68 +337,61 @@ oppia.directive('imageWithRegionsEditor', [ $scope.onMouseoverRegion = function(index) { if ($scope.hoveredRegion === null) { $scope.hoveredRegion = index; - $scope.moveRegion = index; } }; - $scope.onMouseMoveRegion = function(index) { - if ($scope.moveRegion !== index) { - hideDirectionCursor(); + $scope.onMouseMoveRegion = function() { + if ($scope.userIsCurrentlyDragging || + $scope.userIsCurrentlyResizing) { + return; } region = reg = cornerAndDimensionsFromRegionArea( $scope.$parent.value.labeledRegions[ $scope.hoveredRegion].region.area); - if ($scope.mouseY <= region.y + $scope.resizeRegionBorder && + if (!$scope.xDirectionToggled && !$scope.yDirectionToggled) { + if ($scope.mouseY <= region.y + $scope.resizeRegionBorder && $scope.mouseX >= region.width + region.x - - $scope.resizeRegionBorder && $scope.flag === 0 && - $scope.resizeDirection !== 'nw' && - $scope.resizeDirection !== 'se') { - $scope.resizeDirection = 'ne'; - $scope.nResize = false; - $scope.eResize = false; - $scope.neResize = true; - } else if ($scope.mouseY <= region.y + $scope.resizeRegionBorder && - $scope.mouseX <= region.x + $scope.resizeRegionBorder && - $scope.flag === 0 && $scope.resizeDirection !== 'se') { - $scope.resizeDirection = 'nw'; - $scope.nResize = false; - $scope.wResize = false; - $scope.nwResize = true; - } else if ($scope.mouseY >= region.height + region.y - - $scope.resizeRegionBorder && $scope.mouseX >= region.width + - region.x - $scope.resizeRegionBorder && $scope.flag === 0) { - $scope.resizeDirection = 'se'; - $scope.sResize = false; - $scope.eResize = false; - $scope.seResize = true; - } else if ($scope.mouseY >= region.height + region.y - - $scope.resizeRegionBorder && $scope.mouseX <= region.x + - $scope.resizeRegionBorder && $scope.flag === 0) { - $scope.resizeDirection = 'sw'; - $scope.sResize = false; - $scope.wResize = false; - $scope.swResize = true; - } else if ($scope.mouseY <= region.y + $scope.resizeRegionBorder && - $scope.flag === 0 && $scope.resizeDirection !== 's' && - $scope.resizeDirection !== 'se') { - $scope.resizeDirection = 'n'; - $scope.nResize = true; - } else if ($scope.mouseX <= region.x + $scope.resizeRegionBorder && - $scope.flag === 0 && $scope.resizeDirection !== 'e') { - $scope.resizeDirection = 'w'; - $scope.wResize = true; - } else if ($scope.mouseX >= region.width + region.x - - $scope.resizeRegionBorder && $scope.flag === 0 && - $scope.resizeDirection !== 'se') { - $scope.resizeDirection = 'e'; - $scope.eResize = true; - } else if ($scope.mouseY >= region.height + region.y - - $scope.resizeRegionBorder && $scope.flag === 0) { - $scope.resizeDirection = 's'; - $scope.sResize = true; - } else { - $scope.hideDirectionCursor(); - if ($scope.flag === 0) { - $scope.resizeDirection = ''; + $scope.resizeRegionBorder) { + // Along top right + $scope.xDirection = -1; + $scope.yDirection = 1; + } else if ($scope.mouseY <= region.y + $scope.resizeRegionBorder && + $scope.mouseX <= region.x + $scope.resizeRegionBorder) { + // Along top left + $scope.xDirection = 1; + $scope.yDirection = 1; + } else if ($scope.mouseY >= region.height + region.y - + $scope.resizeRegionBorder && $scope.mouseX >= region.width + + region.x - $scope.resizeRegionBorder) { + // Along bottom right + $scope.xDirection = -1; + $scope.yDirection = -1; + } else if ($scope.mouseY >= region.height + region.y - + $scope.resizeRegionBorder && $scope.mouseX <= region.x + + $scope.resizeRegionBorder) { + // Along bottom left + $scope.xDirection = 1; + $scope.yDirection = -1; + } else if ($scope.mouseY <= region.y + $scope.resizeRegionBorder) { + // Along top + $scope.yDirection = 1; + $scope.xDirection = 0; + } else if ($scope.mouseX <= region.x + $scope.resizeRegionBorder) { + // Along left + $scope.yDirection = 0; + $scope.xDirection = 1; + } else if ($scope.mouseX >= region.width + region.x - + $scope.resizeRegionBorder) { + // Along right + $scope.yDirection = 0; + $scope.xDirection = -1; + } else if ($scope.mouseY >= region.height + region.y - + $scope.resizeRegionBorder) { + // Along bottom + $scope.xDirection = 0; + $scope.yDirection = -1; + } else { + $scope.xDirection = 0; + $scope.yDirection = 0; } } }; @@ -691,11 +399,14 @@ oppia.directive('imageWithRegionsEditor', [ if ($scope.hoveredRegion === index) { $scope.hoveredRegion = null; } - $scope.hideDirectionCursor(); + if (!$scope.userIsCurrentlyResizing) { + $scope.xDirection = 0; + $scope.yDirection = 0; + } }; $scope.onMousedownRegion = function() { $scope.userIsCurrentlyDragging = true; - if ($scope.resizeDirection !== '') { + if ($scope.xDirection || $scope.yDirection) { $scope.userIsCurrentlyDragging = false; $scope.userIsCurrentlyResizing = true; } @@ -715,22 +426,30 @@ oppia.directive('imageWithRegionsEditor', [ $scope.regionDrawMode = true; }; $scope.getCursorStyle = function() { - if ($scope.nResize) { - return 'n-resize'; - } else if ($scope.eResize) { - return 'e-resize'; - } else if ($scope.wResize) { - return 'w-resize'; - } else if ($scope.sResize) { - return 's-resize'; - } else if ($scope.neResize) { - return 'ne-resize'; - } else if ($scope.seResize) { - return 'se-resize'; - } else if ($scope.nwResize) { - return 'nw-resize'; - } else if ($scope.swResize) { - return 'sw-resize'; + var xDirectionCursor = ''; + var yDirectionCursor = ''; + if ($scope.xDirection || $scope.yDirection) { + if (($scope.xDirection === 1 && $scope.xDirectionToggled === 0) || + ($scope.xDirection === -1 && $scope.xDirectionToggled === 1)) { + xDirectionCursor = 'w'; + } else if (($scope.xDirection === -1 && + $scope.xDirectionToggled === 0) || ($scope.xDirection === 1 && + $scope.xDirectionToggled === 1)) { + xDirectionCursor = 'e'; + } else { + xDirectionCursor = ''; + } + if (($scope.yDirection === 1 && $scope.yDirectionToggled === 0) || + ($scope.yDirection === -1 && $scope.yDirectionToggled === 1)) { + yDirectionCursor = 'n'; + } else if (($scope.yDirection === -1 && + $scope.yDirectionToggled === 0) || ($scope.yDirection === 1 && + $scope.yDirectionToggled === 1)) { + yDirectionCursor = 's'; + } else { + yDirectionCursor = ''; + } + return yDirectionCursor + xDirectionCursor + '-resize'; } return ($scope.regionDrawMode) ? 'crosshair' : 'default'; }; diff --git a/extensions/objects/templates/image_with_regions_editor.html b/extensions/objects/templates/image_with_regions_editor.html index 847cc9c82755..792c848d3804 100644 --- a/extensions/objects/templates/image_with_regions_editor.html +++ b/extensions/objects/templates/image_with_regions_editor.html @@ -33,7 +33,7 @@ ng-attr-style="<[getRegionStyle($index)]>" ng-mouseover="onMouseoverRegion($index)" ng-mouseout="onMouseoutRegion($index)" - ng-mousemove="onMouseMoveRegion($index)" + ng-mousemove="onMouseMoveRegion()" ng-mousedown="onMousedownRegion($index)"> Date: Sat, 11 Mar 2017 06:45:45 +0530 Subject: [PATCH 040/173] Fixes part of #3064: pull the directive specific css in corresponding files from oppia.css (#3077) * Fixes #3064: initial script to scrape out oppia classes from directive html file [NOT FOR MERGE] * Fixes #3064: linting issues solved * Fixes #3064: linting issues solved * Fixes #3064: linting issues solved * Fixes #3064: linting issues * Fixes #3064: script modified to search entire directory and subdirectory and give files and unique classes that belong to it * Fixes #3064: linting issues fixed * Fixes #3064: linting issues fixed * Fixes #3064: linting issues fixed * Fixes #3064: linting issues fixed * Fixes #3064: script modified for looking unique classes among all html files instead of among all directive files * Fixes #3064: script modified * FIXES #3064: documentation added for script * Fixes #3064: unique css transferred for schema_based_list_editor_directive.html * Fixes #3064: unique css shifter for answer_group_editor_directive.html * Fixes #3064: unique css shifted for side_navigation_bar_directive.html * Fixes #3064: unique css shifted for embed_exploration_modal_directive.html * Fixes #3064: unique css shifted for schema_based_int_editor_directive.html * Fixes #3064: shifted css to before the html and made some changes in side_navigation_bar_directive.html * Fixes #3064: newline added in end of search_results_directive.html * Fixes #3064: shifted .css-class-name to directive-name.css-class-name for scoping purposes * Fixes #3064: side_navgation_bar_directive file updated * Fixes #3064: css classes in directive files updated to directive-name .css-class * Fixes #3064: embed_exploration_modal_directive updated --- .../answer_group_editor_directive.html | 44 +++++ .../embed_exploration_modal_directive.html | 44 +++-- .../schema_based_int_editor_directive.html | 7 + .../schema_based_list_editor_directive.html | 18 ++ .../side_navigation_bar_directive.html | 95 +++++++++++ core/templates/dev/head/css/oppia.css | 157 +----------------- .../library/search_results_directive.html | 38 ++--- 7 files changed, 217 insertions(+), 186 deletions(-) diff --git a/core/templates/dev/head/components/answer_group_editor_directive.html b/core/templates/dev/head/components/answer_group_editor_directive.html index f2390c94552a..15e63846a654 100644 --- a/core/templates/dev/head/components/answer_group_editor_directive.html +++ b/core/templates/dev/head/components/answer_group_editor_directive.html @@ -1,4 +1,48 @@ diff --git a/core/templates/dev/head/components/forms/schema_editors/schema_based_int_editor_directive.html b/core/templates/dev/head/components/forms/schema_editors/schema_based_int_editor_directive.html index 51b8b46717d1..81323bf7304d 100644 --- a/core/templates/dev/head/components/forms/schema_editors/schema_based_int_editor_directive.html +++ b/core/templates/dev/head/components/forms/schema_editors/schema_based_int_editor_directive.html @@ -1,4 +1,11 @@ - - From 2526e32e0035c08fae73e21293c39e5fdb86cf2a Mon Sep 17 00:00:00 2001 From: kirankonduru Date: Sun, 12 Mar 2017 00:16:32 +0530 Subject: [PATCH 041/173] Image region resizable code refactor --- .../templates/ImageWithRegionsEditor.js | 97 +++++++------------ 1 file changed, 35 insertions(+), 62 deletions(-) diff --git a/extensions/objects/templates/ImageWithRegionsEditor.js b/extensions/objects/templates/ImageWithRegionsEditor.js index 1dfdb79655d1..3432bc43d9c9 100644 --- a/extensions/objects/templates/ImageWithRegionsEditor.js +++ b/extensions/objects/templates/ImageWithRegionsEditor.js @@ -82,8 +82,11 @@ oppia.directive('imageWithRegionsEditor', [ // 1 -> Top -1 -> Bottom 0 -> No resize $scope.yDirection = 0; // Flags to check whether the direction changes while resizing - $scope.yDirectionToggled = 0; - $scope.xDirectionToggled = 0; + $scope.yDirectionToggled = false; + $scope.xDirectionToggled = false; + // A boolean that is set whenever the cursor moves out of the + // rectangular region while resizing. + $scope.outOfRegion = false; // The region along borders which when hovered provides resize cursor $scope.resizeRegionBorder = 10; // Dimensions of original image @@ -216,32 +219,28 @@ oppia.directive('imageWithRegionsEditor', [ var width = $scope.originalRectArea.width; var height = $scope.originalRectArea.height; if (height - $scope.yDirection * deltaY <= 0 && - $scope.yDirectionToggled === 0) { - $scope.yDirectionToggled = 1; + !$scope.yDirectionToggled) { + $scope.yDirectionToggled = true; } else if (height - $scope.yDirection * deltaY >= 0 && - $scope.yDirectionToggled === 1) { - $scope.yDirectionToggled = 0; + $scope.yDirectionToggled) { + $scope.yDirectionToggled = false; } if ($scope.yDirection === 1) { - y = y + !$scope.yDirectionToggled * deltaY + - $scope.yDirectionToggled * height; + y += $scope.yDirectionToggled ? height : deltaY; } else if ($scope.yDirection === -1) { - y = y + $scope.yDirectionToggled * deltaY + - $scope.yDirectionToggled * height; + y += $scope.yDirectionToggled * (deltaY + height); } if (width - $scope.xDirection * deltaX <= 0 && - $scope.xDirectionToggled === 0) { - $scope.xDirectionToggled = 1; + !$scope.xDirectionToggled) { + $scope.xDirectionToggled = true; } else if (width - $scope.xDirection * deltaX >= 0 && - $scope.xDirectionToggled === 1) { - $scope.xDirectionToggled = 0; + $scope.xDirectionToggled) { + $scope.xDirectionToggled = false; } if ($scope.xDirection === 1) { - x = x + !$scope.xDirectionToggled * deltaX + - $scope.xDirectionToggled * width; + x += $scope.xDirectionToggled ? width : deltaX; } else if ($scope.xDirection === -1) { - x = x + $scope.xDirectionToggled * deltaX + - $scope.xDirectionToggled * width; + x += $scope.xDirectionToggled * (deltaX + width); } height = height - $scope.yDirection * deltaY; width = width - $scope.xDirection * deltaX; @@ -292,12 +291,13 @@ oppia.directive('imageWithRegionsEditor', [ $scope.userIsCurrentlyDrawing = false; $scope.userIsCurrentlyDragging = false; $scope.userIsCurrentlyResizing = false; - if ($scope.xDirectionToggled || $scope.yDirectionToggled) { + if ($scope.outOfRegion) { $scope.xDirection = 0; $scope.yDirection = 0; } - $scope.yDirectionToggled = 0; - $scope.xDirectionToggled = 0; + $scope.outOfRegion = false; + $scope.yDirectionToggled = false; + $scope.xDirectionToggled = false; if ($scope.regionDrawMode) { $scope.regionDrawMode = false; if ($scope.rectWidth !== 0 && $scope.rectHeight !== 0) { @@ -338,6 +338,7 @@ oppia.directive('imageWithRegionsEditor', [ if ($scope.hoveredRegion === null) { $scope.hoveredRegion = index; } + $scope.outOfRegion = false; }; $scope.onMouseMoveRegion = function() { if ($scope.userIsCurrentlyDragging || @@ -348,50 +349,21 @@ oppia.directive('imageWithRegionsEditor', [ $scope.$parent.value.labeledRegions[ $scope.hoveredRegion].region.area); if (!$scope.xDirectionToggled && !$scope.yDirectionToggled) { - if ($scope.mouseY <= region.y + $scope.resizeRegionBorder && - $scope.mouseX >= region.width + region.x - - $scope.resizeRegionBorder) { - // Along top right - $scope.xDirection = -1; - $scope.yDirection = 1; - } else if ($scope.mouseY <= region.y + $scope.resizeRegionBorder && - $scope.mouseX <= region.x + $scope.resizeRegionBorder) { - // Along top left - $scope.xDirection = 1; + if ($scope.mouseY <= region.y + $scope.resizeRegionBorder) { $scope.yDirection = 1; } else if ($scope.mouseY >= region.height + region.y - - $scope.resizeRegionBorder && $scope.mouseX >= region.width + - region.x - $scope.resizeRegionBorder) { - // Along bottom right - $scope.xDirection = -1; - $scope.yDirection = -1; - } else if ($scope.mouseY >= region.height + region.y - - $scope.resizeRegionBorder && $scope.mouseX <= region.x + $scope.resizeRegionBorder) { - // Along bottom left - $scope.xDirection = 1; $scope.yDirection = -1; - } else if ($scope.mouseY <= region.y + $scope.resizeRegionBorder) { - // Along top - $scope.yDirection = 1; - $scope.xDirection = 0; - } else if ($scope.mouseX <= region.x + $scope.resizeRegionBorder) { - // Along left + } else { $scope.yDirection = 0; + } + if ($scope.mouseX <= region.x + $scope.resizeRegionBorder) { $scope.xDirection = 1; } else if ($scope.mouseX >= region.width + region.x - $scope.resizeRegionBorder) { - // Along right - $scope.yDirection = 0; $scope.xDirection = -1; - } else if ($scope.mouseY >= region.height + region.y - - $scope.resizeRegionBorder) { - // Along bottom - $scope.xDirection = 0; - $scope.yDirection = -1; } else { $scope.xDirection = 0; - $scope.yDirection = 0; } } }; @@ -403,6 +375,7 @@ oppia.directive('imageWithRegionsEditor', [ $scope.xDirection = 0; $scope.yDirection = 0; } + $scope.outOfRegion = true; }; $scope.onMousedownRegion = function() { $scope.userIsCurrentlyDragging = true; @@ -429,22 +402,22 @@ oppia.directive('imageWithRegionsEditor', [ var xDirectionCursor = ''; var yDirectionCursor = ''; if ($scope.xDirection || $scope.yDirection) { - if (($scope.xDirection === 1 && $scope.xDirectionToggled === 0) || - ($scope.xDirection === -1 && $scope.xDirectionToggled === 1)) { + if (($scope.xDirection === 1 && !$scope.xDirectionToggled) || + ($scope.xDirection === -1 && $scope.xDirectionToggled)) { xDirectionCursor = 'w'; } else if (($scope.xDirection === -1 && - $scope.xDirectionToggled === 0) || ($scope.xDirection === 1 && - $scope.xDirectionToggled === 1)) { + !$scope.xDirectionToggled) || ($scope.xDirection === 1 && + $scope.xDirectionToggled)) { xDirectionCursor = 'e'; } else { xDirectionCursor = ''; } - if (($scope.yDirection === 1 && $scope.yDirectionToggled === 0) || - ($scope.yDirection === -1 && $scope.yDirectionToggled === 1)) { + if (($scope.yDirection === 1 && !$scope.yDirectionToggled) || + ($scope.yDirection === -1 && $scope.yDirectionToggled)) { yDirectionCursor = 'n'; } else if (($scope.yDirection === -1 && - $scope.yDirectionToggled === 0) || ($scope.yDirection === 1 && - $scope.yDirectionToggled === 1)) { + !$scope.yDirectionToggled) || ($scope.yDirection === 1 && + $scope.yDirectionToggled)) { yDirectionCursor = 's'; } else { yDirectionCursor = ''; From ea83634e4253c6559b5399a405e7d1b085f4692a Mon Sep 17 00:00:00 2001 From: kirankonduru Date: Sun, 12 Mar 2017 22:04:26 +0530 Subject: [PATCH 042/173] Image region resizable margin part added --- .../templates/ImageWithRegionsEditor.js | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/extensions/objects/templates/ImageWithRegionsEditor.js b/extensions/objects/templates/ImageWithRegionsEditor.js index 3432bc43d9c9..654241b369ca 100644 --- a/extensions/objects/templates/ImageWithRegionsEditor.js +++ b/extensions/objects/templates/ImageWithRegionsEditor.js @@ -218,6 +218,14 @@ oppia.directive('imageWithRegionsEditor', [ var y = $scope.originalRectArea.y; var width = $scope.originalRectArea.width; var height = $scope.originalRectArea.height; + var newWidth = width - $scope.xDirection * deltaX; + var newHeight = height - $scope.yDirection * deltaY; + // Margin between the position mouse is pressed to resize and the y + // or the x co-ordinate. + var marginX = Math.abs($scope.originalRectArea.x - + $scope.originalMouseX); + var marginY = Math.abs($scope.originalRectArea.y - + $scope.originalMouseY); if (height - $scope.yDirection * deltaY <= 0 && !$scope.yDirectionToggled) { $scope.yDirectionToggled = true; @@ -226,9 +234,9 @@ oppia.directive('imageWithRegionsEditor', [ $scope.yDirectionToggled = false; } if ($scope.yDirection === 1) { - y += $scope.yDirectionToggled ? height : deltaY; + y += $scope.yDirectionToggled ? (height + marginY) : deltaY; } else if ($scope.yDirection === -1) { - y += $scope.yDirectionToggled * (deltaY + height); + y += $scope.yDirectionToggled * (deltaY + marginY); } if (width - $scope.xDirection * deltaX <= 0 && !$scope.xDirectionToggled) { @@ -238,14 +246,12 @@ oppia.directive('imageWithRegionsEditor', [ $scope.xDirectionToggled = false; } if ($scope.xDirection === 1) { - x += $scope.xDirectionToggled ? width : deltaX; + x += $scope.xDirectionToggled ? (width + marginX) : deltaX; } else if ($scope.xDirection === -1) { - x += $scope.xDirectionToggled * (deltaX + width); + x += $scope.xDirectionToggled * (deltaX + marginX); } - height = height - $scope.yDirection * deltaY; - width = width - $scope.xDirection * deltaX; resizedRegion.area = regionAreaFromCornerAndDimensions(x, y, - width, height); + newWidth, newHeight); }; $scope.onSvgMouseMove = function(evt) { @@ -291,6 +297,20 @@ oppia.directive('imageWithRegionsEditor', [ $scope.userIsCurrentlyDrawing = false; $scope.userIsCurrentlyDragging = false; $scope.userIsCurrentlyResizing = false; + if ($scope.yDirectionToggled) { + if ($scope.yDirection === 1) { + $scope.yDirection = -1; + } else { + $scope.yDirection = 1; + } + } + if ($scope.xDirectionToggled) { + if ($scope.xDirection === 1) { + $scope.xDirection = -1; + } else { + $scope.xDirection = 1; + } + } if ($scope.outOfRegion) { $scope.xDirection = 0; $scope.yDirection = 0; @@ -378,10 +398,10 @@ oppia.directive('imageWithRegionsEditor', [ $scope.outOfRegion = true; }; $scope.onMousedownRegion = function() { - $scope.userIsCurrentlyDragging = true; if ($scope.xDirection || $scope.yDirection) { - $scope.userIsCurrentlyDragging = false; $scope.userIsCurrentlyResizing = true; + } else { + $scope.userIsCurrentlyDragging = true; } $scope.selectedRegion = $scope.hoveredRegion; $scope.originalRectArea = cornerAndDimensionsFromRegionArea( From 1d528c24fd8681536e621775d08f2dfef5c393fe Mon Sep 17 00:00:00 2001 From: kirankonduru Date: Sun, 12 Mar 2017 23:00:02 +0530 Subject: [PATCH 043/173] Image region resize code refactor --- .../templates/ImageWithRegionsEditor.js | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/extensions/objects/templates/ImageWithRegionsEditor.js b/extensions/objects/templates/ImageWithRegionsEditor.js index 654241b369ca..7fe7baa38019 100644 --- a/extensions/objects/templates/ImageWithRegionsEditor.js +++ b/extensions/objects/templates/ImageWithRegionsEditor.js @@ -226,11 +226,9 @@ oppia.directive('imageWithRegionsEditor', [ $scope.originalMouseX); var marginY = Math.abs($scope.originalRectArea.y - $scope.originalMouseY); - if (height - $scope.yDirection * deltaY <= 0 && - !$scope.yDirectionToggled) { + if (newHeight <= 0 && !$scope.yDirectionToggled) { $scope.yDirectionToggled = true; - } else if (height - $scope.yDirection * deltaY >= 0 && - $scope.yDirectionToggled) { + } else if (newHeight >= 0 && $scope.yDirectionToggled) { $scope.yDirectionToggled = false; } if ($scope.yDirection === 1) { @@ -238,11 +236,9 @@ oppia.directive('imageWithRegionsEditor', [ } else if ($scope.yDirection === -1) { y += $scope.yDirectionToggled * (deltaY + marginY); } - if (width - $scope.xDirection * deltaX <= 0 && - !$scope.xDirectionToggled) { + if (newWidth <= 0 && !$scope.xDirectionToggled) { $scope.xDirectionToggled = true; - } else if (width - $scope.xDirection * deltaX >= 0 && - $scope.xDirectionToggled) { + } else if (newWidth >= 0 && $scope.xDirectionToggled) { $scope.xDirectionToggled = false; } if ($scope.xDirection === 1) { @@ -298,18 +294,10 @@ oppia.directive('imageWithRegionsEditor', [ $scope.userIsCurrentlyDragging = false; $scope.userIsCurrentlyResizing = false; if ($scope.yDirectionToggled) { - if ($scope.yDirection === 1) { - $scope.yDirection = -1; - } else { - $scope.yDirection = 1; - } + $scope.yDirection = ($scope.yDirection === 1) ? -1 : 1; } if ($scope.xDirectionToggled) { - if ($scope.xDirection === 1) { - $scope.xDirection = -1; - } else { - $scope.xDirection = 1; - } + $scope.xDirection = ($scope.xDirection === 1) ? -1 : 1; } if ($scope.outOfRegion) { $scope.xDirection = 0; From 5783a2474d1e9c0662a62850de4dabd2a07e221f Mon Sep 17 00:00:00 2001 From: Saeed Jassani Date: Mon, 13 Mar 2017 03:30:31 +0530 Subject: [PATCH 044/173] Fix part of #2394: Added docstrings in cron.py (#3182) * docstring added in cron.py * docstrings added in cron.py * docstrings added in cron.py * changes in docstrings * changes made in docstrings * # This is a combination of 2 commits. # The first commit's message is: changes in docstring and merging conflicts caused by Localisation updates from https://translatewiki.net. # This is the 2nd commit message: docstrings added in cron.py --- core/controllers/cron.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/core/controllers/cron.py b/core/controllers/cron.py index fdcc8cc70473..a44a6d3fee1f 100644 --- a/core/controllers/cron.py +++ b/core/controllers/cron.py @@ -35,9 +35,19 @@ MAX_JOBS_TO_REPORT_ON = 50 -def require_cron_or_superadmin(handler): +def require_cron_or_superadmin(func): """Decorator to ensure that the handler is being called by cron or by a superadmin of the application. + + Args: + func: function. The cron function to be decorated. + + Returns: + function: The decorated cron function. + + Raises: + UnauthorizedUserException: An unauthorized user accesses the + handler URL. """ def _require_cron_or_superadmin(self, *args, **kwargs): if (self.request.headers.get('X-AppEngine-Cron') is None @@ -45,7 +55,7 @@ def _require_cron_or_superadmin(self, *args, **kwargs): raise self.UnauthorizedUserException( 'You do not have the credentials to access this page.') else: - return handler(self, *args, **kwargs) + return func(self, *args, **kwargs) return _require_cron_or_superadmin From 37a43ca249ffd2b60bf98f791995048ce0ec5269 Mon Sep 17 00:00:00 2001 From: Tony Jiang Date: Sun, 12 Mar 2017 18:17:35 -0400 Subject: [PATCH 045/173] Fix part of #2863: Create front-end domain object for Interactions (#3049) * Create Interaction front-end domain object. * Cleanup * fix front end tests * remove console logs * Lint * Remove comment * Remove console logs * add interaction object dependency to player * Convert backend state dicts to front end domain obejcts for computeGraphService * Convert states to objects for history tab * Convert state objects to backend dicts for YAML conversion in history tab * Renaming in historyTab protractor test * Add classifier_model_id to state domain object * renaming * rename rule_specs * rename to confirmedUnclassifiedAnswers * confirmedUnclassifiedAnswers rename in tests * rename default_outcome and customization_args in interaction object to camelCase * Missed renaming * Remove console logs * messed up merge * Remove console logs * Fixes * Create StatesObjectFactory * Fix new state template test --- .../VersionDiffVisualizationDirective.js | 4 +- .../exploration/AnswerGroupObjectFactory.js | 5 ++ .../exploration/ExplorationObjectFactory.js | 2 +- .../exploration/InteractionObjectFactory.js | 67 +++++++++++++++++++ .../domain/exploration/StateObjectFactory.js | 34 +++------- .../domain/exploration/StatesObjectFactory.js | 35 ++++++++++ .../exploration_editor/EditorServices.js | 39 +++++------ .../exploration_editor/EditorServicesSpec.js | 10 +-- .../ExplorationDiffService.js | 6 +- .../ExplorationSaveService.js | 10 ++- .../ParameterMetadataService.js | 2 +- .../editor_tab/StateEditor.js | 10 +-- .../editor_tab/StateEditorSpec.js | 57 ++++++++-------- .../editor_tab/StateInteraction.js | 8 +-- .../editor_tab/StateInteractionSpec.js | 6 +- .../editor_tab/StateResponses.js | 4 +- .../exploration_editor.html | 2 + .../history_tab/HistoryServices.js | 11 +-- .../statistics_tab/StatisticsTab.js | 7 +- .../AnswerClassificationService.js | 4 +- .../AnswerFeedbackPairDirective.js | 4 +- .../exploration_player.html | 1 + 22 files changed, 218 insertions(+), 110 deletions(-) create mode 100644 core/templates/dev/head/domain/exploration/InteractionObjectFactory.js create mode 100644 core/templates/dev/head/domain/exploration/StatesObjectFactory.js diff --git a/core/templates/dev/head/components/VersionDiffVisualizationDirective.js b/core/templates/dev/head/components/VersionDiffVisualizationDirective.js index 4fc58597fa45..19a4b25e75d5 100644 --- a/core/templates/dev/head/components/VersionDiffVisualizationDirective.js +++ b/core/templates/dev/head/components/VersionDiffVisualizationDirective.js @@ -258,7 +258,7 @@ oppia.directive('versionDiffVisualization', [function() { if (newState) { $http.post(STATE_YAML_URL, { - state_dict: newState, + state_dict: newState.toBackendDict(), width: 50 }).then(function(response) { $scope.yamlStrs.leftPane = response.data.yaml; @@ -274,7 +274,7 @@ oppia.directive('versionDiffVisualization', [function() { if (oldState) { $http.post(STATE_YAML_URL, { - state_dict: oldState, + state_dict: oldState.toBackendDict(), width: 50 }).then(function(response) { $scope.yamlStrs.rightPane = response.data.yaml; diff --git a/core/templates/dev/head/domain/exploration/AnswerGroupObjectFactory.js b/core/templates/dev/head/domain/exploration/AnswerGroupObjectFactory.js index 32dc65459bca..aca1b0df4580 100644 --- a/core/templates/dev/head/domain/exploration/AnswerGroupObjectFactory.js +++ b/core/templates/dev/head/domain/exploration/AnswerGroupObjectFactory.js @@ -12,6 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +/** + * @fileoverview Factory for creating new frontend instances of AnswerGroup + * domain objects. + */ + oppia.factory('AnswerGroupObjectFactory', [function() { var AnswerGroup = function(ruleSpecs, outcome, correct) { this.ruleSpecs = ruleSpecs; diff --git a/core/templates/dev/head/domain/exploration/ExplorationObjectFactory.js b/core/templates/dev/head/domain/exploration/ExplorationObjectFactory.js index 853f3c3e7d1f..b5c9d126ad70 100644 --- a/core/templates/dev/head/domain/exploration/ExplorationObjectFactory.js +++ b/core/templates/dev/head/domain/exploration/ExplorationObjectFactory.js @@ -68,7 +68,7 @@ oppia.factory('ExplorationObjectFactory', [ Exploration.prototype.getInteractionCustomizationArgs = function(stateName) { - return this.states[stateName].interaction.customization_args; + return this.states[stateName].interaction.customizationArgs; }; Exploration.prototype.getInteractionInstructions = function(stateName) { diff --git a/core/templates/dev/head/domain/exploration/InteractionObjectFactory.js b/core/templates/dev/head/domain/exploration/InteractionObjectFactory.js new file mode 100644 index 000000000000..2b24eeb38cc4 --- /dev/null +++ b/core/templates/dev/head/domain/exploration/InteractionObjectFactory.js @@ -0,0 +1,67 @@ +// Copyright 2015 The Oppia Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Factory for creating new frontend instances of Interaction + * domain objects. + */ + +oppia.factory('InteractionObjectFactory', [ + 'AnswerGroupObjectFactory', function(AnswerGroupObjectFactory) { + var Interaction = function(answerGroupBackendDicts, + confirmedUnclassifiedAnswers, customizationArgs, defaultOutcome, + fallbacks, id) { + this.answerGroups = generateAnswerGroupsFromBackend( + answerGroupBackendDicts); + this.confirmedUnclassifiedAnswers = confirmedUnclassifiedAnswers; + this.customizationArgs = customizationArgs; + this.defaultOutcome = defaultOutcome; + this.fallbacks = fallbacks; + this.id = id; + }; + + var generateAnswerGroupsFromBackend = function(answerGroupBackendDicts) { + var answerGroups = answerGroupBackendDicts.map(function(answerGroupDict) { + return AnswerGroupObjectFactory.create( + answerGroupDict.rule_specs, answerGroupDict.outcome, false); + }); + + return answerGroups; + }; + + Interaction.prototype.toBackendDict = function() { + return { + answer_groups: this.answerGroups.map(function(answerGroup) { + return answerGroup.toBackendDict(); + }), + confirmed_unclassified_answers: this.confirmedUnclassifiedAnswers, + customization_args: this.customizationArgs, + default_outcome: this.defaultOutcome, + fallbacks: this.fallbacks, + id: this.id + }; + }; + + Interaction.create = function(interactionDict) { + return new Interaction( + interactionDict.answer_groups, + interactionDict.confirmed_unclassified_answers, + interactionDict.customization_args, + interactionDict.default_outcome, + interactionDict.fallbacks, + interactionDict.id); + }; + + return Interaction; +}]); diff --git a/core/templates/dev/head/domain/exploration/StateObjectFactory.js b/core/templates/dev/head/domain/exploration/StateObjectFactory.js index 9c6f03e8c85a..ef6aa3609eb4 100644 --- a/core/templates/dev/head/domain/exploration/StateObjectFactory.js +++ b/core/templates/dev/head/domain/exploration/StateObjectFactory.js @@ -18,40 +18,23 @@ */ oppia.factory('StateObjectFactory', [ - 'AnswerGroupObjectFactory', - function(AnswerGroupObjectFactory) { - var State = function(name, content, interaction, paramChanges) { + 'AnswerGroupObjectFactory', 'InteractionObjectFactory', + function(AnswerGroupObjectFactory, InteractionObjectFactory) { + var State = function(name, classifierModelId, content, + interactionBackendDict, paramChanges) { this.name = name; + this.classifierModelId = classifierModelId; this.content = content; - this.interaction = { - answer_groups: - generateAnswerGroupsFromBackend(interaction.answer_groups), - confirmed_unclassified_answers: - interaction.confirmed_unclassified_answers, - customization_args: interaction.customization_args, - default_outcome: interaction.default_outcome, - fallbacks: interaction.fallbacks, - id: interaction.id - }; + this.interaction = InteractionObjectFactory.create(interactionBackendDict); this.paramChanges = paramChanges; }; - var generateAnswerGroupsFromBackend = function(answerGroupBackendDicts) { - var answerGroups = answerGroupBackendDicts.map(function(answerGroupDict) { - return AnswerGroupObjectFactory.create( - answerGroupDict.rule_specs, - answerGroupDict.outcome, - answerGroupDict.correct); - }); - - return answerGroups; - }; - // Instance methods. State.prototype.toBackendDict = function() { return { content: this.content, - interaction: this.interaction, + classifier_model_id: this.classifierModelId, + interaction: this.interaction.toBackendDict(), param_changes: this.paramChanges }; }; @@ -61,6 +44,7 @@ oppia.factory('StateObjectFactory', [ State.create = function(stateName, stateDict) { return new State( stateName, + stateDict.classifier_model_id, stateDict.content, stateDict.interaction, stateDict.param_changes); diff --git a/core/templates/dev/head/domain/exploration/StatesObjectFactory.js b/core/templates/dev/head/domain/exploration/StatesObjectFactory.js new file mode 100644 index 000000000000..db96f16da98c --- /dev/null +++ b/core/templates/dev/head/domain/exploration/StatesObjectFactory.js @@ -0,0 +1,35 @@ +// Copyright 2015 The Oppia Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Factory for creating new frontend instances of State + * domain objects given a list of backend state dictionaries. + */ + + oppia.factory('StatesObjectFactory', ['StateObjectFactory', + function(StateObjectFactory) { + var create = function(statesDict) { + var stateObjectsDict = {}; + for (var stateName in statesDict) { + stateObjectsDict[stateName] = StateObjectFactory.create( + stateName, statesDict[stateName]); + } + return stateObjectsDict; + }; + + return { + create: create + }; + } +]); diff --git a/core/templates/dev/head/pages/exploration_editor/EditorServices.js b/core/templates/dev/head/pages/exploration_editor/EditorServices.js index abf45dad1662..c8d634079770 100644 --- a/core/templates/dev/head/pages/exploration_editor/EditorServices.js +++ b/core/templates/dev/head/pages/exploration_editor/EditorServices.js @@ -804,15 +804,15 @@ oppia.factory('explorationStatesService', [ // Maps backend names to the corresponding frontend dict accessor lists. var PROPERTY_REF_DATA = { - answer_groups: ['interaction', 'answer_groups'], + answer_groups: ['interaction', 'answerGroups'], confirmed_unclassified_answers: [ - 'interaction', 'confirmed_unclassified_answers'], + 'interaction', 'confirmedUnclassifiedAnswers'], content: ['content'], - default_outcome: ['interaction', 'default_outcome'], + default_outcome: ['interaction', 'defaultOutcome'], param_changes: ['paramChanges'], fallbacks: ['interaction', 'fallbacks'], widget_id: ['interaction', 'id'], - widget_customization_args: ['interaction', 'customization_args'] + widget_customization_args: ['interaction', 'customizationArgs'] }; var _setState = function(stateName, stateData, refreshGraph) { @@ -1040,15 +1040,15 @@ oppia.factory('explorationStatesService', [ delete _states[deleteStateName]; for (var otherStateName in _states) { var interaction = _states[otherStateName].interaction; - var groups = interaction.answer_groups; + var groups = interaction.answerGroups; for (var i = 0; i < groups.length; i++) { if (groups[i].outcome.dest === deleteStateName) { groups[i].outcome.dest = otherStateName; } } - if (interaction.default_outcome) { - if (interaction.default_outcome.dest === deleteStateName) { - interaction.default_outcome.dest = otherStateName; + if (interaction.defaultOutcome) { + if (interaction.defaultOutcome.dest === deleteStateName) { + interaction.defaultOutcome.dest = otherStateName; } } @@ -1091,15 +1091,15 @@ oppia.factory('explorationStatesService', [ for (var otherStateName in _states) { var interaction = _states[otherStateName].interaction; - var groups = interaction.answer_groups; + var groups = interaction.answerGroups; for (var i = 0; i < groups.length; i++) { if (groups[i].outcome.dest === oldStateName) { groups[i].outcome.dest = newStateName; } } - if (interaction.default_outcome) { - if (interaction.default_outcome.dest === oldStateName) { - interaction.default_outcome.dest = newStateName; + if (interaction.defaultOutcome) { + if (interaction.defaultOutcome.dest === oldStateName) { + interaction.defaultOutcome.dest = newStateName; } } @@ -1650,11 +1650,12 @@ oppia.factory('newStateTemplateService', getNewStateTemplate: function(newStateName) { var newStateTemplate = angular.copy(GLOBALS.NEW_STATE_TEMPLATE); var newState = StateObjectFactory.create(newStateName, { + classifier_model_id: newStateTemplate.classifier_model_id, content: newStateTemplate.content, interaction: newStateTemplate.interaction, param_changes: newStateTemplate.param_changes }); - newState.interaction.default_outcome.dest = newStateName; + newState.interaction.defaultOutcome.dest = newStateName; return newState; } }; @@ -1676,7 +1677,7 @@ oppia.factory('computeGraphService', [ nodes[stateName] = stateName; if (interaction.id) { - var groups = interaction.answer_groups; + var groups = interaction.answerGroups; for (var h = 0; h < groups.length; h++) { links.push({ source: stateName, @@ -1685,10 +1686,10 @@ oppia.factory('computeGraphService', [ }); } - if (interaction.default_outcome) { + if (interaction.defaultOutcome) { links.push({ source: stateName, - target: interaction.default_outcome.dest, + target: interaction.defaultOutcome.dest, isFallback: false }); } @@ -1928,7 +1929,7 @@ oppia.factory('explorationWarningsService', [ var _getAnswerGroupIndexesWithEmptyClassifiers = function(state) { var indexes = []; - var answerGroups = state.interaction.answer_groups; + var answerGroups = state.interaction.answerGroups; for (var i = 0; i < answerGroups.length; i++) { var group = answerGroups[i]; if (group.ruleSpecs.length === 1 && @@ -1974,8 +1975,8 @@ oppia.factory('explorationWarningsService', [ 'oppiaInteractive' + _states[stateName].interaction.id + 'Validator'); var interactionWarnings = $filter(validatorName)( - stateName, interaction.customization_args, - interaction.answer_groups, interaction.default_outcome); + stateName, interaction.customizationArgs, + interaction.answerGroups, interaction.defaultOutcome); for (var j = 0; j < interactionWarnings.length; j++) { if (stateWarnings.hasOwnProperty(stateName)) { diff --git a/core/templates/dev/head/pages/exploration_editor/EditorServicesSpec.js b/core/templates/dev/head/pages/exploration_editor/EditorServicesSpec.js index 26e84cbc3fd4..0abeb6c42ec7 100644 --- a/core/templates/dev/head/pages/exploration_editor/EditorServicesSpec.js +++ b/core/templates/dev/head/pages/exploration_editor/EditorServicesSpec.js @@ -801,6 +801,7 @@ describe('New state template service', function() { beforeEach(inject(function($injector) { GLOBALS.NEW_STATE_TEMPLATE = { + classifier_model_id: null, content: [{ type: 'text', value: '' @@ -834,14 +835,15 @@ describe('New state template service', function() { nsts.getNewStateTemplate(NEW_STATE_NAME) ))).toEqual({ name: 'new state name', + classifierModelId: null, content: [{ type: 'text', value: '' }], interaction: { - answer_groups: [], - confirmed_unclassified_answers: [], - customization_args: { + answerGroups: [], + confirmedUnclassifiedAnswers: [], + customizationArgs: { rows: { value: 1 }, @@ -849,7 +851,7 @@ describe('New state template service', function() { value: 'Type your answer here.' } }, - default_outcome: { + defaultOutcome: { dest: NEW_STATE_NAME, feedback: [], param_changes: [] diff --git a/core/templates/dev/head/pages/exploration_editor/ExplorationDiffService.js b/core/templates/dev/head/pages/exploration_editor/ExplorationDiffService.js index 063baf19b28b..c0fedac02083 100644 --- a/core/templates/dev/head/pages/exploration_editor/ExplorationDiffService.js +++ b/core/templates/dev/head/pages/exploration_editor/ExplorationDiffService.js @@ -227,13 +227,13 @@ oppia.factory('ExplorationDiffService', [ } for (var stateName in states) { var interaction = states[stateName].interaction; - var groups = interaction.answer_groups; + var groups = interaction.answerGroups; for (var h = 0; h < groups.length; h++) { var dest = groups[h].outcome.dest; adjMatrix[stateIds[stateName]][stateIds[dest]] = true; } - if (interaction.default_outcome) { - var defaultDest = interaction.default_outcome.dest; + if (interaction.defaultOutcome) { + var defaultDest = interaction.defaultOutcome.dest; adjMatrix[stateIds[stateName]][stateIds[defaultDest]] = true; } } diff --git a/core/templates/dev/head/pages/exploration_editor/ExplorationSaveService.js b/core/templates/dev/head/pages/exploration_editor/ExplorationSaveService.js index e64fa0bc7b9e..f050114f733d 100644 --- a/core/templates/dev/head/pages/exploration_editor/ExplorationSaveService.js +++ b/core/templates/dev/head/pages/exploration_editor/ExplorationSaveService.js @@ -25,6 +25,7 @@ oppia.factory('explorationSaveService', [ 'explorationWarningsService', 'ExplorationDiffService', 'explorationInitStateNameService', 'routerService', 'focusService', 'changeListService', 'siteAnalyticsService', + 'StateObjectFactory', function( $modal, $timeout, $rootScope, $log, $q, alertsService, explorationData, explorationStatesService, @@ -33,7 +34,8 @@ oppia.factory('explorationSaveService', [ explorationLanguageCodeService, explorationRightsService, explorationWarningsService, ExplorationDiffService, explorationInitStateNameService, routerService, - focusService, changeListService, siteAnalyticsService) { + focusService, changeListService, siteAnalyticsService, + StateObjectFactory) { // Whether or not a save action is currently in progress // (request has been sent to backend but no reply received yet) var saveIsInProgress = false; @@ -448,7 +450,11 @@ oppia.factory('explorationSaveService', [ } explorationData.getLastSavedData().then(function(data) { - var oldStates = data.states; + var oldStates = {}; + for (var stateName in data.states) { + oldStates[stateName] = + (StateObjectFactory.create(stateName, data.states[stateName])); + } var newStates = explorationStatesService.getStates(); var diffGraphData = ExplorationDiffService.getDiffGraphData( oldStates, newStates, [{ diff --git a/core/templates/dev/head/pages/exploration_editor/ParameterMetadataService.js b/core/templates/dev/head/pages/exploration_editor/ParameterMetadataService.js index 6ce063928805..c24d7f3e9fe2 100644 --- a/core/templates/dev/head/pages/exploration_editor/ParameterMetadataService.js +++ b/core/templates/dev/head/pages/exploration_editor/ParameterMetadataService.js @@ -105,7 +105,7 @@ oppia.factory('parameterMetadataService', [ }); // Finally, the rule feedback strings are evaluated. - state.interaction.answer_groups.forEach(function(group) { + state.interaction.answerGroups.forEach(function(group) { for (var k = 0; k < group.outcome.feedback.length; k++) { expressionInterpolationService.getParamsFromString( group.outcome.feedback[k]).forEach(function(paramName) { diff --git a/core/templates/dev/head/pages/exploration_editor/editor_tab/StateEditor.js b/core/templates/dev/head/pages/exploration_editor/editor_tab/StateEditor.js index 01428c700c9b..75261e0c09ce 100644 --- a/core/templates/dev/head/pages/exploration_editor/editor_tab/StateEditor.js +++ b/core/templates/dev/head/pages/exploration_editor/editor_tab/StateEditor.js @@ -320,13 +320,13 @@ oppia.factory('trainingDataService', [ var potentialOutcomes = []; var interaction = state.interaction; - for (var i = 0; i < interaction.answer_groups.length; i++) { - potentialOutcomes.push(interaction.answer_groups[i].outcome); + for (var i = 0; i < interaction.answerGroups.length; i++) { + potentialOutcomes.push(interaction.answerGroups[i].outcome); } - if (interaction.default_outcome) { - var outcome = interaction.default_outcome; - potentialOutcomes.push(interaction.default_outcome); + if (interaction.defaultOutcome) { + var outcome = interaction.defaultOutcome; + potentialOutcomes.push(interaction.defaultOutcome); } return potentialOutcomes; diff --git a/core/templates/dev/head/pages/exploration_editor/editor_tab/StateEditorSpec.js b/core/templates/dev/head/pages/exploration_editor/editor_tab/StateEditorSpec.js index a71617125e73..1a2da22f9d7e 100644 --- a/core/templates/dev/head/pages/exploration_editor/editor_tab/StateEditorSpec.js +++ b/core/templates/dev/head/pages/exploration_editor/editor_tab/StateEditorSpec.js @@ -263,12 +263,11 @@ describe('State Editor controller', function() { }); var state = ess.getState('State'); - rs.init({ - answerGroups: state.interaction.answer_groups, - defaultOutcome: state.interaction.default_outcome, + answerGroups: state.interaction.answerGroups, + defaultOutcome: state.interaction.defaultOutcome, confirmedUnclassifiedAnswers: ( - state.interaction.confirmed_unclassified_answers) + state.interaction.confirmedUnclassifiedAnswers) }); ecs.setActiveStateName('State'); @@ -314,7 +313,7 @@ describe('State Editor controller', function() { // Training the first answer of a group should add a new classifier. tds.trainAnswerGroup(0, 'text answer'); var state = ess.getState('State'); - expect(state.interaction.answer_groups[0].ruleSpecs[1]).toEqual({ + expect(state.interaction.answerGroups[0].ruleSpecs[1]).toEqual({ rule_type: CLASSIFIER_RULESPEC_STR, inputs: { training_data: ['text answer'] @@ -325,7 +324,7 @@ describe('State Editor controller', function() { // the training data. tds.trainAnswerGroup(0, 'second answer'); state = ess.getState('State'); - expect(state.interaction.answer_groups[0].ruleSpecs[1]).toEqual({ + expect(state.interaction.answerGroups[0].ruleSpecs[1]).toEqual({ rule_type: CLASSIFIER_RULESPEC_STR, inputs: { training_data: ['text answer', 'second answer'] @@ -336,7 +335,7 @@ describe('State Editor controller', function() { // unclassified answers. tds.trainDefaultResponse('third answer'); state = ess.getState('State'); - expect(state.interaction.confirmed_unclassified_answers).toEqual([ + expect(state.interaction.confirmedUnclassifiedAnswers).toEqual([ 'third answer' ]); }); @@ -351,39 +350,39 @@ describe('State Editor controller', function() { // Verify initial state. var state = ess.getState('State'); - expect(state.interaction.answer_groups[0].ruleSpecs[1]).toEqual({ + expect(state.interaction.answerGroups[0].ruleSpecs[1]).toEqual({ rule_type: CLASSIFIER_RULESPEC_STR, inputs: { training_data: ['text answer', 'second answer'] } }); - expect(state.interaction.confirmed_unclassified_answers).toEqual([ + expect(state.interaction.confirmedUnclassifiedAnswers).toEqual([ 'third answer' ]); // Try to retrain the second answer (answer group -> default response). tds.trainDefaultResponse('second answer'); state = ess.getState('State'); - expect(state.interaction.answer_groups[0].ruleSpecs[1]).toEqual({ + expect(state.interaction.answerGroups[0].ruleSpecs[1]).toEqual({ rule_type: CLASSIFIER_RULESPEC_STR, inputs: { training_data: ['text answer'] } }); - expect(state.interaction.confirmed_unclassified_answers).toEqual([ + expect(state.interaction.confirmedUnclassifiedAnswers).toEqual([ 'third answer', 'second answer' ]); // Try to retrain the third answer (default response -> answer group). tds.trainAnswerGroup(0, 'third answer'); state = ess.getState('State'); - expect(state.interaction.answer_groups[0].ruleSpecs[1]).toEqual({ + expect(state.interaction.answerGroups[0].ruleSpecs[1]).toEqual({ rule_type: CLASSIFIER_RULESPEC_STR, inputs: { training_data: ['text answer', 'third answer'] } }); - expect(state.interaction.confirmed_unclassified_answers).toEqual([ + expect(state.interaction.confirmedUnclassifiedAnswers).toEqual([ 'second answer' ]); }); @@ -395,46 +394,46 @@ describe('State Editor controller', function() { // Verify initial state. var state = ess.getState('State'); - expect(state.interaction.answer_groups[0].ruleSpecs[1]).toEqual({ + expect(state.interaction.answerGroups[0].ruleSpecs[1]).toEqual({ rule_type: CLASSIFIER_RULESPEC_STR, inputs: { training_data: ['text answer'] } }); - expect(state.interaction.confirmed_unclassified_answers).toEqual([ + expect(state.interaction.confirmedUnclassifiedAnswers).toEqual([ 'second answer' ]); // Ensure emptying the default unclassified answers is handled properly. tds.trainAnswerGroup(0, 'second answer'); state = ess.getState('State'); - expect(state.interaction.answer_groups[0].ruleSpecs[1]).toEqual({ + expect(state.interaction.answerGroups[0].ruleSpecs[1]).toEqual({ rule_type: CLASSIFIER_RULESPEC_STR, inputs: { training_data: ['text answer', 'second answer'] } }); - expect(state.interaction.confirmed_unclassified_answers).toEqual([]); + expect(state.interaction.confirmedUnclassifiedAnswers).toEqual([]); // Ensure emptying the answer group's classifier properly deletes the rule // since there is another rule in the group. tds.trainDefaultResponse('second answer'); tds.trainDefaultResponse('text answer'); state = ess.getState('State'); - expect(state.interaction.answer_groups[0].ruleSpecs).toEqual([{ + expect(state.interaction.answerGroups[0].ruleSpecs).toEqual([{ rule_type: 'Contains', inputs: { x: 'Test' } }]); - expect(state.interaction.confirmed_unclassified_answers).toEqual([ + expect(state.interaction.confirmedUnclassifiedAnswers).toEqual([ 'second answer', 'text answer' ]); // Training the answer group should add the classifier back. tds.trainAnswerGroup(0, 'second answer'); state = ess.getState('State'); - expect(state.interaction.answer_groups[0].ruleSpecs).toEqual([{ + expect(state.interaction.answerGroups[0].ruleSpecs).toEqual([{ rule_type: 'Contains', inputs: { x: 'Test' @@ -449,18 +448,18 @@ describe('State Editor controller', function() { // Removing the the 'contains' rule from the group and then removing the // training data should not remove the classifier. - state.interaction.answer_groups[0].ruleSpecs.splice(0, 1); + state.interaction.answerGroups[0].ruleSpecs.splice(0, 1); ess.setState('State', state); rs.init({ - answerGroups: state.interaction.answer_groups, - defaultOutcome: state.interaction.default_outcome, + answerGroups: state.interaction.answerGroups, + defaultOutcome: state.interaction.defaultOutcome, confirmedUnclassifiedAnswers: ( - state.interaction.confirmed_unclassified_answers) + state.interaction.confirmedUnclassifiedAnswers) }); tds.trainDefaultResponse('second answer'); state = ess.getState('State'); - expect(state.interaction.answer_groups[0].ruleSpecs).toEqual([{ + expect(state.interaction.answerGroups[0].ruleSpecs).toEqual([{ rule_type: CLASSIFIER_RULESPEC_STR, inputs: { training_data: [] @@ -475,20 +474,20 @@ describe('State Editor controller', function() { // Verify initial state. var state = ess.getState('State'); - expect(state.interaction.answer_groups[0].ruleSpecs[1]).toEqual({ + expect(state.interaction.answerGroups[0].ruleSpecs[1]).toEqual({ rule_type: CLASSIFIER_RULESPEC_STR, inputs: { training_data: ['text answer'] } }); - expect(state.interaction.confirmed_unclassified_answers).toEqual([ + expect(state.interaction.confirmedUnclassifiedAnswers).toEqual([ 'second answer' ]); // Training a duplicate answer for the answer group should change nothing. tds.trainAnswerGroup(0, 'text answer'); state = ess.getState('State'); - expect(state.interaction.answer_groups[0].ruleSpecs[1]).toEqual({ + expect(state.interaction.answerGroups[0].ruleSpecs[1]).toEqual({ rule_type: CLASSIFIER_RULESPEC_STR, inputs: { training_data: ['text answer'] @@ -499,7 +498,7 @@ describe('State Editor controller', function() { // nothing. tds.trainDefaultResponse('second answer'); state = ess.getState('State'); - expect(state.interaction.answer_groups[0].ruleSpecs[1]).toEqual({ + expect(state.interaction.answerGroups[0].ruleSpecs[1]).toEqual({ rule_type: CLASSIFIER_RULESPEC_STR, inputs: { training_data: ['text answer'] diff --git a/core/templates/dev/head/pages/exploration_editor/editor_tab/StateInteraction.js b/core/templates/dev/head/pages/exploration_editor/editor_tab/StateInteraction.js index 015dbb9b6452..8e33a3e4f055 100644 --- a/core/templates/dev/head/pages/exploration_editor/editor_tab/StateInteraction.js +++ b/core/templates/dev/head/pages/exploration_editor/editor_tab/StateInteraction.js @@ -102,14 +102,14 @@ oppia.controller('StateInteraction', [ stateInteractionIdService.init( $scope.stateName, stateData.interaction.id); stateCustomizationArgsService.init( - $scope.stateName, stateData.interaction.customization_args); + $scope.stateName, stateData.interaction.customizationArgs); $rootScope.$broadcast('initializeAnswerGroups', { interactionId: stateData.interaction.id, - answerGroups: stateData.interaction.answer_groups, - defaultOutcome: stateData.interaction.default_outcome, + answerGroups: stateData.interaction.answerGroups, + defaultOutcome: stateData.interaction.defaultOutcome, confirmedUnclassifiedAnswers: ( - stateData.interaction.confirmed_unclassified_answers) + stateData.interaction.confirmedUnclassifiedAnswers) }); _updateInteractionPreviewAndAnswerChoices(); diff --git a/core/templates/dev/head/pages/exploration_editor/editor_tab/StateInteractionSpec.js b/core/templates/dev/head/pages/exploration_editor/editor_tab/StateInteractionSpec.js index 166092f5fd82..c9f2be4222b9 100644 --- a/core/templates/dev/head/pages/exploration_editor/editor_tab/StateInteractionSpec.js +++ b/core/templates/dev/head/pages/exploration_editor/editor_tab/StateInteractionSpec.js @@ -137,7 +137,7 @@ describe('State Interaction controller', function() { siis.init( 'First State', state.interaction.id, state.interaction, 'widget_id'); scas.init( - 'First State', state.interaction.customization_args, + 'First State', state.interaction.customizationArgs, state.interaction, 'widget_customization_args'); siis.displayed = 'TerminalInteraction'; @@ -158,7 +158,7 @@ describe('State Interaction controller', function() { siis.init( 'End State', state.interaction.id, state.interaction, 'widget_id'); scas.init( - 'End State', state.interaction.customization_args, + 'End State', state.interaction.customizationArgs, state.interaction, 'widget_customization_args'); siis.displayed = 'TerminalInteraction'; @@ -180,7 +180,7 @@ describe('State Interaction controller', function() { siis.init( 'End State', state.interaction.id, state.interaction, 'widget_id'); scas.init( - 'End State', state.interaction.customization_args, + 'End State', state.interaction.customizationArgs, state.interaction, 'widget_customization_args'); siis.displayed = 'TextInput'; diff --git a/core/templates/dev/head/pages/exploration_editor/editor_tab/StateResponses.js b/core/templates/dev/head/pages/exploration_editor/editor_tab/StateResponses.js index 6a403b212ef3..b52d497476e2 100644 --- a/core/templates/dev/head/pages/exploration_editor/editor_tab/StateResponses.js +++ b/core/templates/dev/head/pages/exploration_editor/editor_tab/StateResponses.js @@ -635,8 +635,8 @@ oppia.controller('StateResponses', [ var answerGroupIndex = classificationResult.answerGroupIndex; var ruleSpecIndex = classificationResult.ruleSpecIndex; if (answerGroupIndex !== - _state.interaction.answer_groups.length && - _state.interaction.answer_groups[ + _state.interaction.answerGroups.length && + _state.interaction.answerGroups[ answerGroupIndex].ruleSpecs[ ruleSpecIndex].rule_type !== CLASSIFIER_RULESPEC_STR) { diff --git a/core/templates/dev/head/pages/exploration_editor/exploration_editor.html b/core/templates/dev/head/pages/exploration_editor/exploration_editor.html index f3e44007f41a..9ec611deabca 100644 --- a/core/templates/dev/head/pages/exploration_editor/exploration_editor.html +++ b/core/templates/dev/head/pages/exploration_editor/exploration_editor.html @@ -288,6 +288,8 @@ + + diff --git a/core/templates/dev/head/pages/exploration_editor/history_tab/HistoryServices.js b/core/templates/dev/head/pages/exploration_editor/history_tab/HistoryServices.js index 8a236748ed7a..d5a37b03acf1 100644 --- a/core/templates/dev/head/pages/exploration_editor/history_tab/HistoryServices.js +++ b/core/templates/dev/head/pages/exploration_editor/history_tab/HistoryServices.js @@ -127,10 +127,10 @@ oppia.factory('versionsTreeService', [function() { oppia.factory('compareVersionsService', [ '$http', '$q', 'versionsTreeService', 'explorationData', - 'ExplorationDiffService', + 'ExplorationDiffService', 'StateObjectFactory', 'StatesObjectFactory', function( $http, $q, versionsTreeService, explorationData, - ExplorationDiffService) { + ExplorationDiffService, StateObjectFactory, StatesObjectFactory) { /** * Constructs the combined list of changes needed to get from v1 to v2. * @@ -198,12 +198,15 @@ oppia.factory('compareVersionsService', [ v1Data: $http.get(explorationDataUrl + v1), v2Data: $http.get(explorationDataUrl + v2) }).then(function(response) { - var v1States = response.v1Data.data.states; - var v2States = response.v2Data.data.states; + var v1StatesDict = response.v1Data.data.states; + var v2StatesDict = response.v2Data.data.states; // Track changes from v1 to LCA, and then from LCA to v2. var lca = versionsTreeService.findLCA(v1, v2); + var v1States = StatesObjectFactory.create(v1StatesDict); + var v2States = StatesObjectFactory.create(v2StatesDict); + var diffGraphData = ExplorationDiffService.getDiffGraphData( v1States, v2States, [{ changeList: _getCombinedChangeList(lca, v1, false), diff --git a/core/templates/dev/head/pages/exploration_editor/statistics_tab/StatisticsTab.js b/core/templates/dev/head/pages/exploration_editor/statistics_tab/StatisticsTab.js index 9b782e3e0de0..4f2b6b844508 100644 --- a/core/templates/dev/head/pages/exploration_editor/statistics_tab/StatisticsTab.js +++ b/core/templates/dev/head/pages/exploration_editor/statistics_tab/StatisticsTab.js @@ -20,9 +20,11 @@ oppia.controller('StatisticsTab', [ '$scope', '$http', '$modal', 'alertsService', 'explorationStatesService', 'explorationData', 'computeGraphService', 'oppiaDatetimeFormatter', + 'StateObjectFactory', 'StatesObjectFactory', function( $scope, $http, $modal, alertsService, explorationStatesService, - explorationData, computeGraphService, oppiaDatetimeFormatter) { + explorationData, computeGraphService, oppiaDatetimeFormatter, + StateObjectFactory, StatesObjectFactory) { $scope.COMPLETION_RATE_CHART_OPTIONS = { chartAreaWidth: 300, colors: ['green', 'firebrick'], @@ -59,7 +61,8 @@ oppia.controller('StatisticsTab', [ '/createhandler/data/' + explorationData.explorationId); $http.get(explorationDataUrl).then(function(response) { - var states = response.data.states; + var statesDict = response.data.states; + var states = StatesObjectFactory.create(statesDict); var initStateName = response.data.init_state_name; $scope.statsGraphData = computeGraphService.compute( initStateName, states); diff --git a/core/templates/dev/head/pages/exploration_player/AnswerClassificationService.js b/core/templates/dev/head/pages/exploration_player/AnswerClassificationService.js index 1aeead251e99..9ed381a01907 100644 --- a/core/templates/dev/head/pages/exploration_player/AnswerClassificationService.js +++ b/core/templates/dev/head/pages/exploration_player/AnswerClassificationService.js @@ -98,8 +98,8 @@ oppia.factory('AnswerClassificationService', [ interactionRulesService) { var deferred = $q.defer(); var result = null; - var answerGroups = oldState.interaction.answer_groups; - var defaultOutcome = oldState.interaction.default_outcome; + var answerGroups = oldState.interaction.answerGroups; + var defaultOutcome = oldState.interaction.defaultOutcome; if (interactionRulesService) { result = classifyAnswer( answer, answerGroups, defaultOutcome, interactionRulesService); diff --git a/core/templates/dev/head/pages/exploration_player/AnswerFeedbackPairDirective.js b/core/templates/dev/head/pages/exploration_player/AnswerFeedbackPairDirective.js index 0ec7881bbd39..bffd893c5770 100644 --- a/core/templates/dev/head/pages/exploration_player/AnswerFeedbackPairDirective.js +++ b/core/templates/dev/head/pages/exploration_player/AnswerFeedbackPairDirective.js @@ -44,7 +44,7 @@ oppia.directive('answerFeedbackPair', [function() { if ($scope.data) { return oppiaExplorationHtmlFormatterService.getAnswerHtml( $scope.data.learnerAnswer, interaction.id, - interaction.customization_args); + interaction.customizationArgs); } }; @@ -59,7 +59,7 @@ oppia.directive('answerFeedbackPair', [function() { shortAnswerHtml = ( oppiaExplorationHtmlFormatterService.getShortAnswerHtml( $scope.data.learnerAnswer, interaction.id, - interaction.customization_args)); + interaction.customizationArgs)); } return shortAnswerHtml; }; diff --git a/core/templates/dev/head/pages/exploration_player/exploration_player.html b/core/templates/dev/head/pages/exploration_player/exploration_player.html index fef8c1be4d2e..e78887d2b6cb 100644 --- a/core/templates/dev/head/pages/exploration_player/exploration_player.html +++ b/core/templates/dev/head/pages/exploration_player/exploration_player.html @@ -187,6 +187,7 @@

Suggest a Change

+ From c4b24e615d695a7d8c9574a7b70e0e7d37b84122 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 13 Mar 2017 09:37:21 +0100 Subject: [PATCH 046/173] Localisation updates from https://translatewiki.net. --- assets/i18n/br.json | 30 ++++++++++++++++++++++++++++++ assets/i18n/hi.json | 2 +- assets/i18n/id.json | 10 +++++++++- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/assets/i18n/br.json b/assets/i18n/br.json index 56158fd3fc97..7c275a7c2117 100644 --- a/assets/i18n/br.json +++ b/assets/i18n/br.json @@ -13,6 +13,8 @@ "I18N_CREATE_EXPLORATION_QUESTION": "C'hoant ho peus da grouiñ un ergerzhadenn ?", "I18N_CREATE_EXPLORATION_TITLE": "Krouiñ un Ergerzhadenn", "I18N_CREATE_EXPLORATION_UPLOAD": "Pellgargañ", + "I18N_CREATE_NO_THANKS": "Nann, trugarez", + "I18N_CREATE_YES_PLEASE": "Ya, mar plij !", "I18N_DASHBOARD_COLLECTIONS": "Dastumadoù", "I18N_DASHBOARD_EXPLORATIONS": "Ergerzhadennoù", "I18N_DASHBOARD_OPEN_FEEDBACK": "Digeriñ an evezhiadennoù", @@ -26,6 +28,7 @@ "I18N_ERROR_HEADER_401": "Fazi 401", "I18N_ERROR_HEADER_404": "Fazi 404", "I18N_ERROR_HEADER_500": "Fazi 500", + "I18N_ERROR_MESSAGE_400": "Gwechoù ne c'hall ket an ardivinkoù kompren an dud. Setu unan eus ar gwechoù-se.", "I18N_ERROR_MESSAGE_401": "Ne c'hallit mont tre amañ. Buan, kit war-gil a-raok ne zegouezhfe ar c'helenner !", "I18N_ERROR_MESSAGE_404": "Hon digarezit, klasket hag azklasket hon eus c'hoazh hag adarre met ne c'hallomp ket, end-eeun, kavout ar bajenn-mañ.", "I18N_ERROR_MESSAGE_500": "Un dra zo tremenet fall met n'eo dre ho faot. Ur fazi diabarzh zo c'hoarvezet.", @@ -44,6 +47,7 @@ "I18N_FOOTER_DONATE": "Ober un donezon", "I18N_FOOTER_FOLLOW_US": "Heulhit ni", "I18N_FOOTER_FORUM": "Forom", + "I18N_FOOTER_GET_INVOLVED": "Kemer perzh", "I18N_FOOTER_GET_STARTED": "Kregiñ", "I18N_FOOTER_OPPIA_FOUNDATION": "An diazezadur Oppia", "I18N_FOOTER_PARTICIPATION_PLAYBOOK": "Levrig perzhiañ", @@ -56,6 +60,7 @@ "I18N_FORMS_TYPE_NUMBER_AT_MOST": "Ebarzhit un niver izeloc'h pe par da <[valeurMax]>, mar plij.", "I18N_FORMS_TYPE_NUMBER_INVALID_DECIMAL": "Ebarzhit un niver degel, mar plij", "I18N_GET_STARTED_PAGE_TITLE": "Kregiñ", + "I18N_INTERACTIONS_GRAPH_ADD_NODE": "Ouzhpennañ ur skoulm", "I18N_INTERACTIONS_GRAPH_DELETE": "Diverkañ", "I18N_INTERACTIONS_GRAPH_ERROR_INVALID": "N'eo ket mat ar grafik !", "I18N_INTERACTIONS_GRAPH_MOVE": "Dilec'hiañ", @@ -78,6 +83,7 @@ "I18N_INTERACTIONS_LOGIC_PROOF_SUBMIT": "Kas", "I18N_INTERACTIONS_MUSIC_CLEAR": "Diverkañ", "I18N_INTERACTIONS_MUSIC_PLAY": "C'hoari", + "I18N_INTERACTIONS_MUSIC_PLAY_SEQUENCE": "C'hoari ar sekañs pal", "I18N_INTERACTIONS_MUSIC_SUBMIT": "Kas", "I18N_INTERACTIONS_PENCILCODEEDITOR_ARE_YOU_SURE_YOU_WANT_TO_RESET_YOUR_CODE": "Ha sur oc'h hoc'h eus c'hoant da adderaouekaat ho kod ?", "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "Nullañ", @@ -147,6 +153,7 @@ "I18N_LIBRARY_NO_EXPLORATION_FOR_QUERY": "C'hem, ne glot hoc'h enklask gant ergerzhadenn ebet, war a seblant.", "I18N_LIBRARY_NO_EXPLORATION_GROUPS": "N'eus strollad ergerzhet ebet hag a c'haller diskwel.", "I18N_LIBRARY_NO_OBJECTIVE": "Pal diferet ebet.", + "I18N_LIBRARY_N_CATEGORIES": "{categoriesCount, plural, =1{1 Rummad} other{# Rummadoù}}", "I18N_LIBRARY_N_LANGUAGES": "{languagesCount, plural, =1{1 yezh} other{# yezh}}", "I18N_LIBRARY_PAGE_TITLE": "Levraoueg ergezhadennoù - Oppia", "I18N_LIBRARY_RATINGS_TOOLTIP": "Priziadennoù", @@ -174,13 +181,16 @@ "I18N_PLAYER_PREVIOUS_RESPONSES": "Respontoù kent (<[previousResponses]>)", "I18N_PLAYER_RATE_EXPLORATION": "Desket ho peus un dra bennak nevez ? Peseurt notenn al lakfec'h d'an ergerhadenn-mañ ?", "I18N_PLAYER_RATINGS_TOOLTIP": "Priziañ", + "I18N_PLAYER_REPORT_MODAL_BODY_AD": "Endalc'had toueller, postel strobus pe bruderezh", "I18N_PLAYER_REPORT_MODAL_BODY_ADDITIONAL_DETAILS": "Roit muioc'h a ditouroù evit an habaskaerien, mar plij :", "I18N_PLAYER_REPORT_MODAL_BODY_HEADER": "Petra eo ar gudenn ?", "I18N_PLAYER_REPORT_MODAL_BODY_OTHER": "All", "I18N_PLAYER_REPORT_MODAL_FOOTER_CANCEL": "Nullañ", "I18N_PLAYER_REPORT_MODAL_FOOTER_SUBMIT": "Kas", + "I18N_PLAYER_REPORT_SUCCESS_MODAL_BODY": "Kaset eo bet ho kemenn d'an habaskaerien evit gwririañ.", "I18N_PLAYER_REPORT_SUCCESS_MODAL_CLOSE": "Serriñ", "I18N_PLAYER_REPORT_SUCCESS_MODAL_HEADER": "Trugarez !", + "I18N_PLAYER_REPORT_TOOLTIP": "Kemenn an ergerzhadenn", "I18N_PLAYER_RETURN_TO_COLLECTION": "Distreiñ da <[collectionTitle]>", "I18N_PLAYER_RETURN_TO_EDITOR": "Ditreiñ d'an embanner", "I18N_PLAYER_SHARE_EXPLORATION": "Plijet oc'h bet gant an ergezhadenn ? Rannit anezhi gant ho mignoned !", @@ -205,6 +215,9 @@ "I18N_PREFERENCES_PICTURE": "Skeudenn", "I18N_PREFERENCES_PREFERRED_SITE_LANGUAGE_EXPLAIN": "Ar yezh eo a zo skritellet el lec'hienn diskwelet.", "I18N_PREFERENCES_PROFILE_PICTURE_ADD": "Ouzhpennañ ur yezh d'ar profil", + "I18N_PREFERENCES_PROFILE_PICTURE_DRAG": "Lakaat da riklañ evit adsterniañ hag adventañ :", + "I18N_PREFERENCES_PROFILE_PICTURE_ERROR": "Fazi : Ne c'haller ket lenn ar restr skeudenn.", + "I18N_PREFERENCES_PROFILE_PICTURE_UPLOAD": "Pellgargañ skeudenn ar profil", "I18N_PREFERENCES_SUBJECT_INTERESTS": "Diduadennoù", "I18N_PREFERENCES_SUBJECT_INTERESTS_HELP_BLOCK": "Da skouer : jedoniezh, urzhiataerezh, arz...", "I18N_PREFERENCES_SUBJECT_INTERESTS_INVALID_SEARCH": "Ouzhpennañ un diduamant nevez (en ur implijout lizherennoù bihan hag esaouennoù)...", @@ -224,18 +237,34 @@ "I18N_SIGNUP_COMPLETE_REGISTRATION": "Echuit hoc'h enrolladenn", "I18N_SIGNUP_DO_NOT_SEND_EMAILS": "Arabat kas ar posteloù-mañ", "I18N_SIGNUP_EMAIL": "Postel", + "I18N_SIGNUP_EMAIL_PREFERENCES": "Dibaboù postel", + "I18N_SIGNUP_ERROR_NO_USERNAME": "Ebarzhit un an implijer, mar plij.", + "I18N_SIGNUP_ERROR_USERNAME_NOT_AVAILABLE": "Ne c'haller ket kaout an anv implijer-mañ.", + "I18N_SIGNUP_ERROR_USERNAME_ONLY_ALPHANUM": "Ne c'hall bezañ nemet arouezennoù lizhersifrennek en anvioù implijer.", + "I18N_SIGNUP_ERROR_USERNAME_TAKEN": "Hon digarezit, kemeret eo an anv implijer-mañ dija.", + "I18N_SIGNUP_ERROR_USERNAME_WITH_ADMIN": "Miret e vez an anvioù implijer gant \"admin\".", + "I18N_SIGNUP_ERROR_USERNAME_WITH_SPACES": "Bezit sur n'eus esaouenn ebet en hoc'h anv implijer.", + "I18N_SIGNUP_FIELD_REQUIRED": "Rekis eo ar vaezienn-mañ.", "I18N_SIGNUP_LOADING": "O kargañ", + "I18N_SIGNUP_PAGE_TITLE": "Deuit e-barzh ar gumuniezh - Oppia", + "I18N_SIGNUP_REGISTRATION": "Enskrivadur", + "I18N_SIGNUP_SEND_ME_NEWS": "Kasit din an nevezentioù hag an hizivadurioù graet el lec'hienn", + "I18N_SIGNUP_UPDATE_WARNING": "Notennit, mar plij, hon eus hizivaet hon Divizoù implij nevez zo", "I18N_SIGNUP_USERNAME": "Anv implijer", + "I18N_SIGNUP_USERNAME_EXPLANATION": "Diskwelet e vo hoc'h anv implijer e-kichen ho tegasadennoù.", "I18N_SIGNUP_WHY_LICENSE": "Perak CC-BY-SA ?", "I18N_SPLASH_JAVASCRIPT_ERROR_THANKS": "Trugarez deoc'h", "I18N_SPLASH_JAVASCRIPT_ERROR_TITLE": "Ezhomm hon eus da gaout JavaScript en ho merdeer", "I18N_SPLASH_PAGE_TITLE": "Oppia : Kelenn, Deskiñ, Ergerzhañ", "I18N_SPLASH_SITE_FEEDBACK": "Evezhidennoù al lec'hienn", + "I18N_SPLASH_TITLE": "Prederit e-maez al levrioù.", + "I18N_SUBSCRIBE_BUTTON_TEXT": "Koumanantiñ", "I18N_TOPNAV_ABOUT": "Diwar-benn", "I18N_TOPNAV_ABOUT_OPPIA": "Diwar-benn Oppia", "I18N_TOPNAV_ADMIN_PAGE": "Pajenn ar melestrour", "I18N_TOPNAV_BLOG": "Blog", "I18N_TOPNAV_CONTACT_US": "Mont e darempred ganeomp", + "I18N_TOPNAV_DASHBOARD": "Taolenn-stur ar c'hrouer", "I18N_TOPNAV_DONATE": "Ober un donezon", "I18N_TOPNAV_FORUM": "Forom", "I18N_TOPNAV_GET_STARTED": "Kregiñ", @@ -246,5 +275,6 @@ "I18N_TOPNAV_PREFERENCES": "Penndibaboù", "I18N_TOPNAV_SIGN_IN": "Kevreañ", "I18N_TOPNAV_TEACH_WITH_OPPIA": "Kelenn gant Oppia", + "I18N_TOTAL_SUBSCRIBERS_TEXT": "Un hollad a <[totalSubscribers]>koumananter hoc'h eus", "I18N_UNSUBSCRIBE_BUTTON_TEXT": "Digoumanantiñ" } diff --git a/assets/i18n/hi.json b/assets/i18n/hi.json index bd819818cf9e..14c01600d445 100644 --- a/assets/i18n/hi.json +++ b/assets/i18n/hi.json @@ -88,7 +88,7 @@ "I18N_INTERACTIONS_MUSIC_PLAY_SEQUENCE": "लक्ष्य अनुक्रम चलाएँ", "I18N_INTERACTIONS_MUSIC_SUBMIT": "जमा करें", "I18N_INTERACTIONS_PENCILCODEEDITOR_ARE_YOU_SURE_YOU_WANT_TO_RESET_YOUR_CODE": "क्या आप वाकई कोड रीसेट करना चाहते हैं", - "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "नहींं", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "रद्द करें", "I18N_INTERACTIONS_PENCILCODEEDITOR_CONFIRMATION_REQUIRED": "पुष्टिकरण आवश्यक", "I18N_INTERACTIONS_PENCILCODEEDITOR_RESET_CODE": "रीसेट", "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "जोडें", diff --git a/assets/i18n/id.json b/assets/i18n/id.json index d9e37981a2d2..2f6f917d63bc 100644 --- a/assets/i18n/id.json +++ b/assets/i18n/id.json @@ -18,6 +18,7 @@ "I18N_DASHBOARD_COLLECTIONS": "Koleksi", "I18N_DASHBOARD_EXPLORATIONS": "Eksplorasi", "I18N_DASHBOARD_OPEN_FEEDBACK": "Umpan Balik Terbuka", + "I18N_DASHBOARD_SUBSCRIBERS": "Pelanggan", "I18N_DIRECTIVES_DRAG_IMAGE_HERE": "Tarik gambar ke sini", "I18N_DIRECTIVES_OR_SELECT_FILE": "atau pilih file gambar:", "I18N_ERROR_DISABLED_EXPLORATION": "Eksplorasi Nonaktif", @@ -86,6 +87,10 @@ "I18N_INTERACTIONS_MUSIC_PLAY": "Mainkan", "I18N_INTERACTIONS_MUSIC_PLAY_SEQUENCE": "Mainkan Rangkaian Target", "I18N_INTERACTIONS_MUSIC_SUBMIT": "Simpan", + "I18N_INTERACTIONS_PENCILCODEEDITOR_ARE_YOU_SURE_YOU_WANT_TO_RESET_YOUR_CODE": "Apakah Anda yakin ingin mengatur ulang kode Anda?", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "Batal", + "I18N_INTERACTIONS_PENCILCODEEDITOR_CONFIRMATION_REQUIRED": "Konfirmasi diperlukan", + "I18N_INTERACTIONS_PENCILCODEEDITOR_RESET_CODE": "Atur ulang kode", "I18N_INTERACTIONS_SET_INPUT_ADD_ITEM": "Tambah isian", "I18N_INTERACTIONS_SET_INPUT_DUPLICATES_ERROR": "Ups, sepertinya isian anda mengandung duplikat!", "I18N_INTERACTIONS_SET_INPUT_EMPTY_SET": "(Tambahkan satu isian per baris.)", @@ -158,6 +163,7 @@ "I18N_LIBRARY_SUB_HEADER": "Mulai petualangan anda dengan melihat koleksi eksplorasi kami.", "I18N_LIBRARY_VIEWS_TOOLTIP": "Dilihat", "I18N_LIBRARY_VIEW_ALL": "Lihat semua", + "I18N_ONE_SUBSCRIBER_TEXT": "Anda mempunyai 1 pelanggan", "I18N_PLAYER_BACK": "Kembali", "I18N_PLAYER_CARD_NUMBER_TOOLTIP": "Kartu #", "I18N_PLAYER_COMMUNITY_EDITABLE_TOOLTIP": "Dapat diubah komunitas", @@ -279,6 +285,7 @@ "I18N_SPLASH_SUBTITLE": "Oppia mempermudah penciptaan pelajaran interaktif yang mendidik dan menarik.", "I18N_SPLASH_THIRD_EXPLORATION_DESCRIPTION": "Oppia memungkinkan Anda untuk menciptakan dan membagikan eksplorasi dalam berbagai subjek, terbatas hanya pada imajinasi Anda.", "I18N_SPLASH_TITLE": "Berpikir di luar buku.", + "I18N_SUBSCRIBE_BUTTON_TEXT": "Berlangganan", "I18N_TOPNAV_ABOUT": "Tentang", "I18N_TOPNAV_ABOUT_OPPIA": "Tentang Oppia", "I18N_TOPNAV_ADMIN_PAGE": "Halaman Admin", @@ -294,5 +301,6 @@ "I18N_TOPNAV_NOTIFICATIONS": "Notifikasi", "I18N_TOPNAV_PREFERENCES": "Preferensi", "I18N_TOPNAV_SIGN_IN": "Masuk", - "I18N_TOPNAV_TEACH_WITH_OPPIA": "Mengajar dengan Oppia" + "I18N_TOPNAV_TEACH_WITH_OPPIA": "Mengajar dengan Oppia", + "I18N_UNSUBSCRIBE_BUTTON_TEXT": "Berhenti berlangganan" } From a0975faf1807aa05c1ccc44411ade321b9a17acb Mon Sep 17 00:00:00 2001 From: kirankonduru Date: Mon, 13 Mar 2017 14:24:30 +0530 Subject: [PATCH 047/173] Resize image region code refactor --- .../templates/ImageWithRegionsEditor.js | 93 ++++++++++--------- 1 file changed, 50 insertions(+), 43 deletions(-) diff --git a/extensions/objects/templates/ImageWithRegionsEditor.js b/extensions/objects/templates/ImageWithRegionsEditor.js index 7fe7baa38019..27519b1e2c92 100644 --- a/extensions/objects/templates/ImageWithRegionsEditor.js +++ b/extensions/objects/templates/ImageWithRegionsEditor.js @@ -54,10 +54,10 @@ oppia.directive('imageWithRegionsEditor', [ // Current mouse position in SVG coordinates $scope.mouseX = 0; $scope.mouseY = 0; - // Original mouse click position for rectangle drawing + // Original mouse click position for rectangle drawing. $scope.originalMouseX = 0; $scope.originalMouseY = 0; - // Original position and dimensions for dragged rectangle + // Original position and dimensions for dragged rectangle. $scope.originalRectArea = { x: 0, y: 0, @@ -75,28 +75,28 @@ oppia.directive('imageWithRegionsEditor', [ $scope.userIsCurrentlyDragging = false; // Is user currently resizing an existing region? $scope.userIsCurrentlyResizing = false; - // The horizontal direction along which user resize occurs - // 1 -> Left -1 -> Right 0 -> No resize + // The horizontal direction along which user resize occurs. + // 1 -> Left -1 -> Right 0 -> No resize $scope.xDirection = 0; - // The vertical direction along which user resize occurs - // 1 -> Top -1 -> Bottom 0 -> No resize + // The vertical direction along which user resize occurs. + // 1 -> Top -1 -> Bottom 0 -> No resize $scope.yDirection = 0; - // Flags to check whether the direction changes while resizing + // Flags to check whether the direction changes while resizing. $scope.yDirectionToggled = false; $scope.xDirectionToggled = false; // A boolean that is set whenever the cursor moves out of the // rectangular region while resizing. - $scope.outOfRegion = false; - // The region along borders which when hovered provides resize cursor - $scope.resizeRegionBorder = 10; - // Dimensions of original image + $scope.movedOutOfRegion = false; + // The region along borders which when hovered provides resize cursor. + $scope.resizableBorderWidthPx = 10; + // Dimensions of original image. $scope.originalImageWidth = 0; $scope.originalImageHeight = 0; // Is the user preparing to draw a rectangle? $scope.regionDrawMode = false; - // Index of region currently hovered over + // Index of region currently hovered over. $scope.hoveredRegion = null; - // Index of region currently selected + // Index of region currently selected. $scope.selectedRegion = null; // Temporary label list @@ -188,8 +188,6 @@ oppia.directive('imageWithRegionsEditor', [ // Convert to and from region area (which is stored as a fraction of // image width and height) and actual width and height var regionAreaFromCornerAndDimensions = function(x, y, width, height) { - height = Math.abs(height); - width = Math.abs(width); return [ convertCoordsToFraction( [x, y], @@ -220,12 +218,14 @@ oppia.directive('imageWithRegionsEditor', [ var height = $scope.originalRectArea.height; var newWidth = width - $scope.xDirection * deltaX; var newHeight = height - $scope.yDirection * deltaY; - // Margin between the position mouse is pressed to resize and the y - // or the x co-ordinate. - var marginX = Math.abs($scope.originalRectArea.x - - $scope.originalMouseX); - var marginY = Math.abs($scope.originalRectArea.y - - $scope.originalMouseY); + // The distance between where the mouse was first clicked to initiate + // the resize action and the left-most x co-ordinate of rectangle. + var marginX = Math.abs( + $scope.originalRectArea.x - $scope.originalMouseX); + // The distance between where the mouse was first clicked to initiate + // the resize action and the top-most y co-ordinate of rectangle. + var marginY = Math.abs( + $scope.originalRectArea.y - $scope.originalMouseY); if (newHeight <= 0 && !$scope.yDirectionToggled) { $scope.yDirectionToggled = true; } else if (newHeight >= 0 && $scope.yDirectionToggled) { @@ -246,8 +246,10 @@ oppia.directive('imageWithRegionsEditor', [ } else if ($scope.xDirection === -1) { x += $scope.xDirectionToggled * (deltaX + marginX); } - resizedRegion.area = regionAreaFromCornerAndDimensions(x, y, - newWidth, newHeight); + // Whenever the direction changes the value of newHeight and newWidth + // computed is negative, hence the absolute value is taken. + resizedRegion.area = regionAreaFromCornerAndDimensions( + x, y, Math.abs(newWidth), Math.abs(newHeight)); }; $scope.onSvgMouseMove = function(evt) { @@ -299,11 +301,11 @@ oppia.directive('imageWithRegionsEditor', [ if ($scope.xDirectionToggled) { $scope.xDirection = ($scope.xDirection === 1) ? -1 : 1; } - if ($scope.outOfRegion) { + if ($scope.movedOutOfRegion) { $scope.xDirection = 0; $scope.yDirection = 0; } - $scope.outOfRegion = false; + $scope.movedOutOfRegion = false; $scope.yDirectionToggled = false; $scope.xDirectionToggled = false; if ($scope.regionDrawMode) { @@ -346,29 +348,32 @@ oppia.directive('imageWithRegionsEditor', [ if ($scope.hoveredRegion === null) { $scope.hoveredRegion = index; } - $scope.outOfRegion = false; + $scope.movedOutOfRegion = false; }; $scope.onMouseMoveRegion = function() { - if ($scope.userIsCurrentlyDragging || + if ( + $scope.userIsCurrentlyDragging || $scope.userIsCurrentlyResizing) { return; } - region = reg = cornerAndDimensionsFromRegionArea( + region = cornerAndDimensionsFromRegionArea( $scope.$parent.value.labeledRegions[ $scope.hoveredRegion].region.area); if (!$scope.xDirectionToggled && !$scope.yDirectionToggled) { - if ($scope.mouseY <= region.y + $scope.resizeRegionBorder) { + if ($scope.mouseY <= region.y + $scope.resizableBorderWidthPx) { $scope.yDirection = 1; - } else if ($scope.mouseY >= region.height + region.y - - $scope.resizeRegionBorder) { + } else if ( + $scope.mouseY >= region.height + region.y - + $scope.resizableBorderWidthPx) { $scope.yDirection = -1; } else { $scope.yDirection = 0; } - if ($scope.mouseX <= region.x + $scope.resizeRegionBorder) { + if ($scope.mouseX <= region.x + $scope.resizableBorderWidthPx) { $scope.xDirection = 1; - } else if ($scope.mouseX >= region.width + region.x - - $scope.resizeRegionBorder) { + } else if ( + $scope.mouseX >= region.width + region.x - + $scope.resizableBorderWidthPx) { $scope.xDirection = -1; } else { $scope.xDirection = 0; @@ -383,7 +388,7 @@ oppia.directive('imageWithRegionsEditor', [ $scope.xDirection = 0; $scope.yDirection = 0; } - $scope.outOfRegion = true; + $scope.movedOutOfRegion = true; }; $scope.onMousedownRegion = function() { if ($scope.xDirection || $scope.yDirection) { @@ -410,22 +415,24 @@ oppia.directive('imageWithRegionsEditor', [ var xDirectionCursor = ''; var yDirectionCursor = ''; if ($scope.xDirection || $scope.yDirection) { - if (($scope.xDirection === 1 && !$scope.xDirectionToggled) || + if ( + ($scope.xDirection === 1 && !$scope.xDirectionToggled) || ($scope.xDirection === -1 && $scope.xDirectionToggled)) { xDirectionCursor = 'w'; - } else if (($scope.xDirection === -1 && - !$scope.xDirectionToggled) || ($scope.xDirection === 1 && - $scope.xDirectionToggled)) { + } else if ( + ($scope.xDirection === -1 && !$scope.xDirectionToggled) || + ($scope.xDirection === 1 && $scope.xDirectionToggled)) { xDirectionCursor = 'e'; } else { xDirectionCursor = ''; } - if (($scope.yDirection === 1 && !$scope.yDirectionToggled) || + if ( + ($scope.yDirection === 1 && !$scope.yDirectionToggled) || ($scope.yDirection === -1 && $scope.yDirectionToggled)) { yDirectionCursor = 'n'; - } else if (($scope.yDirection === -1 && - !$scope.yDirectionToggled) || ($scope.yDirection === 1 && - $scope.yDirectionToggled)) { + } else if ( + ($scope.yDirection === -1 && !$scope.yDirectionToggled) || + ($scope.yDirection === 1 && $scope.yDirectionToggled)) { yDirectionCursor = 's'; } else { yDirectionCursor = ''; From 82da8446db4543239cb1945692ae24fc35e05248 Mon Sep 17 00:00:00 2001 From: FangYo Date: Mon, 13 Mar 2017 12:04:39 -0400 Subject: [PATCH 048/173] Fix #3099: Clarify error message for Item Selection and Multiple Choice interaction (#3184) * Error msg for Item Selection and Multiple Choice interaction changed as issue 3099 suggested * fixing error msg for ItemSelection and MultipleChoice * fixing error msg for ItemSelection and MultipleChoice --- extensions/interactions/ItemSelectionInput/validator.js | 4 ++-- extensions/interactions/MultipleChoiceInput/validator.js | 4 ++-- .../interactions/MultipleChoiceInput/validatorSpec.js | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/extensions/interactions/ItemSelectionInput/validator.js b/extensions/interactions/ItemSelectionInput/validator.js index a49859ae826c..b654e2d3fd94 100644 --- a/extensions/interactions/ItemSelectionInput/validator.js +++ b/extensions/interactions/ItemSelectionInput/validator.js @@ -141,8 +141,8 @@ oppia.filter('oppiaInteractiveItemSelectionInputValidator', [ warningsList.push({ type: WARNING_TYPES.ERROR, message: ( - 'Please clarify the default outcome so it is less confusing to ' + - 'the user.') + 'Please add something for Oppia to say in the ' + + '\"All other answers\" response.') }); } } diff --git a/extensions/interactions/MultipleChoiceInput/validator.js b/extensions/interactions/MultipleChoiceInput/validator.js index 200212175a02..cbde36fcecc4 100644 --- a/extensions/interactions/MultipleChoiceInput/validator.js +++ b/extensions/interactions/MultipleChoiceInput/validator.js @@ -95,8 +95,8 @@ oppia.filter('oppiaInteractiveMultipleChoiceInputValidator', [ warningsList.push({ type: WARNING_TYPES.ERROR, message: ( - 'Please clarify the default outcome so it is less confusing to ' + - 'the user.') + 'Please add something for Oppia to say in the ' + + '\"All other answers\" response.') }); } } diff --git a/extensions/interactions/MultipleChoiceInput/validatorSpec.js b/extensions/interactions/MultipleChoiceInput/validatorSpec.js index c6ae514eaa41..c009a26796c5 100644 --- a/extensions/interactions/MultipleChoiceInput/validatorSpec.js +++ b/extensions/interactions/MultipleChoiceInput/validatorSpec.js @@ -139,16 +139,16 @@ describe('oppiaInteractiveMultipleChoiceInputValidator', function() { expect(warnings).toEqual([{ type: WARNING_TYPES.ERROR, message: ( - 'Please clarify the default outcome so it is less confusing to ' + - 'the user.') + 'Please add something for Oppia to say in the ' + + '\"All other answers\" response.') }]); warnings = validator( currentState, customizationArguments, goodAnswerGroups, badOutcome); expect(warnings).toEqual([{ type: WARNING_TYPES.ERROR, message: ( - 'Please clarify the default outcome so it is less confusing to ' + - 'the user.') + 'Please add something for Oppia to say in the ' + + '\"All other answers\" response.') }]); }); }); From 23dcb46454e128174217a8ad85f22d5d4fd4c7d4 Mon Sep 17 00:00:00 2001 From: MAKOSCAFEE Date: Tue, 14 Mar 2017 08:12:50 +0300 Subject: [PATCH 049/173] retained formbuilder.js in base.html --- core/templates/dev/head/pages/admin/admin.html | 3 +-- .../dev/head/pages/exploration_editor/exploration_editor.html | 1 - .../dev/head/pages/exploration_player/exploration_player.html | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/core/templates/dev/head/pages/admin/admin.html b/core/templates/dev/head/pages/admin/admin.html index 23d06a75e518..41031f95ec5c 100644 --- a/core/templates/dev/head/pages/admin/admin.html +++ b/core/templates/dev/head/pages/admin/admin.html @@ -28,7 +28,7 @@ DEV_MODE: JSON.parse('{{DEV_MODE|js_string}}') } - + {% include 'pages/header_js_libs.html' %} @@ -92,7 +92,6 @@ - diff --git a/core/templates/dev/head/pages/exploration_editor/exploration_editor.html b/core/templates/dev/head/pages/exploration_editor/exploration_editor.html index fd0b8b50eba2..1601ce5e83c6 100644 --- a/core/templates/dev/head/pages/exploration_editor/exploration_editor.html +++ b/core/templates/dev/head/pages/exploration_editor/exploration_editor.html @@ -170,7 +170,6 @@ - diff --git a/core/templates/dev/head/pages/exploration_player/exploration_player.html b/core/templates/dev/head/pages/exploration_player/exploration_player.html index 90f82bc57be9..4062e87793d1 100644 --- a/core/templates/dev/head/pages/exploration_player/exploration_player.html +++ b/core/templates/dev/head/pages/exploration_player/exploration_player.html @@ -172,7 +172,6 @@

Suggest a Change

{% block footer_js %} {{ super() }} - From d3648288a1f5a57750c6683c8718e3a96bb198e9 Mon Sep 17 00:00:00 2001 From: MAKOSCAFEE Date: Tue, 14 Mar 2017 09:04:25 +0300 Subject: [PATCH 050/173] added back formbuilder js in admin.html --- core/templates/dev/head/pages/admin/admin.html | 1 + 1 file changed, 1 insertion(+) diff --git a/core/templates/dev/head/pages/admin/admin.html b/core/templates/dev/head/pages/admin/admin.html index 41031f95ec5c..e91a2cd06bcb 100644 --- a/core/templates/dev/head/pages/admin/admin.html +++ b/core/templates/dev/head/pages/admin/admin.html @@ -106,6 +106,7 @@ + From e31848569e49af5db62a5a27972c0621f9b83267 Mon Sep 17 00:00:00 2001 From: Mohammad Shahebaz Date: Thu, 16 Mar 2017 01:53:42 +0530 Subject: [PATCH 051/173] Fix #3101: Discard-draft Tooltip (#3205) The tag prevented the discard draft tooltip which resulted same tool tips for both Save and Discard. This is a fix --- .../exploration_save_and_publish_buttons_directive.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/templates/dev/head/pages/exploration_editor/exploration_save_and_publish_buttons_directive.html b/core/templates/dev/head/pages/exploration_editor/exploration_save_and_publish_buttons_directive.html index cdf97a631892..facb26757adf 100644 --- a/core/templates/dev/head/pages/exploration_editor/exploration_save_and_publish_buttons_directive.html +++ b/core/templates/dev/head/pages/exploration_editor/exploration_save_and_publish_buttons_directive.html @@ -59,7 +59,7 @@ From 4d7be26bd57ced65081e7a63445874bbecce0205 Mon Sep 17 00:00:00 2001 From: Sean Lip Date: Wed, 15 Mar 2017 21:32:08 -0700 Subject: [PATCH 052/173] Fix #3162: set expiration explicitly for files in the build directory. (#3203) --- app.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/app.yaml b/app.yaml index cba9a066cade..aeddc785345f 100644 --- a/app.yaml +++ b/app.yaml @@ -32,6 +32,7 @@ handlers: - url: /build static_dir: build secure: always + expiration: 30d http_headers: Cache-Control: 'public, max-age=2592000' - url: /assets/common From ad7dcb32d337625359ba0bc8c4b4f8e40cc307da Mon Sep 17 00:00:00 2001 From: Sean Lip Date: Wed, 15 Mar 2017 23:06:16 -0700 Subject: [PATCH 053/173] Fix broken backend test. (#3208) --- core/controllers/editor_test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/controllers/editor_test.py b/core/controllers/editor_test.py index 3b839a12f440..92f895507bea 100644 --- a/core/controllers/editor_test.py +++ b/core/controllers/editor_test.py @@ -1335,7 +1335,9 @@ class EditorAutosaveTest(BaseEditorControllerTest): EXP_ID1 = '1' EXP_ID2 = '2' EXP_ID3 = '3' - NEWER_DATETIME = datetime.datetime.strptime('2017-03-16', '%Y-%m-%d') + # 30 days into the future. + NEWER_DATETIME = datetime.datetime.utcnow() + datetime.timedelta(30) + # A date in the past. OLDER_DATETIME = datetime.datetime.strptime('2015-03-16', '%Y-%m-%d') DRAFT_CHANGELIST = [{ 'cmd': 'edit_exploration_property', From 4ef938d6f11621c830dc56d1a29d107e0b349938 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 16 Mar 2017 08:48:12 +0100 Subject: [PATCH 054/173] Localisation updates from https://translatewiki.net. --- assets/i18n/fi.json | 3 +++ assets/i18n/lb.json | 1 + 2 files changed, 4 insertions(+) diff --git a/assets/i18n/fi.json b/assets/i18n/fi.json index 00be88eab361..0cc62779cf2f 100644 --- a/assets/i18n/fi.json +++ b/assets/i18n/fi.json @@ -143,6 +143,7 @@ "I18N_LIBRARY_LOADING": "Ladataan", "I18N_LIBRARY_N/A": "N/A", "I18N_LIBRARY_RATINGS_TOOLTIP": "Arvioinnit", + "I18N_LIBRARY_VIEW_ALL": "Näytä kaikki", "I18N_ONE_SUBSCRIBER_TEXT": "Sinulla on 1 tilaaja.", "I18N_PLAYER_BACK": "Takaisin", "I18N_PLAYER_CARD_NUMBER_TOOLTIP": "Kortti #", @@ -202,6 +203,7 @@ "I18N_SIGNUP_DO_NOT_SEND_EMAILS": "Älä lähetä näitä sähköposteja", "I18N_SIGNUP_EMAIL": "Sähköpostiosoite", "I18N_SIGNUP_EMAIL_PREFERENCES": "Sähköpostiasetukset", + "I18N_SIGNUP_ERROR_NO_USERNAME": "Syötä käyttäjänimi.", "I18N_SIGNUP_ERROR_USERNAME_MORE_50_CHARS": "Käyttäjätunnus voi olla enintään 50 merkkiä.", "I18N_SIGNUP_ERROR_USERNAME_NOT_AVAILABLE": "Tämä käyttäjätunnus ei ole saatavilla.", "I18N_SIGNUP_ERROR_USERNAME_ONLY_ALPHANUM": "Käyttäjätunnuksissa voi olla vain aakkosnumeerisia merkkejä.", @@ -214,6 +216,7 @@ "I18N_SIGNUP_USERNAME": "Käyttäjänimi", "I18N_SIGNUP_WHY_LICENSE": "Miksi CC-BY-SA?", "I18N_SPLASH_JAVASCRIPT_ERROR_THANKS": "Kiitos.", + "I18N_SPLASH_PAGE_TITLE": "Oppia: Opeta, Opi, Tutki", "I18N_SUBSCRIBE_BUTTON_TEXT": "Tilaa", "I18N_TOPNAV_ABOUT": "Tietoja", "I18N_TOPNAV_ABOUT_OPPIA": "Tietoja Oppiasta", diff --git a/assets/i18n/lb.json b/assets/i18n/lb.json index 7dbf9130c745..7e9fc0d02da4 100644 --- a/assets/i18n/lb.json +++ b/assets/i18n/lb.json @@ -42,6 +42,7 @@ "I18N_INTERACTIONS_PENCILCODEEDITOR_ARE_YOU_SURE_YOU_WANT_TO_RESET_YOUR_CODE": "Sidd Dir sécher datt Dir Äre Code zrécksetze wëllt?", "I18N_INTERACTIONS_PENCILCODEEDITOR_CANCEL": "Ofbriechen", "I18N_INTERACTIONS_PENCILCODEEDITOR_CONFIRMATION_REQUIRED": "Confirmatioun erfuerdert", + "I18N_INTERACTIONS_PENCILCODEEDITOR_RESET_CODE": "Code zrécksetzen", "I18N_INTERACTIONS_SET_INPUT_NO_ANSWER": "Keng Äntwert ginn.", "I18N_LIBRARY_ALL_CATEGORIES": "All Kategorien", "I18N_LIBRARY_ALL_LANGUAGES": "All Sproochen", From f3bd9b469ba498074eeee538a261d20e99d3ab86 Mon Sep 17 00:00:00 2001 From: giritheja Date: Thu, 16 Mar 2017 15:48:58 +0530 Subject: [PATCH 055/173] Addressed review comments --- core/domain/classifier_domain.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/core/domain/classifier_domain.py b/core/domain/classifier_domain.py index 4438953d14d1..bf1cc8205097 100644 --- a/core/domain/classifier_domain.py +++ b/core/domain/classifier_domain.py @@ -89,14 +89,11 @@ def validate(self): if not isinstance(self.id, basestring): raise utils.ValidationError( 'Expected id to be a string, received %s' % self.id) - utils.require_valid_name( - self.id, 'the classifier id') if not isinstance(self.exp_id, basestring): raise utils.ValidationError( 'Expected exp_id to be a string, received %s' % self.exp_id) - utils.require_valid_name( - self.exp_id, 'the exploration id') + utils.require_valid_name(self.exp_id, 'the exploration id') if not isinstance(self.exp_version_when_created, int): raise utils.ValidationError( @@ -106,8 +103,7 @@ def validate(self): if not isinstance(self.state_name, basestring): raise utils.ValidationError( 'Expected id to be a string, received %s' % self.state_name) - utils.require_valid_name( - self.state_name, 'the state name') + utils.require_valid_name(self.state_name, 'the state name') if not isinstance(self.algorithm_id, basestring): raise utils.ValidationError( @@ -115,8 +111,8 @@ def validate(self): self.algorithm_id)) utils.require_valid_name( self.algorithm_id, 'the algorithm id') - if (self.algorithm_id != ( - feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput'])): + if (self.algorithm_id != + feconf.INTERACTION_CLASSIFIER_MAPPING['TextInput']): raise utils.ValidationError( 'Invalid algorithm_id: %s' % self.algorithm_id) From b53b0fe49c5e8637a11123a9ee07cd139f6ae978 Mon Sep 17 00:00:00 2001 From: Tony Jiang Date: Fri, 17 Mar 2017 15:25:46 -0400 Subject: [PATCH 056/173] Fix part of #2863: Create front-end fallback domain object (#3207) * FallbackObjectFactory * Undefined tobackenddict fallback * Lint * Remove undefined, bugs exist * add fallbacks to front-end tests * Renamings * separate createNew from createFromBackendDict * Fix * Renaming in AnswerGroupObjectFactory * More renaming in AnswerGroupObjectFactory * Missed renaming * Missed renaming * Lint --- .../exploration/AnswerGroupObjectFactory.js | 18 ++++++--- .../exploration/ExplorationObjectFactory.js | 33 +++++++-------- .../exploration/FallbackObjectFactory.js | 40 +++++++++++++++++++ .../exploration/InteractionObjectFactory.js | 30 +++++++++----- .../domain/exploration/StateObjectFactory.js | 10 ++--- .../domain/exploration/StatesObjectFactory.js | 10 ++--- .../exploration_editor/EditorServices.js | 5 +-- .../ExplorationSaveService.js | 3 +- .../editor_tab/StateEditor.js | 2 +- .../editor_tab/StateEditorSpec.js | 10 +++-- .../editor_tab/StateInteractionSpec.js | 6 ++- .../editor_tab/StateResponses.js | 2 +- .../exploration_editor.html | 1 + .../feedback_tab/FeedbackTab.js | 3 +- .../history_tab/HistoryServices.js | 6 ++- .../history_tab/HistoryServicesSpec.js | 3 +- .../statistics_tab/StatisticsTab.js | 2 +- .../AnswerClassificationServiceSpec.js | 15 ++++--- .../exploration_player/PlayerServices.js | 6 ++- .../exploration_player.html | 1 + 20 files changed, 141 insertions(+), 65 deletions(-) create mode 100644 core/templates/dev/head/domain/exploration/FallbackObjectFactory.js diff --git a/core/templates/dev/head/domain/exploration/AnswerGroupObjectFactory.js b/core/templates/dev/head/domain/exploration/AnswerGroupObjectFactory.js index aca1b0df4580..19918689e8f7 100644 --- a/core/templates/dev/head/domain/exploration/AnswerGroupObjectFactory.js +++ b/core/templates/dev/head/domain/exploration/AnswerGroupObjectFactory.js @@ -18,9 +18,10 @@ */ oppia.factory('AnswerGroupObjectFactory', [function() { - var AnswerGroup = function(ruleSpecs, outcome, correct) { - this.ruleSpecs = ruleSpecs; - this.outcome = outcome; + var AnswerGroup = function(ruleSpecsList, outcomeDict, + correct) { + this.ruleSpecs = ruleSpecsList; + this.outcome = outcomeDict; this.correct = correct; }; @@ -34,8 +35,15 @@ oppia.factory('AnswerGroupObjectFactory', [function() { // Static class methods. Note that "this" is not available in // static contexts. - AnswerGroup.create = function(ruleSpecs, outcome, correct) { - return new AnswerGroup(ruleSpecs, outcome, correct); + AnswerGroup.createNew = function(ruleSpecsList, outcomeDict, correct) { + return new AnswerGroup(ruleSpecsList, outcomeDict, correct); + }; + + AnswerGroup.createFromBackendDict = function(answerGroupBackendDict) { + return new AnswerGroup( + answerGroupBackendDict.rule_specs, + answerGroupBackendDict.outcome, + false); }; return AnswerGroup; diff --git a/core/templates/dev/head/domain/exploration/ExplorationObjectFactory.js b/core/templates/dev/head/domain/exploration/ExplorationObjectFactory.js index b5c9d126ad70..df8bc9f5f2ba 100644 --- a/core/templates/dev/head/domain/exploration/ExplorationObjectFactory.js +++ b/core/templates/dev/head/domain/exploration/ExplorationObjectFactory.js @@ -24,19 +24,20 @@ oppia.factory('ExplorationObjectFactory', [ INTERACTION_SPECS, INTERACTION_DISPLAY_MODE_INLINE, StateObjectFactory, UrlInterpolationService) { var Exploration = function( - initStateName, paramChanges, paramSpecs, skinCustomizations, states, - title, languageCode) { + initStateName, paramChangesBackendList, paramSpecsBackendDict, + skinCustomizationsBackendDict, statesBackendDict, title, + languageCode) { this.initStateName = initStateName; - this.paramChanges = paramChanges; - this.paramSpecs = paramSpecs; - this.skinCustomizations = skinCustomizations; + this.paramChanges = paramChangesBackendList; + this.paramSpecs = paramSpecsBackendDict; + this.skinCustomizations = skinCustomizationsBackendDict; this.title = title; this.languageCode = languageCode; this.states = {}; - for (var stateName in states) { - this.states[stateName] = StateObjectFactory.create( - stateName, states[stateName]); + for (var stateName in statesBackendDict) { + this.states[stateName] = StateObjectFactory.createFromBackendDict( + stateName, statesBackendDict[stateName]); } }; @@ -127,15 +128,15 @@ oppia.factory('ExplorationObjectFactory', [ // Static class methods. Note that "this" is not available in // static contexts. - Exploration.create = function(explorationDict) { + Exploration.createFromBackendDict = function(explorationBackendDict) { return new Exploration( - explorationDict.init_state_name, - explorationDict.param_changes, - explorationDict.param_specs, - explorationDict.skin_customizations, - explorationDict.states, - explorationDict.title, - explorationDict.language_code); + explorationBackendDict.init_state_name, + explorationBackendDict.param_changes, + explorationBackendDict.param_specs, + explorationBackendDict.skin_customizations, + explorationBackendDict.states, + explorationBackendDict.title, + explorationBackendDict.language_code); }; return Exploration; diff --git a/core/templates/dev/head/domain/exploration/FallbackObjectFactory.js b/core/templates/dev/head/domain/exploration/FallbackObjectFactory.js new file mode 100644 index 000000000000..0d22a06b49a9 --- /dev/null +++ b/core/templates/dev/head/domain/exploration/FallbackObjectFactory.js @@ -0,0 +1,40 @@ +// Copyright 2015 The Oppia Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Factory for creating new frontend instances of Fallback + * domain objects. + */ + +oppia.factory('FallbackObjectFactory', [function() { + var Fallback = function(triggerBackendDict, outcomeBackendDict) { + this.trigger = triggerBackendDict; + this.outcome = outcomeBackendDict; + }; + + Fallback.prototype.toBackendDict = function() { + return { + trigger: this.trigger, + outcome: this.outcome + }; + }; + + Fallback.createFromBackendDict = function(fallbackBackendDict) { + return new Fallback( + fallbackBackendDict.trigger, + fallbackBackendDict.outcome); + }; + + return Fallback; +}]); diff --git a/core/templates/dev/head/domain/exploration/InteractionObjectFactory.js b/core/templates/dev/head/domain/exploration/InteractionObjectFactory.js index 2b24eeb38cc4..3269cc55b1c5 100644 --- a/core/templates/dev/head/domain/exploration/InteractionObjectFactory.js +++ b/core/templates/dev/head/domain/exploration/InteractionObjectFactory.js @@ -18,28 +18,36 @@ */ oppia.factory('InteractionObjectFactory', [ - 'AnswerGroupObjectFactory', function(AnswerGroupObjectFactory) { + 'AnswerGroupObjectFactory', 'FallbackObjectFactory', + function(AnswerGroupObjectFactory, FallbackObjectFactory) { var Interaction = function(answerGroupBackendDicts, - confirmedUnclassifiedAnswers, customizationArgs, defaultOutcome, - fallbacks, id) { + confirmedUnclassifiedAnswers, customizationArgs, defaultOutcomeBackendDict, + fallbacksBackendList, id) { this.answerGroups = generateAnswerGroupsFromBackend( answerGroupBackendDicts); this.confirmedUnclassifiedAnswers = confirmedUnclassifiedAnswers; this.customizationArgs = customizationArgs; - this.defaultOutcome = defaultOutcome; - this.fallbacks = fallbacks; + this.defaultOutcome = defaultOutcomeBackendDict; + this.fallbacks = generateFallbacksFromBackend(fallbacksBackendList); this.id = id; }; var generateAnswerGroupsFromBackend = function(answerGroupBackendDicts) { - var answerGroups = answerGroupBackendDicts.map(function(answerGroupDict) { - return AnswerGroupObjectFactory.create( - answerGroupDict.rule_specs, answerGroupDict.outcome, false); + var answerGroups = answerGroupBackendDicts.map(function( + answerGroupBackendDict) { + return AnswerGroupObjectFactory.createFromBackendDict( + answerGroupBackendDict); }); - return answerGroups; }; + var generateFallbacksFromBackend = function(fallbackBackendDicts) { + var fallbacks = fallbackBackendDicts.map(function(fallbackBackendDict) { + return FallbackObjectFactory.createFromBackendDict(fallbackBackendDict); + }); + return fallbacks; + }; + Interaction.prototype.toBackendDict = function() { return { answer_groups: this.answerGroups.map(function(answerGroup) { @@ -48,7 +56,9 @@ oppia.factory('InteractionObjectFactory', [ confirmed_unclassified_answers: this.confirmedUnclassifiedAnswers, customization_args: this.customizationArgs, default_outcome: this.defaultOutcome, - fallbacks: this.fallbacks, + fallbacks: this.fallbacks.map(function(fallback) { + return fallback.toBackendDict(); + }), id: this.id }; }; diff --git a/core/templates/dev/head/domain/exploration/StateObjectFactory.js b/core/templates/dev/head/domain/exploration/StateObjectFactory.js index ef6aa3609eb4..36a4aff34ade 100644 --- a/core/templates/dev/head/domain/exploration/StateObjectFactory.js +++ b/core/templates/dev/head/domain/exploration/StateObjectFactory.js @@ -20,13 +20,13 @@ oppia.factory('StateObjectFactory', [ 'AnswerGroupObjectFactory', 'InteractionObjectFactory', function(AnswerGroupObjectFactory, InteractionObjectFactory) { - var State = function(name, classifierModelId, content, - interactionBackendDict, paramChanges) { + var State = function(name, classifierModelId, contentBackendList, + interactionBackendDict, paramChangesBackendList) { this.name = name; this.classifierModelId = classifierModelId; - this.content = content; + this.content = contentBackendList; this.interaction = InteractionObjectFactory.create(interactionBackendDict); - this.paramChanges = paramChanges; + this.paramChanges = paramChangesBackendList; }; // Instance methods. @@ -41,7 +41,7 @@ oppia.factory('StateObjectFactory', [ // Static class methods. Note that "this" is not available in // static contexts. - State.create = function(stateName, stateDict) { + State.createFromBackendDict = function(stateName, stateDict) { return new State( stateName, stateDict.classifier_model_id, diff --git a/core/templates/dev/head/domain/exploration/StatesObjectFactory.js b/core/templates/dev/head/domain/exploration/StatesObjectFactory.js index db96f16da98c..414b4b6df22a 100644 --- a/core/templates/dev/head/domain/exploration/StatesObjectFactory.js +++ b/core/templates/dev/head/domain/exploration/StatesObjectFactory.js @@ -19,17 +19,17 @@ oppia.factory('StatesObjectFactory', ['StateObjectFactory', function(StateObjectFactory) { - var create = function(statesDict) { + var createFromBackendDict = function(statesBackendDict) { var stateObjectsDict = {}; - for (var stateName in statesDict) { - stateObjectsDict[stateName] = StateObjectFactory.create( - stateName, statesDict[stateName]); + for (var stateName in statesBackendDict) { + stateObjectsDict[stateName] = StateObjectFactory.createFromBackendDict( + stateName, statesBackendDict[stateName]); } return stateObjectsDict; }; return { - create: create + createFromBackendDict: createFromBackendDict }; } ]); diff --git a/core/templates/dev/head/pages/exploration_editor/EditorServices.js b/core/templates/dev/head/pages/exploration_editor/EditorServices.js index c8d634079770..91fe5120a0dc 100644 --- a/core/templates/dev/head/pages/exploration_editor/EditorServices.js +++ b/core/templates/dev/head/pages/exploration_editor/EditorServices.js @@ -877,7 +877,7 @@ oppia.factory('explorationStatesService', [ _states = {}; for (var stateName in states) { var stateData = angular.copy(states[stateName]); - _states[stateName] = StateObjectFactory.create( + _states[stateName] = StateObjectFactory.createFromBackendDict( stateName, stateData); } }, @@ -1639,7 +1639,6 @@ oppia.factory('explorationGadgetsService', [ ]); // A service that returns the frontend representation of a newly-added state. -// TODO: refactor into factory as StateObjectFactory.createNewState() oppia.factory('newStateTemplateService', ['StateObjectFactory', function(StateObjectFactory) { return { @@ -1649,7 +1648,7 @@ oppia.factory('newStateTemplateService', // NB: clients should ensure that the desired state name is valid. getNewStateTemplate: function(newStateName) { var newStateTemplate = angular.copy(GLOBALS.NEW_STATE_TEMPLATE); - var newState = StateObjectFactory.create(newStateName, { + var newState = StateObjectFactory.createFromBackendDict(newStateName, { classifier_model_id: newStateTemplate.classifier_model_id, content: newStateTemplate.content, interaction: newStateTemplate.interaction, diff --git a/core/templates/dev/head/pages/exploration_editor/ExplorationSaveService.js b/core/templates/dev/head/pages/exploration_editor/ExplorationSaveService.js index f050114f733d..cd743f65219b 100644 --- a/core/templates/dev/head/pages/exploration_editor/ExplorationSaveService.js +++ b/core/templates/dev/head/pages/exploration_editor/ExplorationSaveService.js @@ -453,7 +453,8 @@ oppia.factory('explorationSaveService', [ var oldStates = {}; for (var stateName in data.states) { oldStates[stateName] = - (StateObjectFactory.create(stateName, data.states[stateName])); + (StateObjectFactory.createFromBackendDict( + stateName, data.states[stateName])); } var newStates = explorationStatesService.getStates(); var diffGraphData = ExplorationDiffService.getDiffGraphData( diff --git a/core/templates/dev/head/pages/exploration_editor/editor_tab/StateEditor.js b/core/templates/dev/head/pages/exploration_editor/editor_tab/StateEditor.js index 75261e0c09ce..1864f1a49005 100644 --- a/core/templates/dev/head/pages/exploration_editor/editor_tab/StateEditor.js +++ b/core/templates/dev/head/pages/exploration_editor/editor_tab/StateEditor.js @@ -464,7 +464,7 @@ oppia.directive('trainingPanel', [function() { if ($scope.classification.newOutcome) { // Create a new answer group with the given feedback. var answerGroups = responsesService.getAnswerGroups(); - answerGroups.push(AnswerGroupObjectFactory.create( + answerGroups.push(AnswerGroupObjectFactory.createNew( [], angular.copy($scope.classification.newOutcome), false)); responsesService.save( answerGroups, responsesService.getDefaultOutcome()); diff --git a/core/templates/dev/head/pages/exploration_editor/editor_tab/StateEditorSpec.js b/core/templates/dev/head/pages/exploration_editor/editor_tab/StateEditorSpec.js index 1a2da22f9d7e..7bdc15392409 100644 --- a/core/templates/dev/head/pages/exploration_editor/editor_tab/StateEditorSpec.js +++ b/core/templates/dev/head/pages/exploration_editor/editor_tab/StateEditorSpec.js @@ -67,7 +67,8 @@ describe('State Editor controller', function() { ruleSpecs: [{ dest: 'Second State' }] - }] + }], + fallbacks: [] }, param_changes: [] }, @@ -82,7 +83,8 @@ describe('State Editor controller', function() { ruleSpecs: [{ dest: 'Second State' }] - }] + }], + fallbacks: [] }, param_changes: [] }, @@ -97,7 +99,8 @@ describe('State Editor controller', function() { ruleSpecs: [{ dest: 'Second State' }] - }] + }], + fallbacks: [] }, param_changes: [{ name: 'comparison', @@ -256,6 +259,7 @@ describe('State Editor controller', function() { feedback: 'Default', dest: 'State' }, + fallbacks: [], confirmed_unclassified_answers: [] }, param_changes: [] diff --git a/core/templates/dev/head/pages/exploration_editor/editor_tab/StateInteractionSpec.js b/core/templates/dev/head/pages/exploration_editor/editor_tab/StateInteractionSpec.js index c9f2be4222b9..f2ebeeb12f30 100644 --- a/core/templates/dev/head/pages/exploration_editor/editor_tab/StateInteractionSpec.js +++ b/core/templates/dev/head/pages/exploration_editor/editor_tab/StateInteractionSpec.js @@ -77,7 +77,8 @@ describe('State Interaction controller', function() { ruleSpecs: [{ dest: 'End State' }] - }] + }], + fallbacks: [] }, param_changes: [] }, @@ -92,7 +93,8 @@ describe('State Interaction controller', function() { ruleSpecs: [{ dest: 'End State' }] - }] + }], + fallbacks: [] }, param_changes: [] } diff --git a/core/templates/dev/head/pages/exploration_editor/editor_tab/StateResponses.js b/core/templates/dev/head/pages/exploration_editor/editor_tab/StateResponses.js index ec3966bf2edf..5cd29eeb99a7 100644 --- a/core/templates/dev/head/pages/exploration_editor/editor_tab/StateResponses.js +++ b/core/templates/dev/head/pages/exploration_editor/editor_tab/StateResponses.js @@ -729,7 +729,7 @@ oppia.controller('StateResponses', [ ] }).result.then(function(result) { // Create a new answer group. - $scope.answerGroups.push(AnswerGroupObjectFactory.create( + $scope.answerGroups.push(AnswerGroupObjectFactory.createNew( [result.tmpRule], result.tmpOutcome, false)); responsesService.save($scope.answerGroups, $scope.defaultOutcome); $scope.changeActiveAnswerGroupIndex($scope.answerGroups.length - 1); diff --git a/core/templates/dev/head/pages/exploration_editor/exploration_editor.html b/core/templates/dev/head/pages/exploration_editor/exploration_editor.html index 1601ce5e83c6..55f2fd51c2c3 100644 --- a/core/templates/dev/head/pages/exploration_editor/exploration_editor.html +++ b/core/templates/dev/head/pages/exploration_editor/exploration_editor.html @@ -289,6 +289,7 @@ + diff --git a/core/templates/dev/head/pages/exploration_editor/feedback_tab/FeedbackTab.js b/core/templates/dev/head/pages/exploration_editor/feedback_tab/FeedbackTab.js index 883c95f7465f..7e734e385eec 100644 --- a/core/templates/dev/head/pages/exploration_editor/feedback_tab/FeedbackTab.js +++ b/core/templates/dev/head/pages/exploration_editor/feedback_tab/FeedbackTab.js @@ -212,7 +212,8 @@ oppia.controller('FeedbackTab', [ var suggestion = $scope.activeThread.suggestion; var stateName = suggestion.state_name; var stateDict = explorationData.data.states[stateName]; - var state = StateObjectFactory.create(stateName, stateDict); + var state = StateObjectFactory.createFromBackendDict( + stateName, stateDict); state.content[0].value = suggestion.state_content.value; explorationData.data.version += 1; explorationStatesService.setState(stateName, state); diff --git a/core/templates/dev/head/pages/exploration_editor/history_tab/HistoryServices.js b/core/templates/dev/head/pages/exploration_editor/history_tab/HistoryServices.js index d5a37b03acf1..f1be240f4f82 100644 --- a/core/templates/dev/head/pages/exploration_editor/history_tab/HistoryServices.js +++ b/core/templates/dev/head/pages/exploration_editor/history_tab/HistoryServices.js @@ -204,8 +204,10 @@ oppia.factory('compareVersionsService', [ // Track changes from v1 to LCA, and then from LCA to v2. var lca = versionsTreeService.findLCA(v1, v2); - var v1States = StatesObjectFactory.create(v1StatesDict); - var v2States = StatesObjectFactory.create(v2StatesDict); + var v1States = StatesObjectFactory.createFromBackendDict( + v1StatesDict); + var v2States = StatesObjectFactory.createFromBackendDict( + v2StatesDict); var diffGraphData = ExplorationDiffService.getDiffGraphData( v1States, v2States, [{ diff --git a/core/templates/dev/head/pages/exploration_editor/history_tab/HistoryServicesSpec.js b/core/templates/dev/head/pages/exploration_editor/history_tab/HistoryServicesSpec.js index 93eb023ca175..e682196598a9 100644 --- a/core/templates/dev/head/pages/exploration_editor/history_tab/HistoryServicesSpec.js +++ b/core/templates/dev/head/pages/exploration_editor/history_tab/HistoryServicesSpec.js @@ -210,7 +210,8 @@ describe('Compare versions service', function() { value: statesDetails[stateName].contentStr }], interaction: { - answer_groups: [] + answer_groups: [], + fallbacks: [] } }; newStateData.interaction.answer_groups = diff --git a/core/templates/dev/head/pages/exploration_editor/statistics_tab/StatisticsTab.js b/core/templates/dev/head/pages/exploration_editor/statistics_tab/StatisticsTab.js index 4f2b6b844508..bd186800d138 100644 --- a/core/templates/dev/head/pages/exploration_editor/statistics_tab/StatisticsTab.js +++ b/core/templates/dev/head/pages/exploration_editor/statistics_tab/StatisticsTab.js @@ -62,7 +62,7 @@ oppia.controller('StatisticsTab', [ $http.get(explorationDataUrl).then(function(response) { var statesDict = response.data.states; - var states = StatesObjectFactory.create(statesDict); + var states = StatesObjectFactory.createFromBackendDict(statesDict); var initStateName = response.data.init_state_name; $scope.statsGraphData = computeGraphService.compute( initStateName, states); diff --git a/core/templates/dev/head/pages/exploration_player/AnswerClassificationServiceSpec.js b/core/templates/dev/head/pages/exploration_player/AnswerClassificationServiceSpec.js index 320cf30807ed..7751c00b7f2a 100644 --- a/core/templates/dev/head/pages/exploration_player/AnswerClassificationServiceSpec.js +++ b/core/templates/dev/head/pages/exploration_player/AnswerClassificationServiceSpec.js @@ -42,7 +42,7 @@ describe('Answer classification service with string classifier disabled', successHandler = jasmine.createSpy('success'); failHandler = jasmine.createSpy('fail'); - state = sof.create('stateName', { + state = sof.createFromBackendDict('stateName', { content: [{ type: 'text', value: 'content' @@ -83,7 +83,8 @@ describe('Answer classification service with string classifier disabled', }], correct: false }], - default_outcome: 'default' + default_outcome: 'default', + fallbacks: [] }, param_changes: [] }); @@ -160,7 +161,7 @@ describe('Answer classification service with string classifier disabled', it('should fail if no answer group matches and no default rule is ' + 'provided', function() { - var state2 = sof.create('stateName', { + var state2 = sof.createFromBackendDict('stateName', { content: [{ type: 'text', value: 'content' @@ -176,7 +177,8 @@ describe('Answer classification service with string classifier disabled', rule_type: 'Equals' }], correct: false - }] + }], + fallbacks: [] }, param_changes: [] }); @@ -218,7 +220,7 @@ describe('Answer classification service with string classifier enabled', successHandler = jasmine.createSpy('success'); failHandler = jasmine.createSpy('fail'); - state = sof.create('stateName', { + state = sof.createFromBackendDict('stateName', { content: [{ type: 'text', value: 'content' @@ -254,7 +256,8 @@ describe('Answer classification service with string classifier enabled', }], correct: false }], - default_outcome: 'default' + default_outcome: 'default', + fallbacks: [] }, param_changes: [] }); diff --git a/core/templates/dev/head/pages/exploration_player/PlayerServices.js b/core/templates/dev/head/pages/exploration_player/PlayerServices.js index f72767780f24..c389b033597c 100644 --- a/core/templates/dev/head/pages/exploration_player/PlayerServices.js +++ b/core/templates/dev/head/pages/exploration_player/PlayerServices.js @@ -197,7 +197,8 @@ oppia.factory('oppiaPlayerService', [ apply_draft: true } }).then(function(response) { - exploration = ExplorationObjectFactory.create(response.data); + exploration = ExplorationObjectFactory.createFromBackendDict( + response.data); exploration.setInitialStateName(initStateName); initParams(manualParamChanges); _loadInitialState(successCallback); @@ -209,7 +210,8 @@ oppia.factory('oppiaPlayerService', [ }) + (version ? '?v=' + version : ''); $http.get(explorationDataUrl).then(function(response) { var data = response.data; - exploration = ExplorationObjectFactory.create(data.exploration); + exploration = ExplorationObjectFactory.createFromBackendDict( + data.exploration); version = data.version; initParams([]); diff --git a/core/templates/dev/head/pages/exploration_player/exploration_player.html b/core/templates/dev/head/pages/exploration_player/exploration_player.html index 4062e87793d1..3def261af754 100644 --- a/core/templates/dev/head/pages/exploration_player/exploration_player.html +++ b/core/templates/dev/head/pages/exploration_player/exploration_player.html @@ -190,6 +190,7 @@

Suggest a Change

+ From 8985e36a27bdc4adaa992996b82a0c4c8559613c Mon Sep 17 00:00:00 2001 From: Xinyu Wu Date: Sat, 18 Mar 2017 11:25:51 -0400 Subject: [PATCH 057/173] Add a check that user contributions for logged in users exist on the home page redirect check (#3213) --- main.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index a50b2730292c..907bd71458c8 100644 --- a/main.py +++ b/main.py @@ -76,8 +76,9 @@ def get(self): # 'Creator' is a user who has created or edited an exploration. user_is_creator = ( - len(user_contributions.created_exploration_ids) > 0 or - len(user_contributions.edited_exploration_ids) > 0) + user_contributions is not None and + (len(user_contributions.created_exploration_ids) > 0 or + len(user_contributions.edited_exploration_ids) > 0)) if user_is_creator: self.redirect(feconf.DASHBOARD_URL) else: From 6d0cb2a6ec3ee4071de94237c3c287ceb97ff2d6 Mon Sep 17 00:00:00 2001 From: Xinyu Wu Date: Sat, 18 Mar 2017 20:18:37 -0400 Subject: [PATCH 058/173] Add test (#3217) --- core/controllers/base_test.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/core/controllers/base_test.py b/core/controllers/base_test.py index 00e23d5f85ee..478a429f22e1 100644 --- a/core/controllers/base_test.py +++ b/core/controllers/base_test.py @@ -25,6 +25,7 @@ from core.controllers import base from core.domain import exp_services from core.domain import rights_manager +from core.domain import user_services from core.platform import models from core.tests import test_utils import feconf @@ -36,6 +37,7 @@ import webtest current_user_services = models.Registry.import_current_user_services() +(user_models,) = models.Registry.import_models([models.NAMES.user]) FORTY_EIGHT_HOURS_IN_SECS = 48 * 60 * 60 PADDING = 1 @@ -131,6 +133,23 @@ def test_root_redirect_rules_for_logged_in_learners(self): self.assertIn('library', response.headers['location']) self.logout() + def test_root_redirect_rules_for_users_with_no_user_contribution_model( + self): + self.login(self.TEST_LEARNER_EMAIL) + # delete the UserContributionModel + user_id = user_services.get_user_id_from_username( + self.TEST_LEARNER_USERNAME) + user_contribution_model = user_models.UserContributionsModel.get( + user_id) + user_contribution_model.delete() + + # Since no exploration has been created, going to '/' should redirect + # to the library page. + response = self.testapp.get('/') + self.assertEqual(response.status_int, 302) + self.assertIn('library', response.headers['location']) + self.logout() + def test_root_redirect_rules_for_logged_in_creators(self): self.login(self.TEST_CREATOR_EMAIL) creator_user_id = self.get_user_id_from_email(self.TEST_CREATOR_EMAIL) From 9e7f3c782e0e728582e46afa79b5f4be12a12381 Mon Sep 17 00:00:00 2001 From: Yogesh Sharma Date: Sun, 19 Mar 2017 11:44:37 +0530 Subject: [PATCH 059/173] Fixes #3192: shifted back css of oppia-sidebar-menu-open back to oppia.css (#3211) --- .../side_navigation_bar_directive.html | 15 --------------- core/templates/dev/head/css/oppia.css | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/core/templates/dev/head/components/side_navigation_bar/side_navigation_bar_directive.html b/core/templates/dev/head/components/side_navigation_bar/side_navigation_bar_directive.html index 62d35058fb5b..5668d28440cd 100644 --- a/core/templates/dev/head/components/side_navigation_bar/side_navigation_bar_directive.html +++ b/core/templates/dev/head/components/side_navigation_bar/side_navigation_bar_directive.html @@ -78,21 +78,6 @@ padding-top: 6px; } - side-navigation-bar .oppia-sidebar-menu-open .oppia-sidebar-menu { - box-shadow: 1px 0 3px rgba(0,0,0,0.12), 1px 0 2px rgba(0,0,0,0.24); - overflow-y: scroll; - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - visibility: visible; - } - side-navigation-bar .oppia-sidebar-menu-open .oppia-sidebar-menu::after { - height: 0; - opacity: 0; - -webkit-transition: opacity 0.5s, width 0.1s 0.5s, height 0.1s 0.5s; - transition: opacity 0.5s, width 0.1s 0.5s, height 0.1s 0.5s; - width: 0; - } -