def get_html_field_types_to_rule_specs(cls, state_schema_version=None): """Returns a dict containing a html_field_types_to_rule_specs dict of the specified state schema verison, if available. Args: state_schema_version: int|None. The state schema version to retrieve the html_field_types_to_rule_specs for. If None, the current state schema version's html_field_types_to_rule_specs will be returned. Returns: dict. The html_field_types_to_rule_specs specs for the given state schema version. Raises: Exception. No html_field_types_to_rule_specs json file found for the given state schema version. """ cached = ( state_schema_version in cls._state_schema_version_to_html_field_types_to_rule_specs) if not cached and state_schema_version is None: cls._state_schema_version_to_html_field_types_to_rule_specs[ state_schema_version] = json.loads( python_utils.get_package_file_contents( 'extensions', feconf .HTML_FIELD_TYPES_TO_RULE_SPECS_EXTENSIONS_MODULE_PATH)) elif not cached: file_name = 'html_field_types_to_rule_specs_state_v%i.json' % ( state_schema_version) spec_file = os.path.join( feconf .LEGACY_HTML_FIELD_TYPES_TO_RULE_SPECS_EXTENSIONS_MODULE_DIR, file_name) try: specs_from_json = json.loads( python_utils.get_package_file_contents( 'extensions', spec_file)) except Exception as e: raise Exception( 'No specs json file found for state schema v%i' % state_schema_version) from e cls._state_schema_version_to_html_field_types_to_rule_specs[ state_schema_version] = specs_from_json return cls._state_schema_version_to_html_field_types_to_rule_specs[ state_schema_version]
def _refresh(cls): """Repopulate the registry.""" cls._rte_components.clear() package, filepath = os.path.split( feconf.RTE_EXTENSIONS_DEFINITIONS_PATH) cls._rte_components = constants.parse_json_from_ts( python_utils.get_package_file_contents(package, filepath))
def get_default_object_values(): """Returns a dictionary containing the default object values.""" # TODO(wxy): Cache this as it is accessed many times. return json.loads( python_utils.get_package_file_contents( 'extensions', feconf.OBJECT_DEFAULT_VALUES_EXTENSIONS_MODULE_PATH))
class BaseRteComponent: """Base Rte Component class. This is the superclass for rich text components in Oppia, such as Image and Video. """ package, filepath = os.path.split(feconf.RTE_EXTENSIONS_DEFINITIONS_PATH) rich_text_component_specs = constants.parse_json_from_ts( python_utils.get_package_file_contents(package, filepath)) obj_types_to_obj_classes = { 'unicode': objects.UnicodeString, 'html': objects.Html, 'Filepath': objects.Filepath, 'SanitizedUrl': objects.SanitizedUrl, 'MathExpressionContent': objects.MathExpressionContent, 'ListOfTabs': objects.ListOfTabs, 'SvgFilename': objects.SvgFilename, 'int': objects.Int, 'bool': objects.Boolean, 'SkillSelector': objects.SkillSelector } @classmethod def validate(cls, value_dict): """Validates customization args for a rich text component. Raises: TypeError. If any customization arg is invalid. """ arg_names_to_obj_classes = {} customization_arg_specs = cls.rich_text_component_specs[ cls.__name__]['customization_arg_specs'] for customization_arg_spec in customization_arg_specs: arg_name = '%s-with-value' % customization_arg_spec['name'] schema = customization_arg_spec['schema'] if schema['type'] != 'custom': obj_type = schema['type'] else: obj_type = schema['obj_type'] obj_class = cls.obj_types_to_obj_classes[obj_type] arg_names_to_obj_classes[arg_name] = obj_class required_attr_names = list(arg_names_to_obj_classes.keys()) attr_names = list(value_dict.keys()) if set(attr_names) != set(required_attr_names): missing_attr_names = list( set(required_attr_names) - set(attr_names)) extra_attr_names = list(set(attr_names) - set(required_attr_names)) raise utils.ValidationError( 'Missing attributes: %s, Extra attributes: %s' % (', '.join(missing_attr_names), ', '.join(extra_attr_names))) for arg_name in required_attr_names: arg_obj_class = arg_names_to_obj_classes[arg_name] arg_obj_class.normalize(value_dict[arg_name])
def rules_dict(self): """A dict of rule names to rule properties.""" if self._cached_rules_dict is not None: return self._cached_rules_dict rules_index_dict = json.loads( python_utils.get_package_file_contents( 'extensions', feconf.RULES_DESCRIPTIONS_EXTENSIONS_MODULE_PATH)) self._cached_rules_dict = rules_index_dict[self.id] return self._cached_rules_dict
def test_html_field_types_to_rule_specs_mapping_are_valid(self): """Test that the structure of the file html_field_types_to_rule_specs. json are valid. This test ensures that whenever any new type of interaction or rule type with HTML string is added, the file html_field_types_to_rule_specs.json should be updated accordingly. """ # The file having the information about the assembly of the html in the # rule specs. html_field_types_to_rule_specs_dict = json.loads( python_utils.get_package_file_contents( 'extensions', feconf.HTML_FIELD_TYPES_TO_RULE_SPECS_EXTENSIONS_MODULE_PATH)) # The file having the templates for the structure of the rule specs. # Contents of the file html_field_types_to_rule_specs.json will be # verified against this file. rule_descriptions_dict = json.loads( python_utils.get_package_file_contents( 'extensions', feconf.RULES_DESCRIPTIONS_EXTENSIONS_MODULE_PATH)) # In the following part, we generate the html_field_types_to_rule_specs # dict based on the values in the rule_descriptions.json file. generated_html_field_types_dict = (collections.defaultdict( lambda: collections.defaultdict(lambda: collections.defaultdict( lambda: collections.defaultdict(lambda: collections. defaultdict(set)))))) # Verify that each html_type dict has a format key, which must be # unique. After verification we delete the key for comparison with # the generated html_field_types_to_rule_specs dict. We compare # everything except the format, because the format can't be generated # from the rule_descriptions.json file. for html_type, html_type_dict in ( html_field_types_to_rule_specs_dict.items()): self.assertTrue('format' in html_type_dict) self.assertTrue(html_type_dict['format'] in feconf.ALLOWED_HTML_RULE_VARIABLE_FORMATS) del html_type_dict['format'] for interaction_id, interaction_rules in ( rule_descriptions_dict.items()): for rule_type, rule_description in interaction_rules.items(): description = rule_description['description'] # Extract the input variables and html_types from the rule # description. input_variables_with_html_type = (re.findall( r'{{([a-z])\|([^}]*)}', description)) input_variables = set() input_variables_to_html_type_mapping_dict = ( collections.defaultdict(set)) for value in input_variables_with_html_type: if 'Html' in value[1] and 'HtmlContentId' not in value[1]: input_variables_to_html_type_mapping_dict[ value[1]].add(value[0]) # We need to iterate through the html_types for each rule_type, # because only after visiting each rule_type the inner dict # structure for each html_type gets generated. for html_type, input_variables in ( input_variables_to_html_type_mapping_dict.items()): html_type_dict = ( generated_html_field_types_dict[html_type]) # TODO(#9588): This generation (and the format of the # html_field_types_dict) assumes that there is at most one # interaction ID that uses a given HTML object type. If this # changes in the future, the structure of the dict needs to # be amended so that the each HTML object type can # accommodate more than one interaction. Corresponding # checks in state_domain.AnswerGroup # get_all_html_content_strings() also needs to be updated. if isinstance(html_type_dict['interactionId'], str): # The above type check is required because, # all the keys in the generated html type # dict is initialized as defaultdict object. # Below, we raise an exception if the existing # interaction ID is overwritten by another # interaction ID. if (html_type_dict['interactionId'] != interaction_id): raise Exception( 'Each html type should refer to only' ' one interaction_id.') html_type_dict['interactionId'] = interaction_id html_type_dict['ruleTypes'][rule_type][ 'htmlInputVariables'] = sorted(input_variables) self.assertEqual(html_field_types_to_rule_specs_dict, dict(generated_html_field_types_dict))
Returns: str. Text with all its comments removed. """ return re.sub(r' //.*\n', r'', text) class Constants(dict): # type: ignore[type-arg] """Transforms dict to object, attributes can be accessed by dot notation.""" # Here `value` has the type Any because it parses and stores the values of # contants defined in constants.ts file and we cannot define a single type # which works for all of them. def __setattr__(self, name: str, value: Any) -> None: self[name] = value # The return value here refers to the `value` in the above method, hence the # type Any is used for it. def __getattr__(self, name: str) -> Any: return self[name] constants = Constants( parse_json_from_ts( # pylint:disable=invalid-name python_utils.get_package_file_contents('assets', 'constants.ts'))) release_constants = Constants( # pylint:disable=invalid-name json.loads( python_utils.get_package_file_contents('assets', 'release_constants.json')))