def test_write_parameter(self): model = Model(id='model', version='0.0.1', wc_lang_version='0.0.1') submodel = model.submodels.create(id='submodel') species_type = model.species_types.create(id='st', structure=ChemicalStructure( molecular_weight=1., charge=0)) compartment = model.compartments.create(id='c') species = model.species.create(species_type=species_type, compartment=compartment) species.id = species.gen_id() reaction = submodel.reactions.create(id='reaction', model=model) reaction.participants.create(species=species, coefficient=1.) rate_law = reaction.rate_laws.create( model=model, direction=RateLawDirection.forward) rate_law.id = rate_law.gen_id() rate_law_eq = rate_law.expression = RateLawExpression( expression='parameter') parameter = rate_law_eq.parameters.create( id='parameter', value=1., units=unit_registry.parse_units('dimensionless'), model=model) filename = os.path.join(self.tempdir, 'model.xlsx') Writer().run(filename, model, data_repo_metadata=False) parameter.model = Model(id='model2', version='0.0.1', wc_lang_version='0.0.1') with self.assertRaisesRegex(ValueError, 'must be set to the instance of `Model`'): Writer().run(filename, model, data_repo_metadata=False)
def test_merge_models(self): in_paths = [ path.join(self.tempdir, 'in-0.xlsx'), path.join(self.tempdir, 'in-1.xlsx'), ] out_path = path.join(self.tempdir, 'out.xlsx') # write models model_0 = Model(id='model', name='test model', version='0.0.1a', wc_lang_version='0.0.1') model_0.species_types.create(id='a') model_1 = Model(id='model', name='test model', version='0.0.1a', wc_lang_version='0.0.1') model_1.species_types.create(id='b') Writer().run(in_paths[0], model_0, data_repo_metadata=False) Writer().run(in_paths[1], model_1, data_repo_metadata=False) # merge models with __main__.App(argv=[ 'merge-models', '-p', in_paths[0], '-s', in_paths[1], '-o', out_path ]) as app: app.run() # read merged model merged_model = Reader().run(out_path)[Model][0] # verify merged model self.assertEqual(len(merged_model.species_types), 2) self.assertNotEqual(merged_model.species_types.get_one(id='a'), None) self.assertNotEqual(merged_model.species_types.get_one(id='b'), None)
def test_difference(self): now = datetime.datetime.now().replace(microsecond=0) model1 = Model(id='model', name='test model', version='0.0.1a', wc_lang_version='0.0.0', created=now, updated=now) filename1 = path.join(self.tempdir, 'model1.xlsx') Writer().run(filename1, model1, data_repo_metadata=False) model2 = Model(id='model', name='test model', version='0.0.1a', wc_lang_version='0.0.0', created=now, updated=now) filename2 = path.join(self.tempdir, 'model2.xlsx') Writer().run(filename2, model2, data_repo_metadata=False) model3 = Model(id='model', name='test model', version='0.0.1a', wc_lang_version='0.0.1', created=now, updated=now) filename3 = path.join(self.tempdir, 'model3.xlsx') Writer().run(filename3, model3, data_repo_metadata=False) with CaptureOutput(relay=False) as capturer: with __main__.App( argv=['difference', filename1, filename2]) as app: app.run() self.assertEqual(capturer.get_text(), 'Models are identical') with CaptureOutput(relay=False) as capturer: with __main__.App(argv=[ 'difference', filename1, filename2, '--compare-files' ]) as app: app.run() self.assertEqual(capturer.get_text(), 'Models are identical') with CaptureOutput(relay=False) as capturer: with __main__.App( argv=['difference', filename1, filename3]) as app: app.run() diff = ( 'Objects (Model: "model", Model: "model") have different attribute values:\n ' '`wc_lang_version` are not equal:\n 0.0.0 != 0.0.1') self.assertEqual(capturer.get_text(), diff) with CaptureOutput(relay=False) as capturer: with __main__.App(argv=[ 'difference', filename1, filename3, '--compare-files' ]) as app: app.run() diff = 'Sheet !!Model:\n Row 7:\n Cell B: 0.0.0 != 0.0.1' self.assertEqual(capturer.get_text(), diff)
def _default(self): args = self.app.pargs model = Reader().run(args.source)[Model][0] if args.dest: Writer().run(args.dest, model, data_repo_metadata=False, protected=(not args.unprotected)) else: Writer().run(args.source, model, data_repo_metadata=False, protected=(not args.unprotected))
def test_convert_sloppy(self): filename_xls1 = os.path.join(self.tempdir, 'model1.xlsx') filename_xls2 = os.path.join(self.tempdir, 'model2.xlsx') filename_csv = os.path.join(self.tempdir, 'model-*.csv') Writer().run(filename_xls1, self.model, data_repo_metadata=False) wb = read_workbook(filename_xls1) row = wb['!!Model'].pop(3) wb['!!Model'].insert(4, row) write_workbook(filename_xls1, wb) with self.assertRaisesRegex(ValueError, "The rows of worksheet '!!Model' must be defined in this order"): convert(filename_xls1, filename_csv) env = EnvironmentVarGuard() env.set('CONFIG__DOT__wc_lang__DOT__io__DOT__strict', '0') with env: convert(filename_xls1, filename_csv) self.assertTrue(os.path.isfile(os.path.join(self.tempdir, 'model-Model.csv'))) self.assertTrue(os.path.isfile(os.path.join(self.tempdir, 'model-Taxon.csv'))) model = Reader().run(filename_csv)[Model][0] self.assertTrue(model.is_equal(self.model)) convert(filename_csv, filename_xls2) model = Reader().run(filename_xls2)[Model][0] self.assertTrue(model.is_equal(self.model))
def test_write_with_optional_args(self): # write model to file, passing models filename = os.path.join(self.tempdir, 'model.xlsx') Writer().run(filename, self.model, models=Writer.MODELS) # read model and verify that it validates model = Reader().run(filename)[Model][0] self.assertEqual(model.validate(), None) # write model to file, passing validate filename = os.path.join(self.tempdir, 'model.xlsx') Writer().run(filename, self.model, validate=True) # read model and verify that it validates model = Reader().run(filename)[Model][0] self.assertEqual(model.validate(), None)
def test_normalize(self): filename_xls_1 = path.join(self.tempdir, 'model-1.xlsx') filename_xls_2 = path.join(self.tempdir, 'model-2.xlsx') model = Model(id='model', name='test model', version='0.0.1a', wc_lang_version='0.0.0') Writer().run(filename_xls_1, model, data_repo_metadata=False) # with same destination with __main__.App(argv=['normalize', filename_xls_1]) as app: app.run() model2 = Reader().run(filename_xls_1)[Model][0] self.assertTrue(model2.is_equal(model)) # with different destination with __main__.App( argv=['normalize', filename_xls_1, '--dest', filename_xls_2 ]) as app: app.run() model2 = Reader().run(filename_xls_2)[Model][0] self.assertTrue(model2.is_equal(model))
def test_read_write(self): fixture_filename = os.path.join(os.path.dirname(__file__), 'fixtures', 'example-model.xlsx') model = Reader().run(fixture_filename)[Model][0] self.assertEqual(model.validate(), None) # compare excel files Writer().run(self.filename, model, data_repo_metadata=False) original = read_workbook(fixture_filename) copy = read_workbook(self.filename) remove_ws_metadata(original) remove_ws_metadata(copy) original.pop('!!' + obj_tables.core.TOC_SHEET_NAME) copy.pop('!!' + obj_tables.core.TOC_SHEET_NAME) # note that models must be sorted by id for this assertion to hold for sheet in original.keys(): for i_row, (copy_row, original_row) in enumerate(zip(copy[sheet], original[sheet])): self.assertEqual(copy_row, original_row, msg='Rows {} of {} sheets are not equal'.format(i_row, sheet)) self.assertEqual(copy[sheet], original[sheet], msg='{} sheets are not equal'.format(sheet)) self.assertEqual(copy, original) # compare models model2 = Reader().run(self.filename)[Model][0] self.assertTrue(model2.is_equal(model)) self.assertTrue(model.difference(model2) == '')
def _default(self): args = self.app.pargs model = wc_lang.sbml.io.SbmlReader().run(args.in_dir) Writer().run(args.out_path, model, data_repo_metadata=False, protected=(not args.unprotected))
def test_read_without_validation(self): # write model to file filename = os.path.join(self.tempdir, 'model.xlsx') Writer().run(filename, self.model, data_repo_metadata=False) # read model and verify that it validates model = Reader().run(filename)[Model][0] self.assertEqual(model.validate(), None) # introduce error into model file wb = read_workbook(filename) wb['!!Model'][4][1] = '1000' write_workbook(filename, wb) # read model and verify that it doesn't validate with self.assertRaisesRegex( ValueError, 'The model cannot be loaded because it fails to validate'): Reader().run(filename) env = EnvironmentVarGuard() env.set('CONFIG__DOT__wc_lang__DOT__io__DOT__validate', '0') with env: model = Reader().run(filename)[Model][0] self.assertNotEqual(model.validate(), None)
def _default(self): args = self.app.pargs model = Reader().run(args.path)[Model][0] model.wc_lang_version = wc_lang.__version__ Writer().run(args.path, model, data_repo_metadata=args.data_repo_metadata, protected=(not args.unprotected))
def setUp(self): self.model = model = Model(id='test', version='0.1') c = model.compartments.create(id='comp') c.init_density = model.parameters.create(id='density_compartment_1', value=1100, units=unit_registry.parse_units('g l^-1')) t0 = model.species_types.create(id='s0', type=onto['WC:metabolite']) t1 = model.species_types.create(id='s1', type=onto['WC:metabolite']) t2 = model.species_types.create(id='s2', type=onto['WC:metabolite']) s0 = model.species.create(id='s0[comp]', species_type=t0, compartment=c) s1 = model.species.create(id='s1[comp]', species_type=t1, compartment=c) s2 = model.species.create(id='s2[comp]', species_type=t2, compartment=c) self.submodel = submodel = model.submodels.create(id='submodel', framework=onto['WC:stochastic_simulation_algorithm']) self.r0 = r0 = model.reactions.create(id='r0', reversible=True, submodel=submodel) r0.participants.create(species=s0, coefficient=-2) r0.participants.create(species=s1, coefficient=3) r0_f = r0.rate_laws.create(id='r0-forward', direction=RateLawDirection.forward, model=model) a = model.parameters.create(id='a', value=1., units=unit_registry.parse_units('s^-1')) r0_f.expression, error = RateLawExpression.deserialize('a', {Parameter: {'a': a}}) assert error is None, str(error) r0_b = r0.rate_laws.create(id='r0-backward', direction=RateLawDirection.backward, model=model) b = model.parameters.create(id='b', value=1., units=unit_registry.parse_units('s^-1')) r0_b.expression, error = RateLawExpression.deserialize('b', {Parameter: {'b': b}}) assert error is None, str(error) r0.references.create(id='ref_0', model=model) r0.identifiers.create(namespace='x', id='y') self.r1 = r1 = model.reactions.create(id='r1', reversible=False, submodel=submodel) r1.participants.create(species=s1, coefficient=-3) r1.participants.create(species=s2, coefficient=4) r1_f = r1.rate_laws.create(id='r1-forward', direction=RateLawDirection.forward, model=model) c = model.parameters.create(id='c', value=1., units=unit_registry.parse_units('s^-1')) r1_f.expression, error = RateLawExpression.deserialize('c', {Parameter: {'c': c}}) assert error is None, str(error) r1.references.create(id='ref_1', model=model) r1.identifiers.create(namespace='xx', id='yy') self.tempdir = tempfile.mkdtemp() # check model's integrity by writing and reading with validate=True filename = os.path.join(self.tempdir, 'model_for_tranformation.xlsx') Writer().run(filename, model, data_repo_metadata=False) # turn off validate_element_charge_balance validation so that simple species and reactions validate with EnvironUtils.temp_config_env([(['wc_lang', 'validation', 'validate_element_charge_balance'], 'False')]): model_read = Reader().run(filename, validate=True)[Model][0] self.assertTrue(model_read.is_equal(model))
def test_write_read(self): filename = os.path.join(self.tempdir, 'model.xlsx') Writer().run(filename, self.model, data_repo_metadata=False) model = Reader().run(filename)[Model][0] self.assertEqual(model.validate(), None) self.assertTrue(model.is_equal(self.model)) self.assertEqual(self.model.difference(model), '')
def test_write_other(self): model = Model(id='model', version='0.0.1', wc_lang_version='0.0.1') species_type = model.species_types.create(id='species_type') compartment = model.compartments.create(id='compartment') species = Species( model=model, species_type=species_type, compartment=compartment) species.id = species.gen_id() s_id = species.serialize() obs_expr, _ = ObservableExpression.deserialize(s_id, {Species: {s_id: species}}) observable = model.observables.create(id='observable', expression=obs_expr) filename = os.path.join(self.tempdir, 'model.xlsx') Writer().run(filename, model, data_repo_metadata=False) model2 = Model(id='model2', version='0.0.1', wc_lang_version='0.0.1') observable.model = model2 with self.assertRaisesRegex(ValueError, 'must be set to the instance of `Model`'): Writer().run(filename, model, data_repo_metadata=False)
def test(self): test_model = Model(id='model', version='0.0.1', wc_lang_version='0.0.1') submodel = test_model.submodels.create(id='submodel') filename_yaml = os.path.join(self.tempdir, 'model.yaml') Writer().run(filename_yaml, test_model) model = Reader().run(filename_yaml)[Model][0] self.assertEqual(model.validate(), None) self.assertTrue(model.is_equal(test_model)) self.assertEqual(test_model.difference(model), '')
def _default(self): args = self.app.pargs primary_model = Reader().run(args.primary_path)[Model][0] for secondary_path in args.secondary_paths: secondary_model = Reader().run(secondary_path)[Model][0] primary_model.merge(secondary_model) Writer().run(args.out_path, primary_model, data_repo_metadata=False, protected=(not args.unprotected))
def test_validate(self): model = Model(id='model', name='test model', version='0.0.1a', wc_lang_version='0.0.1') self.assertEqual(Validator().run(model, get_related=True), None) filename = path.join(self.tempdir, 'model.xlsx') Writer().run(filename, model, data_repo_metadata=False) with CaptureOutput(relay=False) as capturer: with __main__.App(argv=['validate', filename]) as app: app.run() self.assertEqual(capturer.get_text(), 'Model is valid')
def test_transform_exception(self): source = path.join(self.tempdir, 'source.xlsx') model = Model(id='model', name='test model', version='0.0.1a', wc_lang_version='0.0.0') Writer().run(source, model, data_repo_metadata=False) dest = path.join(self.tempdir, 'dest.xlsx') with self.assertRaisesRegex(SystemExit, 'Please select at least one transform'): with __main__.App(argv=['transform', source, dest]) as app: app.run()
def _default(self): args = self.app.pargs # read model model = Reader().run(args.in_file)[Model][0] # split submodels into separate models core, submodels = model.submodels.gen_models() # create output directory, if it doesn't exist if not os.path.isdir(args.out_dir): os.makedirs(args.out_dir) # save separated submodels to file Writer().run(os.path.join(args.out_dir, 'core.xlsx'), core, data_repo_metadata=False, protected=(not args.unprotected)) for submodel in submodels: Writer().run(os.path.join( args.out_dir, '{}.xlsx'.format(submodel.submodels[0].id)), submodel, data_repo_metadata=False, protected=(not args.unprotected))
def test_convert(self): filename_xls = path.join(self.tempdir, 'model.xlsx') filename_csv = path.join(self.tempdir, 'model-*.csv') model = Model(id='model', name='test model', version='0.0.1a', wc_lang_version='0.0.0') Writer().run(filename_xls, model, data_repo_metadata=False) with __main__.App(argv=['convert', filename_xls, filename_csv]) as app: app.run() self.assertTrue(path.isfile(path.join(self.tempdir, 'model-Model.csv')))
def test_transform(self): source = path.join(self.tempdir, 'source.xlsx') model = Model(id='model', name='test model', version='0.0.1a', wc_lang_version='0.0.0') Writer().run(source, model, data_repo_metadata=False) dest = path.join(self.tempdir, 'dest.xlsx') with __main__.App(argv=[ 'transform', source, dest, '--transform', 'MergeAlgorithmicallyLikeSubmodels' ]) as app: app.run() self.assertTrue(path.isfile(dest))
def test_convert(self): filename_xls1 = os.path.join(self.tempdir, 'model1.xlsx') filename_xls2 = os.path.join(self.tempdir, 'model2.xlsx') filename_csv = os.path.join(self.tempdir, 'model-*.csv') Writer().run(filename_xls1, self.model, data_repo_metadata=False) convert(filename_xls1, filename_csv) self.assertTrue(os.path.isfile(os.path.join(self.tempdir, 'model-Model.csv'))) self.assertTrue(os.path.isfile(os.path.join(self.tempdir, 'model-Taxon.csv'))) model = Reader().run(filename_csv)[Model][0] self.assertTrue(model.is_equal(self.model)) convert(filename_csv, filename_xls2) model = Reader().run(filename_xls2)[Model][0] self.assertTrue(model.is_equal(self.model))
def test_write_with_repo_metadata(self): # create temp git repo & write file into it test_repo_name = 'test_wc_lang_test_io' test_github_repo = GitHubRepoForTests(test_repo_name) repo = test_github_repo.make_test_repo(self.tempdir) # write data repo metadata in data_file data_file = os.path.join(self.tempdir, 'test.xlsx') Writer().run(data_file, self.model, data_repo_metadata=True) # deliberately read metadata objs_read = Reader().run(data_file, [utils.DataRepoMetadata] + list(Writer.MODELS)) data_repo_metadata = objs_read[utils.DataRepoMetadata][0] self.assertTrue(data_repo_metadata.url.startswith('https://github.com/')) self.assertEqual(data_repo_metadata.branch, 'master') self.assertEqual(len(data_repo_metadata.revision), 40) test_github_repo.delete_test_repo()
def test_update_version_metadata(self): filename = path.join(self.tempdir, 'model.xlsx') model = Model(id='model', name='test model', version='0.0.1a', wc_lang_version='0.0.0') self.assertNotEqual(model.wc_lang_version, wc_lang.__version__) Writer().run(filename, model, data_repo_metadata=False) with __main__.App(argv=[ 'update-version-metadata', filename, '--ignore-repo-metadata' ]) as app: app.run() model = Reader().run(filename)[Model][0] self.assertEqual(model.wc_lang_version, wc_lang.__version__)
def test_validate_exception(self): model = Model(id='model', name='test model', version='0.0.1a', wc_lang_version='0.0.1') model.parameters.append( Parameter(id='param_1', value=1., units=unit_registry.parse_units('dimensionless'))) model.parameters.append( Parameter(id='param_1', value=1., units=unit_registry.parse_units('dimensionless'))) self.assertNotEqual(Validator().run(model, get_related=True), None) filename = path.join(self.tempdir, 'model.xlsx') Writer().run(filename, model, data_repo_metadata=False) with self.assertRaisesRegex(SystemExit, '^Model is invalid: '): with __main__.App(argv=['validate', filename]) as app: app.run()
def _default(self): args = self.app.pargs if not args.transforms: raise SystemExit('Please select at least one transform') # read model model = Reader().run(args.source)[Model][0] # apply transforms transforms = transform.get_transforms() for id in args.transforms: cls = transforms[id] instance = cls() instance.run(model) # write model Writer().run(args.dest, model, data_repo_metadata=False, protected=(not args.unprotected))
def test_write_read_sloppy(self): filename = os.path.join(self.tempdir, 'model.xlsx') Writer().run(filename, self.model, data_repo_metadata=False) wb = read_workbook(filename) row = wb['!!Model'].pop(3) wb['!!Model'].insert(4, row) write_workbook(filename, wb) with self.assertRaisesRegex(ValueError, "The rows of worksheet '!!Model' must be defined in this order"): Reader().run(filename) env = EnvironmentVarGuard() env.set('CONFIG__DOT__wc_lang__DOT__io__DOT__strict', '0') with env: model = Reader().run(filename)[Model][0] self.assertEqual(model.validate(), None) self.assertTrue(model.is_equal(self.model)) self.assertEqual(self.model.difference(model), '')
def do_test_dfba_obj_expr(self, obj_expression, reversible_rxns, expected_obj_expr, dfba_obj_reaction_id='unused'): model, submodel, dfba_rxn = self.prep_dfba_obj_test(obj_expression, reversible_rxns, expected_obj_expr, dfba_obj_reaction_id=dfba_obj_reaction_id) SplitReversibleReactionsTransform().run(model) self.assertEqual(len(model.dfba_objs), 1) self.assertEqual(model.dfba_objs[0], submodel.dfba_obj) # make DfbaObjectiveExpression & DfbaObjective from expected_obj_expr, and compare all_reactions = {Reaction: {rxn.id: rxn for rxn in model.reactions}, DfbaObjReaction: {dfba_obj_reaction_id: dfba_rxn}} expected_dfba_obj_expr, error = DfbaObjectiveExpression.deserialize(expected_obj_expr, all_reactions) self.assertEqual(error, None) transformed_dfba_obj = submodel.dfba_obj submodel.dfba_obj = None dfba_obj = DfbaObjective(id='dfba-obj-submodel', submodel=submodel, expression=expected_dfba_obj_expr) self.assertEqual(dfba_obj.validate(), None) # the list of ObjTablesTokens captures the semantics of a parsed expression self.assertEqual(dfba_obj.expression._parsed_expression._obj_tables_tokens, transformed_dfba_obj.expression._parsed_expression._obj_tables_tokens) # test io model, _, _ = self.prep_dfba_obj_test(obj_expression, reversible_rxns, expected_obj_expr, dfba_obj_reaction_id=dfba_obj_reaction_id) SplitReversibleReactionsTransform().run(model) filename = os.path.join(self.tempdir, 'split_rxn_model.xlsx') Writer().run(filename, model, data_repo_metadata=False) # turn off validate_element_charge_balance validation so that simple species and reactions validate with EnvironUtils.temp_config_env([(['wc_lang', 'validation', 'validate_element_charge_balance'], 'False')]): model_read = Reader().run(filename, validate=True)[Model][0] self.assertTrue(model_read.is_equal(model))
def test_convert_to_sbml(self): from wc_lang.sbml import io as sbml_io from wc_lang.io import Reader, Writer from wc_sim.testing.utils import read_model_for_test for model_name in self.models: print(f'converting {model_name}') dirname = os.path.join(os.path.dirname(__file__), 'fixtures', 'dynamic_tests') model_filename = os.path.join(dirname, f'{model_name}.xlsx') integration_framework = 'ordinary_differential_equations' model = read_model_for_test(model_filename, integration_framework=f'WC:{integration_framework}') # write wc_lang file sbml_dirname = os.path.join(dirname, f'{model_name}_sbml') if not os.path.isdir(sbml_dirname): os.makedirs(sbml_dirname) lang_filename = os.path.join(sbml_dirname, f'{model_name}.xlsx') print(f'writing {lang_filename}') Writer().run(lang_filename, model) model_from_file = Reader().run(lang_filename)[Model][0] assert model_from_file.validate() is None # write SBML file sbml_io.SbmlWriter().run(model_from_file, sbml_dirname)
def test_cut_submodels(self): timestamp = datetime.datetime(2018, 1, 1, 12, 0, 0) model = Model(id='model', name='test model', version='0.0.1a', wc_lang_version='0.0.1', created=timestamp, updated=timestamp) model.submodels.create(id='submodel_1') model.submodels.create(id='submodel_2') model.references.create(id='ref_0') model.references.create(id='ref_1', submodels=model.submodels[0:1]) model.references.create(id='ref_2', submodels=model.submodels[1:2]) model.references.create(id='ref_3', submodels=model.submodels[0:2]) in_path = path.join(self.tempdir, 'in.xlsx') Writer().run(in_path, model, data_repo_metadata=False) out_path = path.join(self.tempdir, 'out') with __main__.App(argv=['cut-submodels', in_path, out_path]) as app: app.run() model_0 = Reader().run(path.join(out_path, 'core.xlsx'))[Model][0] model_1 = Reader().run(path.join(out_path, 'submodel_1.xlsx'))[Model][0] model_2 = Reader().run(path.join(out_path, 'submodel_2.xlsx'))[Model][0] exp_model_0 = Model(id='model', name='test model', version='0.0.1a', wc_lang_version='0.0.1', created=timestamp, updated=timestamp) exp_model_0.references.create(id='ref_0') self.assertTrue(model_0.is_equal(exp_model_0)) exp_model_1 = Model(id='model', name='test model', version='0.0.1a', wc_lang_version='0.0.1', created=timestamp, updated=timestamp) exp_model_1.submodels.create(id='submodel_1') exp_model_1.references.create(id='ref_1', submodels=exp_model_1.submodels) exp_model_1.references.create(id='ref_3', submodels=exp_model_1.submodels) self.assertTrue(model_1.is_equal(exp_model_1)) exp_model_2 = Model(id='model', name='test model', version='0.0.1a', wc_lang_version='0.0.1', created=timestamp, updated=timestamp) exp_model_2.submodels.create(id='submodel_2') exp_model_2.references.create(id='ref_2', submodels=exp_model_2.submodels) exp_model_2.references.create(id='ref_3', submodels=exp_model_2.submodels) self.assertTrue(model_2.is_equal(exp_model_2))